Custom sidebars: in-process renderer by default, Settings section, worker resize pump fix - #5867
Conversation
New catalog section customSidebars with a JSON-backed renderer key
(~/.config/cmux/cmux.json: { "customSidebars": { "renderer":
"inProcess" } }), defaulting to the crash-isolated remote worker.
Unknown raw values fall back to the safe default; covered by store-level
behavior tests.
Part of the sidebar vibe-coding architecture program (spike 2:
in-process mount + containment + renderer flag).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
RecursionBudget grows a second axis: total produced RenderNodes (default 3000, an order of magnitude above a rich real sidebar). ForEach loops and ViewBuilder walks early-out once tripped, and the top-level evaluate returns nil for a truncated walk so the host's existing last-good-sticky publish keeps the previous render up instead of flashing a partial tree. A pathological ForEach(0..<100_000) now trips in milliseconds instead of handing SwiftUI a 100k-node tree that freezes the host. Covered by behavior tests at the interpreter level (nil + speed bound + no false trip at 400 rows) and at the publish level (CustomSidebarModel keeps the last good render when a saved edit trips the budget). Part of the sidebar vibe-coding architecture program (spike 2). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cess CustomSidebarSurface is the single mount seam: it renders the selected custom sidebar through the out-of-process worker by default and mounts the previously never-used in-process CustomSidebarView when customSidebars.renderer is inProcess, switching live when the setting changes. In-process gains native input (hover, focus, keyboard) and same-frame resize for trusted local files; remote stays the containment lane and the default. ContentView reads the flag with @LiveSetting per main-window convention; the surface itself stays settings-agnostic so the package needs no settings dependency and tests can drive both branches. Documented in docs/custom-sidebars.md. Part of the sidebar vibe-coding architecture program (spike 2). This is the mount the live-eval engine (tier 1) lands on. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fidelity
Lowers TextField("placeholder", text: $name) to a .textField node and
renders it in-process as a real SwiftUI TextField backed by view-local
@State. The static IR has no binding concept, so the typed value never
round-trips into the interpreter environment; this exists purely to
demonstrate that the in-process mount delivers native focus/caret/typing
where the remote worker cannot. The real binding story is the live-eval
engine (spike 1 of the program).
Accepts the SwiftViewInterpreter.swift length-budget growth (669 -> 695)
for the spike.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… timing Adds CMUX_RENDER_WORKER_DEBUG-gated timing logs to the sidebar render worker: geometry application, rootView republish, and pump commits now log CACurrentMediaTime timestamps and pump duration. No behavior change. This makes the resize repaint lag measurable: with only this commit, a resize message logs "geometry applied" and a pump, but the visible repaint (the next "rootView republished" + pump) only appears when the next scene tick arrives, up to a full second later. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds configurable custom sidebar renderer mode (in-process vs remote), integrates renderer selection into settings UI/navigation/search, strengthens interpreter containment with node-count budgeting, and provides end-to-end wiring with tests, remote-worker pump/hosting plumbing, docs, and localization. ChangesCustom Sidebar Renderer Configuration and Containment
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (4 errors, 1 warning, 1 inconclusive)
✅ Passed checks (15 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
Greptile SummaryThis PR completes the custom-sidebars runtime by making the in-process renderer (
Confidence Score: 5/5Safe to merge — the in-process renderer path, node budget, display pump, and Settings section are all well-isolated and covered by targeted tests. The three main changes (in-process renderer default, node budget, display-link pump) each have direct unit tests that exercise the failure paths. Actor isolation is correct throughout: the coordinator and pump are @mainactor, RenderPumpGate is a plain value type, and the NotificationCenter callback uses MainActor.assumeIsolated appropriately. All new user-facing strings have both en and ja entries. No blocking primitives, no sleep-based synchronization, no raw print/NSLog in new production paths. The one acknowledged rough edge (one-tick @LiveSetting lag on initial mount) was already reviewed and noted in comments. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
LV["@LiveSetting\ncustomSidebars.renderer"] --> CSS
CSS["CustomSidebarSurface\nrendersInProcess: Bool"]
CSS -->|true| IPR["CustomSidebarView\nnative SwiftUI in-process\nhover · focus · keyboard"]
CSS -->|false| RCH["RemoteCustomSidebarHost\nout-of-process worker\nclicks only"]
RCH --> RWC["RenderWorkerCoordinator\n@MainActor"]
RWC --> PUMP["RemoteWorkerDisplayPump\nCADisplayLink gated"]
PUMP --> GATE["RenderPumpGate\nmarkDirty / tickAction / pumpCompleted"]
RWC --> RHVIEW["RemoteWorkerHostingView\nneedsLayout/needsDisplay → noteInvalidation"]
RWC --> RWWIN["RemoteWorkerWindow\nviewsNeedDisplay → noteInvalidation"]
RHVIEW --> PUMP
RWWIN --> PUMP
SVI["SwiftViewInterpreter.evaluate"] -->|nil if nodesExceeded| LG["last-good sticky render"]
SVI -->|node tree| RWC
subgraph Budget
RB["RecursionBudget\ndepth ≤ 400 · nodes ≤ 3000"]
end
SVI --> RB
Reviews (5): Last reviewed commit: "Accept file-length budget growth for ren..." | Re-trigger Greptile |
| // Periodic tick so the custom sidebar re-renders live (clock, | ||
| // countdowns, and refreshed workspace/data context), mirroring the | ||
| // default sidebar's TimelineView. No banned timers involved. | ||
| // Fully out-of-process: the render worker interprets AND renders | ||
| // the file; this view only hosts the worker's remote layer and | ||
| // forwards input, so no file-derived view code runs in the host. | ||
| // The surface mounts the out-of-process worker by default (no | ||
| // file-derived view code runs in the host); the | ||
| // `customSidebars.renderer` setting switches it to the in-process | ||
| // renderer for trusted local files (native hover/focus/keyboard, | ||
| // same-frame resize). The @LiveSetting's initial value lags one | ||
| // store round-trip on remount, so an `inProcess` choice can mount | ||
| // the worker for one tick before flipping; harmless (the host | ||
| // shuts the short-lived client down on unmount). | ||
| TimelineView(.periodic(from: .now, by: 1)) { timeline in | ||
| // No .id(customSidebarURL): the worker swaps files in place on | ||
| // the next scene message, so remounting the surface would only | ||
| // flash the previous sidebar's pixels during the switch. | ||
| RemoteCustomSidebarHost( | ||
| CustomSidebarSurface( | ||
| fileURL: customSidebarURL, | ||
| dataContext: customSidebarDataContext(now: timeline.date), | ||
| dispatch: makeCmuxSidebarActionDispatch(), | ||
| contentInsets: CustomSidebarContentInsets( | ||
| top: SidebarWorkspaceScrollInsets.workspaceList.top, | ||
| bottom: SidebarWorkspaceScrollInsets.workspaceList.bottom | ||
| ) | ||
| ), | ||
| rendersInProcess: customSidebarRenderer == .inProcess | ||
| ) |
There was a problem hiding this comment.
@LiveSetting initial-value lag unconditionally spawns a remote worker for every inProcess launch
When customSidebars.renderer is inProcess, @LiveSetting delivers the stored value asynchronously after one store round-trip. For that first render tick customSidebarRenderer holds the default (.remote), so rendersInProcess: false is passed to CustomSidebarSurface, mounting RemoteCustomSidebarHost and spawning the out-of-process worker. When the @LiveSetting update arrives, VerticalTabsSidebar.body re-renders, unmounts the host, and mounts CustomSidebarView instead.
The in-code comment calls this "harmless," but the PR description describes it differently: "an inProcess run can briefly spawn a worker at launch which then lingers idle until the next renderer flip." Whether the worker terminates on unmount or truly lingers is unclear from the diff alone, but either way a process is unconditionally spawned on every mount when the user has set inProcess. The candidate fix (synchronous catalog read at init, mirroring CmuxExtensionSidebarSelection.customSidebarsEnabled) is identified but not implemented.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Packages/CmuxSwiftRender/Sources/CmuxSwiftRender/SwiftViewInterpreter.swift`:
- Around line 191-202: Remove the SPIKE TextField lowering implemented in
SwiftViewInterpreter (the entire case "TextField" branch that builds a
RenderNode(kind: .textField, ...)) because it creates a non-shipping editable
path; either delete this branch and any references to RenderNode.kind
.textField, or wrap it behind a clearly non-shipping feature flag (e.g., guard
enableTextFieldSpike else { fallthrough/handle as before }) so production builds
never use it; ensure any helper usage like stringArgument(...) and bindingSource
mapping is also removed or behind the same gate to avoid leaving a
dead/inconsistent stub.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4cd7bb68-ac9f-4755-8201-c0d5a9e7caa7
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (15)
Packages/CmuxSettings/Sources/CmuxSettings/Keys/CustomSidebarsCatalogSection.swiftPackages/CmuxSettings/Sources/CmuxSettings/Keys/SettingCatalog.swiftPackages/CmuxSettings/Sources/CmuxSettings/Values/CustomSidebarRendererMode.swiftPackages/CmuxSettings/Tests/CmuxSettingsTests/CustomSidebarRendererSettingTests.swiftPackages/CmuxSidebarInterpreterService/Sources/CmuxSidebarRemoteRender/CustomSidebarSurface.swiftPackages/CmuxSwiftRender/Sources/CmuxSwiftRender/RecursionBudget.swiftPackages/CmuxSwiftRender/Sources/CmuxSwiftRender/RenderNode.swiftPackages/CmuxSwiftRender/Sources/CmuxSwiftRender/SwiftViewInterpreter.swiftPackages/CmuxSwiftRender/Tests/CmuxSwiftRenderTests/EvaluationNodeBudgetTests.swiftPackages/CmuxSwiftRender/Tests/CmuxSwiftRenderTests/TextFieldStubTests.swiftPackages/CmuxSwiftRenderUI/Sources/CmuxSwiftRenderUI/Rendering/RenderNodeView.swiftPackages/CmuxSwiftRenderUI/Sources/CmuxSwiftRenderUI/Rendering/TextFieldNodeView.swiftPackages/CmuxSwiftRenderUI/Tests/CmuxSwiftRenderUITests/CustomSidebarLastGoodTests.swiftSources/ContentView.swiftdocs/custom-sidebars.md
Fixes the 1-3 s drag-resize repaint lag in remotely rendered custom sidebars. Two changes in the worker (CmuxSidebarRemoteRender): 1. The geometry handler now republishes rootView before its pump. Resizing the hosting view only marks AppKit layout dirty; SwiftUI's own render update waits for display-cycle work that never runs in the never-ordered window, so the old pump committed a stale tree and the visible repaint rode the host's next 1 s scene tick. 2. A display-refresh-driven pump (NSScreen.displayLink, macOS 14+, the non-deprecated CVDisplayLink replacement) commits invalidations that arrive between host messages. RemoteWorkerHostingView and RemoteWorkerWindow forward needsLayout/needsDisplay/viewsNeedDisplay flips into a RenderPumpGate; dirtiness resumes the paused link, each tick pumps at most once, and the first clean tick re-pauses it, so an idle worker has zero periodic wakeups (no timers, no polling). Geometry republishes reuse the cached interpretation and the displayed state, so a resize during a broken on-disk save keeps the last-good sticky render instead of flipping to an error state. Behavior tests cover the gate: arm/coalesce/pump/pause transitions and commit absorption of invalidations raised during a pump. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A pump's own layout work re-marks the hosting view dirty mid-commit, which resumed the link for one throwaway clean tick after every pump. Pausing directly in pumpCompleted() makes the idle worker truly wakeup-free between host messages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Automated UI sweep result (codex computer-use against the tagged inproc build, with differential): PASS: TextField gets a focus ring and accepts live typing in-process. PASS: drag-resize of the sidebar repaints same-frame with no stale-pixel lag. PASS: vibe-demo renders cleanly (no blank flashes or color glitches). Confirmed gap: .help() tooltips on sidebar rows never appear in-process. Differential rules out a synthetic-input artifact: under the same synthetic hover, the native + titlebar button shows its New workspace tooltip while sidebar rows show nothing. Likely mechanism: the 1s TimelineView re-evaluation churns view identity under the cursor, resetting the tooltip rest timer before it fires. Worth checking against the live-eval engine from #5866, whose per-box invalidation avoids whole-tree re-render. Hover row-highlight was also absent but the demo content declares none, so that part is expected. 🤖 Generated with Claude Code |
…picker New settings section between Sidebar and Beta Features. The toggle binds to the existing betaFeatures.customSidebars defaults key (same gate as the Beta Features row); the picker binds to the customSidebars.renderer JSON key (remote | inProcess) through JSONValueModel and is disabled while custom sidebars are off. Search: section keywords, two curated entries, and the row-anchor contract lists updated. 10 new localized strings (en + ja). CmuxSettingsUI tests 30/30 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
App-side SettingsNavigationTarget gains the customSidebars case (title, symbol, search text, alias, two setting entries, customSidebars.renderer path anchor) so settings search and socket navigation reach the new section, and the CLI accepts 'cmux settings open custom-sidebars'. Verified on the tagged inproc build: the command opens Settings scrolled to the section with the toggle and renderer picker rendered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
customSidebars.beta.enabled now defaults true. The catalog default is the single source of truth (readers fall back to key.defaultValue), so no other code changes. Users who toggled it off keep their stored false. Docs updated to describe the off switch instead of opt-in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e input fidelity" This reverts commit c24ada3.
customSidebars.renderer now defaults to inProcess: native input (hover, focus, keyboard) and same-frame resize out of the box. The remote worker stays one settings flip away as the containment lane for untrusted sources; an explicit "remote" in cmux.json is honored unchanged. Renderer tests updated to the new default (72 green), docs and DocC flipped to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nk-pump # Conflicts: # Packages/CmuxSidebarInterpreterService/Sources/CmuxSidebarRemoteRender/RenderWorkerCoordinator.swift
…s-flag # Conflicts: # Sources/ContentView.swift # docs/custom-sidebars.md
…to feat-sidebar-inprocess-flag
Threads the client binding from main's instant-remount change (PR 5864) through the surface seam, and defaults rendersInProcess to true to match the new renderer default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SwiftViewInterpreter 669->683 (node-budget containment), SettingsNavigation 589->599 (customSidebars nav case + search entries), SettingsWindowScene 523->531 (section mount), ContentView 19248->19256 (renderer flag wiring). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Packages/CmuxSwiftRender/Sources/CmuxSwiftRender/SwiftViewInterpreter.swift (1)
419-426:⚠️ Potential issue | 🟠 Major | ⚡ Quick winStop
Reorderableiteration immediately once node budget is exceeded.At Line 419,
evalReorderablekeeps iterating allitemsand appendingidseven afterenv.budget.nodesExceededis true. That bypasses the new fast-cutoff behavior added elsewhere (evalForEach/evalFor) and can still do large O(n) work beforeevaluatediscards the tree.🔧 Suggested fix
for item in items { + if env.budget.nodesExceeded { break } let scope = env.makeChild() if let paramName { scope.define(paramName, item) } scope.define("$0", item) let rowNodes = evalItems(closure.statements, scope) + if env.budget.nodesExceeded { break } rows.append(rowNodes.count == 1 ? rowNodes[0] : RenderNode(kind: .vstack, children: rowNodes)) ids.append(item.member(idField)?.displayString ?? "") }As per coding guidelines, for Swift production paths over scalable user data, nested/full rescans and unbounded work in hot paths should be flagged and cut off with bounded behavior.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Packages/CmuxSwiftRender/Sources/CmuxSwiftRender/SwiftViewInterpreter.swift` around lines 419 - 426, In evalReorderable, stop iterating items immediately when env.budget.nodesExceeded becomes true (matching the fast-cutoff behavior in evalForEach/evalFor): inside the for item in items loop (in SwiftViewInterpreter.swift) check env.budget.nodesExceeded at the top and break out before doing any work (scope creation, evalItems, appending rows or ids); also ensure you do not append to ids after the cutoff (i.e., only append ids if nodesExceeded is false). This ensures evalItems/rows and ids growth are bounded and mirrors the early-return behavior used elsewhere.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@Packages/CmuxSidebarInterpreterService/Sources/CmuxSidebarRemoteRender/RemoteWorkerDisplayPump.swift`:
- Around line 48-51: noteInvalidation() currently only calls resumeLink() on a
clean→dirty transition so if resumeLink() returns early (no screen) isDirty
remains true and future invalidations never retry; update noteInvalidation() to
still call resumeLink() when gate.markDirty() returns false but link == nil
(i.e. if link is nil, attempt to resume/rebuild the link regardless of dirty
transition). Also apply the same principle to the other places mentioned: where
resumeLink() or rebuildLinkIfNeeded() bail out based on isDirty or link (refer
to resumeLink(), rebuildLinkIfNeeded(), gate.markDirty()/isDirty and the link
property) so ensure they retry link creation when link == nil even if isDirty is
already set.
---
Outside diff comments:
In `@Packages/CmuxSwiftRender/Sources/CmuxSwiftRender/SwiftViewInterpreter.swift`:
- Around line 419-426: In evalReorderable, stop iterating items immediately when
env.budget.nodesExceeded becomes true (matching the fast-cutoff behavior in
evalForEach/evalFor): inside the for item in items loop (in
SwiftViewInterpreter.swift) check env.budget.nodesExceeded at the top and break
out before doing any work (scope creation, evalItems, appending rows or ids);
also ensure you do not append to ids after the cutoff (i.e., only append ids if
nodesExceeded is false). This ensures evalItems/rows and ids growth are bounded
and mirrors the early-return behavior used elsewhere.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: a0d53df3-3735-46a7-99ec-d4ec896506f1
📒 Files selected for processing (13)
Packages/CmuxSidebarInterpreterService/Package.swiftPackages/CmuxSidebarInterpreterService/Sources/CmuxSidebarRemoteRender/CustomSidebarSurface.swiftPackages/CmuxSidebarInterpreterService/Sources/CmuxSidebarRemoteRender/RemoteWorkerDisplayPump.swiftPackages/CmuxSidebarInterpreterService/Sources/CmuxSidebarRemoteRender/RemoteWorkerHostingView.swiftPackages/CmuxSidebarInterpreterService/Sources/CmuxSidebarRemoteRender/RemoteWorkerWindow.swiftPackages/CmuxSidebarInterpreterService/Sources/CmuxSidebarRemoteRender/RenderPumpGate.swiftPackages/CmuxSidebarInterpreterService/Sources/CmuxSidebarRemoteRender/RenderWorkerCoordinator.swiftPackages/CmuxSidebarInterpreterService/Tests/CmuxSidebarRemoteRenderTests/RenderPumpGateTests.swiftPackages/CmuxSwiftRender/Sources/CmuxSwiftRender/RecursionBudget.swiftPackages/CmuxSwiftRender/Sources/CmuxSwiftRender/SwiftViewInterpreter.swiftResources/Localizable.xcstringsSources/ContentView.swiftdocs/custom-sidebars.md
💤 Files with no reviewable changes (1)
- Resources/Localizable.xcstrings
| func noteInvalidation() { | ||
| guard gate.markDirty() else { return } | ||
| resumeLink() | ||
| } |
There was a problem hiding this comment.
Retry link creation when dirty and link is nil to avoid a stalled pump.
At Line 49, noteInvalidation() only calls resumeLink() on a clean→dirty transition. If resumeLink() returned early at Line 87 (no screen available), isDirty stays true and subsequent invalidations never retry link creation; rebuildLinkIfNeeded() at Line 96 also bails when link == nil.
🔧 Suggested fix
func noteInvalidation() {
- guard gate.markDirty() else { return }
- resumeLink()
+ let mustResume = gate.markDirty()
+ if mustResume || link == nil {
+ resumeLink()
+ }
}
@@
private func rebuildLinkIfNeeded() {
- guard link != nil else { return }
- link?.invalidate()
- link = nil
+ link?.invalidate()
+ link = nil
if gate.isDirty {
resumeLink()
}
}Also applies to: 83-93, 95-101
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@Packages/CmuxSidebarInterpreterService/Sources/CmuxSidebarRemoteRender/RemoteWorkerDisplayPump.swift`
around lines 48 - 51, noteInvalidation() currently only calls resumeLink() on a
clean→dirty transition so if resumeLink() returns early (no screen) isDirty
remains true and future invalidations never retry; update noteInvalidation() to
still call resumeLink() when gate.markDirty() returns false but link == nil
(i.e. if link is nil, attempt to resume/rebuild the link regardless of dirty
transition). Also apply the same principle to the other places mentioned: where
resumeLink() or rebuildLinkIfNeeded() bail out based on isDirty or link (refer
to resumeLink(), rebuildLinkIfNeeded(), gate.markDirty()/isDirty and the link
property) so ensure they retry link creation when link == nil even if isDirty is
already set.
…browser CLI, pairing QR, iOS PRs included: - manaflow-ai#5816 ControlCommandCoordinator extraction (package coordinator skeleton; fork keeps legacy v2* dispatchers) - manaflow-ai#5859 sidebar perf - manaflow-ai#5857 RendererRealization (added as SurfaceHibernation adapter) - manaflow-ai#5867 in-process custom sidebars - manaflow-ai#5778 browser CLI / system-proxy bypass - manaflow-ai#5872 minimal pairing QR - iOS pairing/manual-entry stack - 30+ hot fixes Fork-side adjustments: - Skip 21 TerminalController+Control* extension files (PR manaflow-ai#5816 architecture refactor not adopted) - Add Sources/App/RendererRealizationSettingsAdapter.swift to bridge new RendererRealizationSettings to fork's existing SurfaceHibernationSettings - Restore v2SurfaceDragToSplit shim removed by upstream - Add SettingsNavigationTarget.customSidebars case - Stub ghostty_surface_set_renderer_realized callsites pending GhosttyKit rebuild (zig 0.15.2 required, host has 0.16.0) - Update ghostty submodule to 44b2baa81 (cherry-pick the 3 renderer commits onto fork's manaflow-ai#5128 link-fix pointer) - Keep fork's CMUXSessionDaemon module pbxproj refs and SurfaceHibernation settings
One consolidated PR for the sidebar vibe-coding program's runtime work (per owner direction), based on the merged live-eval engine foundation #5866.
In-process rendering is now the default for custom sidebars: CustomSidebarSurface mounts CustomSidebarView (real SwiftUI, native hover/focus/keyboard, same-frame resize) and the remote out-of-process worker stays one flip away as the containment lane for untrusted sources. The choice is the customSidebars.renderer setting (inProcess default, remote opt-in), live without restart.
Also in this PR: a Custom Sidebars section in Settings (enable toggle + renderer picker, search wired, en+ja localized), cmux settings open custom-sidebars CLI target, customSidebars.beta.enabled now defaults on, an evaluation node budget that keeps last-good output on pathological sources, and the worker display-link pump fix from #5898 (geometry republish + dirtiness-gated pump; measured resize repaint p50 422ms to 0.51ms).
The throwaway textField stub from the spike was reverted; real bindings come from the live-eval engine. Verified on tag inproc: typing/focus/hover in-process, same-frame resize, Settings section renders and navigates, codex computer-use sweep + differential on PR comments.
Design and evidence: cmuxterm-hq plans/sidebar-vibe-coding/ (DESIGN.md, REPORT.md, spikes/).
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Documentation