chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage - #1233

Merged
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign
Jul 19, 2026
Merged

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage#1233
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign

Conversation

@jackwener

Copy link
Copy Markdown
Member

One batched campaign PR (goal: 深入分析 UI 质量问题、UX 问题,优化&修复&打磨). Two parallel audits (visual: 45 fixtures × light+dark, 90 captures examined; UX: real-app flow driving + source tracing) produced 13 verified findings; two fix waves + cross-wave integration deliver them.

Visual wave

  • info/warning were indistinguishable — default theme's --info was amber h70 beside --warning h55; now the azure family (h240, matching the blue-info alt themes), dark counterpart included. Contrast contract recomputes and stays green.
  • Two hero-badge treatments (amber Badge vs hand-rolled blue span) → one calm Badge variant=secondary; dead CSS pruned.
  • 权限 已授权 summary tile de-colored (was green while its own row chips were neutral — fix(desktop): status-color restraint — expected states stop shouting green #651).
  • Alert consolidation: four bespoke notice families (settingsNotice / settingsBotInfoNotice / providerUnavailableNotice / settingsHealthError, ~18 call sites in 11 files) migrated onto the Alert primitive; a new quiet passive variant absorbs passive notices without a CSS fork.
  • Bare-text empty states (task ledger, bot overview) → EmptyState primitive; plan countdown pill de-tinted.

UX wave

  • Orphaned 账号 settings section RETIRED — it rendered with 通用's header, no nav highlight, bounced persistence, and dead button-shaped chips; connections live in 模型. Every consumer rerouted (health notice CTA, onboarding blocked CTA, maka-uri, fixtures); headers now derive from a per-section copy map so an unrouted section fails at the type boundary (new contract).
  • Post-skip dead end: no-model state now shows an inline 前往模型设置 hint + explanatory disabled-Send title (constant-footprint contract respected).
  • 使用 no longer clobbers a non-empty composer draft (appends instead).
  • IM 接入 terminology unified (扫码登录/快捷绑定 → 接入 family; fixed the redundant WeChat dialog aria).
  • @ / mention discoverability: zh placeholder now teaches @ 引用文件 / 选择技能 (also fixed a zh/en asymmetry).
  • Fixture/CI coverage: 9 registered-but-never-captured scenarios added to the capture driver (incl. module-mcp and settings-health — the two surfaces the audit flagged hardest); driver teardown SIGTERM→SIGKILL (60s → 2-3s per capture); oauth-relogin fixture now opens the connection detail sheet it was built for.

Deferred with a written plan: a deterministic waiting-state QR-modal fixture (needs a scenario-aware onboarding adapter).

Gates (integrated tree)

desktop 2729 · ui 191 · typecheck · check-dead-css · knip ×2 · console/a11y/copy checks · alignment auditor all fixtures clean. ~20 contracts re-pinned across both waves, none deleted; audits also cleared 8 hypotheses as not-real (documented in the audit reports). Implemented by two opus worktree agents from maintainer-consolidated audit findings.

Default --info was oklch(0.75 0.16 70) amber, colliding with --warning's
orange h55 — info and warning tiers were indistinguishable. Retune the
default light/dark --info to the azure-family blue (h240, matching the
blue-info alt themes) so it reads as informational and stays distinct from
warning. --info-text stays the same color-mix formula; the 406 contrast
contract recomputes dynamically and still passes.
health-center used Badge variant=info while voice used a hand-rolled
.settingsFeatureStatusBadge span (blue via --nav-active) — two treatments
for the same quiet-metadata role (只读快照 / 本地自检). Both now use
Badge variant=secondary (the calm neutral pill, same as plan-reminder's
已完成). Deletes the now-dead .settingsFeatureStatusBadge CSS.
The 已授权 summary tile colored an expected steady state success while its
own granted row chip and health's 正常 tile are neutral. Switch to
tone=neutral per #651 exception-only status color.
task-ledger empty (当前会话没有待推进任务) now uses the compact inline
EmptyState; bot-chat-overview active-list empty (还没有正在使用的渠道) uses
the card EmptyState with a MessageSquare icon, matching skills/daily-review.
Deletes the dead .settingsRemoteAccessEmptyRow CSS, adds a compact-padding
.settingsRemoteAccessEmpty override.
The next-run countdown pill was accent-tinted (--nav-active) on an ordinary
scheduled state. Make it a neutral foreground wash per #651; colour stays
reserved for genuinely exceptional states.
…s in 模型 (U1/U7/U2)
The 'account' SettingsSection rendered AccountSettingsPage but was never in
SETTINGS_NAV, so the header fell back to 通用 copy over the account body, the
sidebar showed no highlight, and a persisted 'account' bounced to models on
reopen. Its connection list and lone 测试凭据 action duplicated the Models page.
- Remove 'account' from SETTINGS_SECTIONS; delete account-settings-page.tsx and
the account-only account-auth-ui.ts + their .account/.auth copy blocks.
- Route every health-notice/onboarding/deep-link consumer to 'models'; drop the
goToAccount copy and the now-single-target ternary. (U2 dead Chips vanish.)
- Derive the settings header from getSettingsNavigationCopy(...).sections[section]
instead of a nav[0] fallback, so an unrouted section fails at the
Record<SettingsSection> boundary instead of silently borrowing a title.
- Add contract tests pinning: every SettingsSection is a nav item (no orphans),
and the header is keyed by the active section, not nav[0].
After an onboarding skip with zero model connections, Send was disabled with no
explanation. Add an inline hint above the composer (一句话 + 前往模型设置
link-button wired to openSettingsSection('models')) and an explanatory title on
the disabled Send. The hint renders OUTSIDE the <form> so it never grows the
composer's constant footprint (#740), honors the same hidden state, and is
theme-aware via tokens.
…ve (F4)
Consolidate four bespoke notice CSS families onto the shared <Alert>:
- .settingsNotice (info + passive) → Alert variant=info / new variant=passive
- .settingsBotInfoNotice → Alert variant=info (drops the ⓘ glyph)
- .providerUnavailableNotice → Alert variant=info + AlertTitle/Description/Action
- .settingsHealthError → Alert variant=error
Adds a quiet neutral 'passive' variant to the Alert primitive (foreground-5
wash, muted text) so passive notices keep their calm look without a CSS fork.
Migrates ~18 call sites across 11 files; role=status semantics preserved.
Deletes the migrated CSS (and now-dead .settingsNotice[data-tone=passive] /
.settingsNoticeAction). .settingsNotice base rule stays for
account-settings-page.tsx (owned by a separate change).
Contract re-pins: radius-converge drops the two deleted selectors; voice-capture
-smoke, settings-app-info and renderer-startup-fail-soft re-pinned to the Alert
structure. Full desktop (2741) + ui (190) suites, typecheck, check-dead-css,
knip ×2, audit-alignment all green.
…4/U5/U6)
U4: useSkillInChat now appendText()s the skill invocation instead of setText,
so an in-progress draft survives (appendText falls back to a plain set on an
empty draft). Re-pin composer-send-guard.
U5: standardize the IM-bot connect verb to 接入 in bot-chat-detail
(扫码登录/快捷绑定 → 扫码接入/快捷接入) and bot-onboarding-modal (WeChat title
扫码登录 → 连接微信, fixing the redundant 扫码登录扫码接入 dialog aria; WeCom
subtitle 快捷绑定 → 快捷接入). 扫码 gesture kept where literal. e2e re-pinned.
(bot-chat-overview.tsx:138 is owned by another agent — left untouched; see report.)
U6: extend the composer placeholder to teach @ 引用文件 / 选择技能 — one subtle,
theme-agnostic placement that also fixes a zh/en asymmetry (en already hinted).
Also prune the dead account-page CSS the retire left behind (settingsAuthContract*,
settingsAuthActionPill, settingsConnectionDefaultBadge, settingsConnectionList,
settingsEmptyState) and re-pin the geometry/roadmap/a11y contracts that referenced
them. Bind the health-notice label to a local so the static a11y checker sees it.
… oauth-relogin detail sheet (#7a/b/d)
7a: add the 9 registered-but-uncaptured scenarios to capture-screenshots
ALL_SCENARIOS (oauth-relogin, settings-permissions/voice/gateway/search/usage/
health, module-mcp, long-transcript) so screenshot review has evidence of them.
7b: driver teardown after the capture marker now SIGKILLs instead of SIGTERM —
the marker is already on disk so Electron has nothing to flush; the audit
measured ~60s of graceful-teardown wait per capture, now 2-3s. No cleanup
regression (per-run user-data dir is under os.tmpdir()).
7d: oauth-relogin fixture now opens the seeded codex-oauth connection's detail
sheet (needs_reauth) instead of just the 模型 section, so the re-login
affordance is what gets captured. Threads a new openConnectionDetailSlug through
VisualSmokeState → app-shell-visual-smoke → useSettingsModal.openConnectionDetail
→ overlays → SettingsModal → SettingsSurface → ProvidersPanel.initialConnectionSlug,
mirroring the existing openProviderCatalog path.
…ple (U1)
conversation-localization: pin the new @ 引用文件 / 选择技能 placeholder in both
locales. markdown-body: the internal-nav-link sanitization example used
maka://settings/account, now invalid after the account section retire — use
maka://settings/models.
Renders Composer with noModelConnection and asserts the inline hint, the
前往模型设置 link-button, and the explanatory disabled-Send title appear (and are
absent by default) in both locales.
…mnants
Wave A kept the .settingsNotice base rule alive solely for
account-settings-page; Wave B retired that page, so the rule and its
radius-converge entry go with it.
@jackwener
jackwener merged commit ec2b565 into mainJul 19, 2026
3 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage - #1233

Merged
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign
Jul 19, 2026
Merged

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage#1233
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign

Conversation

@jackwener

Copy link
Copy Markdown
Member

One batched campaign PR (goal: 深入分析 UI 质量问题、UX 问题,优化&修复&打磨). Two parallel audits (visual: 45 fixtures × light+dark, 90 captures examined; UX: real-app flow driving + source tracing) produced 13 verified findings; two fix waves + cross-wave integration deliver them.

Visual wave

  • info/warning were indistinguishable — default theme's --info was amber h70 beside --warning h55; now the azure family (h240, matching the blue-info alt themes), dark counterpart included. Contrast contract recomputes and stays green.
  • Two hero-badge treatments (amber Badge vs hand-rolled blue span) → one calm Badge variant=secondary; dead CSS pruned.
  • 权限 已授权 summary tile de-colored (was green while its own row chips were neutral — fix(desktop): status-color restraint — expected states stop shouting green #651).
  • Alert consolidation: four bespoke notice families (settingsNotice / settingsBotInfoNotice / providerUnavailableNotice / settingsHealthError, ~18 call sites in 11 files) migrated onto the Alert primitive; a new quiet passive variant absorbs passive notices without a CSS fork.
  • Bare-text empty states (task ledger, bot overview) → EmptyState primitive; plan countdown pill de-tinted.

UX wave

  • Orphaned 账号 settings section RETIRED — it rendered with 通用's header, no nav highlight, bounced persistence, and dead button-shaped chips; connections live in 模型. Every consumer rerouted (health notice CTA, onboarding blocked CTA, maka-uri, fixtures); headers now derive from a per-section copy map so an unrouted section fails at the type boundary (new contract).
  • Post-skip dead end: no-model state now shows an inline 前往模型设置 hint + explanatory disabled-Send title (constant-footprint contract respected).
  • 使用 no longer clobbers a non-empty composer draft (appends instead).
  • IM 接入 terminology unified (扫码登录/快捷绑定 → 接入 family; fixed the redundant WeChat dialog aria).
  • @ / mention discoverability: zh placeholder now teaches @ 引用文件 / 选择技能 (also fixed a zh/en asymmetry).
  • Fixture/CI coverage: 9 registered-but-never-captured scenarios added to the capture driver (incl. module-mcp and settings-health — the two surfaces the audit flagged hardest); driver teardown SIGTERM→SIGKILL (60s → 2-3s per capture); oauth-relogin fixture now opens the connection detail sheet it was built for.

Deferred with a written plan: a deterministic waiting-state QR-modal fixture (needs a scenario-aware onboarding adapter).

Gates (integrated tree)

desktop 2729 · ui 191 · typecheck · check-dead-css · knip ×2 · console/a11y/copy checks · alignment auditor all fixtures clean. ~20 contracts re-pinned across both waves, none deleted; audits also cleared 8 hypotheses as not-real (documented in the audit reports). Implemented by two opus worktree agents from maintainer-consolidated audit findings.

Default --info was oklch(0.75 0.16 70) amber, colliding with --warning's
orange h55 — info and warning tiers were indistinguishable. Retune the
default light/dark --info to the azure-family blue (h240, matching the
blue-info alt themes) so it reads as informational and stays distinct from
warning. --info-text stays the same color-mix formula; the 406 contrast
contract recomputes dynamically and still passes.
health-center used Badge variant=info while voice used a hand-rolled
.settingsFeatureStatusBadge span (blue via --nav-active) — two treatments
for the same quiet-metadata role (只读快照 / 本地自检). Both now use
Badge variant=secondary (the calm neutral pill, same as plan-reminder's
已完成). Deletes the now-dead .settingsFeatureStatusBadge CSS.
The 已授权 summary tile colored an expected steady state success while its
own granted row chip and health's 正常 tile are neutral. Switch to
tone=neutral per #651 exception-only status color.
task-ledger empty (当前会话没有待推进任务) now uses the compact inline
EmptyState; bot-chat-overview active-list empty (还没有正在使用的渠道) uses
the card EmptyState with a MessageSquare icon, matching skills/daily-review.
Deletes the dead .settingsRemoteAccessEmptyRow CSS, adds a compact-padding
.settingsRemoteAccessEmpty override.
The next-run countdown pill was accent-tinted (--nav-active) on an ordinary
scheduled state. Make it a neutral foreground wash per #651; colour stays
reserved for genuinely exceptional states.
…s in 模型 (U1/U7/U2)
The 'account' SettingsSection rendered AccountSettingsPage but was never in
SETTINGS_NAV, so the header fell back to 通用 copy over the account body, the
sidebar showed no highlight, and a persisted 'account' bounced to models on
reopen. Its connection list and lone 测试凭据 action duplicated the Models page.
- Remove 'account' from SETTINGS_SECTIONS; delete account-settings-page.tsx and
the account-only account-auth-ui.ts + their .account/.auth copy blocks.
- Route every health-notice/onboarding/deep-link consumer to 'models'; drop the
goToAccount copy and the now-single-target ternary. (U2 dead Chips vanish.)
- Derive the settings header from getSettingsNavigationCopy(...).sections[section]
instead of a nav[0] fallback, so an unrouted section fails at the
Record<SettingsSection> boundary instead of silently borrowing a title.
- Add contract tests pinning: every SettingsSection is a nav item (no orphans),
and the header is keyed by the active section, not nav[0].
After an onboarding skip with zero model connections, Send was disabled with no
explanation. Add an inline hint above the composer (一句话 + 前往模型设置
link-button wired to openSettingsSection('models')) and an explanatory title on
the disabled Send. The hint renders OUTSIDE the <form> so it never grows the
composer's constant footprint (#740), honors the same hidden state, and is
theme-aware via tokens.
…ve (F4)
Consolidate four bespoke notice CSS families onto the shared <Alert>:
- .settingsNotice (info + passive) → Alert variant=info / new variant=passive
- .settingsBotInfoNotice → Alert variant=info (drops the ⓘ glyph)
- .providerUnavailableNotice → Alert variant=info + AlertTitle/Description/Action
- .settingsHealthError → Alert variant=error
Adds a quiet neutral 'passive' variant to the Alert primitive (foreground-5
wash, muted text) so passive notices keep their calm look without a CSS fork.
Migrates ~18 call sites across 11 files; role=status semantics preserved.
Deletes the migrated CSS (and now-dead .settingsNotice[data-tone=passive] /
.settingsNoticeAction). .settingsNotice base rule stays for
account-settings-page.tsx (owned by a separate change).
Contract re-pins: radius-converge drops the two deleted selectors; voice-capture
-smoke, settings-app-info and renderer-startup-fail-soft re-pinned to the Alert
structure. Full desktop (2741) + ui (190) suites, typecheck, check-dead-css,
knip ×2, audit-alignment all green.
…4/U5/U6)
U4: useSkillInChat now appendText()s the skill invocation instead of setText,
so an in-progress draft survives (appendText falls back to a plain set on an
empty draft). Re-pin composer-send-guard.
U5: standardize the IM-bot connect verb to 接入 in bot-chat-detail
(扫码登录/快捷绑定 → 扫码接入/快捷接入) and bot-onboarding-modal (WeChat title
扫码登录 → 连接微信, fixing the redundant 扫码登录扫码接入 dialog aria; WeCom
subtitle 快捷绑定 → 快捷接入). 扫码 gesture kept where literal. e2e re-pinned.
(bot-chat-overview.tsx:138 is owned by another agent — left untouched; see report.)
U6: extend the composer placeholder to teach @ 引用文件 / 选择技能 — one subtle,
theme-agnostic placement that also fixes a zh/en asymmetry (en already hinted).
Also prune the dead account-page CSS the retire left behind (settingsAuthContract*,
settingsAuthActionPill, settingsConnectionDefaultBadge, settingsConnectionList,
settingsEmptyState) and re-pin the geometry/roadmap/a11y contracts that referenced
them. Bind the health-notice label to a local so the static a11y checker sees it.
… oauth-relogin detail sheet (#7a/b/d)
7a: add the 9 registered-but-uncaptured scenarios to capture-screenshots
ALL_SCENARIOS (oauth-relogin, settings-permissions/voice/gateway/search/usage/
health, module-mcp, long-transcript) so screenshot review has evidence of them.
7b: driver teardown after the capture marker now SIGKILLs instead of SIGTERM —
the marker is already on disk so Electron has nothing to flush; the audit
measured ~60s of graceful-teardown wait per capture, now 2-3s. No cleanup
regression (per-run user-data dir is under os.tmpdir()).
7d: oauth-relogin fixture now opens the seeded codex-oauth connection's detail
sheet (needs_reauth) instead of just the 模型 section, so the re-login
affordance is what gets captured. Threads a new openConnectionDetailSlug through
VisualSmokeState → app-shell-visual-smoke → useSettingsModal.openConnectionDetail
→ overlays → SettingsModal → SettingsSurface → ProvidersPanel.initialConnectionSlug,
mirroring the existing openProviderCatalog path.
…ple (U1)
conversation-localization: pin the new @ 引用文件 / 选择技能 placeholder in both
locales. markdown-body: the internal-nav-link sanitization example used
maka://settings/account, now invalid after the account section retire — use
maka://settings/models.
Renders Composer with noModelConnection and asserts the inline hint, the
前往模型设置 link-button, and the explanatory disabled-Send title appear (and are
absent by default) in both locales.
…mnants
Wave A kept the .settingsNotice base rule alive solely for
account-settings-page; Wave B retired that page, so the rule and its
radius-converge entry go with it.
@jackwener
jackwener merged commit ec2b565 into mainJul 19, 2026
3 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage - #1233

Merged
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign
Jul 19, 2026
Merged

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage#1233
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign

Conversation

@jackwener

Copy link
Copy Markdown
Member

One batched campaign PR (goal: 深入分析 UI 质量问题、UX 问题,优化&修复&打磨). Two parallel audits (visual: 45 fixtures × light+dark, 90 captures examined; UX: real-app flow driving + source tracing) produced 13 verified findings; two fix waves + cross-wave integration deliver them.

Visual wave

  • info/warning were indistinguishable — default theme's --info was amber h70 beside --warning h55; now the azure family (h240, matching the blue-info alt themes), dark counterpart included. Contrast contract recomputes and stays green.
  • Two hero-badge treatments (amber Badge vs hand-rolled blue span) → one calm Badge variant=secondary; dead CSS pruned.
  • 权限 已授权 summary tile de-colored (was green while its own row chips were neutral — fix(desktop): status-color restraint — expected states stop shouting green #651).
  • Alert consolidation: four bespoke notice families (settingsNotice / settingsBotInfoNotice / providerUnavailableNotice / settingsHealthError, ~18 call sites in 11 files) migrated onto the Alert primitive; a new quiet passive variant absorbs passive notices without a CSS fork.
  • Bare-text empty states (task ledger, bot overview) → EmptyState primitive; plan countdown pill de-tinted.

UX wave

  • Orphaned 账号 settings section RETIRED — it rendered with 通用's header, no nav highlight, bounced persistence, and dead button-shaped chips; connections live in 模型. Every consumer rerouted (health notice CTA, onboarding blocked CTA, maka-uri, fixtures); headers now derive from a per-section copy map so an unrouted section fails at the type boundary (new contract).
  • Post-skip dead end: no-model state now shows an inline 前往模型设置 hint + explanatory disabled-Send title (constant-footprint contract respected).
  • 使用 no longer clobbers a non-empty composer draft (appends instead).
  • IM 接入 terminology unified (扫码登录/快捷绑定 → 接入 family; fixed the redundant WeChat dialog aria).
  • @ / mention discoverability: zh placeholder now teaches @ 引用文件 / 选择技能 (also fixed a zh/en asymmetry).
  • Fixture/CI coverage: 9 registered-but-never-captured scenarios added to the capture driver (incl. module-mcp and settings-health — the two surfaces the audit flagged hardest); driver teardown SIGTERM→SIGKILL (60s → 2-3s per capture); oauth-relogin fixture now opens the connection detail sheet it was built for.

Deferred with a written plan: a deterministic waiting-state QR-modal fixture (needs a scenario-aware onboarding adapter).

Gates (integrated tree)

desktop 2729 · ui 191 · typecheck · check-dead-css · knip ×2 · console/a11y/copy checks · alignment auditor all fixtures clean. ~20 contracts re-pinned across both waves, none deleted; audits also cleared 8 hypotheses as not-real (documented in the audit reports). Implemented by two opus worktree agents from maintainer-consolidated audit findings.

Default --info was oklch(0.75 0.16 70) amber, colliding with --warning's
orange h55 — info and warning tiers were indistinguishable. Retune the
default light/dark --info to the azure-family blue (h240, matching the
blue-info alt themes) so it reads as informational and stays distinct from
warning. --info-text stays the same color-mix formula; the 406 contrast
contract recomputes dynamically and still passes.
health-center used Badge variant=info while voice used a hand-rolled
.settingsFeatureStatusBadge span (blue via --nav-active) — two treatments
for the same quiet-metadata role (只读快照 / 本地自检). Both now use
Badge variant=secondary (the calm neutral pill, same as plan-reminder's
已完成). Deletes the now-dead .settingsFeatureStatusBadge CSS.
The 已授权 summary tile colored an expected steady state success while its
own granted row chip and health's 正常 tile are neutral. Switch to
tone=neutral per #651 exception-only status color.
task-ledger empty (当前会话没有待推进任务) now uses the compact inline
EmptyState; bot-chat-overview active-list empty (还没有正在使用的渠道) uses
the card EmptyState with a MessageSquare icon, matching skills/daily-review.
Deletes the dead .settingsRemoteAccessEmptyRow CSS, adds a compact-padding
.settingsRemoteAccessEmpty override.
The next-run countdown pill was accent-tinted (--nav-active) on an ordinary
scheduled state. Make it a neutral foreground wash per #651; colour stays
reserved for genuinely exceptional states.
…s in 模型 (U1/U7/U2)
The 'account' SettingsSection rendered AccountSettingsPage but was never in
SETTINGS_NAV, so the header fell back to 通用 copy over the account body, the
sidebar showed no highlight, and a persisted 'account' bounced to models on
reopen. Its connection list and lone 测试凭据 action duplicated the Models page.
- Remove 'account' from SETTINGS_SECTIONS; delete account-settings-page.tsx and
the account-only account-auth-ui.ts + their .account/.auth copy blocks.
- Route every health-notice/onboarding/deep-link consumer to 'models'; drop the
goToAccount copy and the now-single-target ternary. (U2 dead Chips vanish.)
- Derive the settings header from getSettingsNavigationCopy(...).sections[section]
instead of a nav[0] fallback, so an unrouted section fails at the
Record<SettingsSection> boundary instead of silently borrowing a title.
- Add contract tests pinning: every SettingsSection is a nav item (no orphans),
and the header is keyed by the active section, not nav[0].
After an onboarding skip with zero model connections, Send was disabled with no
explanation. Add an inline hint above the composer (一句话 + 前往模型设置
link-button wired to openSettingsSection('models')) and an explanatory title on
the disabled Send. The hint renders OUTSIDE the <form> so it never grows the
composer's constant footprint (#740), honors the same hidden state, and is
theme-aware via tokens.
…ve (F4)
Consolidate four bespoke notice CSS families onto the shared <Alert>:
- .settingsNotice (info + passive) → Alert variant=info / new variant=passive
- .settingsBotInfoNotice → Alert variant=info (drops the ⓘ glyph)
- .providerUnavailableNotice → Alert variant=info + AlertTitle/Description/Action
- .settingsHealthError → Alert variant=error
Adds a quiet neutral 'passive' variant to the Alert primitive (foreground-5
wash, muted text) so passive notices keep their calm look without a CSS fork.
Migrates ~18 call sites across 11 files; role=status semantics preserved.
Deletes the migrated CSS (and now-dead .settingsNotice[data-tone=passive] /
.settingsNoticeAction). .settingsNotice base rule stays for
account-settings-page.tsx (owned by a separate change).
Contract re-pins: radius-converge drops the two deleted selectors; voice-capture
-smoke, settings-app-info and renderer-startup-fail-soft re-pinned to the Alert
structure. Full desktop (2741) + ui (190) suites, typecheck, check-dead-css,
knip ×2, audit-alignment all green.
…4/U5/U6)
U4: useSkillInChat now appendText()s the skill invocation instead of setText,
so an in-progress draft survives (appendText falls back to a plain set on an
empty draft). Re-pin composer-send-guard.
U5: standardize the IM-bot connect verb to 接入 in bot-chat-detail
(扫码登录/快捷绑定 → 扫码接入/快捷接入) and bot-onboarding-modal (WeChat title
扫码登录 → 连接微信, fixing the redundant 扫码登录扫码接入 dialog aria; WeCom
subtitle 快捷绑定 → 快捷接入). 扫码 gesture kept where literal. e2e re-pinned.
(bot-chat-overview.tsx:138 is owned by another agent — left untouched; see report.)
U6: extend the composer placeholder to teach @ 引用文件 / 选择技能 — one subtle,
theme-agnostic placement that also fixes a zh/en asymmetry (en already hinted).
Also prune the dead account-page CSS the retire left behind (settingsAuthContract*,
settingsAuthActionPill, settingsConnectionDefaultBadge, settingsConnectionList,
settingsEmptyState) and re-pin the geometry/roadmap/a11y contracts that referenced
them. Bind the health-notice label to a local so the static a11y checker sees it.
… oauth-relogin detail sheet (#7a/b/d)
7a: add the 9 registered-but-uncaptured scenarios to capture-screenshots
ALL_SCENARIOS (oauth-relogin, settings-permissions/voice/gateway/search/usage/
health, module-mcp, long-transcript) so screenshot review has evidence of them.
7b: driver teardown after the capture marker now SIGKILLs instead of SIGTERM —
the marker is already on disk so Electron has nothing to flush; the audit
measured ~60s of graceful-teardown wait per capture, now 2-3s. No cleanup
regression (per-run user-data dir is under os.tmpdir()).
7d: oauth-relogin fixture now opens the seeded codex-oauth connection's detail
sheet (needs_reauth) instead of just the 模型 section, so the re-login
affordance is what gets captured. Threads a new openConnectionDetailSlug through
VisualSmokeState → app-shell-visual-smoke → useSettingsModal.openConnectionDetail
→ overlays → SettingsModal → SettingsSurface → ProvidersPanel.initialConnectionSlug,
mirroring the existing openProviderCatalog path.
…ple (U1)
conversation-localization: pin the new @ 引用文件 / 选择技能 placeholder in both
locales. markdown-body: the internal-nav-link sanitization example used
maka://settings/account, now invalid after the account section retire — use
maka://settings/models.
Renders Composer with noModelConnection and asserts the inline hint, the
前往模型设置 link-button, and the explanatory disabled-Send title appear (and are
absent by default) in both locales.
…mnants
Wave A kept the .settingsNotice base rule alive solely for
account-settings-page; Wave B retired that page, so the rule and its
radius-converge entry go with it.
@jackwener
jackwener merged commit ec2b565 into mainJul 19, 2026
3 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage - #1233

Merged
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign
Jul 19, 2026
Merged

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage#1233
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign

Conversation

@jackwener

Copy link
Copy Markdown
Member

One batched campaign PR (goal: 深入分析 UI 质量问题、UX 问题,优化&修复&打磨). Two parallel audits (visual: 45 fixtures × light+dark, 90 captures examined; UX: real-app flow driving + source tracing) produced 13 verified findings; two fix waves + cross-wave integration deliver them.

Visual wave

  • info/warning were indistinguishable — default theme's --info was amber h70 beside --warning h55; now the azure family (h240, matching the blue-info alt themes), dark counterpart included. Contrast contract recomputes and stays green.
  • Two hero-badge treatments (amber Badge vs hand-rolled blue span) → one calm Badge variant=secondary; dead CSS pruned.
  • 权限 已授权 summary tile de-colored (was green while its own row chips were neutral — fix(desktop): status-color restraint — expected states stop shouting green #651).
  • Alert consolidation: four bespoke notice families (settingsNotice / settingsBotInfoNotice / providerUnavailableNotice / settingsHealthError, ~18 call sites in 11 files) migrated onto the Alert primitive; a new quiet passive variant absorbs passive notices without a CSS fork.
  • Bare-text empty states (task ledger, bot overview) → EmptyState primitive; plan countdown pill de-tinted.

UX wave

  • Orphaned 账号 settings section RETIRED — it rendered with 通用's header, no nav highlight, bounced persistence, and dead button-shaped chips; connections live in 模型. Every consumer rerouted (health notice CTA, onboarding blocked CTA, maka-uri, fixtures); headers now derive from a per-section copy map so an unrouted section fails at the type boundary (new contract).
  • Post-skip dead end: no-model state now shows an inline 前往模型设置 hint + explanatory disabled-Send title (constant-footprint contract respected).
  • 使用 no longer clobbers a non-empty composer draft (appends instead).
  • IM 接入 terminology unified (扫码登录/快捷绑定 → 接入 family; fixed the redundant WeChat dialog aria).
  • @ / mention discoverability: zh placeholder now teaches @ 引用文件 / 选择技能 (also fixed a zh/en asymmetry).
  • Fixture/CI coverage: 9 registered-but-never-captured scenarios added to the capture driver (incl. module-mcp and settings-health — the two surfaces the audit flagged hardest); driver teardown SIGTERM→SIGKILL (60s → 2-3s per capture); oauth-relogin fixture now opens the connection detail sheet it was built for.

Deferred with a written plan: a deterministic waiting-state QR-modal fixture (needs a scenario-aware onboarding adapter).

Gates (integrated tree)

desktop 2729 · ui 191 · typecheck · check-dead-css · knip ×2 · console/a11y/copy checks · alignment auditor all fixtures clean. ~20 contracts re-pinned across both waves, none deleted; audits also cleared 8 hypotheses as not-real (documented in the audit reports). Implemented by two opus worktree agents from maintainer-consolidated audit findings.

Default --info was oklch(0.75 0.16 70) amber, colliding with --warning's
orange h55 — info and warning tiers were indistinguishable. Retune the
default light/dark --info to the azure-family blue (h240, matching the
blue-info alt themes) so it reads as informational and stays distinct from
warning. --info-text stays the same color-mix formula; the 406 contrast
contract recomputes dynamically and still passes.
health-center used Badge variant=info while voice used a hand-rolled
.settingsFeatureStatusBadge span (blue via --nav-active) — two treatments
for the same quiet-metadata role (只读快照 / 本地自检). Both now use
Badge variant=secondary (the calm neutral pill, same as plan-reminder's
已完成). Deletes the now-dead .settingsFeatureStatusBadge CSS.
The 已授权 summary tile colored an expected steady state success while its
own granted row chip and health's 正常 tile are neutral. Switch to
tone=neutral per #651 exception-only status color.
task-ledger empty (当前会话没有待推进任务) now uses the compact inline
EmptyState; bot-chat-overview active-list empty (还没有正在使用的渠道) uses
the card EmptyState with a MessageSquare icon, matching skills/daily-review.
Deletes the dead .settingsRemoteAccessEmptyRow CSS, adds a compact-padding
.settingsRemoteAccessEmpty override.
The next-run countdown pill was accent-tinted (--nav-active) on an ordinary
scheduled state. Make it a neutral foreground wash per #651; colour stays
reserved for genuinely exceptional states.
…s in 模型 (U1/U7/U2)
The 'account' SettingsSection rendered AccountSettingsPage but was never in
SETTINGS_NAV, so the header fell back to 通用 copy over the account body, the
sidebar showed no highlight, and a persisted 'account' bounced to models on
reopen. Its connection list and lone 测试凭据 action duplicated the Models page.
- Remove 'account' from SETTINGS_SECTIONS; delete account-settings-page.tsx and
the account-only account-auth-ui.ts + their .account/.auth copy blocks.
- Route every health-notice/onboarding/deep-link consumer to 'models'; drop the
goToAccount copy and the now-single-target ternary. (U2 dead Chips vanish.)
- Derive the settings header from getSettingsNavigationCopy(...).sections[section]
instead of a nav[0] fallback, so an unrouted section fails at the
Record<SettingsSection> boundary instead of silently borrowing a title.
- Add contract tests pinning: every SettingsSection is a nav item (no orphans),
and the header is keyed by the active section, not nav[0].
After an onboarding skip with zero model connections, Send was disabled with no
explanation. Add an inline hint above the composer (一句话 + 前往模型设置
link-button wired to openSettingsSection('models')) and an explanatory title on
the disabled Send. The hint renders OUTSIDE the <form> so it never grows the
composer's constant footprint (#740), honors the same hidden state, and is
theme-aware via tokens.
…ve (F4)
Consolidate four bespoke notice CSS families onto the shared <Alert>:
- .settingsNotice (info + passive) → Alert variant=info / new variant=passive
- .settingsBotInfoNotice → Alert variant=info (drops the ⓘ glyph)
- .providerUnavailableNotice → Alert variant=info + AlertTitle/Description/Action
- .settingsHealthError → Alert variant=error
Adds a quiet neutral 'passive' variant to the Alert primitive (foreground-5
wash, muted text) so passive notices keep their calm look without a CSS fork.
Migrates ~18 call sites across 11 files; role=status semantics preserved.
Deletes the migrated CSS (and now-dead .settingsNotice[data-tone=passive] /
.settingsNoticeAction). .settingsNotice base rule stays for
account-settings-page.tsx (owned by a separate change).
Contract re-pins: radius-converge drops the two deleted selectors; voice-capture
-smoke, settings-app-info and renderer-startup-fail-soft re-pinned to the Alert
structure. Full desktop (2741) + ui (190) suites, typecheck, check-dead-css,
knip ×2, audit-alignment all green.
…4/U5/U6)
U4: useSkillInChat now appendText()s the skill invocation instead of setText,
so an in-progress draft survives (appendText falls back to a plain set on an
empty draft). Re-pin composer-send-guard.
U5: standardize the IM-bot connect verb to 接入 in bot-chat-detail
(扫码登录/快捷绑定 → 扫码接入/快捷接入) and bot-onboarding-modal (WeChat title
扫码登录 → 连接微信, fixing the redundant 扫码登录扫码接入 dialog aria; WeCom
subtitle 快捷绑定 → 快捷接入). 扫码 gesture kept where literal. e2e re-pinned.
(bot-chat-overview.tsx:138 is owned by another agent — left untouched; see report.)
U6: extend the composer placeholder to teach @ 引用文件 / 选择技能 — one subtle,
theme-agnostic placement that also fixes a zh/en asymmetry (en already hinted).
Also prune the dead account-page CSS the retire left behind (settingsAuthContract*,
settingsAuthActionPill, settingsConnectionDefaultBadge, settingsConnectionList,
settingsEmptyState) and re-pin the geometry/roadmap/a11y contracts that referenced
them. Bind the health-notice label to a local so the static a11y checker sees it.
… oauth-relogin detail sheet (#7a/b/d)
7a: add the 9 registered-but-uncaptured scenarios to capture-screenshots
ALL_SCENARIOS (oauth-relogin, settings-permissions/voice/gateway/search/usage/
health, module-mcp, long-transcript) so screenshot review has evidence of them.
7b: driver teardown after the capture marker now SIGKILLs instead of SIGTERM —
the marker is already on disk so Electron has nothing to flush; the audit
measured ~60s of graceful-teardown wait per capture, now 2-3s. No cleanup
regression (per-run user-data dir is under os.tmpdir()).
7d: oauth-relogin fixture now opens the seeded codex-oauth connection's detail
sheet (needs_reauth) instead of just the 模型 section, so the re-login
affordance is what gets captured. Threads a new openConnectionDetailSlug through
VisualSmokeState → app-shell-visual-smoke → useSettingsModal.openConnectionDetail
→ overlays → SettingsModal → SettingsSurface → ProvidersPanel.initialConnectionSlug,
mirroring the existing openProviderCatalog path.
…ple (U1)
conversation-localization: pin the new @ 引用文件 / 选择技能 placeholder in both
locales. markdown-body: the internal-nav-link sanitization example used
maka://settings/account, now invalid after the account section retire — use
maka://settings/models.
Renders Composer with noModelConnection and asserts the inline hint, the
前往模型设置 link-button, and the explanatory disabled-Send title appear (and are
absent by default) in both locales.
…mnants
Wave A kept the .settingsNotice base rule alive solely for
account-settings-page; Wave B retired that page, so the rule and its
radius-converge entry go with it.
@jackwener
jackwener merged commit ec2b565 into mainJul 19, 2026
3 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage - #1233

Merged
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign
Jul 19, 2026
Merged

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage#1233
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign

Conversation

@jackwener

Copy link
Copy Markdown
Member

One batched campaign PR (goal: 深入分析 UI 质量问题、UX 问题,优化&修复&打磨). Two parallel audits (visual: 45 fixtures × light+dark, 90 captures examined; UX: real-app flow driving + source tracing) produced 13 verified findings; two fix waves + cross-wave integration deliver them.

Visual wave

  • info/warning were indistinguishable — default theme's --info was amber h70 beside --warning h55; now the azure family (h240, matching the blue-info alt themes), dark counterpart included. Contrast contract recomputes and stays green.
  • Two hero-badge treatments (amber Badge vs hand-rolled blue span) → one calm Badge variant=secondary; dead CSS pruned.
  • 权限 已授权 summary tile de-colored (was green while its own row chips were neutral — fix(desktop): status-color restraint — expected states stop shouting green #651).
  • Alert consolidation: four bespoke notice families (settingsNotice / settingsBotInfoNotice / providerUnavailableNotice / settingsHealthError, ~18 call sites in 11 files) migrated onto the Alert primitive; a new quiet passive variant absorbs passive notices without a CSS fork.
  • Bare-text empty states (task ledger, bot overview) → EmptyState primitive; plan countdown pill de-tinted.

UX wave

  • Orphaned 账号 settings section RETIRED — it rendered with 通用's header, no nav highlight, bounced persistence, and dead button-shaped chips; connections live in 模型. Every consumer rerouted (health notice CTA, onboarding blocked CTA, maka-uri, fixtures); headers now derive from a per-section copy map so an unrouted section fails at the type boundary (new contract).
  • Post-skip dead end: no-model state now shows an inline 前往模型设置 hint + explanatory disabled-Send title (constant-footprint contract respected).
  • 使用 no longer clobbers a non-empty composer draft (appends instead).
  • IM 接入 terminology unified (扫码登录/快捷绑定 → 接入 family; fixed the redundant WeChat dialog aria).
  • @ / mention discoverability: zh placeholder now teaches @ 引用文件 / 选择技能 (also fixed a zh/en asymmetry).
  • Fixture/CI coverage: 9 registered-but-never-captured scenarios added to the capture driver (incl. module-mcp and settings-health — the two surfaces the audit flagged hardest); driver teardown SIGTERM→SIGKILL (60s → 2-3s per capture); oauth-relogin fixture now opens the connection detail sheet it was built for.

Deferred with a written plan: a deterministic waiting-state QR-modal fixture (needs a scenario-aware onboarding adapter).

Gates (integrated tree)

desktop 2729 · ui 191 · typecheck · check-dead-css · knip ×2 · console/a11y/copy checks · alignment auditor all fixtures clean. ~20 contracts re-pinned across both waves, none deleted; audits also cleared 8 hypotheses as not-real (documented in the audit reports). Implemented by two opus worktree agents from maintainer-consolidated audit findings.

Default --info was oklch(0.75 0.16 70) amber, colliding with --warning's
orange h55 — info and warning tiers were indistinguishable. Retune the
default light/dark --info to the azure-family blue (h240, matching the
blue-info alt themes) so it reads as informational and stays distinct from
warning. --info-text stays the same color-mix formula; the 406 contrast
contract recomputes dynamically and still passes.
health-center used Badge variant=info while voice used a hand-rolled
.settingsFeatureStatusBadge span (blue via --nav-active) — two treatments
for the same quiet-metadata role (只读快照 / 本地自检). Both now use
Badge variant=secondary (the calm neutral pill, same as plan-reminder's
已完成). Deletes the now-dead .settingsFeatureStatusBadge CSS.
The 已授权 summary tile colored an expected steady state success while its
own granted row chip and health's 正常 tile are neutral. Switch to
tone=neutral per #651 exception-only status color.
task-ledger empty (当前会话没有待推进任务) now uses the compact inline
EmptyState; bot-chat-overview active-list empty (还没有正在使用的渠道) uses
the card EmptyState with a MessageSquare icon, matching skills/daily-review.
Deletes the dead .settingsRemoteAccessEmptyRow CSS, adds a compact-padding
.settingsRemoteAccessEmpty override.
The next-run countdown pill was accent-tinted (--nav-active) on an ordinary
scheduled state. Make it a neutral foreground wash per #651; colour stays
reserved for genuinely exceptional states.
…s in 模型 (U1/U7/U2)
The 'account' SettingsSection rendered AccountSettingsPage but was never in
SETTINGS_NAV, so the header fell back to 通用 copy over the account body, the
sidebar showed no highlight, and a persisted 'account' bounced to models on
reopen. Its connection list and lone 测试凭据 action duplicated the Models page.
- Remove 'account' from SETTINGS_SECTIONS; delete account-settings-page.tsx and
the account-only account-auth-ui.ts + their .account/.auth copy blocks.
- Route every health-notice/onboarding/deep-link consumer to 'models'; drop the
goToAccount copy and the now-single-target ternary. (U2 dead Chips vanish.)
- Derive the settings header from getSettingsNavigationCopy(...).sections[section]
instead of a nav[0] fallback, so an unrouted section fails at the
Record<SettingsSection> boundary instead of silently borrowing a title.
- Add contract tests pinning: every SettingsSection is a nav item (no orphans),
and the header is keyed by the active section, not nav[0].
After an onboarding skip with zero model connections, Send was disabled with no
explanation. Add an inline hint above the composer (一句话 + 前往模型设置
link-button wired to openSettingsSection('models')) and an explanatory title on
the disabled Send. The hint renders OUTSIDE the <form> so it never grows the
composer's constant footprint (#740), honors the same hidden state, and is
theme-aware via tokens.
…ve (F4)
Consolidate four bespoke notice CSS families onto the shared <Alert>:
- .settingsNotice (info + passive) → Alert variant=info / new variant=passive
- .settingsBotInfoNotice → Alert variant=info (drops the ⓘ glyph)
- .providerUnavailableNotice → Alert variant=info + AlertTitle/Description/Action
- .settingsHealthError → Alert variant=error
Adds a quiet neutral 'passive' variant to the Alert primitive (foreground-5
wash, muted text) so passive notices keep their calm look without a CSS fork.
Migrates ~18 call sites across 11 files; role=status semantics preserved.
Deletes the migrated CSS (and now-dead .settingsNotice[data-tone=passive] /
.settingsNoticeAction). .settingsNotice base rule stays for
account-settings-page.tsx (owned by a separate change).
Contract re-pins: radius-converge drops the two deleted selectors; voice-capture
-smoke, settings-app-info and renderer-startup-fail-soft re-pinned to the Alert
structure. Full desktop (2741) + ui (190) suites, typecheck, check-dead-css,
knip ×2, audit-alignment all green.
…4/U5/U6)
U4: useSkillInChat now appendText()s the skill invocation instead of setText,
so an in-progress draft survives (appendText falls back to a plain set on an
empty draft). Re-pin composer-send-guard.
U5: standardize the IM-bot connect verb to 接入 in bot-chat-detail
(扫码登录/快捷绑定 → 扫码接入/快捷接入) and bot-onboarding-modal (WeChat title
扫码登录 → 连接微信, fixing the redundant 扫码登录扫码接入 dialog aria; WeCom
subtitle 快捷绑定 → 快捷接入). 扫码 gesture kept where literal. e2e re-pinned.
(bot-chat-overview.tsx:138 is owned by another agent — left untouched; see report.)
U6: extend the composer placeholder to teach @ 引用文件 / 选择技能 — one subtle,
theme-agnostic placement that also fixes a zh/en asymmetry (en already hinted).
Also prune the dead account-page CSS the retire left behind (settingsAuthContract*,
settingsAuthActionPill, settingsConnectionDefaultBadge, settingsConnectionList,
settingsEmptyState) and re-pin the geometry/roadmap/a11y contracts that referenced
them. Bind the health-notice label to a local so the static a11y checker sees it.
… oauth-relogin detail sheet (#7a/b/d)
7a: add the 9 registered-but-uncaptured scenarios to capture-screenshots
ALL_SCENARIOS (oauth-relogin, settings-permissions/voice/gateway/search/usage/
health, module-mcp, long-transcript) so screenshot review has evidence of them.
7b: driver teardown after the capture marker now SIGKILLs instead of SIGTERM —
the marker is already on disk so Electron has nothing to flush; the audit
measured ~60s of graceful-teardown wait per capture, now 2-3s. No cleanup
regression (per-run user-data dir is under os.tmpdir()).
7d: oauth-relogin fixture now opens the seeded codex-oauth connection's detail
sheet (needs_reauth) instead of just the 模型 section, so the re-login
affordance is what gets captured. Threads a new openConnectionDetailSlug through
VisualSmokeState → app-shell-visual-smoke → useSettingsModal.openConnectionDetail
→ overlays → SettingsModal → SettingsSurface → ProvidersPanel.initialConnectionSlug,
mirroring the existing openProviderCatalog path.
…ple (U1)
conversation-localization: pin the new @ 引用文件 / 选择技能 placeholder in both
locales. markdown-body: the internal-nav-link sanitization example used
maka://settings/account, now invalid after the account section retire — use
maka://settings/models.
Renders Composer with noModelConnection and asserts the inline hint, the
前往模型设置 link-button, and the explanatory disabled-Send title appear (and are
absent by default) in both locales.
…mnants
Wave A kept the .settingsNotice base rule alive solely for
account-settings-page; Wave B retired that page, so the rule and its
radius-converge entry go with it.
@jackwener
jackwener merged commit ec2b565 into mainJul 19, 2026
3 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage - #1233

Merged
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign
Jul 19, 2026
Merged

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage#1233
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign

Conversation

@jackwener

Copy link
Copy Markdown
Member

One batched campaign PR (goal: 深入分析 UI 质量问题、UX 问题,优化&修复&打磨). Two parallel audits (visual: 45 fixtures × light+dark, 90 captures examined; UX: real-app flow driving + source tracing) produced 13 verified findings; two fix waves + cross-wave integration deliver them.

Visual wave

  • info/warning were indistinguishable — default theme's --info was amber h70 beside --warning h55; now the azure family (h240, matching the blue-info alt themes), dark counterpart included. Contrast contract recomputes and stays green.
  • Two hero-badge treatments (amber Badge vs hand-rolled blue span) → one calm Badge variant=secondary; dead CSS pruned.
  • 权限 已授权 summary tile de-colored (was green while its own row chips were neutral — fix(desktop): status-color restraint — expected states stop shouting green #651).
  • Alert consolidation: four bespoke notice families (settingsNotice / settingsBotInfoNotice / providerUnavailableNotice / settingsHealthError, ~18 call sites in 11 files) migrated onto the Alert primitive; a new quiet passive variant absorbs passive notices without a CSS fork.
  • Bare-text empty states (task ledger, bot overview) → EmptyState primitive; plan countdown pill de-tinted.

UX wave

  • Orphaned 账号 settings section RETIRED — it rendered with 通用's header, no nav highlight, bounced persistence, and dead button-shaped chips; connections live in 模型. Every consumer rerouted (health notice CTA, onboarding blocked CTA, maka-uri, fixtures); headers now derive from a per-section copy map so an unrouted section fails at the type boundary (new contract).
  • Post-skip dead end: no-model state now shows an inline 前往模型设置 hint + explanatory disabled-Send title (constant-footprint contract respected).
  • 使用 no longer clobbers a non-empty composer draft (appends instead).
  • IM 接入 terminology unified (扫码登录/快捷绑定 → 接入 family; fixed the redundant WeChat dialog aria).
  • @ / mention discoverability: zh placeholder now teaches @ 引用文件 / 选择技能 (also fixed a zh/en asymmetry).
  • Fixture/CI coverage: 9 registered-but-never-captured scenarios added to the capture driver (incl. module-mcp and settings-health — the two surfaces the audit flagged hardest); driver teardown SIGTERM→SIGKILL (60s → 2-3s per capture); oauth-relogin fixture now opens the connection detail sheet it was built for.

Deferred with a written plan: a deterministic waiting-state QR-modal fixture (needs a scenario-aware onboarding adapter).

Gates (integrated tree)

desktop 2729 · ui 191 · typecheck · check-dead-css · knip ×2 · console/a11y/copy checks · alignment auditor all fixtures clean. ~20 contracts re-pinned across both waves, none deleted; audits also cleared 8 hypotheses as not-real (documented in the audit reports). Implemented by two opus worktree agents from maintainer-consolidated audit findings.

Default --info was oklch(0.75 0.16 70) amber, colliding with --warning's
orange h55 — info and warning tiers were indistinguishable. Retune the
default light/dark --info to the azure-family blue (h240, matching the
blue-info alt themes) so it reads as informational and stays distinct from
warning. --info-text stays the same color-mix formula; the 406 contrast
contract recomputes dynamically and still passes.
health-center used Badge variant=info while voice used a hand-rolled
.settingsFeatureStatusBadge span (blue via --nav-active) — two treatments
for the same quiet-metadata role (只读快照 / 本地自检). Both now use
Badge variant=secondary (the calm neutral pill, same as plan-reminder's
已完成). Deletes the now-dead .settingsFeatureStatusBadge CSS.
The 已授权 summary tile colored an expected steady state success while its
own granted row chip and health's 正常 tile are neutral. Switch to
tone=neutral per #651 exception-only status color.
task-ledger empty (当前会话没有待推进任务) now uses the compact inline
EmptyState; bot-chat-overview active-list empty (还没有正在使用的渠道) uses
the card EmptyState with a MessageSquare icon, matching skills/daily-review.
Deletes the dead .settingsRemoteAccessEmptyRow CSS, adds a compact-padding
.settingsRemoteAccessEmpty override.
The next-run countdown pill was accent-tinted (--nav-active) on an ordinary
scheduled state. Make it a neutral foreground wash per #651; colour stays
reserved for genuinely exceptional states.
…s in 模型 (U1/U7/U2)
The 'account' SettingsSection rendered AccountSettingsPage but was never in
SETTINGS_NAV, so the header fell back to 通用 copy over the account body, the
sidebar showed no highlight, and a persisted 'account' bounced to models on
reopen. Its connection list and lone 测试凭据 action duplicated the Models page.
- Remove 'account' from SETTINGS_SECTIONS; delete account-settings-page.tsx and
the account-only account-auth-ui.ts + their .account/.auth copy blocks.
- Route every health-notice/onboarding/deep-link consumer to 'models'; drop the
goToAccount copy and the now-single-target ternary. (U2 dead Chips vanish.)
- Derive the settings header from getSettingsNavigationCopy(...).sections[section]
instead of a nav[0] fallback, so an unrouted section fails at the
Record<SettingsSection> boundary instead of silently borrowing a title.
- Add contract tests pinning: every SettingsSection is a nav item (no orphans),
and the header is keyed by the active section, not nav[0].
After an onboarding skip with zero model connections, Send was disabled with no
explanation. Add an inline hint above the composer (一句话 + 前往模型设置
link-button wired to openSettingsSection('models')) and an explanatory title on
the disabled Send. The hint renders OUTSIDE the <form> so it never grows the
composer's constant footprint (#740), honors the same hidden state, and is
theme-aware via tokens.
…ve (F4)
Consolidate four bespoke notice CSS families onto the shared <Alert>:
- .settingsNotice (info + passive) → Alert variant=info / new variant=passive
- .settingsBotInfoNotice → Alert variant=info (drops the ⓘ glyph)
- .providerUnavailableNotice → Alert variant=info + AlertTitle/Description/Action
- .settingsHealthError → Alert variant=error
Adds a quiet neutral 'passive' variant to the Alert primitive (foreground-5
wash, muted text) so passive notices keep their calm look without a CSS fork.
Migrates ~18 call sites across 11 files; role=status semantics preserved.
Deletes the migrated CSS (and now-dead .settingsNotice[data-tone=passive] /
.settingsNoticeAction). .settingsNotice base rule stays for
account-settings-page.tsx (owned by a separate change).
Contract re-pins: radius-converge drops the two deleted selectors; voice-capture
-smoke, settings-app-info and renderer-startup-fail-soft re-pinned to the Alert
structure. Full desktop (2741) + ui (190) suites, typecheck, check-dead-css,
knip ×2, audit-alignment all green.
…4/U5/U6)
U4: useSkillInChat now appendText()s the skill invocation instead of setText,
so an in-progress draft survives (appendText falls back to a plain set on an
empty draft). Re-pin composer-send-guard.
U5: standardize the IM-bot connect verb to 接入 in bot-chat-detail
(扫码登录/快捷绑定 → 扫码接入/快捷接入) and bot-onboarding-modal (WeChat title
扫码登录 → 连接微信, fixing the redundant 扫码登录扫码接入 dialog aria; WeCom
subtitle 快捷绑定 → 快捷接入). 扫码 gesture kept where literal. e2e re-pinned.
(bot-chat-overview.tsx:138 is owned by another agent — left untouched; see report.)
U6: extend the composer placeholder to teach @ 引用文件 / 选择技能 — one subtle,
theme-agnostic placement that also fixes a zh/en asymmetry (en already hinted).
Also prune the dead account-page CSS the retire left behind (settingsAuthContract*,
settingsAuthActionPill, settingsConnectionDefaultBadge, settingsConnectionList,
settingsEmptyState) and re-pin the geometry/roadmap/a11y contracts that referenced
them. Bind the health-notice label to a local so the static a11y checker sees it.
… oauth-relogin detail sheet (#7a/b/d)
7a: add the 9 registered-but-uncaptured scenarios to capture-screenshots
ALL_SCENARIOS (oauth-relogin, settings-permissions/voice/gateway/search/usage/
health, module-mcp, long-transcript) so screenshot review has evidence of them.
7b: driver teardown after the capture marker now SIGKILLs instead of SIGTERM —
the marker is already on disk so Electron has nothing to flush; the audit
measured ~60s of graceful-teardown wait per capture, now 2-3s. No cleanup
regression (per-run user-data dir is under os.tmpdir()).
7d: oauth-relogin fixture now opens the seeded codex-oauth connection's detail
sheet (needs_reauth) instead of just the 模型 section, so the re-login
affordance is what gets captured. Threads a new openConnectionDetailSlug through
VisualSmokeState → app-shell-visual-smoke → useSettingsModal.openConnectionDetail
→ overlays → SettingsModal → SettingsSurface → ProvidersPanel.initialConnectionSlug,
mirroring the existing openProviderCatalog path.
…ple (U1)
conversation-localization: pin the new @ 引用文件 / 选择技能 placeholder in both
locales. markdown-body: the internal-nav-link sanitization example used
maka://settings/account, now invalid after the account section retire — use
maka://settings/models.
Renders Composer with noModelConnection and asserts the inline hint, the
前往模型设置 link-button, and the explanatory disabled-Send title appear (and are
absent by default) in both locales.
…mnants
Wave A kept the .settingsNotice base rule alive solely for
account-settings-page; Wave B retired that page, so the rule and its
radius-converge entry go with it.
@jackwener
jackwener merged commit ec2b565 into mainJul 19, 2026
3 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage - #1233

Merged
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign
Jul 19, 2026
Merged

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage#1233
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign

Conversation

@jackwener

Copy link
Copy Markdown
Member

One batched campaign PR (goal: 深入分析 UI 质量问题、UX 问题,优化&修复&打磨). Two parallel audits (visual: 45 fixtures × light+dark, 90 captures examined; UX: real-app flow driving + source tracing) produced 13 verified findings; two fix waves + cross-wave integration deliver them.

Visual wave

  • info/warning were indistinguishable — default theme's --info was amber h70 beside --warning h55; now the azure family (h240, matching the blue-info alt themes), dark counterpart included. Contrast contract recomputes and stays green.
  • Two hero-badge treatments (amber Badge vs hand-rolled blue span) → one calm Badge variant=secondary; dead CSS pruned.
  • 权限 已授权 summary tile de-colored (was green while its own row chips were neutral — fix(desktop): status-color restraint — expected states stop shouting green #651).
  • Alert consolidation: four bespoke notice families (settingsNotice / settingsBotInfoNotice / providerUnavailableNotice / settingsHealthError, ~18 call sites in 11 files) migrated onto the Alert primitive; a new quiet passive variant absorbs passive notices without a CSS fork.
  • Bare-text empty states (task ledger, bot overview) → EmptyState primitive; plan countdown pill de-tinted.

UX wave

  • Orphaned 账号 settings section RETIRED — it rendered with 通用's header, no nav highlight, bounced persistence, and dead button-shaped chips; connections live in 模型. Every consumer rerouted (health notice CTA, onboarding blocked CTA, maka-uri, fixtures); headers now derive from a per-section copy map so an unrouted section fails at the type boundary (new contract).
  • Post-skip dead end: no-model state now shows an inline 前往模型设置 hint + explanatory disabled-Send title (constant-footprint contract respected).
  • 使用 no longer clobbers a non-empty composer draft (appends instead).
  • IM 接入 terminology unified (扫码登录/快捷绑定 → 接入 family; fixed the redundant WeChat dialog aria).
  • @ / mention discoverability: zh placeholder now teaches @ 引用文件 / 选择技能 (also fixed a zh/en asymmetry).
  • Fixture/CI coverage: 9 registered-but-never-captured scenarios added to the capture driver (incl. module-mcp and settings-health — the two surfaces the audit flagged hardest); driver teardown SIGTERM→SIGKILL (60s → 2-3s per capture); oauth-relogin fixture now opens the connection detail sheet it was built for.

Deferred with a written plan: a deterministic waiting-state QR-modal fixture (needs a scenario-aware onboarding adapter).

Gates (integrated tree)

desktop 2729 · ui 191 · typecheck · check-dead-css · knip ×2 · console/a11y/copy checks · alignment auditor all fixtures clean. ~20 contracts re-pinned across both waves, none deleted; audits also cleared 8 hypotheses as not-real (documented in the audit reports). Implemented by two opus worktree agents from maintainer-consolidated audit findings.

Default --info was oklch(0.75 0.16 70) amber, colliding with --warning's
orange h55 — info and warning tiers were indistinguishable. Retune the
default light/dark --info to the azure-family blue (h240, matching the
blue-info alt themes) so it reads as informational and stays distinct from
warning. --info-text stays the same color-mix formula; the 406 contrast
contract recomputes dynamically and still passes.
health-center used Badge variant=info while voice used a hand-rolled
.settingsFeatureStatusBadge span (blue via --nav-active) — two treatments
for the same quiet-metadata role (只读快照 / 本地自检). Both now use
Badge variant=secondary (the calm neutral pill, same as plan-reminder's
已完成). Deletes the now-dead .settingsFeatureStatusBadge CSS.
The 已授权 summary tile colored an expected steady state success while its
own granted row chip and health's 正常 tile are neutral. Switch to
tone=neutral per #651 exception-only status color.
task-ledger empty (当前会话没有待推进任务) now uses the compact inline
EmptyState; bot-chat-overview active-list empty (还没有正在使用的渠道) uses
the card EmptyState with a MessageSquare icon, matching skills/daily-review.
Deletes the dead .settingsRemoteAccessEmptyRow CSS, adds a compact-padding
.settingsRemoteAccessEmpty override.
The next-run countdown pill was accent-tinted (--nav-active) on an ordinary
scheduled state. Make it a neutral foreground wash per #651; colour stays
reserved for genuinely exceptional states.
…s in 模型 (U1/U7/U2)
The 'account' SettingsSection rendered AccountSettingsPage but was never in
SETTINGS_NAV, so the header fell back to 通用 copy over the account body, the
sidebar showed no highlight, and a persisted 'account' bounced to models on
reopen. Its connection list and lone 测试凭据 action duplicated the Models page.
- Remove 'account' from SETTINGS_SECTIONS; delete account-settings-page.tsx and
the account-only account-auth-ui.ts + their .account/.auth copy blocks.
- Route every health-notice/onboarding/deep-link consumer to 'models'; drop the
goToAccount copy and the now-single-target ternary. (U2 dead Chips vanish.)
- Derive the settings header from getSettingsNavigationCopy(...).sections[section]
instead of a nav[0] fallback, so an unrouted section fails at the
Record<SettingsSection> boundary instead of silently borrowing a title.
- Add contract tests pinning: every SettingsSection is a nav item (no orphans),
and the header is keyed by the active section, not nav[0].
After an onboarding skip with zero model connections, Send was disabled with no
explanation. Add an inline hint above the composer (一句话 + 前往模型设置
link-button wired to openSettingsSection('models')) and an explanatory title on
the disabled Send. The hint renders OUTSIDE the <form> so it never grows the
composer's constant footprint (#740), honors the same hidden state, and is
theme-aware via tokens.
…ve (F4)
Consolidate four bespoke notice CSS families onto the shared <Alert>:
- .settingsNotice (info + passive) → Alert variant=info / new variant=passive
- .settingsBotInfoNotice → Alert variant=info (drops the ⓘ glyph)
- .providerUnavailableNotice → Alert variant=info + AlertTitle/Description/Action
- .settingsHealthError → Alert variant=error
Adds a quiet neutral 'passive' variant to the Alert primitive (foreground-5
wash, muted text) so passive notices keep their calm look without a CSS fork.
Migrates ~18 call sites across 11 files; role=status semantics preserved.
Deletes the migrated CSS (and now-dead .settingsNotice[data-tone=passive] /
.settingsNoticeAction). .settingsNotice base rule stays for
account-settings-page.tsx (owned by a separate change).
Contract re-pins: radius-converge drops the two deleted selectors; voice-capture
-smoke, settings-app-info and renderer-startup-fail-soft re-pinned to the Alert
structure. Full desktop (2741) + ui (190) suites, typecheck, check-dead-css,
knip ×2, audit-alignment all green.
…4/U5/U6)
U4: useSkillInChat now appendText()s the skill invocation instead of setText,
so an in-progress draft survives (appendText falls back to a plain set on an
empty draft). Re-pin composer-send-guard.
U5: standardize the IM-bot connect verb to 接入 in bot-chat-detail
(扫码登录/快捷绑定 → 扫码接入/快捷接入) and bot-onboarding-modal (WeChat title
扫码登录 → 连接微信, fixing the redundant 扫码登录扫码接入 dialog aria; WeCom
subtitle 快捷绑定 → 快捷接入). 扫码 gesture kept where literal. e2e re-pinned.
(bot-chat-overview.tsx:138 is owned by another agent — left untouched; see report.)
U6: extend the composer placeholder to teach @ 引用文件 / 选择技能 — one subtle,
theme-agnostic placement that also fixes a zh/en asymmetry (en already hinted).
Also prune the dead account-page CSS the retire left behind (settingsAuthContract*,
settingsAuthActionPill, settingsConnectionDefaultBadge, settingsConnectionList,
settingsEmptyState) and re-pin the geometry/roadmap/a11y contracts that referenced
them. Bind the health-notice label to a local so the static a11y checker sees it.
… oauth-relogin detail sheet (#7a/b/d)
7a: add the 9 registered-but-uncaptured scenarios to capture-screenshots
ALL_SCENARIOS (oauth-relogin, settings-permissions/voice/gateway/search/usage/
health, module-mcp, long-transcript) so screenshot review has evidence of them.
7b: driver teardown after the capture marker now SIGKILLs instead of SIGTERM —
the marker is already on disk so Electron has nothing to flush; the audit
measured ~60s of graceful-teardown wait per capture, now 2-3s. No cleanup
regression (per-run user-data dir is under os.tmpdir()).
7d: oauth-relogin fixture now opens the seeded codex-oauth connection's detail
sheet (needs_reauth) instead of just the 模型 section, so the re-login
affordance is what gets captured. Threads a new openConnectionDetailSlug through
VisualSmokeState → app-shell-visual-smoke → useSettingsModal.openConnectionDetail
→ overlays → SettingsModal → SettingsSurface → ProvidersPanel.initialConnectionSlug,
mirroring the existing openProviderCatalog path.
…ple (U1)
conversation-localization: pin the new @ 引用文件 / 选择技能 placeholder in both
locales. markdown-body: the internal-nav-link sanitization example used
maka://settings/account, now invalid after the account section retire — use
maka://settings/models.
Renders Composer with noModelConnection and asserts the inline hint, the
前往模型设置 link-button, and the explanatory disabled-Send title appear (and are
absent by default) in both locales.
…mnants
Wave A kept the .settingsNotice base rule alive solely for
account-settings-page; Wave B retired that page, so the rule and its
radius-converge entry go with it.
@jackwener
jackwener merged commit ec2b565 into mainJul 19, 2026
3 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage - #1233

Merged
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign
Jul 19, 2026
Merged

chore(ui): UI/UX quality campaign — audits, token fix, Alert consolidation, flow repairs, fixture coverage#1233
jackwener merged 14 commits into
mainfrom
chore/ui-quality-campaign

Conversation

@jackwener

Copy link
Copy Markdown
Member

One batched campaign PR (goal: 深入分析 UI 质量问题、UX 问题,优化&修复&打磨). Two parallel audits (visual: 45 fixtures × light+dark, 90 captures examined; UX: real-app flow driving + source tracing) produced 13 verified findings; two fix waves + cross-wave integration deliver them.

Visual wave

  • info/warning were indistinguishable — default theme's --info was amber h70 beside --warning h55; now the azure family (h240, matching the blue-info alt themes), dark counterpart included. Contrast contract recomputes and stays green.
  • Two hero-badge treatments (amber Badge vs hand-rolled blue span) → one calm Badge variant=secondary; dead CSS pruned.
  • 权限 已授权 summary tile de-colored (was green while its own row chips were neutral — fix(desktop): status-color restraint — expected states stop shouting green #651).
  • Alert consolidation: four bespoke notice families (settingsNotice / settingsBotInfoNotice / providerUnavailableNotice / settingsHealthError, ~18 call sites in 11 files) migrated onto the Alert primitive; a new quiet passive variant absorbs passive notices without a CSS fork.
  • Bare-text empty states (task ledger, bot overview) → EmptyState primitive; plan countdown pill de-tinted.

UX wave

  • Orphaned 账号 settings section RETIRED — it rendered with 通用's header, no nav highlight, bounced persistence, and dead button-shaped chips; connections live in 模型. Every consumer rerouted (health notice CTA, onboarding blocked CTA, maka-uri, fixtures); headers now derive from a per-section copy map so an unrouted section fails at the type boundary (new contract).
  • Post-skip dead end: no-model state now shows an inline 前往模型设置 hint + explanatory disabled-Send title (constant-footprint contract respected).
  • 使用 no longer clobbers a non-empty composer draft (appends instead).
  • IM 接入 terminology unified (扫码登录/快捷绑定 → 接入 family; fixed the redundant WeChat dialog aria).
  • @ / mention discoverability: zh placeholder now teaches @ 引用文件 / 选择技能 (also fixed a zh/en asymmetry).
  • Fixture/CI coverage: 9 registered-but-never-captured scenarios added to the capture driver (incl. module-mcp and settings-health — the two surfaces the audit flagged hardest); driver teardown SIGTERM→SIGKILL (60s → 2-3s per capture); oauth-relogin fixture now opens the connection detail sheet it was built for.

Deferred with a written plan: a deterministic waiting-state QR-modal fixture (needs a scenario-aware onboarding adapter).

Gates (integrated tree)

desktop 2729 · ui 191 · typecheck · check-dead-css · knip ×2 · console/a11y/copy checks · alignment auditor all fixtures clean. ~20 contracts re-pinned across both waves, none deleted; audits also cleared 8 hypotheses as not-real (documented in the audit reports). Implemented by two opus worktree agents from maintainer-consolidated audit findings.

Default --info was oklch(0.75 0.16 70) amber, colliding with --warning's
orange h55 — info and warning tiers were indistinguishable. Retune the
default light/dark --info to the azure-family blue (h240, matching the
blue-info alt themes) so it reads as informational and stays distinct from
warning. --info-text stays the same color-mix formula; the 406 contrast
contract recomputes dynamically and still passes.
health-center used Badge variant=info while voice used a hand-rolled
.settingsFeatureStatusBadge span (blue via --nav-active) — two treatments
for the same quiet-metadata role (只读快照 / 本地自检). Both now use
Badge variant=secondary (the calm neutral pill, same as plan-reminder's
已完成). Deletes the now-dead .settingsFeatureStatusBadge CSS.
The 已授权 summary tile colored an expected steady state success while its
own granted row chip and health's 正常 tile are neutral. Switch to
tone=neutral per #651 exception-only status color.
task-ledger empty (当前会话没有待推进任务) now uses the compact inline
EmptyState; bot-chat-overview active-list empty (还没有正在使用的渠道) uses
the card EmptyState with a MessageSquare icon, matching skills/daily-review.
Deletes the dead .settingsRemoteAccessEmptyRow CSS, adds a compact-padding
.settingsRemoteAccessEmpty override.
The next-run countdown pill was accent-tinted (--nav-active) on an ordinary
scheduled state. Make it a neutral foreground wash per #651; colour stays
reserved for genuinely exceptional states.
…s in 模型 (U1/U7/U2)
The 'account' SettingsSection rendered AccountSettingsPage but was never in
SETTINGS_NAV, so the header fell back to 通用 copy over the account body, the
sidebar showed no highlight, and a persisted 'account' bounced to models on
reopen. Its connection list and lone 测试凭据 action duplicated the Models page.
- Remove 'account' from SETTINGS_SECTIONS; delete account-settings-page.tsx and
the account-only account-auth-ui.ts + their .account/.auth copy blocks.
- Route every health-notice/onboarding/deep-link consumer to 'models'; drop the
goToAccount copy and the now-single-target ternary. (U2 dead Chips vanish.)
- Derive the settings header from getSettingsNavigationCopy(...).sections[section]
instead of a nav[0] fallback, so an unrouted section fails at the
Record<SettingsSection> boundary instead of silently borrowing a title.
- Add contract tests pinning: every SettingsSection is a nav item (no orphans),
and the header is keyed by the active section, not nav[0].
After an onboarding skip with zero model connections, Send was disabled with no
explanation. Add an inline hint above the composer (一句话 + 前往模型设置
link-button wired to openSettingsSection('models')) and an explanatory title on
the disabled Send. The hint renders OUTSIDE the <form> so it never grows the
composer's constant footprint (#740), honors the same hidden state, and is
theme-aware via tokens.
…ve (F4)
Consolidate four bespoke notice CSS families onto the shared <Alert>:
- .settingsNotice (info + passive) → Alert variant=info / new variant=passive
- .settingsBotInfoNotice → Alert variant=info (drops the ⓘ glyph)
- .providerUnavailableNotice → Alert variant=info + AlertTitle/Description/Action
- .settingsHealthError → Alert variant=error
Adds a quiet neutral 'passive' variant to the Alert primitive (foreground-5
wash, muted text) so passive notices keep their calm look without a CSS fork.
Migrates ~18 call sites across 11 files; role=status semantics preserved.
Deletes the migrated CSS (and now-dead .settingsNotice[data-tone=passive] /
.settingsNoticeAction). .settingsNotice base rule stays for
account-settings-page.tsx (owned by a separate change).
Contract re-pins: radius-converge drops the two deleted selectors; voice-capture
-smoke, settings-app-info and renderer-startup-fail-soft re-pinned to the Alert
structure. Full desktop (2741) + ui (190) suites, typecheck, check-dead-css,
knip ×2, audit-alignment all green.
…4/U5/U6)
U4: useSkillInChat now appendText()s the skill invocation instead of setText,
so an in-progress draft survives (appendText falls back to a plain set on an
empty draft). Re-pin composer-send-guard.
U5: standardize the IM-bot connect verb to 接入 in bot-chat-detail
(扫码登录/快捷绑定 → 扫码接入/快捷接入) and bot-onboarding-modal (WeChat title
扫码登录 → 连接微信, fixing the redundant 扫码登录扫码接入 dialog aria; WeCom
subtitle 快捷绑定 → 快捷接入). 扫码 gesture kept where literal. e2e re-pinned.
(bot-chat-overview.tsx:138 is owned by another agent — left untouched; see report.)
U6: extend the composer placeholder to teach @ 引用文件 / 选择技能 — one subtle,
theme-agnostic placement that also fixes a zh/en asymmetry (en already hinted).
Also prune the dead account-page CSS the retire left behind (settingsAuthContract*,
settingsAuthActionPill, settingsConnectionDefaultBadge, settingsConnectionList,
settingsEmptyState) and re-pin the geometry/roadmap/a11y contracts that referenced
them. Bind the health-notice label to a local so the static a11y checker sees it.
… oauth-relogin detail sheet (#7a/b/d)
7a: add the 9 registered-but-uncaptured scenarios to capture-screenshots
ALL_SCENARIOS (oauth-relogin, settings-permissions/voice/gateway/search/usage/
health, module-mcp, long-transcript) so screenshot review has evidence of them.
7b: driver teardown after the capture marker now SIGKILLs instead of SIGTERM —
the marker is already on disk so Electron has nothing to flush; the audit
measured ~60s of graceful-teardown wait per capture, now 2-3s. No cleanup
regression (per-run user-data dir is under os.tmpdir()).
7d: oauth-relogin fixture now opens the seeded codex-oauth connection's detail
sheet (needs_reauth) instead of just the 模型 section, so the re-login
affordance is what gets captured. Threads a new openConnectionDetailSlug through
VisualSmokeState → app-shell-visual-smoke → useSettingsModal.openConnectionDetail
→ overlays → SettingsModal → SettingsSurface → ProvidersPanel.initialConnectionSlug,
mirroring the existing openProviderCatalog path.
…ple (U1)
conversation-localization: pin the new @ 引用文件 / 选择技能 placeholder in both
locales. markdown-body: the internal-nav-link sanitization example used
maka://settings/account, now invalid after the account section retire — use
maka://settings/models.
Renders Composer with noModelConnection and asserts the inline hint, the
前往模型设置 link-button, and the explanatory disabled-Send title appear (and are
absent by default) in both locales.
…mnants
Wave A kept the .settingsNotice base rule alive solely for
account-settings-page; Wave B retired that page, so the rule and its
radius-converge entry go with it.
@jackwener
jackwener merged commit ec2b565 into mainJul 19, 2026
3 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jackwener