feat(appearance): Liquid Glass theme - #908
iceclatterWT wants to merge 78 commits into
Conversation
|
I really like what you did! Thanks for your contribution! |
thanks, i'm working on converting the rest of the ui into glass theme also. curious to hear your thoughts once i have a working prototype |
… guarantee desktopWallpaper (none | preset | image, default none) and glassTerminals (default false) are opt-in, so existing installs see no change. glassTintAlpha picks the smallest tint alpha that keeps the theme foreground at 4.5:1 over both a white and a black backdrop. The 0.95 design ceiling yields to the guarantee for the two Solarized themes (Solarized Light is under 4.5:1 opaque, so it gets alpha 1).
…ache scanStills reads /System/Library/Desktop Pictures at runtime (never bundled), sips converts HEIC to JPEG into <userData>/wallpapers/, and load returns a data: URL, so the renderer CSP is unchanged. The renderer never names a path to read: stills resolve by id against a fresh scan, and an imported image must be a hash-named direct child of the cache dir. Registered in both shells; the Server Edition gets an empty stills list, and a relay tab keeps a stub.
With glassTerminals on, a canvas terminal node gets a translucent tint of its own effective theme (project override included) at the alpha glassTintAlpha computes, plus backdrop blur, a translucent border and a specular header edge. xterm paints no background: the theme background keeps its RGB with alpha 0 and allowTransparency is set, so the WebGL atlas is rasterized without a baked-in background. Both toggle live through applyLiveOptions (the WebGL renderer rebuilds its atlas on any option change). The kanban card modal and the settings preview never pass glass and are unchanged.
The React Flow root is viewport-sized and never transformed, so a background set there stays fixed while the canvas pans and zooms. Gradient presets are CSS; stills and imported images come from wallpaper.load as a data: URL and a failed load falls back to the plain canvas. The dot grid is faded to 30% over a wallpaper rather than removed, since snapping still aligns to it. The kanban overlay paints its own background and is unaffected.
A thumbnail grid (None, the macOS stills core found, six gradients, Choose image…) and a Glass terminals switch. Choose image… uses the existing file dialog and core refuses anything but JPEG/PNG/WebP/HEIC; it is hidden in the Server Edition, where the picker would browse the server's disk. Both keys join Reset appearance.
The opt-in shared renderer paints text on a canvas below the nodes, so a glass tint on the node would cover every glyph.
…render Chromium drops a var()-containing value once it passes about 2 MB. A macOS still's data: URL is about 3 MB, so the background shorthand that appended var(--canvas-bg) resolved to nothing and the canvas stayed black. Set background-image and its size/position longhands instead; the class rule still supplies the canvas colour underneath.
The glass header is tinted with the terminal theme, but its title used the app theme's --text, so a light app theme over a dark terminal theme read at 1.31:1. Under glass the header redefines --text/--muted from the theme foreground.
Canvas re-renders on every drag frame; a fresh style object each time made React re-diff the root's background-image.
Every glass node's backdrop moves under it on each camera frame, so the 28px blur was re-rasterised for all of them. onMoveStart/onMoveEnd toggle a class on the flow wrapper that drops backdrop-filter; the tint, which is what the contrast guarantee rests on, stays.
A null result pinned the plain canvas for the app run even after the file became readable.
…he files Imports were copied at full size, so a large photo passed import and was then silently refused by load's 25 MB cap. On macOS every import now goes through sips to a JPEG no larger than 3840px (sips -Z also upscales, so it is passed only when the image is bigger). Elsewhere an image over the cap is refused at import time, where the picker shows the reason. A saved change of desktopWallpaper, and each import, delete hash-named cache files nothing refers to. Thumbnails, temps and foreign files are never touched, and there is no prune at boot: an unreadable settings.json loads as defaults and must not cost the user their chosen image.
…and black Endpoints suffice only while the text's luminance lies outside the range of composite luminances; inside it some backdrop matches the text exactly. That case now fails explicitly, and a grey-backdrop sweep pins the claim per theme. Foreground alphas are unchanged for every built-in theme.
The tint guarantees 4.5:1 for the theme foreground only. Holding the ANSI palette too would make every built-in theme opaque, so the glass keeps the foreground-only guarantee and the Settings copy now says so.
Re-encoding every macOS import to JPEG flattened PNG/WebP alpha and recompressed JPEGs for nothing. sips now runs only for HEIC, an image over 3840px, or one over the 25 MB load cap.
onMoveStart/onMoveEnd are useCallback'd, and the blur comes back 150 ms after the last move ends so a setViewport-driven wheel zoom does not toggle it per packet. Comments now say what the failed-load cache and the header tokens actually guarantee.
… chrome tint The Glass terminals switch is replaced by a fourth Appearance option, Liquid Glass, whose light/dark base follows the terminal theme like Follow terminal. Choosing it with no wallpaper picks one (the first macOS still, Sonoma Horizon when present, else a gradient) so glass never sits over plain black. The pre-release glassTerminals key maps to it once. glassChromeAlpha computes the chrome fill's alpha for the app theme's --text over --panel. --text is itself translucent, so the foreground moves with the backdrop and the white/black endpoint argument no longer holds; the backdrop is sampled instead (6x6x6 grid plus a grey ramp) and the tests re-check a fine grey sweep for both themes against the real tokens: dark 0.70, light 0.745. App.tsx publishes it as --glass-chrome-bg with data-nt-glass on <html>.
…Liquid Glass All of it is gated on data-nt-glass, so the other appearances are unchanged. Surface tokens (--panel, --surface-*, --tabbar-bg) become the computed chrome fill, which covers the panels that paint them; the floating containers that hard-code rgba(var(--menu-rgb), ...) are listed. Blur goes on outermost containers only (a nested blur re-blurs its parent's pixels); node blur pauses while the camera moves, chrome keeps it. Terminal windows drop their per-node accent: neutral top edge and colour dot (both inline styles, hence !important), neutral resize handles. Selection is a neutral ink outline; unread keeps its accent border and glow. The sessions list and kanban keep node colours. A stylesheet test pins the gate and the neutralising overrides.
The wallpaper text still pointed at the removed Glass terminals switch, and the Appearance description had grown to a paragraph. It is two sentences now, with the contrast note shown only while Liquid Glass is selected.
…n Liquid Glass The wallpaper is painted on .canvas-root, which spans the window including the tab bar row, so the Liquid Glass tab bar is glass over the picture; React Flow's root goes transparent over it and the picture still does not move with pan/zoom. The tab bar's fill is the computed chrome tint, whose 4.5:1 holds over any backdrop, bright sky included. Minimap node rectangles were painted in each node's colour, the accent Liquid Glass removes. They are translucent theme ink now (brighter when selected); working/attention/unread strokes keep their colours, and the mask uses the chrome fill.
settings.canvasDots (default on in every appearance) decides whether the canvas renders its React Flow dot Background. Display only: snap-to-grid and align-to-grid keep using gridSize. Hand-edited values other than a literal false keep the dots. Pure renderer, so the Server Edition gets it too.
Status and accent colours become tokens instead of hues typed at each call site: the Apple system palette (--sys-red ... --sys-gray, dark and light values) and semantic roles that rules name (--state-working, -attention, -unread, -error, -success, -warning, -queued, -automation and the --git-modified/added/deleted/renamed/conflict letters). About 120 CSS sites and the TS literals now read a role, so an appearance can re-map a meaning in one place. lib/palette.ts holds the same table for the JS that needs a literal (xterm find decorations, canvas sprites, the notch HUD), pinned to the CSS by styles.palette.test.ts. Git file-status colours come from one table (lib/gitStatusColors.ts) shared by Source Control and the history commit list. The kanban priority colours are defined once (CardMetaBar) and read by SessionCard. Fix: the minimap status strokes were crossed against the node glows (working drew yellow, unread drew clay). They now read the same roles as the glows. The default look keeps its historical hues for every feature. It changes only where duplicates collapse: - Minimap strokes and halos: working yellow -> clay, unread clay -> accent blue (the fix above). - Greens: #32d74b and #30d158 collapse to system green #30d158 (--success, git "A", diff-add text, git-graph lane 1, trigger/phone/account success). - Loop/schedule purple #bf7af0 -> system purple #bf5af2 (loop status, loop card, trigger accents, loop resize handle). - Reds: GitHub PR-closed #f85149, the SSH error dot #ff6b6b and the git history error text -> the danger token; the SSH connection banner's ad-hoc dark red/amber backgrounds are now mixed from the error/warning role over the overlay surface (near-identical in dark, a readable pale tint instead of dark red under dark ink in light). - Queued-warn chip #ffb340 -> the warning token. - Queued/disarmed/muted grey #8e8e93 -> system gray #98989d (dark). - Diff line washes: the Tokyo-Night green/pink (158,206,106 / 247,118,142) -> system green/red at the same alphas. - Diff hunk headers: teal #6ac4dc -> accent text (the info role). - Git graph lane 3 teal #6ac4dc -> system teal #40cbe0; conflict status letter the same. - Terminal find highlight #ffd54f/#ffb300 -> system yellow/orange. - Accent washes (about 20 hard-coded rgba(10,132,255) sites incl. the unread glow) follow the Accent setting; with the default accent nothing changes. - Light theme: fixed-hex status fills take Apple's light system values; git status letters, kanban priorities and the warning/success text in Settings take the theme's text-safe tokens instead of dark-theme hex on cream; the unknown git status letter draws in --text instead of white. - Mascot clay rgb(217,120,87) -> the Claude brand colour #d97757. Not changed (each would move a whole feature's hue in the default look): working stays clay, unread stays accent, attention stays red, warnings stay orange, git renamed purple / conflict teal, the rope default colour, the onboarding dots, node-kind default colours (persisted into project files), the node colour swatches (an allowlist for stored colours).
Under Liquid Glass each status meaning takes its Apple colour and no hue is shared between two meanings: working and selection use the accent (not the Claude clay any more), needs-you is orange, finished-but-unseen is green, warnings are yellow and errors red. Only the role tokens are redefined, so the node glows, minimap strokes, sidebar signals, header badges and git letters (renamed = accent, conflict = red) follow without per-rule overrides. Status labels on a glass header become ink over a chip tinted with the status hue, as HIG asks for colour on glass: the hue lives in the background and the dot, the text stays monochrome. The remaining hard-coded unread and needs-you text colours now read the --state-unread role and a new --attention-text token; both resolve to their previous values outside Liquid Glass, so the default look is unchanged.
… spacing Visual QA M7 (label row stacked a third band on glass), M6 (swatch abutted the theme name), L2 (pill separator hugged the next token).
The line-endings guard added upstream (issue eneskirca#578) flags reads that slice on a newline literal without normalizing CRLF checkouts first.
…olour turns opaque
An element with its own backdrop-filter is a backdrop root: anything inside it blurs only the root's pixels. The dock's Zoom/Layouts/Add menus (and the Add submenu) therefore showed sharp terminal text straight through, and the context-window and colour popovers inside glass nodes had a translucent fill and no blur at all. - .dock, .dock-menu and .dock-menu__sub keep their glass on a ::before layer (fill, hairline, blur), so none of them is a backdrop root and every menu blurs the canvas. The dock keeps the control fill; its menus are text surfaces. - .ctx-popover and .color-popover (inside a glass node, which is their backdrop root) are solid rgb(var(--menu-rgb)) under glass. - styles.palette.test.ts: the ctx-popover test pins the solid fill; the status-hue literal guard scans declarations only (a selector may name a hue it repaints).
…:1 at Clear (visual QA N2, N5, N6, N12, H4) - glassChromeAlpha takes the highlight washes that stack on the fill (ink lift, accent selection) with the ink they carry, and solves the readable alpha for all of them. Highlighted rows, the active tab and the selected segment use --text-strong. Dark chrome 0.70 -> 0.74, light unchanged 0.745. Active tab label at Readable 3.95 -> 5.09:1, its icons 2.0 -> 3.9:1 (opacity 0.8 on the active tab); light palette active row 4.28 -> 5.91:1. - glassControlClearAlpha: small controls at Clear keep 3:1 icons over every sampled backdrop after the control blur's brightness(--glass-control-dim). Dark stays 0.35; light rises to 0.57 (brightening cannot lift near-black water). - Hovers that re-stacked --panel-header (dock buttons, zoom pill, sessions rows, default Button) are neutral lifts (N5). - Segmented control selected state is a neutral lifted thumb, so the one filled accent per view is the primary action (N12). - ::placeholder uses --glass-placeholder (0.78 dark / 0.86 light), pinned at 4.5:1 on the fill by the test (N6).
…and label polish (visual QA N3, N4, N8-N11, M1, M3, M6, L4) - N3: '+ Add column' joins the text-surface glass list with --text ink (1.89 -> 7.25:1 dark over the photo, 1.19 -> 5.96:1 over Sunrise, 1.96 -> 6.36:1 light). - N4: on the board the toolbar cluster drops into the header strip's row. - N8: the usage pill drops whole trailing segments instead of eliding mid-token. - N9: saturation 1.5 from the Readable tick to Tinted, 1.8 only at Clear (text surfaces 1.5). - N10/L4: refresh button 26px like the RAM pill; the zoom column is 34px like the sessions toggle. - N11: the project menu opens below the glass tab bar (tab-menu--tabbar). - M1: OK-range meters and session context chips are neutral ink under glass (yellow/red keep their meaning); the fills are inline literals shared with the HUD, so the rule matches the serialised green and the test pins it. - M3: card modal comments title, hint, times and events take --muted. - M6: the terminal theme trigger left-aligns its name; .bind-select is an input well on glass. Everything is behind :root[data-nt-glass='on']; the default look is unchanged.
…d (visual QA N7, L5) - The imported-wallpaper tile paints the image itself (useWallpaperBackgroundFor, the canvas's own loader and cache) instead of the words 'Your image', which stay as its label and tooltip. - The chosen wallpaper tile carries a check badge, not only a ring. - Accent swatches expose aria-pressed like the wallpaper tiles.
…ol floor, round-2 polish
…r (visual QA round 3 NC1, NC2, NH1, NH2) Under Liquid Glass the shared surface tokens (--panel, --panel-header, --panel-2, --surface-*, --tabbar-bg) used to BE the translucent fill, so every floating surface nobody had listed became see-through with no blur. They now keep their solid theme colours; only the listed surfaces get the fill, each with a working blur. - NC1: the Explorer and Source Control drawers join the text-surface glass list (blurred). - NC2: context menus that host flyouts carry their glass on ::before like the dock, so they are no backdrop root and the Snap to zone flyout blurs the canvas. Scrolling menus and leaf flyouts keep it on the element (a ::before would scroll away; they host no flyout). - NH1: the node "+ Label" picker and the card modal's Labels and Members pickers paint their opaque token colour; the explicit solid rule for the context/colour popovers is no longer needed. - NH2: tooltips are text-surface glass (portalled, so the blur works); dock tooltips clear its rim. - Pieces inside glass that painted a token take a lift, the input-well sink or nothing (find bar, markdown bar, session chips and fields, Settings sidebar, hover rows in drawers/sidebar/cards). - loop and subagent nodes take the chrome fill explicitly (they had it through the tokens). - Guards: styles.glass-traps.test.ts fails when a rule paints a glass fill on a selector without a backdrop-filter or a paired ::before blur (fails on the previous stylesheet: the :root token block and the drop-target rule); scripts/glass-trap-probe.mjs walks the live page over CDP and reports translucent surfaces whose blur is ineffective (0 across 27 overlay states). Everything is behind :root[data-nt-glass='on']; the default look is unchanged. Claude-Session: https://claude.ai/code/session_011MqqEsWfh1xKLv4fip3aPZ
Code review 5: - eneskirca#1 the hover lift (dock buttons, session rows, the default Button) carries --text-strong: --text on it was 4.11:1 dark / 4.04 light, and the lift is not in the solver's highlight list. - eneskirca#2 a Layouts row is one highlight: its main button no longer stacks a second lift (3.48:1), and takes --text-strong while the row is hovered or focused (3.78:1). - eneskirca#3 the wallpaper cache keeps the two most recent images: opening Settings ("Your image" tile) evicted the canvas still, so the board's next first frame went black and re-read 3 MB. - eneskirca#5 unselected segments get their hover colour back under glass. - eneskirca#6 the project menu's glass offset moves into openMenu (the tab bar's bottom edge) instead of a `top: … !important` that defeated useMenuFlip; the tab-menu--tabbar class goes. - eneskirca#7 the usage pill's first segment always keeps line 1 and elides there; the error mark leads. - eneskirca#8 the highlight-aware readable alpha is tested for every accent swatch, bound 0.85 (Yellow 0.825, Teal 0.76 dark). Visual QA round 3: - NM1 typed text in chrome fields is --text-strong, so an empty field reads empty; the light placeholder is 0.85 (its 4.5:1 floor, never above --text; pinned by the test). - NM2/NM3 kanban column count, close, "+ New session", card-modal meta labels, the column badge and priority chips take --muted with lift hovers; the activity author is ink, weight 600. - NL1 the three dock menus clear the dock by 8px; NL2 the RAM panel rises above the dock and long names elide; NL3 the theme list lines its swatch/name columns up with the trigger, chevron centred; NL5 the current Speech language row is the selection wash; NL7 active-tab icons 0.9; NL8 a menu without icons keeps no icon column (Help). - NM4/M2 (selection ring at 45%) and NL4/L1 (tab-menu chevron) stay skipped, NL6 (dark glass over bright pastels) is left as physically correct dark glass. Everything is behind :root[data-nt-glass='on']; the default look is unchanged. Claude-Session: https://claude.ai/code/session_011MqqEsWfh1xKLv4fip3aPZ
Corrects the false "those five were the only traps" claim (round 3 found six more), documents the opt-in token model, the ::before host rule for context menus, the static guard (styles.glass-traps.test.ts) and the live probe (scripts/glass-trap-probe.mjs, 0 traps over 27 overlay states), the hover-lift ink, every-accent readable alpha and the NM1 placeholder/typed ink. Claude-Session: https://claude.ai/code/session_011MqqEsWfh1xKLv4fip3aPZ
…tion (visual QA round 4 N4-H1, code review 6 eneskirca#5-eneskirca#7) An element with opacity < 1 is a backdrop root. The scrims faded their opacity, so the palette, dialogs, drawers and card modal inside them blurred only the dim scrim and the canvas read sharp through the tint for 120-160 ms, then snapped to frosted; the ::before-hosted menus faded their own opacity the same way. Under Liquid Glass scrims now fade their background colour, glass surfaces enter by transform only, tooltips appear without motion, the focus-mode dock slides and its exit pill rests opaque. The default look keeps its animations. Static guard (styles.glass-traps.test.ts): fails on opacity keyframes or transitions on any glass surface or scrim unless a gated rule overrides them; background-image counts as a paint; the last backdrop-filter declaration decides; coverage keeps :not()/:hover and compares the full selector. Live probe: a mid-animation pass (replays every finite animation, freezes all at 50%, scans, restores); a blur inside a see-through backdrop root and a blurred surface fading its own opacity are traps; ::after layers, gradient fills and mask-border roots are inspected; the scrim list is shared with the static test; the entry guard resolves symlinks and a probe that checked nothing exits 2 instead of 0. Claude-Session: https://claude.ai/code/session_011MqqEsWfh1xKLv4fip3aPZ
…bels and QA round 4 lows (N4-M1-M3, N4-L1-L8, code review 6 eneskirca#2-eneskirca#4, eneskirca#8) - N4-M1: the four popovers that cannot blur (context meter, colour, label and members pickers) paint one solid token under glass, --panel (the colour the glass fill is --panel at an alpha of), with the glass hairline and one shadow; Remote access, Publish, notification consent and the GitHub issue dialogs join the glass text surfaces, so every modal dialog is glass. - N4-M2: the glass placeholder no longer reaches the opaque popovers; they keep the theme's. - N4-M3: the sessions sidebar project name is --text-strong, the unpinned pin is ink. - Lows: one glass hairline on every menu and sheet (L1); Members hover is the selection wash (L2); ink checkmark on the washed speech row (L3); RAM rows reserve the pause slot (L4); the terminal theme list scrolls inside the glass menu and fades its end (L5, a display:contents wrapper in the default look); the node label picker clears the node's rim (L6; its repeated copy is upstream and unchanged); dock icons draw opaque ink faded as one layer (L7); palette rows take the strong ink so the placeholder reads lighter (L8). - Code review 6: the default Button keeps --text-strong on its own hover (eneskirca#2); sessions row actions drop the opaque --panel-header fade and the title line makes room (eneskirca#3); the dino header is a lift, not an opaque band (eneskirca#4); the wallpaper cache evicts least recently used (eneskirca#8). eneskirca#9 (pause the pinned drawer's blur while panning) is skipped: .canvas-moving lives on the flow wrapper, which the drawer is not inside, and the review marked it optional. Claude-Session: https://claude.ai/code/session_011MqqEsWfh1xKLv4fip3aPZ
…ce; guards see motion Claude-Session: https://claude.ai/code/session_011MqqEsWfh1xKLv4fip3aPZ
…probe leaves no state; QA round 5 fixes (code review 7, N5-M1, N5-M2, N5-L1-L3) Code review 7: - eneskirca#1 (HIGH): under Reduce Motion + glass the kanban card modal and its scrim (also the GitHub issue modal's) still ran their opacity fades, kanban-fade/kanban-pop: the glass overrides sat only in a no-preference query. A gated reduce rule switches them off. Live, reduced motion with playback frozen: 0 animations, opacity 1 up the chain; reverted, the scrim ran kanban-fade at opacity 0. - eneskirca#2: the static guard checks both motion preferences separately; an override inside a media query counts only for the preference it names. Reverting eneskirca#1 now fails with exactly .kanban-modal-scrim and .kanban-modal. - eneskirca#3: a fade matches a glass surface when their selectors can hit the same element (either covers the other, or they share a subject class). - eneskirca#4: a blurred ::before layer is a target, so its own opacity fade is caught. - eneskirca#5: a transition shorthand with no property name counts as all; @-webkit-keyframes count; a later unconditional rule setting backdrop-filter without a blur on the same element cancels the blur. Each evasion has a synthetic case; all three new cases fail on the previous guard. - eneskirca#6: the probe's mid-animation pass no longer calls pause()/play(). It seeks only finite running animations and seeks them back (one synchronous evaluate, so the timeline cannot advance), re-sets animation-name on the elements it replayed, removes a style attribute it added, and checks its own restoration (exit 2 if anything is left behind). - eneskirca#7: the probe times out: 20 s per evaluate, 5 s for the target list, then exit 2. - eneskirca#8: the mobile-launch card is glass like every other modal dialog. Visual QA round 5: - N5-M1: destructive menu items on glass have an ink label, and only the icon stays red. Delete measured 7.37:1 dark / 6.16:1 light (was 2.79 / 3.89); the red icon is 3.38 / 3.30. - N5-M2: the placeholder cannot go below 0.77 on dark glass and keep 4.5:1 over the sampled backdrops, so the dark glass dialogs raise their secondary text (--muted) to 0.82, and Remote access's hard-coded 0.55 lines use it. Placeholder ink is now below secondary ink. In light, 0.85 is both the placeholder floor and --text, so typed text stays --text-strong. - N5-L1: Remote access pops like its sibling dialogs. - N5-L2: RAM panel secondary text uses --muted: 6.6-7.5:1 dark, 4.95-5.8:1 light (was 3.95-4.22). - N5-L3: the Report a bug and Set icon titles are semibold, like Remote access. The default look is unchanged: every new rule is behind the glass gate. Claude-Session: https://claude.ai/code/session_011MqqEsWfh1xKLv4fip3aPZ
… probe restoration, dialog secondary text Claude-Session: https://claude.ai/code/session_011MqqEsWfh1xKLv4fip3aPZ
…s, doc fix
- scripts/glass-trap-probe.mjs: move the mid-animation replay/seek loops inside
the try block so the finally restoration always runs; restore an element that
already had a style attribute via setAttribute('style', before) instead of
poking el.style, and remove the attribute only where there was none; note in
the header that replaying an animation fires animationcancel/animationend,
which the app may react to (e.g. the drawer refits maximized nodes and marks
the workspace dirty).
- src/renderer/styles.glass-traps.test.ts: TIMING regex now accepts an optional
leading minus so a negative transition-delay isn't mistaken for a property
name; added a synthetic case (`.pane { transition: 0.2s -0.05s }` on a glass
surface) that fails under the old regex and passes under the fixed one.
- CLAUDE.md: drop the inaccurate claim that `.tab-menu button.danger` keeps red
on its icon under glass — nothing renders or styles that; the sentence now
describes only `.ctx-item.danger`.
Claude-Session: https://claude.ai/code/session_011MqqEsWfh1xKLv4fip3aPZ
e252f07 to
ea53798
Compare
…s glows toDataUrl stat()ed the file and then readFile()d it by path, so the size cap could be checked against one file and the bytes read from another (CodeQL js/file-system-race). Open once, fstat and read the same handle, and refuse a non-regular file. Glass terminal nodes are 14px-rounded while the unread/working/attention glows are drawn at 10px, so the ring sat off the corners. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… files The import-time prune kept only the wallpaper on screen, so with a preset showing and a recent import A, importing B deleted A before the renderer saved the choice; a reload in between left recentWallpaperImage naming a missing file. It now keeps everything the saved settings name. A FIFO or device named x.png hung copyFile/sips and wedged that target's publishOnce job for the run; import now requires a regular file. The docs said the renderer never names a path to read; import does take one. Say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
installGlassCellBackgrounds patches the WebGL addon's shared renderer prototype, and it ran on every WebGL grant whether or not glass was on, so a user who never enabled glass still rendered through a patched private renderer. Install it when a glass terminal needs it: at grant time if the node is already glass, else when its alpha turns non-null. Also: a run whose buffer line is missing read the shared scratch cell left by the previous run; treat it as unknown instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for this, it's a big and careful piece of work. I reviewed the current head ( A note on the rebase first: yesterday I pushed a fix commit to this branch ( What I pushed
Checked on the head merged with main: Changes in the default look (accepted, recorded for the changelog)The description says nothing changes unless you opt in. That holds for the wallpaper, glass and Liquid Glass code, which is well gated. The semantic-colour refactor and one Reduce Motion block do reach the default Dark and Light looks. We've decided to accept these as intended, so they're listed here for the record:
Downgrade and open items
Visual verification so far is your macOS dev build. We'll check it on our side too. |
Every value the probe prints comes back from the page over CDP; a newline or escape in one could forge log lines (CodeQL js/log-injection x4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| for (const r of results) { | ||
| traps += r.traps.length | ||
| if (!r.glass || r.checked === 0 || r.leftovers.length) blind = true | ||
| for (const l of r.leftovers) console.error(`probe left state behind: ${clean(l)}`) |
| for (const l of r.leftovers) console.error(`probe left state behind: ${clean(l)}`) | ||
| if (args.includes('--json')) continue | ||
| if (!r.glass) console.log('Liquid Glass is not on (data-nt-glass) — nothing glass to check') | ||
| console.log(`${clean(r.pass)}: ${clean(r.traps.length)} trap(s) in ${clean(r.checked)} translucent surface(s)${r.pass === 'mid-animation' ? `, ${clean(r.animations)} animation(s) seeked to 50%, restored` : ''}`) |
| if (args.includes('--json')) continue | ||
| if (!r.glass) console.log('Liquid Glass is not on (data-nt-glass) — nothing glass to check') | ||
| console.log(`${clean(r.pass)}: ${clean(r.traps.length)} trap(s) in ${clean(r.checked)} translucent surface(s)${r.pass === 'mid-animation' ? `, ${clean(r.animations)} animation(s) seeked to 50%, restored` : ''}`) | ||
| for (const t of r.traps) console.log(` ${clean(t.el)} ${clean(t.bg)} ${clean(t.why)} [${clean(t.box.join(','))}]`) |
| })() | ||
| if (isMain) | ||
| main().catch((e) => { | ||
| console.error(String(e?.message ?? e).replace(/[\r\n]/g, ' ').replace(/[\u0000-\u001f\u007f]/g, '')) |
Liquid Glass
New
Improved
Good to know
main.https://claude.ai/code/session_011MqqEsWfh1xKLv4fip3aPZ