diff --git a/vocata-web/src/assets/logo.svg b/vocata-web/src/assets/logo.svg new file mode 100644 index 0000000..d1dcdb7 --- /dev/null +++ b/vocata-web/src/assets/logo.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/vocata-web/src/assets/styles/theme.css b/vocata-web/src/assets/styles/theme.css index 008d387..92fc681 100644 --- a/vocata-web/src/assets/styles/theme.css +++ b/vocata-web/src/assets/styles/theme.css @@ -1,27 +1,84 @@ :root { color-scheme: light; - --vt-bg: oklch(98% 0.01 190); + + /* Backgrounds */ + --vt-bg: oklch(98% 0.005 240); --vt-surface: oklch(100% 0 0); - --vt-surface-muted: oklch(97% 0.01 190); - --vt-line: oklch(90% 0.02 190); - --vt-text: oklch(28% 0.03 210); - --vt-text-soft: oklch(48% 0.03 210); - --vt-brand: oklch(74% 0.09 183); - --vt-brand-strong: oklch(64% 0.11 183); - --vt-accent: oklch(74% 0.14 32); - --vt-radius-xl: 28px; - --vt-radius-lg: 20px; - --vt-shadow: 0 24px 60px color-mix(in srgb, var(--vt-brand) 12%, transparent); + --vt-surface-raised: oklch(99% 0.005 240); + --vt-surface-overlay: oklch(96% 0.008 240); + + /* Borders */ + --vt-line: oklch(91% 0.01 240); + --vt-line-subtle: oklch(95% 0.008 240); + + /* Text */ + --vt-text: oklch(15% 0.02 240); + --vt-text-soft: oklch(45% 0.02 240); + --vt-text-muted: oklch(65% 0.015 240); + + /* Brand (紫蓝,类 GPT) */ + --vt-brand: oklch(55% 0.18 270); + --vt-brand-soft: oklch(92% 0.05 270); + --vt-brand-strong: oklch(45% 0.20 270); + + /* Accent & Semantic */ + --vt-accent: oklch(68% 0.16 32); + --vt-danger: oklch(60% 0.20 25); + --vt-success: oklch(60% 0.16 145); + + /* Radius */ + --vt-radius-sm: 8px; + --vt-radius-md: 12px; + --vt-radius-lg: 16px; + --vt-radius-xl: 24px; + + /* Shadows */ + --vt-shadow-sm: 0 1px 3px oklch(0% 0 0 / 0.08); + --vt-shadow-md: 0 4px 16px oklch(0% 0 0 / 0.10); + --vt-shadow-lg: 0 12px 40px oklch(0% 0 0 / 0.12); + + /* Legacy aliases (backward compat) */ + --vt-surface-muted: var(--vt-surface-overlay); + --vt-brand-strong-legacy: var(--vt-brand-strong); + --vt-shadow: var(--vt-shadow-lg); + --vt-radius: var(--vt-radius-xl); + + /* Typography */ --vt-font-body: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans CJK SC', sans-serif; + font-synthesis: none; text-rendering: optimizeLegibility; } +[data-theme="dark"] { + color-scheme: dark; + + --vt-bg: oklch(12% 0.01 240); + --vt-surface: oklch(16% 0.01 240); + --vt-surface-raised: oklch(20% 0.01 240); + --vt-surface-overlay: oklch(22% 0.015 240); + + --vt-line: oklch(28% 0.01 240); + --vt-line-subtle: oklch(24% 0.01 240); + + --vt-text: oklch(95% 0.005 240); + --vt-text-soft: oklch(70% 0.01 240); + --vt-text-muted: oklch(50% 0.01 240); + + --vt-brand: oklch(65% 0.18 270); + --vt-brand-soft: oklch(25% 0.08 270); + --vt-brand-strong: oklch(75% 0.20 270); + + --vt-shadow-sm: 0 1px 3px oklch(0% 0 0 / 0.3); + --vt-shadow-md: 0 4px 16px oklch(0% 0 0 / 0.4); + --vt-shadow-lg: 0 12px 40px oklch(0% 0 0 / 0.5); +} + * { box-sizing: border-box; } diff --git a/vocata-web/src/components/chat/ChatComposer.vue b/vocata-web/src/components/chat/ChatComposer.vue index 7c569ca..9cf994c 100644 --- a/vocata-web/src/components/chat/ChatComposer.vue +++ b/vocata-web/src/components/chat/ChatComposer.vue @@ -5,10 +5,10 @@ type="button" class="chat-composer__tool" data-test="composer-mic" - :title="recording ? '挂断通话' : '录音输入'" + :title="recording ? '挂断通话' : '开始语音对话'" @click="$emit('toggleCall')" > - 🎙 +
@@ -16,20 +16,25 @@ :value="modelValue" :placeholder="connected ? '输入消息或开始语音陪聊…' : '连接中,请稍等…'" :disabled="!connected" + rows="1" @input="$emit('update:modelValue', ($event.target as HTMLTextAreaElement).value)" @keydown.enter.prevent="$emit('send')" />
+
@@ -55,19 +60,16 @@ const hasText = computed(() => props.modelValue.trim().length > 0) const handlePrimaryAction = () => { if (!props.connected) return - if (hasText.value) { emit('send') return } - emit('toggleCall') } diff --git a/vocata-web/src/components/chat/ChatMessageList.vue b/vocata-web/src/components/chat/ChatMessageList.vue index cba1786..2e8a103 100644 --- a/vocata-web/src/components/chat/ChatMessageList.vue +++ b/vocata-web/src/components/chat/ChatMessageList.vue @@ -6,27 +6,25 @@ class="chat-message-list__item" :class="item.type === 'send' ? 'is-user' : 'is-ai'" > -
- - - {{ item.type === 'send' ? userInitial : characterInitial }} +
+ + {{ characterInitial }}
-
+

{{ item.content }}

+ {{ formatTime(item.createDate) }}
-
+
- + {{ characterInitial }}
-
+
@@ -35,6 +33,7 @@ diff --git a/vocata-web/src/composables/useTheme.ts b/vocata-web/src/composables/useTheme.ts new file mode 100644 index 0000000..cd800d8 --- /dev/null +++ b/vocata-web/src/composables/useTheme.ts @@ -0,0 +1,24 @@ +import { ref } from 'vue' + +const isDark = ref(false) + +function init() { + const saved = localStorage.getItem('vt-theme') + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches + isDark.value = saved ? saved === 'dark' : prefersDark + apply() +} + +function apply() { + document.documentElement.setAttribute('data-theme', isDark.value ? 'dark' : 'light') +} + +export function useTheme() { + const toggle = () => { + isDark.value = !isDark.value + apply() + localStorage.setItem('vt-theme', isDark.value ? 'dark' : 'light') + } + + return { isDark, toggle, init } +} diff --git a/vocata-web/src/layouts/BasicLayout.vue b/vocata-web/src/layouts/BasicLayout.vue index 01533ec..5c7d3e1 100644 --- a/vocata-web/src/layouts/BasicLayout.vue +++ b/vocata-web/src/layouts/BasicLayout.vue @@ -1,13 +1,26 @@