Repository navigation
fix(web): Glass popups frost over the new-agent picture - #174
Conversation
NoahHendrickson
left a comment
There was a problem hiding this comment.
Reviewed 9cb1f9e. One confirmed issue to fix before merging: the new unused export causes the CI Lint job to fail, detailed inline. I did not identify another definite defect from source inspection.
Validation: all 26 tests passed across forkGlassNewAgentStage, forkGlassPopupCutout, and forkPopupSurface; targeted lint for the changed TypeScript files and the apps/web typecheck also passed. Inspected the popup selector coverage, frost placement and cleanup, and CSS interaction with the existing popup primitives. This changes desktop Glass rendering; other web palettes and native mobile are outside the affected path. No browser or native desktop visual verification was performed, so the rendered alignment and vibrancy appearance remain unverified.
Review by GPT-6 Astra via the Codex harness in T3 Code.
| '[data-chat-column-maximized-away]:has([data-chat-composer-overlay="true"][data-draft-hero])'; | ||
|
|
||
| /** Stamped on a popup that frosts over the draft card instead of cutting it. */ | ||
| export const FORK_GLASS_FROST_ATTRIBUTE = "data-fork-glass-frost"; |
There was a problem hiding this comment.
[P2] Keep the frost attribute constant private to this module
FORK_GLASS_FROST_ATTRIBUTE is only used by clearFrost and frost in this file; neither the tests nor another module imports it. Exporting it therefore fails the repository's Knip exports check. The current CI Lint job exits with Unused exports (1): FORK_GLASS_FROST_ATTRIBUTE ...:46:14. Remove export here so the change passes the required check without adding an unused public API.
There was a problem hiding this comment.
Fixed in 23ab598: the constant is now module-private, which clears knip's unused-export check.
There was a problem hiding this comment.
Do not approve.
The cutout JS is the right layer. frostPlacement / draftCard sit next to the existing measure loop; frost-or-hole is atomic inside apply(); closing popups keep frost and give the hole back; stop clears stamps. Absolute + JS card-relative lengths is the justified placement — Floating UI's transform would break fixed + anchor(). File sizes stay under 1k. That work is not the problem.
The popup ::after pastes the host ::before four-layer frost and inlines rgb(255 255 255 / 8%) instead of --fork-glass-frost-wash. The sheet still claims one recipe, with hosts differing only in wash / radius / mask. #170 rejected --fork-glass-frost-layers as a one-consumer token; this is the second consumer, so the #158 bar applies. Set the wash token on the stamped popup, mint the layers token on the existing token :is(), and have both pseudos consume it. Delete the paste. Placement, overflow: clip, and the JS lengths stay.
Sent by Cursor Automation: Thermo nuke 4.6
| background: | ||
| linear-gradient(rgb(255 255 255 / 8%), rgb(255 255 255 / 8%)), | ||
| linear-gradient( | ||
| rgb(from var(--fork-glass-frost-floor) r g b / var(--fork-glass-frost-veil)), | ||
| rgb(from var(--fork-glass-frost-floor) r g b / var(--fork-glass-frost-veil)) | ||
| ), | ||
| var(--fork-glass-card-scrim), | ||
| var(--fork-glass-frost-image) 51% top / cover no-repeat; | ||
| mask-image: var(--fork-glass-frost-mask); |
There was a problem hiding this comment.
This is a second copy of the host ::before frost (lines 338–346), with the wash inlined as rgb(255 255 255 / 8%) instead of --fork-glass-frost-wash. #170 rejected --fork-glass-frost-layers because it had one consumer. This PR is the second consumer — same failure mode as #158.
Judo:
- On
body .dropdown-glass[data-fork-glass-frost], set--fork-glass-frost-wash: rgb(255 255 255 / 8%). Do not share that wash with--fork-popup-fill(that token is wash + floor). - Mint
--fork-glass-frost-layerson the existing token:is([data-chat-column-maximized-away], [data-fork-glass-frost])as the four-layer stack that already uses the wash token. - Host
::beforeand this::afterboth becomebackground: var(--fork-glass-frost-layers). - Delete this paste.
Placement, ::after, overflow: clip, the vibrancy gate, and the JS lengths stay. The stage guard currently requires every --fork-glass-frost-wash rule to live inside @supports (anchor-name) — relax that pin so the popup host can state its wash. Popup placement does not use anchors.
There was a problem hiding this comment.
Fixed in 23ab598. The duplicated stack is gone, and the host ::before and the popup ::after both use background: var(--fork-glass-frost-layers). The popup now states its own --fork-glass-frost-wash.
One change from your suggestion: the token isn't declared on the :is([data-chat-column-maximized-away], [data-fork-glass-frost]) token rule. A custom property substitutes its var()s on the element that declares it. On the chat view, var(--fork-glass-frost-wash) would resolve where no wash is set, the layers would become guaranteed-invalid, and every host would lose its frost. So the token goes in one rule whose selector list covers the elements that state a wash: the draft hosts and the stamped popup. The stage guard pins that rule and checks the frost image is referenced exactly once. The anchor-gate pin is relaxed so that only anchors, clip-paths and host washes must sit inside @supports (anchor-name).
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: 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. |
Under Glass, popups cut a hole through the page to the native material. On the new-agent draft that hole cut the portrait away too, so menus opened on the bare desktop. A popup wholly inside the draft card now skips the hole and carries the stage's baked frost instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The popup frost repeated the hosts' four-layer stack with its wash inlined. Both pseudo-elements now read one --fork-glass-frost-layers token, declared on each element that states a wash so the wash resolves there. Drops the unused FORK_GLASS_FROST_ATTRIBUTE export that failed knip. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
23ab598 to
77c878f
Compare
|
Rebased onto Two things needed resolving against #173:
On the rebased branch: 🤖 Generated with Claude Code |


Problem
Under Glass (with desktop vibrancy), open popups cut a hole through the page to the native macOS material. On the new-agent draft, that hole also cut away the stage portrait, so the context pills' menus (Workspace, branch, model picker) opened on the bare app background instead of reading as an overlay on the picture.
Fix
custom/forkGlassPopupCutout.ts: when a popup opens wholly inside the draft card, it gets no hole. Instead it is stampeddata-fork-glass-frost, with the card's box relative to the popup written as four lengths. Popups that reach outside the card, started threads, and other palettes behave as before.theme.custom.glass.css: stamped popups carry the same baked frost as the composer pills. The floor sits under a blurred copy of the portrait, aligned with the stage picture, under a white 8% wash. The copy is an absolute::afterrather than fixed +anchor(), because Floating UI transforms the positioner and that breaks the anchor.overflow: clipkeeps the popup's shadow. The card tokens are now also stated on the stamped popup, since it portals out of the chat view.fork-glass-new-agent-stageandfork-glass-popup-cutout.Opaque Glass clients never stamp, because their popups' backdrop-filter already blurs the picture.
Testing
vp test run src/__fork_guards__: 61 files, 508 tests pass. This includes a new behavioural test forfrostPlacement.tsc --noEmitand lint are clean.🤖 Generated with Claude Code (Claude Opus 5.5, T3 Code)