Skip to content

fix(desktop): status-color restraint — expected states stop shouting green - #651

Merged
jackwener merged 1 commit into
mainfrom
fix/status-color-restraint
Jul 8, 2026
Merged

fix(desktop): status-color restraint — expected states stop shouting green#651
jackwener merged 1 commit into
mainfrom
fix/status-color-restraint

Conversation

@jackwener

Copy link
Copy Markdown
Member

Addresses the maintainer's report: 权限与能力 still shows residual greens, and permission-toggle toasts show a green left edge ('AI 味').

Diagnosis first: not a token leak

All of these greens ride var(--success) through the governed token system — theme governance is intact. The problem is success-color overuse: the EXPECTED state (granted / enabled / verified / healthy) was painted green across large surfaces, which both reads as old-brand residue and is the classic template-design tell (colored stripe on every card).

Rule applied — exception-only status color

Normal states render neutral. Color is reserved for: states needing attention (denied / not_determined / degraded / needs_reauth), tiny dot-tier indicators (bot status dots, model-list status lines), transient action feedback (copy-success flash), and comparative semantics (permission diff old/new tags, hljs-addition).

Changes

  • Toasts: the per-variant colored left stripe (success green included) is gone for ALL variants — the tinted icon on a neutral card is the single signal
  • 权限与能力: granted rows lose stripe + icon fill + green badge (label 已授权 carries the meaning); enabled capability cards, healthy layer text, granted chips, granted summary count all neutral; denied/waiting keep their color; the 已拒绝 tile only turns red when count > 0, and zero-count tiles dim (matching the health page)
  • 健康: healthy tiles/signal rows keep the default hairline
  • 模型连接: verified/authenticated cards use default card chrome; success badges/pills fall to neutral; OAuth logged-in badge neutral
  • 记忆: the save-summary box is a neutral card, not a green wash
  • Adds a settings-permissions visual-smoke fixture (page had no screenshot baseline)

Verification

  • Desktop suite 2262/2262 green, check-dead-css clean
  • CDP before/after of the permission page confirms: only the genuine exception (等待授权 microphone) still carries color

…green

The 权限与能力 page (and siblings) painted the EXPECTED state green
everywhere: granted OS rows wore a green left stripe + green icon fill +
green 已授权 badge, enabled capability cards a green border, healthy
layers green text, granted chips green pills, the granted summary count
a green number — and every toast carried a colored left stripe. None of
these were token leaks (all rode --success); they were success-color
overuse plus the stripe-on-card template trope.

Rule applied (exception-only): the normal state renders neutral; color
is reserved for states needing attention (denied/degraded/waiting),
tiny dot-tier indicators, transient feedback (copy-成功 flash), and
comparative semantics (permission diff old/new, hljs-addition).

- toast.css: per-variant left stripes removed — the tinted icon is the
  single variant signal on a neutral card
- permission page: granted/enabled/success de-tinted across rows, cards,
  chips, layers, summary tiles; denied/not_determined keep their stripes;
  已拒绝 tile only turns red when count > 0 (a red 0 is a false alarm)
  and empty tiles dim like the health page's
- health page: healthy tiles/rows keep the default hairline
- connections: verified/authenticated cards use default chrome;
  success badges/pills fall to the neutral base; OAuth logged-in badge
  neutral
- memory: save-summary box neutral card instead of green wash
- new settings-permissions visual-smoke fixture so this page finally has
  a screenshot baseline
