feat(app): glassmorphism as a 5-step transparency level - #51
Conversation
`glassmorphism` was a switch with one fixed look: you got the frosted chrome the key shipped with, or you got nothing. The look itself was the only thing anyone asked to change about it. Widen the key into a four-step level — `off`, `1`, `2`, `3`, higher meaning more transparent — so the glass can be dialed rather than only switched. Level `1` is what `true` has always resolved to, exactly: `0.50` window opacity, blur pinned to `64`, and the same backdrop / surface / pill / rim values the chrome has carried since the key existed. Nobody's appearance moves. Levels `2` and `3` step the window to `0.35` / `0.20` with every chrome alpha following it down, and the rim mix climbing the other way — a thinner face draws less of its own edge, so the stroke has to inherit more of that job at each step. Blur stays at the key's maximum throughout; there is no higher value left to give the more transparent levels, and they need at least as much as level `1` does. The takeover contract is unchanged in shape and only becomes level-keyed: any on-level still owns `background-opacity` and `background-blur-radius` outright, and the diagnostic that names a silenced key now quotes the pair that level actually installs rather than a fixed one. - `glassmorphism = true` / `false` still parse, to `1` / `off`, so no existing config file has to be touched - Move the overlay alpha onto `ChromePalette` rather than deriving it from `is_glass()` at install time: with four variants the palette has to be the single source for every alpha, and a boolean can no longer say which - Cycle the Settings row through all four steps, re-snapping the two rows the level manages on each one — each level owns a different pair, so the panel would otherwise display an opacity the next reload discards. The restore point stays the user's own values across those re-snaps Claude-Session: https://claude.ai/code/session_0145t6595e9h4jGcNnxtp1gv
Three steps stopped short of what the key was widened for. `4` and `5` carry it to the end: `0.12` and `0.06` window opacity, with the chrome faces thinning to `0.040`/`0.020` backdrop and `0.034`/`0.014` surface. These are panes you read *through* rather than *on* — at `5` the window contributes six percent of its own pixels. What keeps that reading as glass instead of as absence is the rim. A face at `0.014` draws almost none of its own edge, so the stroke inherits the rest: the rim mix climbs to `0.94` and then `1.00`, where the border *is* the foreground color, the same value the pane's text is drawn in. That is also the ceiling in the literal sense — there is nothing past "the edge is as present as the content" — which is why the ladder stops at five. The three lower levels are untouched, `1` still matches `glassmorphism = true` exactly, and blur stays pinned at the key's maximum throughout. - Give the pill alpha the shallowest ladder of the three: by `5` the backdrop has surrendered nine tenths of its opacity and the pill only three quarters, because a search pill that faded in step would go unreadable well before the panel behind it did - Keep the top faces deliberately above zero; at `0.0` the sidebar stops being a plane and reads as text floating on the desktop, which is a different thing from more transparency - Add `GlassLevel::ALL` / `ON_LEVELS` and route every enumeration through them. The panel's cycle and four tests spelled their levels out as slices, which — unlike a `match` — keep compiling when a variant is added and silently drop it off the end; level `4` was unreachable from the panel for exactly one build because of this - Measure the rim ladder on `glassify`'s output rather than on the table `glassify` itself reads, so the assertion can actually fail; and pin the `Display`/parser round trip, which had no test tying the only writer of this key to its only reader Claude-Session: https://claude.ai/code/session_0145t6595e9h4jGcNnxtp1gv
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e514191394
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| Self::Glassmorphism => { | ||
| "Frosted translucent sidebar and tab-overview chrome. Takes over window opacity and blur with its own recommended pair. Applies on save." | ||
| "Frosted translucent sidebar and tab-overview chrome, 4 steps (Off/1/2/3, higher = more transparent). Takes over window opacity and blur with the level's own recommended pair. Applies on save." |
There was a problem hiding this comment.
List levels 4 and 5 in glassmorphism help
When a user focuses this Settings row, the displayed help says the cycle has only four states ending at level 3, but GlassLevel::ALL actually cycles through Off and levels 1–5. This makes the newly added levels 4 and 5 appear undocumented or accidental in the panel; describe the full Off/1/2/3/4/5 cycle and its five on-levels.
Useful? React with 👍 / 👎.
glassmorphismwas an on/off flag with one fixed look, and the look itself was the only thing anyone wanted to change about it. This turns the key into a five-step level —off,1–5, higher meaning more transparent — so the glass can be dialed rather than only switched.What changes
StartupConfig::glassmorphismbecomes aGlassLevelenum, and the two scalar glass constants become level-keyed functions. Per level, windowbackground-opacity/ chrome alphas (backdrop · surface · pill):12345Level
1is byte-identical to whatglassmorphism = truehas always resolved to, so no existing config changes appearance.background-blur-radiusstays pinned at the key's maximum (64) at every on-level.As the faces thin out they stop drawing their own edges, so the rim inherits that job — the rim mix climbs to
1.00at level5, where the border is the foreground color. That is the ceiling in the literal sense, and it is why the ladder stops at five. The top two levels are panes you read through rather than on; the faces stay deliberately above zero, because at0.0the sidebar stops being a plane and reads as text floating on the desktop, which is a different thing from more transparency.glassmorphism = true/falsestill parse, to1/off. The written spelling does change: the Settings panel andnoa --confignow emitoff/1…5, so saving or reverting from the panel rewrites a hand-writtenglassmorphism = trueasglassmorphism = 1.Review angles
chrome.rsandnoa-config/src/lib.rsalpha tables — the five ladders are the substance of the change; everything else follows from them.theme_settings/state.rsrestore-point machinery — each level owns a differentbackground-opacity/background-blur-radiuspair, so the panel re-snaps the two rows it manages on every step while keeping the restore point at the user's own pre-glass values. That interaction is the least obvious part of the diff.GlassLevel::ALL/ON_LEVELS— added because the panel's cycle and four tests enumerated levels as slices, which (unlike amatch) keep compiling when a variant is added and silently drop it. Level4was unreachable from the panel for exactly one build because of this.Verification
cargo fmt --all -- --check,cargo clippy --workspace --all-targets(no warnings), andcargo test --workspaceall pass — the same three gates CI runs.Tests pin: level
1against its original constants (regression lock for existing configs), strict monotonicity of all five ladders measured onglassify's output rather than on the table it reads, theDisplay⇄ parser round trip for every level, the panel's full forward and reverse cycle, and restore-point integrity across a complete lap.Not verified: the appearance of levels
4and5on a real display. Levels1–3are unchanged from the merged implementation.