* fix(editor): render formatted Mermaid labels safely
Enable Mermaid HTML labels everywhere the desktop renders diagrams and
sanitize with foreignObject XHTML kept, form controls and SVG filters removed.
* fix(editor): copy HTML-label diagrams as PNG
Chromium taints a canvas drawn from a blob-URL SVG that holds foreignObject labels; the viewer now loads the diagram from a data URL.
* test(editor): share one Chromium across the Mermaid sanitizer suite instead of per-test timeouts
* Render Mermaid Preview outside its code pre wrapper
Keep the existing copy button and annotation wrapper while checking the original HAST code class. This removes the extra empty page reproduced in Markdown Preview PDF export without shrinking labels.
Credit gum798 for #21310 and #21377, innocarpe for #21328, and m13v for the label-readability review. The height-cap proposals were evaluated but this fix keeps the existing diagram sizing.
* fix(editor): keep a diagram's copy button beside its expand button
Trim the wrapper test to the regression cases and mock the runtime target hook main now uses.
* fix(ssh): prune superseded staged server builds when an update defers
GC ran only after an activation, so a host whose updates kept deferring
over live terminals kept one staged orcad slot per release. Prune on
deferral too, keeping the active, previous, live-daemon and newly
staged builds.
Refs #34149
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ssh): keep recently staged builds; route and harden the deferred-slot e2e
Desktops on different releases can share a host, so the deferral prune
keeps complete builds staged within three days instead of deleting a
peer's candidate and forcing it to re-upload. Windows hosts, which
cannot date their builds, keep them all.
Refs #34149
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ssh): read a build's staging age as unknown when find cannot date it
A find without -mtime used to read as an old build and let the deferral
prune remove a peer's candidate. Report an error instead, which keeps
every candidate; a build without .install-complete reads as not recent.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test(ssh): pin that an undatable staged build keeps every build
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test(ssh): fold the deferred-slot e2e into the update-anyway spec
Keeps the routing table under its size limit and runs the check on the
same deferred-update sources. Also drop the unused readiness argument
from pruneSupersededOrcadCandidates.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ssh): report GC as unfinished when it kept a recently staged build
Callers that memoize a clean pass would otherwise never prune the build
once it ages.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(mobile): draw SVG files with a Preview/Source toggle; pinch, drag and double-tap to zoom images
* test(mobile): render an SVG preview under the shipped shell policy and pinch it
* fix(mobile): double-tap after a drag at fit zooms in instead of re-fitting
* fix(mobile): pinch follows the fingers, Back resets zoom, and writable SVG artifacts get a preview
* fix(mobile): keep a writable SVG's save failure visible in Preview
* fix(wsl): fail closed on an unknown default distro, fence no-boot deploys, and say unverifiable when WSL can't answer
- A background wsl.exe run with no distro named reads the current default from the registry; when it
can't be read, the run is refused as unverifiable instead of allowed.
- A no-boot connect stops before its next guest command once a stop of the distro is heard mid-deploy.
- Background UNC touches trust a running read for 15 s, not 60 s.
- An unreadable running list is a retryable "couldn't confirm" on connect, and "Unknown" in the Add
dialog, never "not running" or "stopped".
* fix(wsl): one default distro per user act, cached registry read, adopted-holder watch, locales
- The default distro comes from one resolver: Lxss first, `wsl --list` when the registry is blocked,
cached for 5 s and single-flight, invalidated by a list change. A user act pins its answer, so the
hook relay and every gate inside the act name the same distro.
- A fenced no-boot connect that adopted a previous run's holder polls its process-table row (no
wsl.exe); the holder found gone counts as a stop.
- The Add dialog's request is a tested helper: Unknown and stopped distros are added with an explicit start.
- New strings translated into the ten locales.
* test(wsl): work an unnamed act leaves behind reads the default fresh after the act settles
* feat(wsl): an automatic release path that never boots a distro or sleeps a terminal
Bring-back 2/4. The undo ports take a mode. 'automatic' connects the host
through the no-boot path (ensureForRuntimeCall), so a stopped distro reads
as unreachable and is deferred, never started. It also adds a preflight
that reads the host's terminals in scope before anything is turned off,
and its terminal stop refuses any live terminal instead of sleeping it,
re-checking just before any sleep. The release path runs the preflight
first, so a refusal never toggles the host's automations. The user's undo
('user', the default) is unchanged. Nothing calls 'automatic' yet.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl): refuse-live never sleeps a host workspace, even after a clean census
Both reviewers: with nothing running, refuse-live fell through to the
sleep loop, so a terminal started between the census and worktree.sleep
would be ended, and idle host workspaces were marked asleep by an
automatic action. refuse-live now returns stopped straight after a clean
census and still-running otherwise; it never calls sleep or waits.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(editor): open SVG as editable source with a vector preview; pan images by dragging
- .svg opens as text with Preview | Source | Changes. Preview sizes the drawing
from its width/height/viewBox instead of Chromium's 300px default, fits up to
fill the pane, and stays sharp at any zoom.
- The image viewer pans by dragging (grab cursor) and by wheel, zooms at the
cursor, and gains Fit and Actual size. Clicking no longer opens a popup.
- Combined-diff images expand into the shared pan and zoom viewer.
- Text plugin editors for .svg now receive the markup.
- The plugin page warns when an editor opens files in place of Orca's viewer.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(editor): measure the pan-zoom viewport from the observer's first report
* i18n: translate the image viewer and plugin takeover strings
* feat(editor): copy expanded images and diagrams as PNG
* i18n: drop strings for the removed image popup and reset zoom
* fix(editor): keep a zoomed image inside the pane after a resize
* fix(editor): address cross-review of the SVG and image viewer
- A host that can only decode an SVG lossily keeps the image path, so non-UTF-8 bytes survive.
- Bytes custom editors stay available on SVG files, now that they open as text.
- A reminted media URL keeps the image's size and zoom.
- A vector fitted far above its natural size can zoom out to actual size.
- Zoom commands that arrive before an image loads are applied once it does, keeping diff sides in step.
- Copy as PNG requests CORS for web images so the canvas is not tainted.
* fix(editor): drop the SVG bytes-editor route and lossy-text fallback
Keeping a bytes editor beside the Source text let two drafts compete, and the
U+FFFD check could not tell lossy decoding from a real replacement character.
SVG now reads like every other text file.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(wsl): retry the setup-runner .git link read and fall back to the distro's Git
A Windows stat over \\wsl.localhost can report a transient ENOENT. Retry the link read once,
then ask the distro's Git asynchronously (fenced) before failing setup-script creation.
* fix(wsl): map a mounted-drive Git answer to its Windows drive
* fix(wsl): hide the D1c Settings rows whenever WSL hosts take no new work
The Windows-drive Git row and the drive-path conversion row showed with the
WSL hosts setting on and preload present, so in serve-only or after a live
policy block they stayed and failed on click with *_unavailable, since main
now gates both conversions on the opt-in. They now follow
useWslHostsAcceptNewWork, which also needs the setting, matching main.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl): keep a converted project's Git state visible when D1c offers hide
WslDriveProjectGitRow is also the read-only row saying Orca runs Git on
Windows for a project with git: windows-host, a preference that still
applies with no new work accepted. The row now always renders that state
and hides only the Use Git for Windows offer, through a new acceptsNewWork
prop; the drive-path conversion row, offer-only, stays hidden as before.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(wsl): a restored chat or a web hooks check never starts a stopped distro (E9)
A chat pane mounting on launch restore or a remount read its transcript as a
user open and booted the transcript's stopped WSL distro. Only the mount that
the user's own switch caused, and paging, now ask main to start it; otherwise
the read refuses and the pane shows the distro's stopped copy at once (a
stopped UNC transcript was reported as missing and spun as pending).
A web client sent repo.hooksCheck without background, which a server treated
as a launch that may open the distro. The server now opens it only for an
explicit wakeWslDistro; clients send it for launches and keep background for
older servers.
* fix(native-chat): key a chat's open wake on its own workspace; update wake assertions
A chat in another workspace mounting inside the switch window no longer takes the
switch's wake. The paging and worktree-removal assertions now expect the explicit
wakeWslDistro flag.
* fix(native-chat): set the pane-key ref in the layout effect, not during render
* feat(wsl): a WSL hosts block learned mid-run stops new work at once
The live policy fetch caches a newly published block, but the gate is
fixed for the run, so the add-host and move entry points and the
in-context offer stayed until the next launch. A block the fetch learns
now stops new work in-session through one predicate,
wslHostsAcceptNewWork(): adding hosts, starting or previewing moves,
automatic and opened-project moves, and Retry. Main pushes the capability
to the renderer, which hides the offer and entry points. The gate and
what is served are unchanged until the next launch, so saved hosts and
moved projects keep working, and cancel, Restore and Not now stay.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl): a live block also ends Stage 1 opt-in and stops mid-step host adds
Review follow-ups:
- wslHostsOptedIn now also needs wslHostsAcceptNewWork, and the Git router
re-applies its opt-in when a block lands, so the D1c router and the
background WSL Git gate uninstall at once, as turning the setting off does.
- D1c conversions (wslDriveProject preview/convert, wslDriveUnc:convert)
gate on the opt-in; the new-project Git default already did.
- The orchestrator re-checks before adding a host, so a step already past
its start gate adds nothing after a block.
- The window broadcast and capability push move to
wsl-host-window-broadcast.ts, keeping wsl-host-handlers.ts under max-lines.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(wsl): keep gate mocks whole, and pin cancel after a live block
The visible-work lifetime suite mocked the gate with one export, so the
handlers' new wslHostsAcceptNewWork and onWslHostsNewWorkChanged calls
failed it; it now spreads the real module and overrides only what it
needs. A new case pins that cancelling a running move still works after
a live block while a new move is refused.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(wsl): offer Start wherever a stopped WSL host leaves the workspace stuck
The stopped card with Start only appeared inside a terminal pane. A file-only
or empty workspace on a stopped distro showed a read error or nothing, with no
way to start it. The editor, Explorer and Source Control error states, the
empty workspace, and the host and workspace row menus now show
"<distro> is stopped" with Start. Only the click boots the distro (E9).
* test(sidebar): stub the stopped-host Start item in context menu tests that mock a partial store
- Codex managed-home ownership check runs async in the distro via runWslProcess, fenced
- transcript running observer reuses fresh running-list reads; 5s cadence, first look at 2s
- synchronous Git refuses a WSL cwd; the setup runner reads a WSL worktree's .git link instead
* feat(sidebar): add emoji picker button to workspace display name field
The Display Name field supported :shortcode autocomplete, but nothing on
screen said so. Add a smiley button inside the field that opens the
existing lazy-loaded emoji picker and inserts at the caret. Its tooltip
mentions the : shortcut.
* fix(ui): use Input adornment variant instead of ad-hoc padding classes
* feat(new-workspace): add emoji picker button to Create worktree name field
Show the button in Smart and Name modes (not search-only tabs). The
picker portals into the enclosing dialog so its search box can take
focus inside the modal focus trap, and opening it closes the source
results list.
* feat(workspace-emoji): replace emoji-picker-react with a compact native picker
The stock library picker looked out of place in Orca's dialogs (oversized
search, skin-tone swatch, large category bar). Build a small panel on the
shortcode catalog the : autocomplete already loads: compact search, a
curated 8x6 grid of workspace-friendly emoji, search results while typing,
arrow-key navigation, and a footer showing the :shortcode: to teach the
shortcut. Drops the 500KB library chunk from this feature.
* fix(workspace-emoji): match the : suggestion list and stop clipping in dialogs
Restyle the picker as a searchable version of the : shortcode list using
Command's supported variants. Portal it to the body instead of the dialog
element, which clipped it at the dialog edge; Radix pauses the dialog's
focus trap for nested layers, so the search box still takes focus.
* perf(workspace-emoji): lazy-load the picker panel and keep typing re-render free
- Lazy-load the panel with lazyWithPreload, preloading on hover/focus
- Memoize the button and read the input value at pick time, so it no
longer re-renders on every keystroke
- Store the curated defaults as literal pairs so opening the picker never
builds the full shortcode catalog (was ~9ms on first open)
- Stop Enter/arrow keys in the picker from reaching the source Command
* perf(workspace-emoji): keep picker defaults out of boot and skip keystroke re-renders
- Move the curated defaults into their own module so only the lazy panel
chunk ships them
- Pass a stable (useLatestHandler) value handler from the Smart field so
the memoized button does not re-render per keystroke
- Add a render-count test that fails without memo
* fix(workspace-emoji): re-render the memoized picker button on UI language change
* fix(wsl): Stage 1 is opt-in: the offer moves only its project, and D1c Git follows the setting
* fix(wsl): the WSL path access-denied explanation follows the setting too
- ORCA_WSL_ORCAD_HOST and ORCA_WSL_DISTRO_GUID joined the server launch env both pane spawn
paths scrub (one shared list with the activation root and idle window), so a user's own orcad
started in a host shell no longer acts as a WSL host.
- An unreadable orca-wsl-host-deployments.json (corrupt, or a newer envelope) used to fail the
whole server list closed while hosts were served, hiding every SSH and paired server. It now
hides only the servers that could be WSL hosts, logs why, and writes still refuse the file so
a newer build's copy is kept.
Co-authored-by: Claude <noreply@anthropic.com>
* fix(windows): stop WSL host servers before releasing holders, in a fixed budget
Uninstall released the holders first, so the server stop could exec into a
distro already idling out. Servers now stop while the holders still keep
their distros up, then the holders go. The stop also no longer spends one
wsl.exe and cold guest CLI per terminal: it closes every worktree's
terminals in parallel in one call and waits for the census inside the
guest in one more, so 20 terminals cost the same four calls as one. The
per-distro budget is 30 s and the preflight cap 60 s.
* fix(windows): end an uninstall server stop's wsl.exe calls when its budget runs out
decommission ran its own wsl.exe calls outside the abort signal, so after
the per-distro timeout they could outlive the holder release and boot a
distro that had idled out. The WSL transport now takes the budget's signal:
once aborted it starts no wsl.exe and ends running ones, and the stop waits
up to 2 s for them to unwind before the holders are released. The census
waits in the guest against an overall 8 s deadline, each call bounded at 4 s.
* test(windows): pin failed uninstall closes and the default census retries
* fix(wsl): restoring or switching to a WSL host workspace never boots its stopped distro
A launch booted the last focused WSL host's distro from an early-wake list,
and the window's focus report (sent for a launch restore as well as a click)
connected the host with boot allowed. Both started a distro the user had
stopped, and the stopped card never appeared. Focus now re-checks and
connects only a running distro; a stopped one is published stopped, so the
panes show Start. Start and New Terminal still boot it. The early wake is
gone, and a launch deletes the list an older build left.
* fix(wsl-host): a stale stopped read never overwrites a newer connect
A focus re-check that read the running list while the distro was stopped
could finish after a user Start or New Terminal had connected, then publish
distro-stopped with terminals exited over the live host. Every ready publish
now bumps a per-host generation; a runtime call whose read a newer ready
overtook re-checks instead of parking.
* Change Plugins shortcut defaults by platform
* Test Plugins shortcut matching on all platforms
* Test current Plugins default in browser shortcut forwarding
* fix(github): start explicit Checks refreshes after the action
* fix(github): give explicit Checks refreshes fresh checks and comments
The same Refresh or Link click that now reads PR metadata after the
action also reads checks and comments after it: an explicit request no
longer joins or waits on a read begun before the click, in the renderer
or (for checks) the host coalescer. A superseded read's callers show the
owner's answer, so a late pre-click reply cannot repaint the panel, and
comments posted during either read are kept.
A lost IPC/RPC reply is reported only to the click that asked; polls stay
silent as before, and a later answer clears a failure older than itself.
The manual hook takes its verdict from its own lookup, the link flow's
card-read failure no longer skips checks, and the web shim forwards the
intent.
* fix(github): land the older Checks reply when a newer explicit read fails
When a click's checks or comments read fails while the read it replaced
succeeds, the click's read now lands that older reply through its owner
path instead of leaving the cache behind it. Post-write comments
refreshes (mutation events, bulk resolve) carry the explicit intent so
they cannot be answered by a read begun before the write.
* test(github): drop type assertions from the explicit-refresh tests
The RPC tests call the handlers with a real runtime service and spies,
and the panel test passes typed fixtures straight to the hooks.
* test(github): type the Checks panel fixtures for the renderer project
The renderer tests build their PR fixture locally instead of importing a
main-process harness, and the panel setter mocks take the hooks'
SetStateAction form.
* Advertise mid-turn queueing as queued-messages.v2
Released clients read the v1 name with an unfinished queue UI; they never
see v2 and keep sending immediately.
* Let each desktop send choose queue or steer while the agent works
The follow-up choice is now a Queue / Steer setting saved under
nativeChatFollowUpBehavior, Steer by default; the old
nativeChatQueueFollowUps switch, which every install saved as true
while queueing was dark, is retired on load. Every reader goes through
native-chat-follow-up-behavior.
While the agent works, Enter follows the setting and Cmd/Ctrl+Enter
sends the draft the other way; the alternate flips the setting before
the queue's own rules (a waiting command still queues, prompts no one
here can answer still send now) and never applies while Stopping.
Cmd/Ctrl+Shift+Enter steers the head card only and leaves the draft.
An idle agent's card reads Send, and the card menu no longer offers
"Turn off queueing".
* Hold Send on the phone to queue or steer a message mid-turn
While the agent works on a host that queues, holding Send opens a
Queue / Steer sheet; a choice other than the host's setting sends the
alternate, resolved against the chat as it is when it lands, by the
desktop's rule. Release and dismiss send nothing. The phone reads the
host's new follow-up setting (absent reads as Steer), and an idle
agent's card reads Send.
* Read the follow-up default from one place on desktop and phone
* Keep phone follow-up choices aligned with live host state
* Update stream and wire fixtures for live follow-up preferences
* Document minimal runtime test fixture assertions
* Keep mobile preference tests in the mobile compiler project
* Preserve queued input order through handover and recovery
* Consolidate input selection type imports
* Keep queue request capability types in the shared compiler graph
* Preserve reopen admission context and bound queue projection work
* feat(native-chat): queue follow-ups by default with a toggle
* fix(tests): preserve projection contracts in the node compiler
* fix(native-chat): retain receipt lookup after journal replacement
* test(mobile): assert shared preference renders on real changes
* Remove obsolete follow-up option translations
* Preserve input places across journal recovery without predecessor snapshots
* Keep recovered card placement tied to the exact handoff
* Preserve replay source types and correct verification fixtures
* Complete indexed queue selection and recovery ordering
* Preserve queued input order across recovery and source replay
* Fix legacy queue recovery fixture types
* Use a specific name for queue pause inputs
* Preserve fallback queue identity across journal recovery
* Place nested storage negative directive on its constrained call
* Constrain journal draft property type parameter
* Remove journal input order import cycle
* Preserve lazy journal counter reads within line limit
* test: preserve legacy conversion and display work contracts
* test: align journal refusal and restore accounting contracts
* Turn Stop into Send while the agent works and a draft has text
While the agent works, the desktop composer's Stop button becomes the
ordinary Send button once the draft holds text or images, and returns to
Stop when the draft is empty. It sends like Enter, so it follows the queue
setting; Esc still stops. Its tooltip names what Cmd/Ctrl+Enter does
instead (steer when queueing is on, queue when it is off), read from the
same resolver the send uses, so it is absent wherever the shortcut would
send no differently.
The Send/Stop decision moves into native-chat-composer-send-state so the
pane stays within its size limit.
* Tie alternate delivery hints to the composer submission
* Combine duplicate composer test imports
* Return snapshot results from composer option fixture
* fix(native-chat): show the account's model list while a new folder's own listing runs
A new Claude or Codex chat in a folder with no saved model list showed only its remembered model (or nothing) until that folder's model probe finished, then the full list appeared 1-2 s later. Every new worktree and every chat after a restart hit this, because these agents keep one list per folder and the account-level list reads already fall back to was never written.
The account's own listing (prewarm names no folder) now also saves that list, and a read whose folder isn't listed yet answers from it, including before its scope resolves. The stand-in names no default as the folder's; the folder's own listing still replaces it.
* fix(native-chat): preserve account catalog publication ownership
* fix(native-chat): retain scoped account inventory under binding pressure
* test: make passive catalog deadline coverage deterministic
* test: consolidate catalog fixture type imports
* test: repair catalog fixture types and integration placement
* fix: retain accepted model catalog authority during refresh