Repository navigation
cmux-next: background and Chief picture settings (video playback, battery, keep clear of panes, picture motion) - #21746
teamleaderleo wants to merge 57 commits into
Conversation
…t chat Every wide painting framed the same fixed point (0.5, 0.45), so with the sidebar and a docked agent chat open the subject often sat under them, and wide or short windows cut faces off. Each work now names its subject box in art/manifest.json, and the art zooms, slides or shrinks (over a blurred fill) so that box lies in the part of the window the columns leave visible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…out, not the App CmuxNextApp may not grow (check-app-size). The sidebar and each workspace's layout root now tell the window's backdrop where they cover the art (WindowMaterialView.setCover), and the App is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…of sight The first frame now fades in over the poster instead of cutting. A video no one can see (hidden, minimized, covered, screens asleep) for a minute drops its items, freeing the decoder and frame buffers, and resumes at the frame it held when seen again. A video held for Low Power Mode or Reduce Motion is still on screen and keeps its frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings > Background gets Add from a Link (https only, judged by its bytes, 50 MB image / 500 MB video caps), a remove button on each of your images (not the one in use), what they take on disk, and Show in Finder. A picked, dropped or linked file must decode or play before it is copied in. Dropping a file on Settings no longer needs experimental appearance (the page already said to). The ops live in BackdropImagesPageProvider (CmuxNextPages); CmuxNextApp shrinks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…default Chief sheet: a still is placed in the circle (drag, zoom) and drawn at 512 px, so a photo of any size fits the daemon's 1 MB; a GIF goes as it is, keeping its motion; Use Default Picture clears it (chief.profile.avatar.set clear). Chief Settings (the default Chief): Choose Picture… and Use Default Picture; GIF and WebP pictures now show in the header and tile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r edge correctly Review fixes for the subject crop: - keepsSubject is measured on the final placement, never assumed. - A shrunk or slid image keeps the subject in the span first (the cover clamp overrode it), and the shrink fit counts the subject's height too. - No vertical fallback that cannot fail; portrait subjects are the head and upper body (top 70%%), which fit a 21:9 window. - The catalog test asserts the subject frame lies in the visible span. - Only a column that comes or goes springs the art; resizes and live window resizes move it at once. Fill filters are made per view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ready Review fixes: the seek after a decoder release waits for the new item to be ready (a seek sent before then can be dropped or undone by the looper) and uses zero tolerance both ways, not the next keyframe. The players take their clock, so the release deadline test steps a manual clock; the restore test checks the time it came back at. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eir name in their folder Review fixes for Add from a Link: - The link's name is sanitized (no folders, colons or leading dots), and the renamed file must stay in the download's own folder (an encoded ../ climbed out). - A delegate-driven data task replaces download(from:delegate:), whose task delegate never saw progress: the declared length and the running count are checked against the cap for the declared type, so a huge body never lands. - Redirects off https are refused; timeouts are a minute idle, ten in all. - The temporary folder is removed on every failure. - Remove refuses the background in use natively, not just on the page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-ups to the Chief picture placement: - A still's bytes and pixels (at most 50 megapixels) are checked before its preview decodes, and the drawing decodes it already scaled to the square. - A GIF is capped at 1 MB on both sides (the daemon's cap), not 5 MB natively. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…leo/next-place-background
Settings > Background > Adjust Placement… fades the window's content and puts an overlay over it: drag moves the picture, scroll or pinch zooms (1x-4x), Reset goes back to the automatic subject framing, Done or Escape ends. The placement (the point at the middle of the visible span, and the zoom) is kept per background in background-placement.json, and every window follows it. It starts from the framing on screen, so the first drag does not jump. CmuxNextApp is unchanged: the op rides BackdropImagesPageProvider. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…me test holds still Re-review fixes: the entry is stored before the readiness observation, so an item already ready (reported inline) seeks and clears its pending restore instead of leaving it set. The resume test restores while held in Low Power, so a restart from 0 cannot reach the mark by playing; it checks 0.95-1.05 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…re they start Re-review fixes: LinkDownload keeps its state in a Mutex and is plainly Sendable (no new unchecked conformance for the crash ratchet). A cancel that comes before the task exists marks the download, and the task is cancelled as soon as it is made, instead of invalidating the session first (making a task on an invalidated session raises). A body that starts like a picture gets the picture cap whatever type the server declared. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eo/next-place-background
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…next-batch/leo-backgrounds
…xt-batch/leo-backgrounds
…into next-batch/leo-backgrounds
…eep clear of panes) appearance.backgroundVideo = play | whenActive | still (play), appearance.backgroundVideoOnBattery = play | pause (play) and appearance.backgroundAvoidPanes (on): Settings rows under Window Background, agent-settable, parsed into DesignSettings.backdropPlayback by the applier. Still Picture never plays and gives its decoder back; Only While cmux Is Active follows the app; Pause watches the power source (IOKit). Turning Keep Paintings Clear of Panes off frames art in the whole window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
home.chiefPictureMotion = always | hover | never (hover), a Home row and agent-settable; Reduce Motion always holds pictures still. Pages get it as data-picture-motion on <html> with the theme: a GIF or animated WebP avatar shows its drawn first frame, and moves only always or while pointed at. The native header shows the first frame unless Always. The sidebar tile is always a still square copy, so a non-square GIF is no longer stretched there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Bead |
This comment has been minimized.
This comment has been minimized.
|
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)
Pending: CI is running on Last result: Unclear: Written by red: cmux-next CmuxNextApp size cap; NEW: green on the base 2d57394; do not merge until green; see lane-rules
Agents: |
|
Unclear: CI failed on
Not re-run automatically: Written by |
|
Agents: |
…ace window, saved in order Review fixes for Adjust Placement: - Compile: the store notification's object is compared by identity (the batch Swift run stopped on 'sending store risks causing data races'). - Saves run on one serial queue, in order; an unreadable file is kept beside. - The overlay opens in the workspace window (WindowManager.active), never the Settings window; one at a time app-wide; it ends when its window shows another background, and fades views added while it is open. - Keyboard: arrows nudge (Shift: further), + and - zoom, 0 resets. - A drag past an edge has no dead travel; windows only re-place for their own background's change; removing a picture drops its placement; the page's 'no picture' note clears when the background changes. - init(coder:) returns nil instead of fatalError (crash ratchet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ace-background # Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextDesign/BackdropArtPlacement.swift # Packages/macOS/CmuxNext/Sources/CmuxNextDesign/WindowMaterialView.swift # Packages/macOS/CmuxNext/Tests/CmuxNextDesignTests/BackdropArtPlacementTests.swift
…ckground-settings # Conflicts: # schemas/settings-export/settings-schema.sources.sha256 # schemas/settings/settings-schema.json
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. |
From fleet export run (step 9fa138a873ed50b32dad9558). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(webviews): strings tables must not be tracked Red on feat-cmux-next: every table gen-strings.mjs writes is committed, and none is ignored (#21946). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: page strings tables become build output (#21946) The generated strings tables (webviews/src/**/generated/strings.json, the diff page's per-locale tables and loader, the home-channels pane table) are no longer committed. bun install (postinstall, best effort), the webviews test preload (only stale pages) and every bundle build (strict) write them from the checkout's xcstrings catalogs and settings schema. Committed copies conflicted in every pull request that changed a catalog or the settings schema, and the PR autofix could rebuild one from a stale schema and drop another branch's labels (#21746). A table made from the tree it sits in can do neither. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: merge rules and atomic writes for every strings output (#21946) The diff page's locale tables and loader and the home-channels pane table are build output too: route them through cmux-generated-v1 and next_batch's WEB_GENERATED, so a branch from before the change that edited one catches up instead of stopping on a modify/delete conflict. gen-strings writes through a temporary file (parallel test preloads), and --best-effort skips a page whose catalog or schema is missing instead of failing bun install. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at f2ef8f4 - regenerated schemas/settings-export/settings-schema.sources.sha256 - vp fmt webviews/scripts/pages/gen-strings.mjs - vp fmt webviews/test/strings-build-output.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39205550635 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
…t frame of a click (#21976) * test(ui): a picker trigger is marked pressed from press to release (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(agent pane): a composer picker keeps its pressed tone on the first frame of a click Opening a picker moves focus into its popup before the held frame paints, which clears :active. The trigger carries data-press-active from press to release, and the composer's picker buttons draw the open-chip tone under it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * guards: drop the pull_request_target publish case removed with publish-pr-tree 17bb49c deleted the dead publish-pr-tree job, so release-notary's 'pull_request_target refs/heads/feat-cmux-next publishes once' cases failed 5 times (run 39205353024). * ci(cmux-next media): rebuild a warm tour's prebuilt modules after a binary pin change (#21971) * test(ci): a warm tour must rebuild after a GhosttyNextKit pin change The cmux-next tour keeps each tag's DerivedData warm, and Xcode reused the explicit module it had precompiled from the old pin's ghostty.h: "ghostty.h has been modified since the module file ... was built" (job 117892119256). This regression runs the tour's build step twice with a stand-in reload.sh that fails the same way; it fails until the fix lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ci): drop a warm tour's prebuilt modules when a binary pin changes A Mac that last built a pull request's tag with GhosttyNextKit e6a27a1fb failed the next tour, which pinned ced99dcde: Xcode copied the new ghostty.h into Build/Products/Debug/include but kept the explicit module it had precompiled from the old one (job 117892119256). scripts/ci/media_module_cache_stamp.py keys a tag's build on every sha256 in the tracked Package.swift files and every Package.resolved. Before reload.sh it removes SwiftExplicitPrecompiledModules, ExplicitPrecompiledModules and ModuleCache.noindex when that key differs from the stamp of the tag's last good build; after the build it writes the stamp. The rest of DerivedData stays warm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * gallery advisory: sample the states a PR adds first (#21850) * gallery advisory: sample the states a PR adds first A PR that touches a shared file (strings, App.tsx) touches every gallery entry, so the first 16 states in order came from unrelated entries and the receipt never showed what the PR added. The manifests now list every touched state and the sample puts the new ones (no base case with that id) first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * gallery advisory: the base keeps every sampled state before removed ones The base filter cut at 16 in base order, so removed states early in that order could push out sampled states that exist in base, which were then compared as new. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 0283a51) * gallery advisory: each stage renders its own manifest Both stages rendered the joined case list, so the base reported the states a PR adds as not ready (it has no such variant) and the capture stopped before the head ran. Now that new states come first, every PR that adds a variant hit this (#21851's first run). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 5b6d825) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * docs(CLAUDE.md): merge freezes are issues; drop the WINDOW fallback (#21958) * docs(CLAUDE.md): merge freezes are issues; drop the WINDOW fallback The guarded fallback for when gh-merge-green couldn't read the coordinator's WINDOW no longer applies: freezes are merge-freeze issues that every host reads through the GitHub API (#21957). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(merge-freeze): gh-merge-green enforces the freeze check Same line as #21957 on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * apps: preserve provider mutation context for owner handlers (#21406) Replays #21406's change onto current feat-cmux-next as one commit. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * test(sidebar): a dragged group drops between every group Regression test for scrawl-1792382497882-1673e-10: on the sidebar document path a dragged group found a drop only on the thin bands of each group header. Fails without the fix. * fix(sidebar): fluid group drag, a drop at every gap between groups - SidebarDragGeometry: a drop that cannot go into a group (a dragged group, groups do not nest) goes before or after the whole group, by the half of its block the probe is in, over its header or any member row. Before, every member row refused and the gap closed. - A dragged group's header, not the tall card's centre, decides the drop. - A refused place keeps the last open gap instead of springing it shut and dimming the card, so rows stop jumping during the drag. * test(layout): a divider drag keeps one daemon command in flight Adds GestureCommandCoalescer and its tests: 125 live values while one command is in flight send the first and the newest only; the ended value goes last; a failed live value with a newer one waiting is superseded. * fix(layout): stop flooding the daemon during divider and column drags NIGHTLY sent one set-viewport-pane-width per mouse move (~125/s); each timed out at 2 s and every other command (pane and tab moves, saves, attaches) timed out behind them. Divider and column gestures now go through GestureCommandCoalescer: one command in flight per gesture, only the newest value waits, the ended value always goes last. A failed live value with a newer one waiting no longer rolls the divider back. * scripts(cmux-next): drag-lag-live.py, column/tab/sidebar drag timing on a fleet app * scripts(cmux-next): drag-lag-live probes the daemon during drags, bigger scene * ci(nightly-next): one-command rollback of the cmux NEXT feed (#21887) * ci(nightly-next): one-command rollback of the cmux NEXT feed nightly-next-rollback.yml takes a bad build, and every newer one, off appcast-arm64.xml so the feed serves the previous signed build again. It runs in release-next on branch nightly-next, guards the appcast and R2 key like publish-nightly, checks the fallback DMG exists, refuses to empty the feed, and stops when the feed changed while it planned. docs/ci/nightly-next-canary.md maps the nightly pipeline and its holds and says what a pull can and cannot do: Sparkle never offers a lower build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(nightly): the delta lane never puts a pulled build back on the feed republish-nightly-deltas skipped its rewrite only when the live NEXT feed served a newer build. After nightly-next-rollback.yml pulls build N, the feed serves N-1 and the lane's two-item appcast would restore N. It now skips whenever the feed serves any other build. The regression cases in test_nightly_republish_delta_guard.py fail on the old guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(nightly-next): rollback runs-on keeps a fork branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(nightly-next): the rollback writes the feed only with If-Match upload-r2-object.py --replace-sha256 HEX reads the object, requires the bytes the caller planned from, and writes with If-Match on its ETag; a changed object is not written (--conflict-exit-code). The rollback uses it instead of a public re-read, so a publication that lands between the plan and the write is never overwritten. Its summary no longer says "nothing to pull" when the plan itself failed. (CodeRabbit on #21900.) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: mark the sidebar card's Core Animation objects as reviewed motion exceptions (#21951) * cmux-next: mark the sidebar card's Core Animation objects as reviewed motion exceptions c5a1f90 built the update card's entrance, exit and crossfade from raw CABasicAnimation, CASpringAnimation, CATransition and CAMediaTimingFunction, so check-motion is red on feat-cmux-next (9 hits). Every duration and spring already comes from the injected MotionPolicy's tokens; Motion.set would write the model value and read the global policy instead. The 9 lines carry a motion-allow and the type doc says why. Comments only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the sidebar card's motion-allow note names what stays literal The curves and the spring's settle tolerance are literal, not tokens; say so, so the allow does not overclaim (coord-2's review of #21951). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * optchat-chief: auto-resume subagents that stop on a model or network error A subagent run that ends on an API/harness error is classified (auto_resume.rs): transient errors (overloaded, 429, 5xx, did not start) are told to continue after a 30/60/120 s backoff, at most 3 times in a row; network errors (connection lost, fetch failed, DNS) are not retried blind: a thread waits until a TCP probe of the model endpoint (the subrouter from ANTHROPIC_BASE_URL or ~/.claude/settings.json) answers, then continues. The Chief's chat gets a quiet '[id] stopped: <reason>' note; the subagent's chat shows each '[auto-resume N]' continue. A stop auto-resume gives up on is reported as usual. OPTCHAT_AUTO_RESUME=off turns it off. * optchat-chief: auto-resume agent CLIs in cmux terminals; fix clone_on_copy terminal_watch.rs: every 30 s the Chief host lists cmux terminals; a Claude Code or Codex terminal that reports 'error', or sits idle at an empty prompt with an API error as its last output, is classified as in auto_resume.rs. Transient errors get '[auto-resume N] ... continue' typed at the prompt after a backoff; network errors wait until the model endpoint answers. Working, blocked (a question) or half-typed prompts are never touched. Each event reaches the Chief chat as '[term_...] stopped: <reason>' (a notice while a continue is planned, a Chief note when it gives up). OPTCHAT_TERMINAL_RESUME=off turns it off. Also: a short reply that only mentions an error is not a stop, and the clippy clone_on_copy in workspaces.rs (a75b0c3) is fixed. * optchat-chief: module order in lib.rs (rustfmt) * fix(acpmux): clear stop timer on client close (#21615) * Markdown: cmux:// workspace, pane, tab and chat links open in the app A cmux:// link in rendered Markdown did nothing or left the app: the Markdown file page classified every non-web scheme as unsafe, Home only linked a Chief subagent's chat, and the agent pane drew only http(s). - One opener, CmuxAppLinks (was ChiefSubagentLinks): a workspace, pane, tab or session link in the `cmux` scheme or this build's scheme, or this Chief's subagent link, runs link.open. Terminals and pages reach it via LinkRouter.appLink. - Markdown file page: `cmux://...` is link kind "app" (hover: "Opens in cmux", 21 locales); cmux.markdown.openLink kind "app" opens it in the app or refuses, never the system. - Home: URL.isCmuxAppLink draws and routes the same links to onAppLink. - Agent chat: replies draw cmux app links; a click after a real gesture goes to the app opener, never NSWorkspace. - javascript:, file:, data:, vbscript:, ssh: and malformed ids stay text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next palette: Go to Workspace (Cmd-P) searches groups, panes, tabs and agent sessions Cmd-P now lists sidebar groups, panes and every tab (terminal, browser, markdown, file, agent chat) next to the workspaces once you type, each row with its kind and its parent workspace and group. Titles, cwd, URL, group names and session ids are searchable; whole-title and prefix matches rank first, then the recently used. - Picking a group shows its first workspace, expanding the group when collapsed (WorkspaceGroupFocus, shared with Next/Previous Workspace Group). - Picking a pane or tab shows its workspace and focuses it (pane.focus, revealTab as Search Tabs). - Cmd-K in the palette shows the row's Actions menu; agent rows offer Copy Session ID: acp:<session id> for acpmux chats (what cmux agent message takes), the harness session UUID for Claude Code / Codex terminals. Handle format documented in skills/cmux-acpmux/SKILL.md. * acpmux: require version evidence for already-current updates (#21398) Replays #21398's change onto current feat-cmux-next as one commit. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cx-dgsp follow-ups: scope-bound wire tickets, hold a refused feed token (#21725) * test(api): the forwarded wire upgrade carries no credential A native header upgrade with stray bearer.x and ticket.y subprotocols reaches UserDO with no Authorization header and only cmux.wire.v1. Green on the current strip; red when wireCredential keeps bearer. or forwardHeaders keeps Authorization. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(api): a wire ticket opens only the scope it was minted for (red) POST /v1/wire/ticket takes {scope}; a feed ticket must not open the user socket, a refused try spends it, an unscoped ticket is a user ticket and an unknown scope mints nothing. Fails now: the feed ticket opens /v1/wire/user (101). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud-client): openWire mints its ticket for the scope it opens (red) Fails now: POST /v1/wire/ticket goes out with no body. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(api): bind a wire ticket to the socket scope it was minted for POST /v1/wire/ticket takes {scope} (user|team|feed|cloud|conv|mux; no body or no scope means user, which the dashboard panel and any client minted before this send). The ticket record keeps the scope; the router entry still spends a ticket on any path, and wire() refuses one of another scope with the usual 401. openWire and the dashboard's wireCredential name their scope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cmux-next): a 401 on the feed wire holds the refused token (red) CloudFeedSource takes an internal Connect seam and reconnect Backoff so the loop runs against a fake; the socket session throws Unauthorized when the upgrade's response is 401. The test fails now: the loop opens a socket for every repeat of the refused token (a, a, a, b), not a then b. Compile-only on this Linux host; it runs in CmuxNextAppTests on macOS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next): stop reconnecting the feed wire with a token the server refused A 401 on the upgrade fed the outer reconnect loop, which opened a socket with the same token every 500 ms..30 s. The loop now remembers the refused token, reports a disconnected reason once, and opens no socket with it again: it asks the token provider on the Backoff schedule (no request reaches the server) until it returns another token. start and reset still restart the loop on a sign-in or account change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(api): the expired-ticket test mints a feed ticket for the feed socket With scope binding, an unscoped (user) ticket on /v1/wire/feed is refused for its scope, so the test no longer reached the expiry check. Red when WireTicketDO.spend ignores expires_at. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the Desktop Wallpaper tile shows the desktop, never a broken image macOS can report a desktop picture that is gone (a deleted file in /tmp), a folder it rotates through, or a .madesktop description. The Settings thumbnail and the window backdrop loaded that URL as is, so the tile drew the webview's broken-image mark and choosing it showed nothing. DesktopPicture resolves the reported URL to a picture that decodes: the file, a picture in the folder, the still a description names, or else a picture of the desktop fill color. The picker swaps a thumbnail that fails to load for an empty framed box. * Home dock: pinned column kind (pinned-dock-v1) Lawrence 2026-10-18: "I should not be able to close Home inside the Home thing ... I should not be able to create more tabs in the Home thing unless I explicitly enable it." Daemon (cmux-tui): a dock column may be pinned. Its first tab stays (no close, move out or reorder away from the first place) and it takes no other tab unless its allow_tabs is on (column.update {allow_tabs}). The column is never removed, undocked or unpinned. Enforced on every path (resource ops, JSON-lines, layout undo, terminal tab creation before the spawn) with reason_code dock-column-pinned. set-column-dock takes pinned; column.update takes pinned and allow_tabs. Spec, docs, bindings updated. App: Home pins its left chat dock on connect (page tab first). Home's tab shows no close button, the strip no new-tab button while the column takes no tabs, Cmd-W keeps Home's tab, new agent chats open beside a pinned dock, and a refused close says "Home stays open in its column." * cmux NEXT: compact attachment strip in the composer (#21739) * test(acpmux): the attachment strip is compact Leo 2026-10-18: pasted screenshots took ~100 px off the composer. One row of 64 px tiles 6 px apart, 8 px under the composer's top, the text 6 px under it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): compact attachment strip, like the Claude desktop composer 64 px tiles (from 80), a 6 px gap, an 8 px top inset, and the prompt starts 6 px under the strip instead of 15. The strip adds about 78 px, not ~100. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): shell field follows the compact strip; tighten the gallery budget Review: in shell mode under attachments the field and glyph kept the 15 px inset, so typing ! moved the text 9 px. The attach-screenshot gallery budget still allowed the old 96 px row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(acpmux): wait for the thumbnails to load instead of decode() CI: decode() rejected while a thumbnail's source was swapped for its copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the popup address field draws no focus ring (#21963) fa8b3fc made the popup URL label selectable without focusRingType = .none, which turns check:text-focus red on every webviews job. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * composer: picking another provider's model keeps the typed prompt (#21937) * test: a harness switch keeps the typed prompt (red) * composer: picking another provider's model keeps the typed prompt through the harness switch * composer: a chat's own saved draft wins over a carried prompt (review) * fix(acpmux): the empty prompt mid-turn teaches ↵ and ⌘↵ (#21870) * test(acpmux): a running turn's empty prompt teaches Return and ⌘Return Leo 2026-10-18: like Claude's "↵ to send, ⌘↵ to queue for later", the empty prompt mid-turn says "↵ to queue, ⌘↵ to steer" (or interrupt, for an agent that cannot steer). The Quick Composer keeps "Queue a follow-up". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): the empty prompt mid-turn teaches ↵ and ⌘↵ Mid-turn the empty prompt says "↵ to queue, ⌘↵ to steer" (or "to interrupt" when the agent cannot take steering), as Claude's composer does. The Quick Composer keeps "Queue a follow-up", since its ⌘↵ opens the window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(acpmux): an attachment-only prompt mid-turn names only the queue key Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): drop the ⌘↵ hint with only attachments, match the Steer button's verb ⌘Return needs typed text, so an attachment-only prompt mid-turn says only "Queue a follow-up". The es, fr, it, ja, ko and uk hints now use the Steer button's own verb. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(acpmux): say when the queued placeholder still shows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: schema doc names peer masking and the lane error cut (#21924) * docs(acpmux): the schema names peer masking and the lane error cut The SessionSummary lane note still said every other field stays raw; since #21871 a peer daemon reads them masked, and since #21799 the lane error is cut to 3072 characters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(acpmux): raw for every client but a proven peer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: every markdown viewer is read-write; Cmd-=/-/0 text size and a default size setting Root cause: a markdown file opened by cmux open, an agent or a link (not the user's own Open File...) was writable only inside a files.roots folder. The roots setting is empty by default and never holds home, so viewers opened from the Home workspace (and any workspace) came up read only with an "outside the workspace" note. - Markdown pages are writable for every document they show; only a real reason keeps one read only (not UTF-8, binary, not writable, or a chat transcript the host opens read only), and the page names the reason. The code editor keeps its root rule. - The markdown page handles the zoomIn/zoomOut/zoomReset page commands the app already sent: one pixel per step (8-40) in rich and source mode, Cmd-0 back to markdown.font.size. Cmd-Shift-= is a Zoom In alias. - Settings > Appearance > Markdown Viewer > Default Font Size (markdown.font.size, 16, 8-40), localized. Co-Authored-By: Claude <noreply@anthropic.com> * fix(home): pasted pictures are small thumbnails in a row in the composer The vendored MessagesLab compose strip copies macOS 27 Messages: every attachment is a 177 x 118 pt tile stacked top to bottom above the text. Two pasted screenshots made the field ~280 pt tall and pushed the text line to the bottom. Images are now aspect-fit in 128 x 64 pt, files are 200 x 52 pt pills, and tiles flow left to right and wrap at the field's right edge. Tests: ComposeAttachmentLayoutTests (MessagesLabHome). * test(home): qualify Attachment (Swift Testing has one too) * cmux-next: page strings tables become build output (#21970) * test(webviews): strings tables must not be tracked Red on feat-cmux-next: every table gen-strings.mjs writes is committed, and none is ignored (#21946). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: page strings tables become build output (#21946) The generated strings tables (webviews/src/**/generated/strings.json, the diff page's per-locale tables and loader, the home-channels pane table) are no longer committed. bun install (postinstall, best effort), the webviews test preload (only stale pages) and every bundle build (strict) write them from the checkout's xcstrings catalogs and settings schema. Committed copies conflicted in every pull request that changed a catalog or the settings schema, and the PR autofix could rebuild one from a stale schema and drop another branch's labels (#21746). A table made from the tree it sits in can do neither. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: merge rules and atomic writes for every strings output (#21946) The diff page's locale tables and loader and the home-channels pane table are build output too: route them through cmux-generated-v1 and next_batch's WEB_GENERATED, so a branch from before the change that edited one catches up instead of stopping on a modify/delete conflict. gen-strings writes through a temporary file (parallel test preloads), and --best-effort skips a page whose catalog or schema is missing instead of failing bun install. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at f2ef8f4 - regenerated schemas/settings-export/settings-schema.sources.sha256 - vp fmt webviews/scripts/pages/gen-strings.mjs - vp fmt webviews/test/strings-build-output.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39205550635 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * cmux NEXT: a new terminal tab spawns at its pane's grid, so its first frame is final The Mac's new-tab request sent no size. The daemon then spawned the PTY at the grid of whichever terminal reported last, anywhere (client_sizing_state.rs creation_size), the shell printed its first prompt at that width, and the pane snapped when the view's first report resized it. newTerminalTab now sends cols/rows (NewTerminalGrid): the fitted grid of the terminal the pane shows (Ghostty's own fit of the same content host), else the content host size, backing scale, a live terminal's cell size and the configured padding through Ghostty's formula (TerminalGridEstimate; Cmd-T's New Tab page, a page or agent tab). A zoomed terminal does not count. With neither known the daemon's fallback applies as before. docs/shared-terminal-sizing.md: a Creation size section (who sizes a new terminal, and why a later attach never draws a wrong frame for others). * cmux NEXT: NewTerminalGridTests builds its padding from the public default * perf-10x daemon: request-latency bench (raw, 10 clients, CLI; 1 and 200 terminals) * cmux-next: clear five base reds in the cmux-next checks lane (#21981) The ErrorJournal (1597dc9) and browser popup (fa8b3fc) commits left five checks red on feat-cmux-next, so every PR into it fails "cmux-next checks" and its web lane skips its tests. Comments and baselines only, no behavior change: - Main-thread concurrency: the harvest loop runs in a detached utility task and the flush delay in the ErrorJournal actor's task, so neither is on main; reviewed concurrency-allow and wakeup-allow comments. - No literal state roots: the app's ~/.acpmux/daemon.log is only read; a keep row in the baseline. - Package conventions: lint:allow on BrowserPopupWindowGeometry (pure geometry). - Type scale: BrowserPopupPanel.swift has no raw font sizes left; drop its row. - Text focus: the popup address field sets focusRingType = .none (was #21963). Motion tokens (SidebarCardTransition) stays with #21951 for its owner. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cx-ncc.104: Debug toggle for the agent pane's in-session status (agent.sessionStatus) (#20953) Recut of #20953 onto the current feat-cmux-next as one commit: the In-session status Debug toggle, its sessionStatus page event, and useSessionStatus() for the plan and recap cards. The base merge kept both sides of the tunables list, the view's observations and the page host's events (the sidebar preview variant landed meanwhile). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: route/list says which routes serve the session's family (#21852) * test(acpmux): route/list names a session's family and which routes serve it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(acpmux): route/list says which routes serve the session's family With a sessionId, _acpmux/route/list now returns the session's family and a serves flag on each route (Route::serves), so the composer's Account menu can list only the routes that can run this chat without copying that logic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: tunable registries list one entry per line (#21972) * cmux-next: tunable registries list one entry per line (#21946) AgentPaneTunables.all and SidebarTunables.all were single lines, so every pull request that added a tunable conflicted with every other one there (14 open PRs on AgentPaneTunables, 4 on SidebarTunables). Each entry now has its own line in alphabetical order, so two additions insert different lines and merge cleanly; TunableCatalog.all lists one module per line. Debug Settings shows the agent-pane and sidebar tunables alphabetically; nothing else depends on the order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the TunableCatalog list as it was: CmuxNextApp may not grow The one-per-line TunableCatalog added 20 source lines to CmuxNextApp, which check-app-size.sh refuses. The conflict hotspots are the AgentPaneTunables and SidebarTunables lists, which live in their own targets and stay one per line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * test(next): cover dictation auto-send prompt routing (#21169) * test(next): cover dictation auto-send prompt routing * fix(next): satisfy dictation test formatting * chore(next): PR autofix at 60f4a49 - regenerated Packages/macOS/CmuxNext/ci-target-graph.json - vp fmt webviews/src/agent-session/acpmux/replyHref.ts - vp fmt webviews/src/pages/markdown/README.md - vp fmt webviews/src/pages/markdown/links.ts - vp fmt webviews/test/markdown-cmux-app-links.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39212288176 * chore: drop the autofix bot's unrelated markdown-link and target-graph edits from this PR They aren't part of this change; base carries its own version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(agent pane): a pressed pill keeps the pill's own open tone The model, effort and access pills open at --acpmux-pill-hover; under the press marker alone they drew the brighter --acpmux-pill-open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ui): window blur, Escape and a drag-out release clear the press marker (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ui): the press marker clears on window blur, Escape and unmount A press whose pointerup never reaches the page (Cmd-Tab away, Escape) left the pill tinted. The plain footer's resting and hover rules also exclude the marker, so it can't flash there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Co-authored-by: Lucas Ribeiro <lucascamribeiro@gmail.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
* feat(agent-pane): accept new tab inline suggestions safely * fix(agent-pane): complete visited URL suggestions * test(new-tab): Tab without a highlighted row moves focus; history rows are not inline-accepted Red: Tab is prevented whenever suggestions show, and Right/End inline-accepts history rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(new-tab): Tab steps only a keyboard-highlighted row; inline completion skips history rows Tab is taken only after Down/Up/Tab moved the highlight, so keyboard users can Tab to Send. Right/End inline-accept only top-row open results (typed address or top site), matching the comment that history rows stay explicit picks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * guards: drop the pull_request_target publish case removed with publish-pr-tree 17bb49c deleted the dead publish-pr-tree job, so release-notary's 'pull_request_target refs/heads/feat-cmux-next publishes once' cases failed 5 times (run 39205353024). * ci(cmux-next media): rebuild a warm tour's prebuilt modules after a binary pin change (#21971) * test(ci): a warm tour must rebuild after a GhosttyNextKit pin change The cmux-next tour keeps each tag's DerivedData warm, and Xcode reused the explicit module it had precompiled from the old pin's ghostty.h: "ghostty.h has been modified since the module file ... was built" (job 117892119256). This regression runs the tour's build step twice with a stand-in reload.sh that fails the same way; it fails until the fix lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ci): drop a warm tour's prebuilt modules when a binary pin changes A Mac that last built a pull request's tag with GhosttyNextKit e6a27a1fb failed the next tour, which pinned ced99dcde: Xcode copied the new ghostty.h into Build/Products/Debug/include but kept the explicit module it had precompiled from the old one (job 117892119256). scripts/ci/media_module_cache_stamp.py keys a tag's build on every sha256 in the tracked Package.swift files and every Package.resolved. Before reload.sh it removes SwiftExplicitPrecompiledModules, ExplicitPrecompiledModules and ModuleCache.noindex when that key differs from the stamp of the tag's last good build; after the build it writes the stamp. The rest of DerivedData stays warm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * gallery advisory: sample the states a PR adds first (#21850) * gallery advisory: sample the states a PR adds first A PR that touches a shared file (strings, App.tsx) touches every gallery entry, so the first 16 states in order came from unrelated entries and the receipt never showed what the PR added. The manifests now list every touched state and the sample puts the new ones (no base case with that id) first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * gallery advisory: the base keeps every sampled state before removed ones The base filter cut at 16 in base order, so removed states early in that order could push out sampled states that exist in base, which were then compared as new. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 0283a51) * gallery advisory: each stage renders its own manifest Both stages rendered the joined case list, so the base reported the states a PR adds as not ready (it has no such variant) and the capture stopped before the head ran. Now that new states come first, every PR that adds a variant hit this (#21851's first run). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 5b6d825) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * docs(CLAUDE.md): merge freezes are issues; drop the WINDOW fallback (#21958) * docs(CLAUDE.md): merge freezes are issues; drop the WINDOW fallback The guarded fallback for when gh-merge-green couldn't read the coordinator's WINDOW no longer applies: freezes are merge-freeze issues that every host reads through the GitHub API (#21957). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(merge-freeze): gh-merge-green enforces the freeze check Same line as #21957 on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * apps: preserve provider mutation context for owner handlers (#21406) Replays #21406's change onto current feat-cmux-next as one commit. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * test(sidebar): a dragged group drops between every group Regression test for scrawl-1792382497882-1673e-10: on the sidebar document path a dragged group found a drop only on the thin bands of each group header. Fails without the fix. * fix(sidebar): fluid group drag, a drop at every gap between groups - SidebarDragGeometry: a drop that cannot go into a group (a dragged group, groups do not nest) goes before or after the whole group, by the half of its block the probe is in, over its header or any member row. Before, every member row refused and the gap closed. - A dragged group's header, not the tall card's centre, decides the drop. - A refused place keeps the last open gap instead of springing it shut and dimming the card, so rows stop jumping during the drag. * test(layout): a divider drag keeps one daemon command in flight Adds GestureCommandCoalescer and its tests: 125 live values while one command is in flight send the first and the newest only; the ended value goes last; a failed live value with a newer one waiting is superseded. * fix(layout): stop flooding the daemon during divider and column drags NIGHTLY sent one set-viewport-pane-width per mouse move (~125/s); each timed out at 2 s and every other command (pane and tab moves, saves, attaches) timed out behind them. Divider and column gestures now go through GestureCommandCoalescer: one command in flight per gesture, only the newest value waits, the ended value always goes last. A failed live value with a newer one waiting no longer rolls the divider back. * scripts(cmux-next): drag-lag-live.py, column/tab/sidebar drag timing on a fleet app * scripts(cmux-next): drag-lag-live probes the daemon during drags, bigger scene * ci(nightly-next): one-command rollback of the cmux NEXT feed (#21887) * ci(nightly-next): one-command rollback of the cmux NEXT feed nightly-next-rollback.yml takes a bad build, and every newer one, off appcast-arm64.xml so the feed serves the previous signed build again. It runs in release-next on branch nightly-next, guards the appcast and R2 key like publish-nightly, checks the fallback DMG exists, refuses to empty the feed, and stops when the feed changed while it planned. docs/ci/nightly-next-canary.md maps the nightly pipeline and its holds and says what a pull can and cannot do: Sparkle never offers a lower build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(nightly): the delta lane never puts a pulled build back on the feed republish-nightly-deltas skipped its rewrite only when the live NEXT feed served a newer build. After nightly-next-rollback.yml pulls build N, the feed serves N-1 and the lane's two-item appcast would restore N. It now skips whenever the feed serves any other build. The regression cases in test_nightly_republish_delta_guard.py fail on the old guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(nightly-next): rollback runs-on keeps a fork branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(nightly-next): the rollback writes the feed only with If-Match upload-r2-object.py --replace-sha256 HEX reads the object, requires the bytes the caller planned from, and writes with If-Match on its ETag; a changed object is not written (--conflict-exit-code). The rollback uses it instead of a public re-read, so a publication that lands between the plan and the write is never overwritten. Its summary no longer says "nothing to pull" when the plan itself failed. (CodeRabbit on #21900.) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: mark the sidebar card's Core Animation objects as reviewed motion exceptions (#21951) * cmux-next: mark the sidebar card's Core Animation objects as reviewed motion exceptions c5a1f90 built the update card's entrance, exit and crossfade from raw CABasicAnimation, CASpringAnimation, CATransition and CAMediaTimingFunction, so check-motion is red on feat-cmux-next (9 hits). Every duration and spring already comes from the injected MotionPolicy's tokens; Motion.set would write the model value and read the global policy instead. The 9 lines carry a motion-allow and the type doc says why. Comments only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the sidebar card's motion-allow note names what stays literal The curves and the spring's settle tolerance are literal, not tokens; say so, so the allow does not overclaim (coord-2's review of #21951). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * optchat-chief: auto-resume subagents that stop on a model or network error A subagent run that ends on an API/harness error is classified (auto_resume.rs): transient errors (overloaded, 429, 5xx, did not start) are told to continue after a 30/60/120 s backoff, at most 3 times in a row; network errors (connection lost, fetch failed, DNS) are not retried blind: a thread waits until a TCP probe of the model endpoint (the subrouter from ANTHROPIC_BASE_URL or ~/.claude/settings.json) answers, then continues. The Chief's chat gets a quiet '[id] stopped: <reason>' note; the subagent's chat shows each '[auto-resume N]' continue. A stop auto-resume gives up on is reported as usual. OPTCHAT_AUTO_RESUME=off turns it off. * optchat-chief: auto-resume agent CLIs in cmux terminals; fix clone_on_copy terminal_watch.rs: every 30 s the Chief host lists cmux terminals; a Claude Code or Codex terminal that reports 'error', or sits idle at an empty prompt with an API error as its last output, is classified as in auto_resume.rs. Transient errors get '[auto-resume N] ... continue' typed at the prompt after a backoff; network errors wait until the model endpoint answers. Working, blocked (a question) or half-typed prompts are never touched. Each event reaches the Chief chat as '[term_...] stopped: <reason>' (a notice while a continue is planned, a Chief note when it gives up). OPTCHAT_TERMINAL_RESUME=off turns it off. Also: a short reply that only mentions an error is not a stop, and the clippy clone_on_copy in workspaces.rs (a75b0c3) is fixed. * optchat-chief: module order in lib.rs (rustfmt) * test(new-tab): a visited page completes inline on Right and opens nothing (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(new-tab): a visited page completes inline again The Tab fix also dropped history rows from inline completion to match an older comment, undoing 1d8f0db. Accepting a completion only fills the field on an explicit Right or End, and Enter or Send still opens it, so a visited page completes as in Chrome. The comment now says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): clear stop timer on client close (#21615) * Markdown: cmux:// workspace, pane, tab and chat links open in the app A cmux:// link in rendered Markdown did nothing or left the app: the Markdown file page classified every non-web scheme as unsafe, Home only linked a Chief subagent's chat, and the agent pane drew only http(s). - One opener, CmuxAppLinks (was ChiefSubagentLinks): a workspace, pane, tab or session link in the `cmux` scheme or this build's scheme, or this Chief's subagent link, runs link.open. Terminals and pages reach it via LinkRouter.appLink. - Markdown file page: `cmux://...` is link kind "app" (hover: "Opens in cmux", 21 locales); cmux.markdown.openLink kind "app" opens it in the app or refuses, never the system. - Home: URL.isCmuxAppLink draws and routes the same links to onAppLink. - Agent chat: replies draw cmux app links; a click after a real gesture goes to the app opener, never NSWorkspace. - javascript:, file:, data:, vbscript:, ssh: and malformed ids stay text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next palette: Go to Workspace (Cmd-P) searches groups, panes, tabs and agent sessions Cmd-P now lists sidebar groups, panes and every tab (terminal, browser, markdown, file, agent chat) next to the workspaces once you type, each row with its kind and its parent workspace and group. Titles, cwd, URL, group names and session ids are searchable; whole-title and prefix matches rank first, then the recently used. - Picking a group shows its first workspace, expanding the group when collapsed (WorkspaceGroupFocus, shared with Next/Previous Workspace Group). - Picking a pane or tab shows its workspace and focuses it (pane.focus, revealTab as Search Tabs). - Cmd-K in the palette shows the row's Actions menu; agent rows offer Copy Session ID: acp:<session id> for acpmux chats (what cmux agent message takes), the harness session UUID for Claude Code / Codex terminals. Handle format documented in skills/cmux-acpmux/SKILL.md. * acpmux: require version evidence for already-current updates (#21398) Replays #21398's change onto current feat-cmux-next as one commit. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cx-dgsp follow-ups: scope-bound wire tickets, hold a refused feed token (#21725) * test(api): the forwarded wire upgrade carries no credential A native header upgrade with stray bearer.x and ticket.y subprotocols reaches UserDO with no Authorization header and only cmux.wire.v1. Green on the current strip; red when wireCredential keeps bearer. or forwardHeaders keeps Authorization. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(api): a wire ticket opens only the scope it was minted for (red) POST /v1/wire/ticket takes {scope}; a feed ticket must not open the user socket, a refused try spends it, an unscoped ticket is a user ticket and an unknown scope mints nothing. Fails now: the feed ticket opens /v1/wire/user (101). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud-client): openWire mints its ticket for the scope it opens (red) Fails now: POST /v1/wire/ticket goes out with no body. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(api): bind a wire ticket to the socket scope it was minted for POST /v1/wire/ticket takes {scope} (user|team|feed|cloud|conv|mux; no body or no scope means user, which the dashboard panel and any client minted before this send). The ticket record keeps the scope; the router entry still spends a ticket on any path, and wire() refuses one of another scope with the usual 401. openWire and the dashboard's wireCredential name their scope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cmux-next): a 401 on the feed wire holds the refused token (red) CloudFeedSource takes an internal Connect seam and reconnect Backoff so the loop runs against a fake; the socket session throws Unauthorized when the upgrade's response is 401. The test fails now: the loop opens a socket for every repeat of the refused token (a, a, a, b), not a then b. Compile-only on this Linux host; it runs in CmuxNextAppTests on macOS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next): stop reconnecting the feed wire with a token the server refused A 401 on the upgrade fed the outer reconnect loop, which opened a socket with the same token every 500 ms..30 s. The loop now remembers the refused token, reports a disconnected reason once, and opens no socket with it again: it asks the token provider on the Backoff schedule (no request reaches the server) until it returns another token. start and reset still restart the loop on a sign-in or account change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(api): the expired-ticket test mints a feed ticket for the feed socket With scope binding, an unscoped (user) ticket on /v1/wire/feed is refused for its scope, so the test no longer reached the expiry check. Red when WireTicketDO.spend ignores expires_at. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the Desktop Wallpaper tile shows the desktop, never a broken image macOS can report a desktop picture that is gone (a deleted file in /tmp), a folder it rotates through, or a .madesktop description. The Settings thumbnail and the window backdrop loaded that URL as is, so the tile drew the webview's broken-image mark and choosing it showed nothing. DesktopPicture resolves the reported URL to a picture that decodes: the file, a picture in the folder, the still a description names, or else a picture of the desktop fill color. The picker swaps a thumbnail that fails to load for an empty framed box. * Home dock: pinned column kind (pinned-dock-v1) Lawrence 2026-10-18: "I should not be able to close Home inside the Home thing ... I should not be able to create more tabs in the Home thing unless I explicitly enable it." Daemon (cmux-tui): a dock column may be pinned. Its first tab stays (no close, move out or reorder away from the first place) and it takes no other tab unless its allow_tabs is on (column.update {allow_tabs}). The column is never removed, undocked or unpinned. Enforced on every path (resource ops, JSON-lines, layout undo, terminal tab creation before the spawn) with reason_code dock-column-pinned. set-column-dock takes pinned; column.update takes pinned and allow_tabs. Spec, docs, bindings updated. App: Home pins its left chat dock on connect (page tab first). Home's tab shows no close button, the strip no new-tab button while the column takes no tabs, Cmd-W keeps Home's tab, new agent chats open beside a pinned dock, and a refused close says "Home stays open in its column." * cmux NEXT: compact attachment strip in the composer (#21739) * test(acpmux): the attachment strip is compact Leo 2026-10-18: pasted screenshots took ~100 px off the composer. One row of 64 px tiles 6 px apart, 8 px under the composer's top, the text 6 px under it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): compact attachment strip, like the Claude desktop composer 64 px tiles (from 80), a 6 px gap, an 8 px top inset, and the prompt starts 6 px under the strip instead of 15. The strip adds about 78 px, not ~100. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): shell field follows the compact strip; tighten the gallery budget Review: in shell mode under attachments the field and glyph kept the 15 px inset, so typing ! moved the text 9 px. The attach-screenshot gallery budget still allowed the old 96 px row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(acpmux): wait for the thumbnails to load instead of decode() CI: decode() rejected while a thumbnail's source was swapped for its copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: the popup address field draws no focus ring (#21963) fa8b3fc made the popup URL label selectable without focusRingType = .none, which turns check:text-focus red on every webviews job. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * composer: picking another provider's model keeps the typed prompt (#21937) * test: a harness switch keeps the typed prompt (red) * composer: picking another provider's model keeps the typed prompt through the harness switch * composer: a chat's own saved draft wins over a carried prompt (review) * fix(acpmux): the empty prompt mid-turn teaches ↵ and ⌘↵ (#21870) * test(acpmux): a running turn's empty prompt teaches Return and ⌘Return Leo 2026-10-18: like Claude's "↵ to send, ⌘↵ to queue for later", the empty prompt mid-turn says "↵ to queue, ⌘↵ to steer" (or interrupt, for an agent that cannot steer). The Quick Composer keeps "Queue a follow-up". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): the empty prompt mid-turn teaches ↵ and ⌘↵ Mid-turn the empty prompt says "↵ to queue, ⌘↵ to steer" (or "to interrupt" when the agent cannot take steering), as Claude's composer does. The Quick Composer keeps "Queue a follow-up", since its ⌘↵ opens the window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(acpmux): an attachment-only prompt mid-turn names only the queue key Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): drop the ⌘↵ hint with only attachments, match the Steer button's verb ⌘Return needs typed text, so an attachment-only prompt mid-turn says only "Queue a follow-up". The es, fr, it, ja, ko and uk hints now use the Steer button's own verb. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(acpmux): say when the queued placeholder still shows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: schema doc names peer masking and the lane error cut (#21924) * docs(acpmux): the schema names peer masking and the lane error cut The SessionSummary lane note still said every other field stays raw; since #21871 a peer daemon reads them masked, and since #21799 the lane error is cut to 3072 characters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(acpmux): raw for every client but a proven peer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: every markdown viewer is read-write; Cmd-=/-/0 text size and a default size setting Root cause: a markdown file opened by cmux open, an agent or a link (not the user's own Open File...) was writable only inside a files.roots folder. The roots setting is empty by default and never holds home, so viewers opened from the Home workspace (and any workspace) came up read only with an "outside the workspace" note. - Markdown pages are writable for every document they show; only a real reason keeps one read only (not UTF-8, binary, not writable, or a chat transcript the host opens read only), and the page names the reason. The code editor keeps its root rule. - The markdown page handles the zoomIn/zoomOut/zoomReset page commands the app already sent: one pixel per step (8-40) in rich and source mode, Cmd-0 back to markdown.font.size. Cmd-Shift-= is a Zoom In alias. - Settings > Appearance > Markdown Viewer > Default Font Size (markdown.font.size, 16, 8-40), localized. Co-Authored-By: Claude <noreply@anthropic.com> * fix(home): pasted pictures are small thumbnails in a row in the composer The vendored MessagesLab compose strip copies macOS 27 Messages: every attachment is a 177 x 118 pt tile stacked top to bottom above the text. Two pasted screenshots made the field ~280 pt tall and pushed the text line to the bottom. Images are now aspect-fit in 128 x 64 pt, files are 200 x 52 pt pills, and tiles flow left to right and wrap at the field's right edge. Tests: ComposeAttachmentLayoutTests (MessagesLabHome). * test(home): qualify Attachment (Swift Testing has one too) * cmux-next: page strings tables become build output (#21970) * test(webviews): strings tables must not be tracked Red on feat-cmux-next: every table gen-strings.mjs writes is committed, and none is ignored (#21946). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: page strings tables become build output (#21946) The generated strings tables (webviews/src/**/generated/strings.json, the diff page's per-locale tables and loader, the home-channels pane table) are no longer committed. bun install (postinstall, best effort), the webviews test preload (only stale pages) and every bundle build (strict) write them from the checkout's xcstrings catalogs and settings schema. Committed copies conflicted in every pull request that changed a catalog or the settings schema, and the PR autofix could rebuild one from a stale schema and drop another branch's labels (#21746). A table made from the tree it sits in can do neither. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: merge rules and atomic writes for every strings output (#21946) The diff page's locale tables and loader and the home-channels pane table are build output too: route them through cmux-generated-v1 and next_batch's WEB_GENERATED, so a branch from before the change that edited one catches up instead of stopping on a modify/delete conflict. gen-strings writes through a temporary file (parallel test preloads), and --best-effort skips a page whose catalog or schema is missing instead of failing bun install. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at f2ef8f4 - regenerated schemas/settings-export/settings-schema.sources.sha256 - vp fmt webviews/scripts/pages/gen-strings.mjs - vp fmt webviews/test/strings-build-output.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39205550635 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * cmux NEXT: a new terminal tab spawns at its pane's grid, so its first frame is final The Mac's new-tab request sent no size. The daemon then spawned the PTY at the grid of whichever terminal reported last, anywhere (client_sizing_state.rs creation_size), the shell printed its first prompt at that width, and the pane snapped when the view's first report resized it. newTerminalTab now sends cols/rows (NewTerminalGrid): the fitted grid of the terminal the pane shows (Ghostty's own fit of the same content host), else the content host size, backing scale, a live terminal's cell size and the configured padding through Ghostty's formula (TerminalGridEstimate; Cmd-T's New Tab page, a page or agent tab). A zoomed terminal does not count. With neither known the daemon's fallback applies as before. docs/shared-terminal-sizing.md: a Creation size section (who sizes a new terminal, and why a later attach never draws a wrong frame for others). * cmux NEXT: NewTerminalGridTests builds its padding from the public default * perf-10x daemon: request-latency bench (raw, 10 clients, CLI; 1 and 200 terminals) * cmux-next: clear five base reds in the cmux-next checks lane (#21981) The ErrorJournal (1597dc9) and browser popup (fa8b3fc) commits left five checks red on feat-cmux-next, so every PR into it fails "cmux-next checks" and its web lane skips its tests. Comments and baselines only, no behavior change: - Main-thread concurrency: the harvest loop runs in a detached utility task and the flush delay in the ErrorJournal actor's task, so neither is on main; reviewed concurrency-allow and wakeup-allow comments. - No literal state roots: the app's ~/.acpmux/daemon.log is only read; a keep row in the baseline. - Package conventions: lint:allow on BrowserPopupWindowGeometry (pure geometry). - Type scale: BrowserPopupPanel.swift has no raw font sizes left; drop its row. - Text focus: the popup address field sets focusRingType = .none (was #21963). Motion tokens (SidebarCardTransition) stays with #21951 for its owner. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cx-ncc.104: Debug toggle for the agent pane's in-session status (agent.sessionStatus) (#20953) Recut of #20953 onto the current feat-cmux-next as one commit: the In-session status Debug toggle, its sessionStatus page event, and useSessionStatus() for the plan and recap cards. The base merge kept both sides of the tunables list, the view's observations and the page host's events (the sidebar preview variant landed meanwhile). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: route/list says which routes serve the session's family (#21852) * test(acpmux): route/list names a session's family and which routes serve it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(acpmux): route/list says which routes serve the session's family With a sessionId, _acpmux/route/list now returns the session's family and a serves flag on each route (Route::serves), so the composer's Account menu can list only the routes that can run this chat without copying that logic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: tunable registries list one entry per line (#21972) * cmux-next: tunable registries list one entry per line (#21946) AgentPaneTunables.all and SidebarTunables.all were single lines, so every pull request that added a tunable conflicted with every other one there (14 open PRs on AgentPaneTunables, 4 on SidebarTunables). Each entry now has its own line in alphabetical order, so two additions insert different lines and merge cleanly; TunableCatalog.all lists one module per line. Debug Settings shows the agent-pane and sidebar tunables alphabetically; nothing else depends on the order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the TunableCatalog list as it was: CmuxNextApp may not grow The one-per-line TunableCatalog added 20 source lines to CmuxNextApp, which check-app-size.sh refuses. The conflict hotspots are the AgentPaneTunables and SidebarTunables lists, which live in their own targets and stay one per line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * test(next): cover dictation auto-send prompt routing (#21169) * test(next): cover dictation auto-send prompt routing * fix(next): satisfy dictation test formatting --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Co-authored-by: Lucas Ribeiro <lucascamribeiro@gmail.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
# Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextApp/Pages/PageFactory.swift # Packages/macOS/CmuxNext/Sources/CmuxNextPages/BackdropImagesPageProvider.swift # Packages/macOS/CmuxNext/Sources/CmuxNextSettings/Schema/HomeSettingsSchema.swift # Packages/macOS/CmuxNext/Sources/CmuxNextSettings/Schema/SettingsSchema.swift # Packages/macOS/CmuxNext/Tests/CmuxNextPagesTests/BackdropImagesPageProviderTests.swift # schemas/defaults/cmux-next.json # schemas/settings-export/settings-schema.sources.sha256 # webviews/src/pages/home-channels/generated/strings.json # webviews/src/pages/settings/components/HostCards.tsx # webviews/src/pages/settings/generated/strings.json # webviews/src/pages/settings/hostCards.test.tsx # webviews/src/pages/settings/mockProvider.ts # webviews/src/pages/settings/ops.ts
|
|
[Medium impact] The changes affect window backgrounds and Home views across cmux. The PR should not merge until the CI graph mismatch and the two outstanding repository-rule findings are fixed. Findings
SummaryAdds settings for video background playback, battery use, pane-aware framing, and animated Chief pictures.
Diagram%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Settings schema and parser] --> B[SettingsApplier]
B --> C[DesignSettings]
C --> D[Background playback and framing]
C --> E[WebTheme]
E --> F[Chief picture motion]
Reviews (2) · Last reviewed commit: "chore(next): PR autofix at a3c55b67ae33" · Reviewed by Greptile |
| /// Tests: pins the playback settings, or being on battery, for this view. | ||
| public var playbackOverride: BackdropPlaybackSettings? { didSet { update() } } | ||
| public var batteryOverride: Bool? { didSet { update() } } |
There was a problem hiding this comment.
Test controls ship in production
playbackOverride and batteryOverride add test-only controls to shipping BackdropVideoView.swift. The repository rule forbids new test/debug seams under Sources/. Remove these overrides. Tests should use @testable import for internal state and inject a real power-source dependency where needed.
Rule Used: Flag Swift files under a production Sources path (matching /Sources/ and not under /Tests/) that add a test-only or debug-only seam: a #if DEBUG (or other test-build-guarded) extension/member exposing internal state only for tests or a debugg... (source)
| enum BackdropPlaybackConfigParser { | ||
| static let path = ["appearance"] | ||
|
|
||
| static func parse(_ root: JSONValue, diagnostics: inout [SettingsDiagnostic]) -> BackdropPlaybackSettings { |
There was a problem hiding this comment.
New helpers lack instance owners
BackdropPlaybackConfigParser is a new caseless enum used only for static helpers. BackdropPlaybackSettingsSchema adds the same pattern. The repository rule forbids new static-only namespaces. Make parsing an instance method on a constructable parser, and put the descriptors on the owning SettingsSchema type or a constructable schema provider.
Rule Used: Flag new ambient global state in production Swift: a top-level (file-scope) func used as API, a top-level mutable var or a stub class/struct holding a global flag/once-token, a caseless enum/empty struct used purely as a static func/static let namesp... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| // `appearance.backgroundAvoidPanes` off: the art frames itself in the whole window. | ||
| let visible = DesignSettings.shared.backdropPlayback.avoidsPanes | ||
| ? BackdropVisibleSpan.uncovered(width: bounds.width, by: coveredColumns) : .whole |
There was a problem hiding this comment.
Background adjustments use different centers
Turning off appearance.backgroundAvoidPanes makes drawing use .whole, but adjustable and visibleMiddle still use the space beside the panes. With a sidebar present, Adjust Placement derives its starting point and drag limits from a different center than the renderer uses. The picture can jump on the first adjustment and stop before reaching an edge. Use the same setting-aware span for drawing, the starting placement, and drag limits.
- Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39366174521
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. |
| "CmuxNextDaemonRecovery", | ||
| "CmuxNextDaemonService", | ||
| "CmuxNextDiff", | ||
| "CmuxNextFeed", |
There was a problem hiding this comment.
CmuxNextFeed is added to CmuxNextAppTests.targets, but CmuxNextAppTests does not declare that dependency in Package.swift. The graph generator records direct dependencies, so regeneration removes this edge and the generated-files check fails. Remove the edge, or declare the dependency in Package.swift if the tests need it, then regenerate the graph.
This comment has been minimized.
This comment has been minimized.
|
owner: 01a15c08-af2e-7471-8e77-d62753886e4f |
|
Landing this through the backgrounds batch, #22033, in reduced form.
I'll close this PR with a pointer once #22033 merges. Agent session: cosmic-gray |
|
Owned by nx-settings on big-red |
|
Partially superseded by #22929 and #21784, which provide the current video engine, Chief picture transport, and aerial catalog. The stale branch is not reusable: the requested user settings policy is absent, while the old keep-clear framing conflicts with the current resize-invariant design in #22562. Closing this stale PR; the remaining video-policy slice needs a fresh owner/cut with current localization and the motion work stays owner-bound. |
Phase 5 of the backgrounds plan: the settings Leo approved (through the coordinator). It's one schema PR, so the Air Blue coordinator clears WINDOW once.
Stacked: this branch is
next-batch/leo-backgrounds(feat-cmux-next plus #21743, which carries #21709 and #21731, plus #21720 and #21740/#21737) with two commits on top. Review only fb08d69 (background video and framing) and 9d8f72c (Chief picture motion). It needs #21709's framing and #21720's video players.What changes
appearance.backgroundVideoappearance.backgroundVideoOnBatteryappearance.backgroundAvoidPaneshome.chiefPictureMotionBackdropPlaybackSettingsSchema). They're agent-settable likeappearance.background.BackdropPlaybackConfigParserusesConfigFieldReader, so a bad value keeps the default and adds a diagnostic.SettingsAppliercopies the result intoDesignSettings.backdropPlayback.BackdropVideoView):NSApp.isActive(didBecome/ResignActive).BackdropPower, a small IOKit power-source watcher on the main run loop.ObservationStream.WindowMaterialView): with Keep Paintings Clear of Panes off, art frames itself in the whole window. The toggle applies live.home.chiefPictureMotion, a Home > Chief Pictures row, agent-settable):data-picture-motionon<html>, carried with the theme (WebTheme.pictureMotion), the same waydata-status-indicatorsis.ChiefPictureMotion.effectivemakes it Never under Reduce Motion.Avatarshows a GIF or animated WebP as its first frame, drawn once to a still. It moves only when the setting is Always, or while the pointer is over it when it's On Hover.squared()now covers animated pictures too), so a non-square GIF is no longer stretched there. The tile is a 20 pt icon, where motion is only noise.WINDOW (settings schema)
Settings schema: 4 new keys. Air Blue coordinator, please clear.
schemas/settings/settings-schema.json, the MDM docs and the defaults snapshot are regenerated by the base-autofix bot (regenerate-settings-exports.sh), per that script.appearance.background, because there are no built-in videos yet; the value grammar change waits for the first oneStrings
14 new keys, all 21 locales, in
CmuxNextSettings/Localizable.xcstrings. It reuses the existingsettings.group.windowBackgroundandsettings.choice.always,onHoverandnever.Tests
CmuxNextSettingsTests/BackdropPlaybackSettingTests:CmuxNextAppTests/VideoWallpaperTests.thePlaybackSettingsHoldTheVideo:CmuxNextAppTests/BackdropArtFramingTests: with avoidPanes off, the art frames to the whole window, and back.chiefPictureMotionParsesAndHasItsRow: parse, a bad value, the row and agent-settable.ChiefPictureMotionTests:ChiefPictureFilesTests.anAnimatedPictureGetsAStillTile: square and non-square GIFs both get a still square PNG.test/home-chief-picture-motion.test.tsx(3/3 pass): always, never, hover in and out, and a PNG is never held back.Verification
lint-ios-package-conventions.sh: clean.Changelog
Added: Settings for video backgrounds (play, only while cmux is active, or a still picture; pause on battery), for keeping paintings clear of the panes, and for when animated Chief pictures move.
🤖 Generated with Claude Code