Repository navigation
fix(web): composer alignment, Cool Darker hovers, transcript cutoff - #107
Conversation
Three fork UI fixes surfaced after the 2026-09-01 upstream sync. Prompt text sat 5px low. Upstream's new attach-files button (pingdotgg#8236) rides the prompt row's action cluster at icon-sm (28px) beside the fork's 24px send button; the row is items-end, so the taller cluster dropped the one-line prompt and grew the 44px base row to 48. Every button in the cluster now shares the 24px square, and the attach button hovers with the ghost controls' 4% lift instead of --accent, which on the Cool palettes is the prompt surface it sits on. Popup row hovers used --accent, which the fork palettes make an opaque grey: the popup's own fill on the Cool palettes (hover vanished), a bluish slab on the wallpaper-tinted floor under Cool Darker glass, and never a match for the branch picker's selected "current" row. Menu, select and combobox rows now hover with the selected row's 8% foreground wash (12% on a selected or checked row). Under Cool Darker glass nothing --accent lands on is #282d30 any more — the stage is a neutral tint with the wallpaper's cast, the cards are washes, the popups stand on the warm floor — so every accent hover in the main pane was a bluish slab. The glass root block and both sidebar blocks restate --accent (and --fork-pill-hover) as rgb(255 255 255 / 8%), which lands on the stage where the opaque token used to so upstream's bg-accent/N variants keep their relative weight. Guards pin the cluster geometry, the attach hover, the popup row rules and the glass washes. Manifest entries updated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
The glass --accent restatement is the right move: one token change instead of chasing ~40 bg-accent utilities. Popup row hover belongs in fork-popup-surface and correctly matches the selected-row wash rather than the opaque grey. Those parts are structured correctly.
Do not merge as-is. The composer cluster rules encode a false invariant — that every direct-child button in [data-chat-composer-actions="right"] is a 24px ghost — and the guard now pins that selector. ComposerPrimaryActions already puts a labeled Refine submit in that same slot as a direct child. Target attach the way send/stop are targeted, and fold the new rules into the size + ghost-hover declarations that already exist.
Sent by Cursor Automation: Thermo nuke 4.6
| :root[data-fork="noahhendrickson-t3code"] [data-chat-composer-actions="right"] > button { | ||
| width: 24px; | ||
| height: 24px; | ||
| min-height: 24px; | ||
| border-radius: 4px; | ||
| } |
There was a problem hiding this comment.
This pushes a false model into an already busy composer overlay: “whatever else rides the prompt row’s action cluster” is a 24px square. That is not true of this slot.
ComposerFooterPrimaryActions is a fragment, so ComposerPrimaryActions children hoist into [data-chat-composer-actions="right"]. The default send/stop path is a direct <button> (already sized by [data-fork-composer-action]). The plan-follow-up Refine path is also a direct child — a labeled size="sm" submit, not an icon — and this rule will squash it to 24×24 / radius 4. The hover sibling above (> button:not([data-fork-composer-action])) will also steal that filled control’s hover for the 4% ghost lift. Implement happens to escape only because it wraps in a div; Refine does not. That is spaghetti via negative space, and the new composer-shell guard locks the over-broad selector in.
Code judo: stop describing attach as “everything that isn’t send.” Give it the same vocabulary send/stop already have (data-fork-composer-action="attach", fenced in ChatComposer the way the other actions are), then:
- fold the 24px box into the existing
[data-fork-composer-action]rule (delete this copy) - fold the hover into the existing
--fork-composer-control-hoverghost rule, or key it on="attach"
A CSS-only :not([type="submit"]) patch would dodge Refine today and still break the next type="button" that lands in this cluster. The cluster is not a 24px icon row; only attach needed to join it.
There was a problem hiding this comment.
Fixed in 6cf45a9. The attach button is now stamped data-fork-composer-action="attach" (fenced in ChatComposer), the 24px box comes from the existing [data-fork-composer-action] rule, and the 4% ghost hover is keyed on ="attach". Both position-based > button rules are gone. The guard now pins the stamp and rejects any rule matching data-chat-composer-actions … > button, so the over-broad selector cannot come back. Verified live: attach 24×24 at 4px radius, pressed fill rgba(255,255,255,0.04), send untouched.
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Upstream pingdotgg#8799 renamed --topbar-scroll-fade-height to --workspace-titlebar-scroll-fade-height. The fork's timeline cutoff folds upstream's top fade into its own mask stack by reading that variable, so whenever the top fade was on the reference resolved to nothing, the whole mask-size declaration went invalid at computed-value time, every layer fell back to auto, and the solid scrollbar-gutter layer covered the scroller — the transcript, streaming replies included, painted straight through the composer. The rule now reads upstream's current name. The guard used to pin the old name as literal text, which is why it stayed green while the cutoff broke; it now reads the variable name out of index.css's own topbar-scroll-fade utility and asserts the fork references that. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
NoahHendrickson
left a comment
There was a problem hiding this comment.
Blocking issue: the new direct-child selector in theme.custom.css sizes every button under [data-chat-composer-actions="right"] to 24x24, not just Attach files. ComposerPrimaryActions renders the labeled Refine submit as a direct child in the showPlanFollowUpPrompt + promptHasText state, so that primary action is clipped to a 24px square. The adjacent ghost-hover selector also matches Refine because it lacks data-fork-composer-action, replacing its message-action hover. Please stamp the attach control explicitly (the way send/stop are stamped) and scope both sizing and hover to that marker; update the guard to pin the narrower contract.
…sition Review on #107: the `[data-chat-composer-actions="right"] > button` rules encoded a false invariant. ComposerPrimaryActions hoists a labelled Refine submit into that cluster as a direct child, so the sizing rule would squash it to a 24px square and the ghost-hover rule would replace its message-action hover. The attach button now carries data-fork-composer-action="attach", fenced in ChatComposer the way send and stop are stamped. The 24px box comes from the existing [data-fork-composer-action] rule and the 4% ghost hover is keyed on ="attach". The guard pins the stamp and rejects any rule that selects the cluster's buttons by position. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Addressed the blocking issue in 6cf45a9: the attach control is stamped |