@jackwener
jackwener merged commit dc49e08 into main Jul 8, 2026
3 checks passed
@jackwener
jackwener deleted the fix/status-color-restraint branch July 8, 2026 18:11
jackwener added a commit that referenced this pull request Jul 9, 2026
…s neutral (#696)

使用统计 and 健康 were the last two settings pages without visual-smoke
fixtures — added (settings-usage / settings-health), and the first sweep
found:
- 使用统计: the amber notice's 显示明细 action was a bare text ghost —
  outline per the text-buttons-wear-edges rule
- 健康: ok/正常 still rendered success-green on chips AND summary tiles,
  while the same #651 restraint rule already made 权限 已授权 neutral —
  expected states are neutral; color stays reserved for signals needing
  attention (提示/警告/错误 keep their tones)

Desktop 2296/2296, dead-css clean.
jackwener added a commit that referenced this pull request Jul 11, 2026
…mary, honest fade (round 16) (#722)

Post-pull discovery sweep over the externally-merged tip (LiteLLM,
Goal #625, FormatJson, tool-row rework — all reviewed clean, incl. the
Goal kill-switch pill which correctly absorbed the earlier review
blockers). Three finds, fixed:

- 每日回顾 已生成 chip still success-green — the EXPECTED outcome per
  the #651 restraint rule (健康 正常 and 权限 已授权 are already
  neutral); failures keep destructive.
- 机器人对话 had NO primary at all — 测试并连接 (the form's completion
  action) promoted from secondary to default.
- first-run provider list: the 28px bottom fade left a half-clipped row
  still half-legible — read as broken clipping; 56px fully veils it.

Also re-learned: the desktop test suite does not compile renderer pages
(a JSX syntax error sailed through 2309 green tests and only vite/
typecheck caught it) — full typecheck now part of this round's gate.
Desktop 2309/2309 + typecheck + dead-css clean; CDP captures verified.
jackwener added a commit that referenced this pull request Jul 18, 2026
* fix(daily-review): compact 跟随设置 label for the panel's inline 分析模型 picker

The run model-option default read 使用设置中的分析模型 — too long for the
compact picker that now rides the PageHeader actions slot (it wrapped to two
lines). Shorten to 跟随设置; a run with no explicit override still falls back to
the model configured in Settings (behavior unchanged — the sentinel value and
bridge mapping are untouched).

* feat(daily-review): real IA redesign of the 每日回顾 module page

The page was 很乱/很不好用: two floating control rows with unclear ownership, a
broken master-detail archive list that left its left column half-empty, a
status Chip on the EXPECTED 已生成 state (#651), a floating orphan line at the
page bottom, and a stats section that vanished entirely on a no-activity day.

Rebuild the information architecture (zero data/IPC/behavior change — same
props, callbacks, guards; contract-pinned run/copy/archive plumbing preserved):

- PageHeader is the page shell: title + subtitle, and the 生成每日回顾 /
  生成深度分析 actions ride its actions slot (skills-page pattern). The analysis
  model select is now a COMPACT generation option in that same cluster, not a
  page-wide row. Header moves into the panel so the actions can compose with the
  panel's run state; the bridge-less fallback keeps its own static header.
- ONE time-scope row under the header: the 今日/本周/本月 segmented + the
  day-stepper are one visual cluster (both are time navigation) instead of two
  widgets at opposite page corners.
- 概览 section is ALWAYS rendered: SectionHeader + the StatTile row shown with
  honest zeros even on a no-activity scope, plus one inline muted hint. This
  absorbs the old bottom-of-page orphan line, which is deleted — the stats
  section no longer disappears into a hole.
- 报告 section replaces the broken master-detail with a stacked, newest-first
  list of full-width report surfaces. Each shows an always-visible meta head
  (date · 模式 · N 对话 · 触发+时间 · 模型 as muted prose — NO 已生成 chip); the
  selected report expands its four content sections in place. Status color is
  exception-only now: only 生成失败 / 缺少模型 raise a Chip. Single-selection
  lazy body load (getArchive) is unchanged — the archive-body-load contract
  pins that path.
- Empty report state is the EmptyState primitive with a CTA wired to
  生成每日回顾 (respects the pending/running guards).
- Export (复制/粘到输入框/保存) rides the 概览 SectionHeader action slot, gated
  to a non-empty scope (guard shape pinned by the copy-feedback contract).

Re-pin: radius-converge-contract's .maka-daily-review-archive-body surface
entry follows the rename to .maka-daily-review-report (the surface owns the
radius; the expanded body divides with a hairline).
jackwener added a commit that referenced this pull request Jul 18, 2026
…page onto the house language (#1205)

* test(visual-smoke): add module-mcp fixture scenario

Seeds the 扩展 → MCP module surface (sidebarSection 'mcp') with an mcp.json
holding two disabled servers, so the market grid, tab row, hero banner and
installed server list all render deterministically for the alignment auditor
and CDP capture. enabled:false keeps visual-smoke from attempting real npx /
HTTP connections, so rows settle in the neutral 已停用 state.

* feat(mcp): polish MCP page onto the house language

Converge the MCP module page onto the same bar as the 技能 page and the 每日回顾
redesign, presentation-only (same IPC/props/callbacks):

- Hero header now rides the shared PageHeader primitive (as=h2, title/subtitle
  + edged actions cluster; 添加 MCP is the primary CTA).
- Market/installed tabs move onto the shared underline TabsList/TabsTrigger
  primitive with count pills, matching the skills tab-row rhythm; the search
  field pins to the tab row's right edge.
- Empty + no-results states use the shared EmptyState primitive.
- Installed server status is exception-only (#651): a healthy/expected server
  shows no status color — its label rides plain muted text and a neutral dot;
  only a real 连接失败 raises a destructive Chip + colored dot.
- The 把 Maka 连接到你的工作环境 banner's pictogram cluster becomes labeled
  library-icon tiles (Terminal/Plug/Globe), and the per-template market marks
  drop their hand-drawn brand SVGs for the catalog text mark on the existing
  brand-tinted tile (de-inlines all 6 inline <svg> in this file — Task A).

* feat(icons): govern the inline-SVG ban and route menu glyphs through the funnel

Hand-drawn inline SVG is an anti-pattern — generic UI glyphs must ride the
@maka/ui/icons lucide funnel so they share one stroke, sizing seam and family.

- menu.tsx: the checkbox + radio ItemIndicator glyphs drop their inline check
  <svg> for <Check> from ../icons.js (same precedent as model-picker's
  ItemIndicator), keeping the lucide funnel intact.
- icon-governance-contract: a new pin machine-walks packages/ui/src and
  apps/desktop/src/renderer (the same tree-walk the repo-wide OverlayScrollbars
  ban uses) and fails on any <svg> literal in a .tsx outside an audited
  brand-mark allowlist (bot-brand-logo.tsx, provider-brand-marks.tsx). The
  allowlist is guarded against rot; new offenders fail with a message pointing
  at @maka/ui/icons. icons.tsx needs no exemption — it is a pure lucide
  re-export with zero <svg> literals.

* chore(audit): cover module-mcp in the row-alignment auditor

The MCP module page now has a visual-smoke fixture; add it to the
audit-alignment FIXTURES so the row-alignment governance tool sweeps the
polished header, tab row, market grid and installed list too.
jackwener added a commit that referenced this pull request Jul 19, 2026
…ation, flow repairs, fixture coverage (#1233)

* fix(tokens): shift default --info from amber to cool blue (F1)

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.

* fix(settings): converge hero metadata badges onto Badge secondary (F2)

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.

* fix(settings): permission 已授权 summary tile neutral not success (F3)

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.

* fix(ui): route bare empty states through EmptyState primitive (F5)

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.

* fix(plans): neutral next-trigger countdown pill on expected state (F6)

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.

* fix(settings): retire orphaned account section, centralize connections 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].

* fix(composer): guide the post-skip no-model dead end (U3)

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.

* fix(settings): migrate hand-rolled notice families onto Alert primitive (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.

* fix(chat): preserve draft on 使用, standardize 接入, teach @/ mentions (U4/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.

* test(visual-smoke): capture the 9 missing scenarios, faster teardown, 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.

* test(ui): re-pin composer placeholder (U6) and markdown nav-link example (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.

* test(ui): pin the U3 no-model composer hint + disabled-send title

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.

* chore(ui): cross-wave integration — retire the last settingsNotice remnants

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 added a commit that referenced this pull request Jul 20, 2026
…itive, dense health/permission rows, dialect cleanup (#1274)

* feat(ui): extract DataTable primitive from usage stats table

Promote the #1252 hairline table recipe out of usage-settings-page into a
shared DataTable primitive (@maka/ui) now that it has health + permission
consumers. The primitive owns the table markup: muted+medium scoped col
headers, first-cell-as-scoped-row-header, hairline row separators,
right-aligned tabular-nums numeric columns, and one grow column that absorbs
slack while the rest size to content on one line.

UsageStatsTable becomes a thin wrapper: it keeps the usage-local empty-tab
routing (EmptyState icon+copy) that a table primitive deliberately omits, and
delegates the non-empty branch to <DataTable>. Rendering is pixel-identical —
every utility class and the settingsUsageTable pin class are preserved.

Re-pin: the moved table markup is now guarded by packages/ui data-table.test
(renderToStaticMarkup); settings-usage-contract's stats-table assertions follow
the split — keep the wrapper's typed-column + EmptyState signature, assert
delegation to <DataTable>, and forbid a hand-rolled <table>.

* refactor(settings): kill the .settingsAboutPrivacy blue-header dialect

The uppercase brand-blue h3 + brand-deep tinted card was a fourth section-
header voice, used by 关于's privacy card AND (confusingly) 数据's 配置导入
导出 header — the reason 数据's export section wore a blue header.

Converge both onto the sanctioned language:
- 关于 privacy → SectionHeader (as=h3) + Alert variant=passive holding the
  privacy points (a quiet 'just so you know' callout; matches the passive
  Alert language already on the data page's backup notice).
- 数据 config → SectionHeader (as=h3, configHelp as subtitle) over the form
  controls, no card chrome (it's a form, not a callout).

Widen SectionHeader's `as` to accept h3 (a section under a page h2). Delete
the .settingsAboutPrivacy CSS block (incl. the green ::marker) for neutral
gap containers. Re-pin radius-converge (drop the stale selector) and
settings-app-info (name now scopes at the section) contracts.

* polish(ui): converge search pre-search hint + fix gateway 5-tile grid

- search-modal: route the pre-search guidance line through the shared
  EmptyState inline primitive instead of a bare <p>, so the 'start typing'
  hint uses the sanctioned empty-state language (keeps the placeholder pin
  class so layout is unchanged).
- open-gateway: the posture strip has five metric tiles but reused the
  four-column .settingsUsageSummary grid, orphaning the fifth on a second
  row. Give it .settingsGatewaySummary — 5-up on wide, clean 3-up (3 + 2)
  wrap when the pane narrows.

* polish(permissions): drop the OS-row status stripe (#651)

Each OS-permission row already shows its state as a Badge; the left-edge
status stripe (amber on 等待授权, red on 已拒绝) was a redundant second status
channel, and the amber stripe read as a standing alarm on an expected waiting
state. Remove both stripe rules and the now-orphaned position:relative anchor;
the row's Chip/text is the single state signal (the denied icon tint stays as
the one exception accent).

* refactor(providers): converge .providerRootHeader onto SectionHeader

The 已连接 and 添加新连接 section titles were a bespoke flex header (bold h3
+ muted help + far-right count) — a fourth section-header dialect. Converge
both onto the shared SectionHeader primitive: title + subtitle, and the
connection count rides SectionHeader's count slot (tabular-nums baked in).

Extend SectionHeader with an optional titleId so the catalog section keeps
its aria-labelledby link. Delete the .providerRootHeader CSS family. Re-pin:
tabular-nums-converge (drop the removed selector, add a SectionHeader
primitive pin) and provider-navigation (catalog title now on SectionHeader
via titleId).

* polish(settings): converge health/permission onto dense shared-language rows

Polish wave (wave 2) — kill the bordered-block dialect on the Health and
Permission Center pages and the redundant gray intro banners.

- Item 2 (健康): HealthSignalRow bordered blocks -> shared Item row
  primitive inside one hairline card; prose message + detail keep stacked
  secondary lines, status is a squared Chip (exception-only color via its
  tone scale), send/capability blockers are exception Chips. Meta on one
  tabular-nums line. Deleted the bespoke .settingsHealthSignalRow* CSS.
- Item 3 (权限): CapabilityRow bordered blocks -> dense hairline-divided
  rows in one card matching the OS permission list above; the nested gray
  boxed .settingsCapabilityLayers dl becomes structured secondary lines
  (no nested surface). readiness Badge -> Chip. Kept the dl + aria-label
  and the diagnostics collapse.
- Item 5: the second gray-banner PageHeader on each page
  (.settingsHealthIntro / .settingsPermissionIntro) restating the page
  title -> SectionHeader; unique subtitle kept, badge/timestamp/refresh
  moved into the action slot, banner chrome dropped.

Re-pins: radius-converge SELECTOR_TIER drops the retired
.settingsHealthIntro/.settingsPermissionIntro/.settingsCapabilityRow radius
entries and adds the new .settingsCapabilityList/.settingsHealthSignalList
card entries. roadmap-cleanup + capability-audit contracts unchanged.

Verified light+dark via direct CDP over settings-health / settings-permissions.
Gates: desktop main + @maka/ui suites, full typecheck, check-dead-css,
audit-alignment (settings-permissions clean) all green.

* test(gateway): re-pin summary-strip aria assertions to settingsGatewaySummary

Wave 1 renamed the tile strip class (fixing the 4+1 orphan grid) and kept
role=group + aria-label, but the network-gateway contract's regexes pinned
the old settingsUsageSummary class name — the one re-pin the wave missed.
Also corrects the earlier triage: the failure was branch-caused, not
pre-existing on main.
Sign up for free to 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