Skip to content

fix(web): Glass popups frost over the new-agent picture - #174

Merged
NoahHendrickson merged 2 commits into
customfrom
fork/glass-draft-popup-frost
Oct 10, 2026
Merged

NoahHendrickson merged 2 commits into
customfrom
fork/glass-draft-popup-frost

Conversation

@NoahHendrickson

Copy link
Copy Markdown
Owner

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 stamped data-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 ::after rather than fixed + anchor(), because Floating UI transforms the positioner and that breaks the anchor. overflow: clip keeps the popup's shadow. The card tokens are now also stated on the stamped popup, since it portals out of the chat view.
  • Guards and manifest entries updated for both fork-glass-new-agent-stage and fork-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 for frostPlacement.
  • tsc --noEmit and lint are clean.
  • Not yet checked visually in the desktop app. Before/after screenshots still need to be added.

🤖 Generated with Claude Code (Claude Opus 5.5, T3 Code)

@github-actions github-actions Bot added size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Oct 10, 2026

@NoahHendrickson NoahHendrickson left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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";

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 23ab598: the constant is now module-private, which clears knip's unused-export check.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Open in Web View Automation 

Sent by Cursor Automation: Thermo nuke 4.6

Comment thread apps/web/src/theme.custom.glass.css Outdated
Comment on lines +451 to +459
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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-layers on 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 ::before and this ::after both become background: 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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 5.0 KiB — 6.8 KiB ✅
Codex Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Codex Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Codex Live turn WebSocket decoded — 20.9 KiB — 29.3 KiB ✅
Codex Live turn messages — 2 — 8 ✅
Claude Total thread wire — 5.0 KiB — 6.8 KiB ✅
Claude Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Claude Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Claude Live turn WebSocket decoded — 21.2 KiB — 29.3 KiB ✅
Claude Live turn messages — 2 — 8 ✅

Baseline: unavailable · PR result: 77c878f · Source CI: success

Scenario and decoded snapshot size

10 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.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

xxxxxxxxxxxxx and others added 2 commits October 10, 2026 11:08
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>
@NoahHendrickson
NoahHendrickson force-pushed the fork/glass-draft-popup-frost branch from 23ab598 to 77c878f Compare October 10, 2026 15:10
@NoahHendrickson
NoahHendrickson changed the base branch from custom to fix/post-release-0.1.41 October 10, 2026 15:10
@NoahHendrickson

Copy link
Copy Markdown
Owner Author

Rebased onto fix/post-release-0.1.41 (#173) and retargeted the base to that branch, so this PR shows only its own diff and GitHub moves it back to custom when #173 merges.

Two things needed resolving against #173:

On the rebased branch: vp test run src/__fork_guards__ 63 files / 513 tests pass, vp run typecheck in apps/web clean.

🤖 Generated with Claude Code

Base automatically changed from fix/post-release-0.1.41 to custom October 10, 2026 15:18
@NoahHendrickson
NoahHendrickson merged commit 033a2de into custom Oct 10, 2026
31 of 51 checks passed
@NoahHendrickson
NoahHendrickson deleted the fork/glass-draft-popup-frost branch October 10, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants