From 4d487729e87d397e0af183b764c5cf40a7cce5ce Mon Sep 17 00:00:00 2001 From: lofyer Date: Fri, 14 Aug 2026 00:41:56 +0800 Subject: [PATCH] fix: improve Chinese typography --- UI-DESIGN.md | 4 +- src/renderer/src/WorkspacePrimitives.test.tsx | 23 +++++- src/renderer/src/styles.css | 78 ++++++++++--------- 3 files changed, 65 insertions(+), 40 deletions(-) diff --git a/UI-DESIGN.md b/UI-DESIGN.md index 3059e00..845a05a 100644 --- a/UI-DESIGN.md +++ b/UI-DESIGN.md @@ -101,7 +101,7 @@ ### 3.3 字体令牌 -界面默认使用随客户端本地打包的 `Inter Variable` 与 `Noto Sans SC Variable`:英文、数字优先使用 Inter,简体中文由 Noto Sans SC 覆盖。系统无衬线字体仅作为启动和缺失字形回退;代码、标识符和原始日志使用等宽字体栈。字体不得通过运行时网络请求加载。 +界面英文和数字优先使用随客户端本地打包的 `Inter Variable`。简体中文优先使用平台原生 UI 字体:macOS 使用 `PingFang SC`,Windows 使用 `Microsoft YaHei UI`,以获得与系统渲染匹配的小字号 hinting;其他平台及缺失字形使用本地打包的 `Noto Sans SC Variable`。代码、标识符和原始日志使用等宽字体栈。字体不得通过运行时网络请求加载。 | 令牌 | 字号 / 行高 | 字重 | 用途 | | --- | --- | --- | --- | @@ -115,7 +115,7 @@ - 业务组件通过 `--font-family-ui` 与字体尺寸令牌继承字体,不创建页面专属字体栈。 - 表单按钮、输入框、选择框和文本域必须继承界面字体,避免回退为原生控件字体。 - 连续阅读内容使用 `14px`,持久辅助信息不得小于 `11px`。 -- 本地字体资源必须随生产包交付,并同时包含 Inter 与 Noto Sans SC 的 OFL 许可证。 +- 本地兜底字体资源必须随生产包交付,并同时包含 Inter 与 Noto Sans SC 的 OFL 许可证。 - 页面内不得通过同时放大字号、加粗和使用强调色制造多个同级主标题。 ### 3.4 圆角、阴影与层级 diff --git a/src/renderer/src/WorkspacePrimitives.test.tsx b/src/renderer/src/WorkspacePrimitives.test.tsx index 15f25ab..4f108ea 100644 --- a/src/renderer/src/WorkspacePrimitives.test.tsx +++ b/src/renderer/src/WorkspacePrimitives.test.tsx @@ -94,7 +94,28 @@ describe('WorkspacePrimitives', () => { expect(stylesheet).toMatch(/--font-caption:\s*11px/u) expect(stylesheet).toMatch(/font-synthesis:\s*style/u) expect(stylesheet).toContain( - '"Inter Variable", "Noto Sans SC Variable"' + '"Inter Variable", "Segoe UI Variable", "SF Pro Text", "PingFang SC"' + ) + expect(stylesheet).toContain( + '"Microsoft YaHei UI", "Noto Sans SC Variable"' + ) + expect(stylesheet).toMatch( + /\.nav-item span:nth-child\(2\)\s*\{[^}]*font-size:\s*var\(--font-body\);[^}]*font-weight:\s*500;/u + ) + expect(stylesheet).toMatch( + /\.section-label\s*\{[^}]*font-size:\s*var\(--font-caption\);[^}]*font-weight:\s*600;/u + ) + expect(stylesheet).toMatch( + /\.conversation-item span\s*\{[^}]*font-size:\s*var\(--font-body\);/u + ) + expect(stylesheet).toMatch( + /\.conversation-item small\s*\{[^}]*font-size:\s*var\(--font-caption\);/u + ) + expect(stylesheet).toMatch( + /\.markdown-content\s*\{[^}]*font-size:\s*14px;[^}]*line-height:\s*1\.65;/u + ) + expect(stylesheet).toMatch( + /\.composer__input textarea\s*\{[^}]*font-size:\s*14px;[^}]*line-height:\s*1\.6;/u ) }) diff --git a/src/renderer/src/styles.css b/src/renderer/src/styles.css index 1cf87e9..da51eb4 100644 --- a/src/renderer/src/styles.css +++ b/src/renderer/src/styles.css @@ -69,8 +69,8 @@ --font-body: 13px; --font-caption: 11px; --font-family-ui: - "Inter Variable", "Noto Sans SC Variable", "Segoe UI Variable", - "SF Pro Text", "PingFang SC", "Microsoft YaHei UI", "Noto Sans CJK SC", + "Inter Variable", "Segoe UI Variable", "SF Pro Text", "PingFang SC", + "Microsoft YaHei UI", "Noto Sans SC Variable", "Noto Sans CJK SC", sans-serif; --font-family-mono: "Cascadia Code", "SFMono-Regular", Consolas, monospace; @@ -1518,13 +1518,14 @@ textarea:focus-visible { .brand__copy strong { font-size: 15px; + font-weight: 600; letter-spacing: 0.01em; } .brand__copy span { color: var(--text-muted); - font-size: 10px; - letter-spacing: 0.08em; + font-size: var(--font-caption); + letter-spacing: 0.06em; text-transform: uppercase; } @@ -1549,7 +1550,7 @@ textarea:focus-visible { border-radius: var(--radius-control); background: var(--surface-raised); color: var(--text-primary); - font-size: 10px; + font-size: var(--font-body); } .project-create-backdrop { @@ -1721,7 +1722,7 @@ textarea:focus-visible { border-radius: 5px; background: rgb(255 255 255 / 12%); color: var(--text-on-accent); - font-size: 9px; + font-size: var(--font-caption); } .sidebar-search { @@ -1790,13 +1791,13 @@ textarea:focus-visible { .nav-item span:nth-child(2) { flex: 1; - font-size: 12px; - font-weight: 550; + font-size: var(--font-body); + font-weight: 500; } .nav-item__hint { color: var(--text-muted); - font-size: 9px; + font-size: var(--font-caption); } .nav-item__badge { @@ -1807,7 +1808,7 @@ textarea:focus-visible { border-radius: 999px; background: var(--danger-solid); color: var(--text-on-accent); - font-size: 8px; + font-size: 10px; font-weight: 700; place-items: center; } @@ -1827,9 +1828,9 @@ textarea:focus-visible { padding: 0 9px; margin: 0 0 7px; color: var(--text-muted); - font-size: 9px; - font-weight: 700; - letter-spacing: 0.1em; + font-size: var(--font-caption); + font-weight: 600; + letter-spacing: 0.06em; text-transform: uppercase; } @@ -1866,14 +1867,14 @@ textarea:focus-visible { .conversation-item span { flex: 1; overflow: hidden; - font-size: 12px; + font-size: var(--font-body); text-overflow: ellipsis; white-space: nowrap; } .conversation-item small { color: var(--text-muted); - font-size: 9px; + font-size: var(--font-caption); } .conversation-source-badge { @@ -1931,8 +1932,8 @@ textarea:focus-visible { border-radius: var(--radius-control); background: var(--accent-subtle); color: var(--accent); - font-size: 10px; - font-weight: 800; + font-size: var(--font-caption); + font-weight: 600; } .user-card__copy { @@ -1944,12 +1945,13 @@ textarea:focus-visible { .user-card__copy strong { color: var(--text-primary); - font-size: 11px; + font-size: var(--font-body); + font-weight: 600; } .user-card__copy small { color: var(--text-muted); - font-size: 9px; + font-size: var(--font-caption); } .workspace { @@ -2906,8 +2908,8 @@ button > svg { color: var(--text-primary); flex: 0 1 auto; gap: 6px; - font-size: 12px; - font-weight: 650; + font-size: var(--font-body); + font-weight: 600; } .conversation-title span { @@ -2936,8 +2938,8 @@ button > svg { margin-right: 8px; background: var(--surface-raised); color: var(--text-secondary); - font-size: 10px; - font-weight: 650; + font-size: var(--font-caption); + font-weight: 600; gap: 6px; } @@ -2998,9 +3000,9 @@ button > svg { .eyebrow { margin: 0 0 8px; color: var(--accent); - font-size: 9px; - font-weight: 800; - letter-spacing: 0.16em; + font-size: var(--font-caption); + font-weight: 700; + letter-spacing: 0.12em; } .welcome h1 { @@ -3014,7 +3016,7 @@ button > svg { .welcome__description { margin: 12px 0; color: var(--text-secondary); - font-size: 12px; + font-size: var(--font-body); } .quick-actions { @@ -3064,13 +3066,14 @@ button > svg { .quick-actions strong { color: var(--text-primary); - font-size: 11px; + font-size: var(--font-body); + font-weight: 600; } .quick-actions small { color: var(--text-muted); - font-size: 10px; - line-height: 1.45; + font-size: 12px; + line-height: 1.5; } .message-list { @@ -3250,18 +3253,19 @@ button > svg { .message__meta strong { color: var(--text-primary); - font-size: 11px; + font-size: 12px; + font-weight: 600; } .message__meta span { color: var(--text-muted); - font-size: 9px; + font-size: var(--font-caption); } .markdown-content { color: var(--text-primary); - font-size: 13px; - line-height: 1.75; + font-size: 14px; + line-height: 1.65; overflow-wrap: anywhere; } @@ -3467,7 +3471,7 @@ button > svg { .message-generated-image figcaption { margin-top: 5px; color: var(--text-muted); - font-size: 9px; + font-size: var(--font-caption); } .message-generated-image > .message-image-actions { @@ -3479,7 +3483,7 @@ button > svg { align-items: flex-start; margin-top: 8px; color: var(--text-muted); - font-size: 10px; + font-size: var(--font-caption); gap: 7px; overflow-wrap: anywhere; white-space: pre-wrap; @@ -4176,7 +4180,7 @@ button > svg { resize: none; background: transparent !important; color: var(--text-primary); - font-size: var(--font-body); + font-size: 14px; line-height: 1.6; }