Files
Hermes-ui/src/views/SettingsView.vue
T
ekko 9eaaa4270d fix: resolve streaming messages splitting into individual bubbles
Simplify addMessage/updateMessage to only write to messages.value,
add syncMessagesToSession() to copy messages back on session switch
and stream completion. Also fix mobile viewport, session list overlay,
hamburger logo, and various responsive improvements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 10:28:53 +08:00

116 lines
4.2 KiB
Vue

<script setup lang="ts">
import { onMounted } from 'vue'
import { NTabs, NTabPane, NSpin, NSwitch, NInput, NInputNumber, useMessage } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { useSettingsStore } from '@/stores/settings'
import DisplaySettings from '@/components/settings/DisplaySettings.vue'
import AgentSettings from '@/components/settings/AgentSettings.vue'
import MemorySettings from '@/components/settings/MemorySettings.vue'
import SessionSettings from '@/components/settings/SessionSettings.vue'
import PrivacySettings from '@/components/settings/PrivacySettings.vue'
import SettingRow from '@/components/settings/SettingRow.vue'
const settingsStore = useSettingsStore()
const message = useMessage()
const { t } = useI18n()
onMounted(() => {
settingsStore.fetchSettings()
})
async function saveApiServer(values: Record<string, any>) {
try {
await settingsStore.saveSection('platforms', { api_server: values })
message.success(t('settings.saved'))
} catch (err: any) {
message.error(t('settings.saveFailed'))
}
}
</script>
<template>
<div class="settings-view">
<header class="page-header">
<h2 class="header-title">{{ t('settings.title') }}</h2>
</header>
<div class="settings-content">
<NSpin :show="settingsStore.loading">
<NTabs type="line" animated>
<NTabPane name="display" :tab="t('settings.tabs.display')">
<DisplaySettings />
</NTabPane>
<NTabPane name="agent" :tab="t('settings.tabs.agent')">
<AgentSettings />
</NTabPane>
<NTabPane name="memory" :tab="t('settings.tabs.memory')">
<MemorySettings />
</NTabPane>
<NTabPane name="session" :tab="t('settings.tabs.session')">
<SessionSettings />
</NTabPane>
<NTabPane name="privacy" :tab="t('settings.tabs.privacy')">
<PrivacySettings />
</NTabPane>
<NTabPane name="api_server" :tab="t('settings.tabs.apiServer')">
<section class="settings-section">
<SettingRow :label="t('settings.apiServer.enable')" :hint="t('settings.apiServer.enableHint')">
<NSwitch
:value="settingsStore.platforms?.api_server?.enabled"
@update:value="v => saveApiServer({ enabled: v })"
/>
</SettingRow>
<SettingRow :label="t('settings.apiServer.host')" :hint="t('settings.apiServer.hostHint')">
<NInput
:value="settingsStore.platforms?.api_server?.host || ''"
size="small" class="input-md"
@update:value="v => saveApiServer({ host: v })"
/>
</SettingRow>
<SettingRow :label="t('settings.apiServer.port')" :hint="t('settings.apiServer.portHint')">
<NInputNumber
:value="settingsStore.platforms?.api_server?.port"
:min="1024" :max="65535"
size="small" class="input-sm"
@update:value="v => v != null && saveApiServer({ port: v })"
/>
</SettingRow>
<SettingRow :label="t('settings.apiServer.key')" :hint="t('settings.apiServer.keyHint')">
<NInput
:value="settingsStore.platforms?.api_server?.key || ''"
type="password" show-password-on="click"
size="small" class="input-md"
@update:value="v => saveApiServer({ key: v })"
/>
</SettingRow>
<SettingRow :label="t('settings.apiServer.cors')" :hint="t('settings.apiServer.corsHint')">
<NInput
:value="settingsStore.platforms?.api_server?.cors_origins || ''"
size="small" class="input-md"
@update:value="v => saveApiServer({ cors_origins: v })"
/>
</SettingRow>
</section>
</NTabPane>
</NTabs>
</NSpin>
</div>
</div>
</template>
<style scoped lang="scss">
@use '@/styles/variables' as *;
.settings-view {
height: calc(100 * var(--vh));
display: flex;
flex-direction: column;
}
.settings-content {
flex: 1;
overflow-y: auto;
padding: 20px;
}
</style>