Problem
Four fork UI defects surfaced after the 2026-09-01 upstream sync (#105), all seen on the Cool Darker palette.
icon-sm(28px) beside the fork's 24px send button. The row isitems-end, so the taller cluster pushed the one-line prompt 5px below the send axis and grew the 44px base row to 48.--accentand marks the selected row with an 8% foreground wash. The fork palettes make--accentan opaque grey: on the Cool palettes it is the popup's own fill, so a hovered row vanished; under Cool Darker glass the popup stands on the wallpaper-tinted floor and the same opaque cool fill landed as a bluish slab on a warm surface (the git actions menu); and in the branch picker the hovered row never matched the "current" row beside it.--accentlands on is#282d30any more — the stage is a neutral tint with the wallpaper's cast, the cards are washes, the popups stand on the warm floor. A sweep of the web app found ~40bg-accenthover/selected utilities in the main pane, all affected.--topbar-scroll-fade-heightto--workspace-titlebar-scroll-fade-height. The fork's timeline cutoff folds upstream's top fade into its own mask stack by reading that variable, so whenever the top fade was on the unresolvedvar()invalidated the wholemask-sizedeclaration at computed-value time. Every layer fell back toauto, the solid scrollbar-gutter layer covered the scroller, and the cutoff disappeared. The guard pinned the old name as literal text, so it stayed green.Fix
theme.custom.css: every button in the prompt row's right action cluster shares the 24px square; the attach button hovers with the ghost controls' 4% lift (--fork-composer-control-hover) rather than--accent.theme.custom.css: menu, select and combobox rows hover with the selected row's 8% foreground wash, 12% on a selected/checked row. Dark only, like the rest of the popup-surface set.theme.custom.palettes.css: the Cool Darker glass root block and both v2 sidebar blocks (docked and floating) restate--accentand--fork-pill-hoverasrgb(255 255 255 / 8%). 8% lands on the stage where the opaque token used to (+19 per channel), so upstream'sbg-accent/Nvariants keep their relative weight. Non-glass Cool Darker keeps its opaque ladder per the flatten-alpha doctrine; the only non-glass failure was the attach button, patched above.theme.custom.css: the cutoff's top-fade companion rule reads--workspace-titlebar-scroll-fade-height.forkComposerShellpins the cluster geometry and the attach hover, and now reads the fade variable name out ofindex.css's owntopbar-scroll-fadeutility instead of pinning a literal;forkPopupSurfacepins the six row slots and both wash rules;forkCoolDarkerSidebarVibrancypins the three glass--accentwashes..fork/customizations.yaml:fork-composer-shell,fork-popup-surfaceandfork-cool-darker-sidebar-vibrancyintents updated.Left alone on purpose:
--muted/--secondaryare the same opaque grey and paint badges, code blocks and tab strips as slabs under glass. That is a fill problem, not a hover one, and belongs in its own change.Verification
Measured in a dev-server preview on Cool Darker (computed styles, not eyeballed):
--accentvs foreground 8%#282d30onrgb(33 28 33)--accentunder glass (root and sidebar)#282d30rgb(255 255 255 / 8%)mask-sizewith the top fade onauto, auto, auto(no cutoff)100% 24px, 100% calc(100% - 24px), 6px 100%vp test run src/__fork_guards__→ 45 files, 381 tests passing (before the cutoff commit;forkComposerShellre-run green after it).vp run typecheckinapps/webclean.Web/desktop only. No contracts, server or mobile changes.
Claude Fable 5.1 via Claude Code.
🤖 Generated with Claude Code