Repository navigation
test(next): glass is never opaque, natively and in webviews; the open pane sidebar is glass - #23366
Conversation
A guard for the standing rule (Leo 2026-10-20: "we want a lot of glass... I hate opaque"). Native: the ast-grep rule no-opaque-glass (prepush gate and the cmux-next checks job) flags an opaque tint or fill on a glass or visual effect view, isOpaque in a glass subclass, and an opaque background next to .glassEffect; `// glass-opaque-allow: <why>` is the reviewed escape. Web: webviews/test/glass-never-opaque.test.ts (scripts/check-glass-never-opaque.ts) flags a blurred surface, glass class or glass token whose fill resolves opaque outside Reduce Transparency. It fails on base: the open acpmux sidebar blurs under the opaque base color. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc6-7623-8ee3-fa94b3900ccd&host=cosmic-gray
The narrow pane's open session sidebar blurred the transcript under the opaque base color, so the blur never showed. It now takes the base at the text floor (--surface-text-min, 92%) under the same blur; Reduce Transparency makes that 100% and the blur none (desktop.css tokens). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc6-7623-8ee3-fa94b3900ccd&host=cosmic-gray
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Owned by palette-audit on cosmic-gray · session |
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Gallery before/after for
States
Pick other states with a red: cmux-next checks (god files, concurrency, crash safety, l10n); no finished base run on 76f7e42 to compare; do not merge until green; see lane-rules |
|
Found 5 test failures on Blacksmith runners: Failures
|
|
Bead |
…heck --glass-surface is what --glass-tint mixes from at 40-60%, never a fill; it says so for webviews/scripts/check-glass-never-opaque.ts (#23366). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16
|
Shipped in:
|
* plans(cmux-next): liquid glass lens design Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cmux-next): liquid glass lens primitive and Glass setting One shared glass surface for every web page and the agent pane: webviews/src/ui/glass/ adds the .ui-glass class, its tokens (--glass-tint, --glass-blur, --glass-frost, --glass-refraction, --glass-rim, --glass-dispersion) and a WebGL lens that bends the host's wallpaper still at each glass rim. WKWebView can't refract a backdrop, so the host sends the still through cmuxTheme.backdrop. Settings > Appearance > Glass: Lens (default), Plain Glass (the blur glass from before) and Spectrum (lens plus a chromatic rim). Reduce Transparency and Low Power Mode fall back to Plain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next glass): review fixes for the lens and backdrop feed - A context-lost event from a canvas stop() dropped no longer turns off the lens that started after it. - The page decodes the wallpaper still only once glass is on screen. - The backdrop blur is sent in CSS px (divided by the page zoom). - Localizable.xcstrings keeps base's order; only the five Glass keys are added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next glass): lens stays inert in a DOM without observers A unit test's light DOM has no MutationObserver, and every page that loads the desktop layer crashed there (whats-new-by-area and others). The lens now leaves such a document to CSS glass. Linux WebKit's headless compositor leaves WebGL out of screenshots, so there the lens test checks the lens's state instead of pixels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix (settings exports for appearance.glass) Applied from the cmux-next PR autofix artifact of run 39555592062 (its push job was skipped). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): generated settings exports follow the base GitHub can't run the cmux-generated-v1 merge driver, so every base regeneration of these exports marked #22815 conflicting. The base autofix regenerates them for appearance.glass after the merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * fix(cmux-next glass): the tint's mix source passes the never-opaque check --glass-surface is what --glass-tint mixes from at 40-60%, never a fill; it says so for webviews/scripts/check-glass-never-opaque.ts (#23366). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * test(cmux-next glass): no void operator in the Linux WebKit skip Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * fix(cmux-next glass): WebGL1 lens reads an explicit mip level where it can texture2D(tex, uv, x) in a WebGL1 fragment shader takes x as a bias over the automatic level, not a level. With EXT_shader_texture_lod the lens now uses texture2DLodEXT; plain WebGL1 keeps the biased read. A browser test compiles and links every dialect (webgl2, webgl1-lod, webgl1) in Chromium and WebKit. Raised by a cross-family review relayed by gallery-perf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * fix(cmux-next glass): the lens engine loads after the page, out of its eager budget The editor page's eager JS went to 507,735 bytes against its 500,000 budget with lens.ts imported statically. desktop.ts now installs only the backdrop fallback (so an early host still is kept) and imports the engine lazily; the editor page is back to 491,770 bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * chore(next): settings exports regenerated for the Glass setting Written by scripts/measure/export-settings-artifacts.sh on the fleet (cmux-ci step 113430d28f44b66f1766cc2f, aws-m4pro-7, at 9e6fe02), as defaults_guard.py check-stamp asks; the PR autofix could not regenerate them (base test-compile red, fixed by #23436, then a GitHub rate limit and a runner setup failure). The defaults snapshot stays the bot's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * chore(next): settings exports regenerated for the Glass setting after the base merge Written by scripts/measure/export-settings-artifacts.sh on the fleet (cmux-ci step e00798fd080a9afce64acaee, aws-m4pro-2, at 5159222). check-stamp passes; the defaults snapshot stays the bot's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * chore(next): settings exports regenerated for the Glass setting after the base merge Written by scripts/measure/export-settings-artifacts.sh on the fleet (cmux-ci step 488cb6a08483a6ed97777436, aws-m4pro-6, at 89078c9). check-stamp passes; the defaults snapshot stays the bot's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * cmux-next glass: the lens is an opt-in toggle; Plain Glass stays the default Leo 2026-10-21: "the lens thing we just need to see and feel... we can put it as a toggle and later promote to default". appearance.glass now defaults to plain, the glass from before the lens, so nobody's look changes. Lens and Spectrum stay in Settings > Appearance > Glass, and the palette's Toggle Liquid Glass Lens (appearance.glass.lens.toggle, CLI `settings toggle-glass-lens`) flips Plain and Lens live and writes the setting. Pages draw Plain before the host sends its mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * feat(next): Liquid Glass lens is a Debug toggle, off by default Leo 2026-10-21: the lens is something to see and feel before it is the default. It moves out of the settings schema: the Debug Settings choice `glass.lens` (Glass and Overlays > Liquid Glass; DEV and NIGHTLY) picks Plain Glass (the default), Lens or Spectrum, and Toggle Liquid Glass Lens in the palette or the Debug menu flips it live. Release never activates Debug Settings, so today's glass is unchanged. The Settings > Appearance > Glass row and its exports are gone until the lens is promoted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * chore(next): action surfaces regenerated for the Debug Liquid Glass toggle Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 * chore(next): action surfaces regenerated after the base merge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c40-f08c-79b2-95af-689409622600&host=cmux16 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The rule: glass is never opaque (Leo 2026-10-20: "we want a lot of glass... I hate opaque"). A surface that blurs what is behind it, or is declared glass, must let that backdrop show. Reduce Transparency is the one place glass turns solid. This PR adds one guard with a native side and a web side, modeled on the pane toolbar's
no-raw-accent-lit-glyph(an ast-grep rule plus a webviews test). It also fixes the one existing violation the guard found.The guard
Native:
scripts/cmux-next/lint/ast-grep-rules/no-opaque-glass.yml. The pinned ast-grep rules run inscripts/cmux-next/prepush-gate.py(pre-push hook) and in CI as the step "Pushed commits add no violations" of thecmux-next checksjob (.github/workflows/cmux-next.yml). It flags:tintColoror layer fill on a view named glass or visual effect. Opaque meansPalette.popupSurface, alpha 1 (withAlphaComponent(1),alpha: 1), or a solid system color.isOpaque = true, orvar isOpaque: Bool { true }, in a subclass ofNSVisualEffectView,NSGlassEffectVieworGlassPanelView..background(...)right before or after.glassEffect(...)in SwiftUI.The Reduce Transparency paths draw a separate plain view (OverlaySurface's
.opaquematerial view, the shortcut chip's fill), so the rule doesn't match them. The reviewed escape is// glass-opaque-allow: <why>, added to the gate'sESCAPES.Web:
webviews/test/glass-never-opaque.test.ts, with the logic inwebviews/scripts/check-glass-never-opaque.ts. CI runs it in the step "Test webviews" ofweb / react-apps-check (N)(ci-web.yml, viaci.ymlon PRs andcmux-next-web.ymlon feat-cmux-next pushes). A plainexpect, so it fails the job. It scans every CSS file underwebviews/src, skipping rules under@media (prefers-reduced-transparency: reduce)or[data-reduce-transparency](not (...)and:not([...])are still checked). It flags:backdrop-filter, or a class named glass, whosebackgroundresolves opaque.--*glass*token set to an opaque color or a 100% share."Opaque" counts only what the scan can prove: a literal color with no alpha,
Canvas, a token whose every definition is opaque (the host's--cmux-*solid*and--cmux-popup-surfacecount), or acolor-mixof two opaque colors. Anything it can't resolve passes, so a solid card that sets no blur (.ui-card) is never flagged. The escape is/* glass-opaque-allow: <why> */on the line before. A second test runs the scan over a fixture, so it can't pass by finding nothing.This complements #23133, which renders gallery stages in Chromium and catches computed opaque glass at runtime. This guard is static: it covers Swift, and CSS states the gallery doesn't render (the open sidebar below is one). The two PRs share no files.
Violations fixed
webviews/src/agent-session/acpmux/styles.css:5,.acpmux-shell[data-sidebar=open] .acpmux-sidebar(the narrow pane's open session sidebar, over the transcript)background: var(--acpmux-base)underbackdrop-filter: var(--surface-blur): an opaque fill, so the blur never showedbackground: color-mix(in srgb, var(--acpmux-base) var(--surface-text-min, 92%), transparent): the text floor desktop.css sets for translucent surfaces that carry text. Under Reduce Transparency, desktop.css makes it 100% and the blurnone, so it is solid againThe native scan of
Packages/macOS/CmuxNext/Sourcesfinds nothing today, so no Swift file changes.#23000's toast (
CmuxNextFloating/ChiefPet/ChiefPetBubble.swift, which another session is reworking) isn't flagged and isn't touched, so it needs no exclusion.Red, then green
Commit 1 (6a9d0a2) adds the guard only. Commit 2 (c3ac1f8) adds the fix.
On a scratch Swift file, the native rule matched all 9 intended cases, including the escaped one (the gate drops that one through
ESCAPES). It did not matchPalette.glassTint,withAlphaComponent(min(a, 1)),(materialView as? GlassPanelView)?.tintColor = look?.veil..., a translucent fill on an effect view,fill.layer?.backgroundColor = Palette.popupSurface(not a glass view),materialView?.layer?.backgroundColor = Palette.popupSurface(the.opaquepath),isOpaquein a plainNSViewsubclass, or.glassEffect().background(.ultraThinMaterial).Changelog
Observed / Not run
bun scripts/check-glass-never-opaque.tsred on commit 1 and green on commit 2 (above). The fixture cases match the test's expected list, checked by running the same fixture through the script. Theast-grepscan of the real sources and of the scratch file is as described above.python3 scripts/cmux-next/prepush-gate.py: "no new violations".scripts/web-lint.sh: passed.bun test test/glass-never-opaque.test.tslocally (this host's guard refusesbun test), so CI's "Test webviews" step is its first run. No Swift build or test, since no Swift changed.No capture: the only visible change is one CSS declaration in a webview (the narrow pane's open session sidebar fill goes from opaque
--acpmux-baseto the shared 92%--surface-text-minmix under its existing blur, and back to solid under Reduce Transparency). No gallery stage renders that open-sidebar state, and the Swift rule changes no app code.Agent session: cosmic-gray
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Enforces the standing rule that glass is never opaque, natively and in webviews, and fixes the one violation the new guard finds.
Old behavior: the agent pane's open session sidebar blurred the transcript underneath an opaque base fill, so the blur never showed. New behavior: the fill is a 92% translucent
color-mix, so the backdrop shows through the blur; Reduce Transparency still makes it solid.The guard has two sides, both wired into CI:
no-opaque-glass) in the pre-push gate and thecmux-next checksjob flags opaque tints/fills orisOpaqueon glass views, and opaque.backgroundnext to.glassEffect. A reviewed escape is// glass-opaque-allow: <why>.check-glass-never-opaque.tsscans every CSS file underwebviews/srcand flags an opaque fill on a blurred or glass-named surface, or an opaque--*glass*token, outside Reduce Transparency contexts. A fixture test ensures the scan can't pass vacuously. Escapes are/* glass-opaque-allow: <why> */.Reduce Transparency paths are exempt on both sides since that's the one place glass is allowed to go solid.
Written for commit c3ac1f8. Summary will update on new commits.