diff --git a/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.i18n.yaml b/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.i18n.yaml index a540a7aa9c..f0999e1aa3 100644 --- a/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-18-settings-font-size-control.md -2026-08-18-settings-font-size-control.md: 9e8b933653359bd45abf34e45fec5497289435ec -2026-08-18-settings-font-size-control.zh.md: cff574bd5009db6d4d855384c10d6f9a034ea343 +2026-08-18-settings-font-size-control.md: 778893a2c0943f1d3af8905a27efb526071e073b +2026-08-18-settings-font-size-control.zh.md: 71ff3b556622e7091891f8e18f5eefcbda2b0851 diff --git a/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.md b/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.md index 9e8b933653..778893a2c0 100644 --- a/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.md +++ b/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.md @@ -24,7 +24,7 @@ The conversation's body text size was fixed (14px after the 0.875 markdown-ladde **Scaling via a multiplier (`em`/percentage) instead of a px delta.** Rejected: multiplying spreads the 12–17px range disproportionately across the ladder (21px h1 would swing ~18–25.5px) and produces fractional line heights; the fixed px shift keeps every step integer and the hierarchy's px gaps intact. -**Scaling every font token (small, code).** Rejected: those variants are dense by design; at −2 the small ladder would hit 10px and code 9px, below legibility. The table variants instead join the secondary tier, whose 13px floor keeps them legible at every setting. +**Scaling every font token (small, code).** Rejected: those variants are dense by design; at −2 the small ladder would hit 10px and code 9px, below legibility. The table variants instead join the secondary tier, bottoming out at 11px at the 12px setting — the same size think text reaches there. ## Consequences diff --git a/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.zh.md b/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.zh.md index cff574bd50..71ff3b5566 100644 --- a/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.zh.md +++ b/.agents/notes/implemented/feature/2026-08-18-settings-font-size-control.zh.md @@ -24,7 +24,7 @@ **用倍率(`em`/百分比)而非像素增量缩放。**否决:乘法会让 12–17px 的范围在阶梯上不成比例地放大(21px 的 h1 会摆动到约 18–25.5px),并产生小数行高;固定像素平移让每一档都是整数,层级间的像素差保持不变。 -**缩放全部字体 token(code、small)。**否决:这些变体按设计就是密集文本;−2 档时 small 阶梯会降到 10px、code 降到 9px,低于可读下限。表格变体改为并入低一档层级,其 13px 下限在任何设置下都保持可读。 +**缩放全部字体 token(code、small)。**否决:这些变体按设计就是密集文本;−2 档时 small 阶梯会降到 10px、code 降到 9px,低于可读下限。表格变体改为并入低一档层级,在 12px 设置下触底 11px——与 think 文本在该设置下相同。 ## 后果 diff --git a/apps/web/tests/settings-chrome.e2e.ts b/apps/web/tests/settings-chrome.e2e.ts index b36508aa5e..425ff5d1ee 100644 --- a/apps/web/tests/settings-chrome.e2e.ts +++ b/apps/web/tests/settings-chrome.e2e.ts @@ -351,7 +351,19 @@ describe('web e2e: settings modal and General preferences', () => { const readFontSize = async (target: Page = page): Promise => await target.evaluate( () => document.body.style.getPropertyValue('--dsh-content-font-size'), ) + // The secondary tier resolved by the real engine: a probe element's + // font-size forces min/max/calc evaluation, which the CSS-text specs + // cannot exercise. Setting −1 at ≤14, setting −2 above. + const readSecondaryFontSize = async (): Promise => await page.evaluate(() => { + const probe = document.createElement('div') + probe.style.fontSize = 'var(--dsh-content-font-size-secondary, 13px)' + document.body.appendChild(probe) + const size = getComputedStyle(probe).fontSize + probe.remove() + return size + }) expect(await readFontSize()).toBe('14px') + expect(await readSecondaryFontSize()).toBe('13px') await page.getByRole('button', { name: '设置', exact: true }).click() const dialog = page.getByRole('dialog', { name: '设置' }) await dialog.waitFor({ timeout: 10_000 }) @@ -360,9 +372,13 @@ describe('web e2e: settings modal and General preferences', () => { const increase = dialog.getByRole('button', { name: '增大字号' }) await increase.click() await dialog.getByText('15', { exact: true }).waitFor({ timeout: 5_000 }) + // 15 is the piecewise boundary: the secondary tier holds at 13px (−2) + // where the ≤14 branch would have given 14px (−1). + await expect.poll(readSecondaryFontSize, { timeout: 5_000 }).toBe('13px') await increase.click() await dialog.getByText('16', { exact: true }).waitFor({ timeout: 5_000 }) await expect.poll(readFontSize, { timeout: 5_000 }).toBe('16px') + await expect.poll(readSecondaryFontSize, { timeout: 5_000 }).toBe('14px') await expect.poll(async () => readFile(join(scaffold.harnessHome, 'settings.yaml'), 'utf8'), { timeout: 5_000 }) .toMatch(/ui-theme:\n(?:\s+\w+: .*\n)*?\s+fontSize: 16/) await page.keyboard.press('Escape') @@ -375,6 +391,7 @@ describe('web e2e: settings modal and General preferences', () => { acknowledgeReloadConnectionLoss(tripwire, warningStart) await page.waitForSelector('[class*="frame"]', { timeout: 30_000 }) await expect.poll(readFontSize, { timeout: 5_000 }).toBe('16px') + expect(await readSecondaryFontSize()).toBe('14px') // Restore the default for the specs that follow (and the dialog golden). await page.getByRole('button', { name: '设置', exact: true }).click() diff --git a/packages/client/ui-chat/src/client/chat/StatsLine.module.css b/packages/client/ui-chat/src/client/chat/StatsLine.module.css index 8d997d93ce..29c0bc3aab 100644 --- a/packages/client/ui-chat/src/client/chat/StatsLine.module.css +++ b/packages/client/ui-chat/src/client/chat/StatsLine.module.css @@ -1,5 +1,6 @@ -/* Session stats row: 12/20 tertiary text under the flow, aligned to the - shared message column axis (--dsh-chat-content-width). */ +/* Session stats row: 13/20 tertiary text (the secondary content tier) under + the flow, aligned to the shared message column axis + (--dsh-chat-content-width). */ .root { /* Block, not flex: text-overflow only elides a block's inline content, so diff --git a/packages/client/ui-theme/README.i18n.yaml b/packages/client/ui-theme/README.i18n.yaml index 8af8bc0e7c..376749a4f8 100644 --- a/packages/client/ui-theme/README.i18n.yaml +++ b/packages/client/ui-theme/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-theme/README.md -README.md: 49ea5147b12f382f37fa550455035f948dec4fe8 -README.zh.md: 427eb4be339f6ad414fc84b7824445edaf934c54 +README.md: 32ec60edb39b30afdb9b54804e62302cdc58e531 +README.zh.md: ab17910ab9cec4e1ff519af0922ee27034a6fa7c diff --git a/packages/client/ui-theme/README.md b/packages/client/ui-theme/README.md index 49ea5147b1..32ec60edb3 100644 --- a/packages/client/ui-theme/README.md +++ b/packages/client/ui-theme/README.md @@ -53,7 +53,7 @@ The service owns theme and font-size state and publishes snapshots. The ui-layou `src/styles/` holds five sheets imported in order by ui-theme's dynamic client entry: `base.css`, `design-platform.css`, `scrollbar.css`, `gradient-shadow-text.css`, and `shiki.css`. The client bundle compiles and injects them as plugin-owned global styles, so unload and HMR remove them with ui-theme. `scrollbar.css` is the sole consumer of the `--dsw-alias-scrollbar-*` tokens and must follow `design-platform.css`, which declares them. -`gradient-shadow-text.css` derives `--dsh-content-font-delta` from `--dsh-content-font-size` and shifts the Markdown heading and base-text ladder by that increment. It also derives the secondary tier `--dsh-content-font-size-secondary` (setting −1 at ≤14, setting −2 above; 13px at the default) with its own `--dsh-content-font-delta-secondary` for the table variants and the flow rows one step under the body. Dense small and code variants stay fixed. The user bubble, the composer draft, and flow-row titles and summaries consume the same variables outside that ladder. +`gradient-shadow-text.css` derives `--dsh-content-font-delta` from `--dsh-content-font-size` and shifts the Markdown heading and base-text ladder by that increment. It also derives the secondary tier `--dsh-content-font-size-secondary` (setting −1 at ≤14, setting −2 above; 13px at the default) with its own `--dsh-content-font-delta-secondary` for the table variants and the flow rows one step under the body. Dense small and code variants stay fixed. Outside the ladder, the user bubble and composer draft read the body pair directly, and flow-row titles and summaries read the secondary pair. ### Scrollbar rebinding diff --git a/packages/client/ui-theme/README.zh.md b/packages/client/ui-theme/README.zh.md index 427eb4be33..ab17910ab9 100644 --- a/packages/client/ui-theme/README.zh.md +++ b/packages/client/ui-theme/README.zh.md @@ -53,7 +53,7 @@ kind: "package-reference" `src/styles/` 下有五张样式表,由 ui-theme 的动态客户端 entry 依次导入:`base.css`、`design-platform.css`、`scrollbar.css`、`gradient-shadow-text.css` 与 `shiki.css`。客户端 bundle 将其编译并注入为插件持有的全局样式,因此卸载与 HMR 会随 ui-theme 一同移除。`scrollbar.css` 是 `--dsw-alias-scrollbar-*` token 的唯一消费方,必须排在声明这些 token 的 `design-platform.css` 之后。 -`gradient-shadow-text.css` 从 `--dsh-content-font-size` 派生 `--dsh-content-font-delta`,并以该增量移动 Markdown 标题与基础文本阶梯。它同时派生低一档变量 `--dsh-content-font-size-secondary`(设置 ≤14 时为设置值 −1,>14 时为设置值 −2;默认设置下为 13px)及配套的 `--dsh-content-font-delta-secondary`,供表格变体与比正文低一档的流内行使用。紧凑的小号文本与代码变体保持固定字号。用户气泡、composer 草稿与流内行的标题及摘要在该阶梯之外消费相同变量。 +`gradient-shadow-text.css` 从 `--dsh-content-font-size` 派生 `--dsh-content-font-delta`,并以该增量移动 Markdown 标题与基础文本阶梯。它同时派生低一档变量 `--dsh-content-font-size-secondary`(设置 ≤14 时为设置值 −1,>14 时为设置值 −2;默认设置下为 13px)及配套的 `--dsh-content-font-delta-secondary`,供表格变体与比正文低一档的流内行使用。紧凑的小号文本与代码变体保持固定字号。阶梯之外,用户气泡与 composer 草稿直接读取正文档变量对,流内行的标题及摘要读取低一档变量对。 ### 滚动条重新绑定 diff --git a/packages/client/ui-theme/src/styles/gradient-shadow-text.css b/packages/client/ui-theme/src/styles/gradient-shadow-text.css index a4910e9a3d..a2d2597d89 100644 --- a/packages/client/ui-theme/src/styles/gradient-shadow-text.css +++ b/packages/client/ui-theme/src/styles/gradient-shadow-text.css @@ -25,8 +25,9 @@ body[data-ds-dark-theme] { (--dsh-content-font-delta)随动,保持层级。低一档文本(think、工具/bash 行的标题与摘要、markdown 表格、流内次级说明等)统一读 --dsh-content-font-size-secondary:设置 ≤14 时为设置值 −1,>14 时为 - 设置值 −2(12→11、13→12、14→13、15→13、16→14、17→15);small、code - 属于密集次级文本,保持固定。 */ + 设置值 −2(12→11、13→12、14→13、15→13、16→14、17→15)。式中 + max(13px,·) 的作用是让低设置取 −1 而非 −2,不是结果下限——12 设置下 + 层级触底 11px。small、code 属于密集次级文本,保持固定。 */ body { --dsh-content-font-delta: calc(var(--dsh-content-font-size, 14px) - 14px); --dsh-content-font-size-secondary: min(calc(var(--dsh-content-font-size, 14px) - 1px), max(13px, calc(var(--dsh-content-font-size, 14px) - 2px))); diff --git a/packages/client/ui-workflow-run/src/client/WorkflowRunPanel.module.css b/packages/client/ui-workflow-run/src/client/WorkflowRunPanel.module.css index 6dd616bcf2..aa4ff56a3d 100644 --- a/packages/client/ui-workflow-run/src/client/WorkflowRunPanel.module.css +++ b/packages/client/ui-workflow-run/src/client/WorkflowRunPanel.module.css @@ -223,6 +223,9 @@ flex: 1; min-width: 0; color: var(--dsw-alias-label-secondary); + /* Body size, not the secondary tier: the member label is the panel's + content (an agent's task), while the run/phase headers, counts, and + statuses around it are chrome one step down. */ font-size: var(--dsh-content-font-size, 14px); line-height: calc(24px + var(--dsh-content-font-delta, 0px)); text-overflow: ellipsis; diff --git a/packages/client/ui-workflow-run/tests/panel-styles.client.spec.ts b/packages/client/ui-workflow-run/tests/panel-styles.client.spec.ts index 375fb46dae..0095a47bbf 100644 --- a/packages/client/ui-workflow-run/tests/panel-styles.client.spec.ts +++ b/packages/client/ui-workflow-run/tests/panel-styles.client.spec.ts @@ -1,8 +1,10 @@ /** * WorkflowRunPanel's font-size-axis adoption as CSS text. jsdom has no * layout, so these read the declarations that make the run/phase headers and - * the expanded member rows follow the Settings font-size preference through - * --dsh-content-font-size / --dsh-content-font-delta. + * the expanded member rows follow the Settings font-size preference: member + * labels at the body size (--dsh-content-font-size / --dsh-content-font-delta), + * the chrome around them on the secondary tier + * (--dsh-content-font-size-secondary / --dsh-content-font-delta-secondary). */ import { readFileSync } from 'node:fs' import { fileURLToPath } from 'node:url'