The palette is restated for dark rather than inverted: elevation reads
through lightness on dark and through darkness on light, so the floor is
the darkest surface here and the second-lightest there. The clay hue is
kept and lifted, because #85432f is 2.1:1 on a dark ground. All 37
themeable tokens are covered, in an OS-preference block and a data-theme
block that a contract test keeps identical, and ink, action, danger,
success and warning are asserted at 4.5:1 against the dark canvas.
Four raw colors that would have stayed light-theme values are tokenised
(the avatar hairline, the sheen sweep, a one-off shadow, a literal
warning hex). The QR keeps literal white in both themes, since scanners
need light modules to be light, and print keeps white paper.
The four root boundary pages cannot read a token, so they restate the
handful they need in both themes. forbidden.tsx also stops painting a
bespoke near-black page in four colours that appear nowhere in the
palette, which broke the rule that dark ink is never a page-scale
surface.
Also fixes what the audit found in DESIGN.md itself: two ink values that
had drifted from the code, a motion tier documented at 360ms that was
never implemented, a breakpoint section claiming three tiers where the
stylesheet has eleven, an undocumented report-paper palette, and an admin
section describing a bespoke panel that antd + Refine replaced. Five
zero-reference admin rules go with it.
The sidebar gets the accent, opaque drawer, heading rank and empty-state
guidance settled earlier, and fenced code blocks finally get a container.
BUG-439.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
The nav buttons defaulted to centered for the desktop icon rail and were
flipped back by `[data-state="expanded"]`. That attribute tracks the
desktop open state, while the mobile drawer runs off `openMobile`, so on
a phone the labels rendered but the override never matched: 新建对话 and
我的报告 floated mid-drawer while every other row sat flush left.
Left alignment is the base layer now, correct for both the drawer and the
expanded desktop sidebar, and centering moved into the >=768px collapsed
block beside the rest of the rail rules. That was the only top-level
`[data-state=...]` selector left in the stylesheet.
personal-report-entry asserted `justify-content: center` on the report
button, which is the defect itself; the value is updated and the rest of
that assertion stands.
BUG-438.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
Narrow screens were wrapping 已执行 into a glyph column and painting every sidebar row the same gray, so history looked like top-level chrome.
Co-authored-by: Cursor <cursoragent@cursor.com>
Thinking disappeared on failure and never reached session storage. Keep the
sanitized chain on disk and on errors, and regroup the sidebar around reports,
charts, favorites, and dated history titles.
Co-authored-by: Cursor <cursoragent@cursor.com>
Use the quiet overlay scrollbar on chat and the house board, replace the blocking mobile dialog with a bottom sheet, and emit agent deltas before billing so users can read progress and stop a run.
Co-authored-by: Cursor <cursoragent@cursor.com>
BUG-322 added an is-rectification modifier on SidebarInset, but the sidebar source contract still required a fixed className and failed staging frontend CI.
Co-authored-by: Cursor <cursoragent@cursor.com>
The sidebar contract read a selector with indexOf, so it returned whichever
rule appeared earliest in the file. A responsive override added above the base
rule made the gate report a missing min-height that was never removed, which
blocked staging on a change that was correct.
Share one helper that strips comments, parses rule by rule, accepts a whole
group as the query, and returns the declarations of every matching rule. The
union also makes a negative assertion mean no rule may declare the property,
which is what these contracts intend.
The helper was duplicated in two files and had no tests of its own; it now has
regressions for each way it read the wrong block.
Co-authored-by: Cursor <cursoragent@cursor.com>