* Report image copies the clipboard refused instead of showing "Copied"
The desktop clipboard image write returned without an error when it refused an
image (too large, not a PNG, undecodable), so "Copy as PNG" and browser screenshot
copies showed success while the clipboard was unchanged.
- Main and web image writes now reject on every refusal.
- The image viewer copies a raster image's own bytes (natural size), reusing the
paste/copy PNG conversion; only SVGs still render at 2x for sharp text.
- Too-large failures say so, through one shared description used by native chat
and the image viewer.
- Browser screenshot copies show success only after the write resolves.
Refs STA-14205
* Keep grab toasts on their own screenshot and show a successful retry as success
Refs STA-14205
* Drop a grab screenshot result once a newer toast, copy or cancel replaced it
Refs STA-14205
* Move the grab screenshot copy owner into its own hook
Refs STA-14205
* Test that native chat explains a too-large image copy behind the IPC error prefix
Adds a Custom… option to the App Icon switcher. The picked PNG/JPEG is
header-checked before decoding (square, 64–2048 px, ≤10 MB), re-encoded as
PNG into userData, and applied to the macOS Dock and every window icon. It
persists across restarts and can be removed, which resets to Classic.
Closes#13316
App themes may set background.videoPath: a muted, looping MP4 or WebM (H.264
recommended, VP9 or AV1; one video track, no audio; <= 60 s, 8 MB, 2.3 MP)
played over the required static background.path. Main checks it from container
headers without decoding and serves it on the orca-plugin-image slot
theme-<i>-video; images stay static-only. The renderer reveals it on its first
frame, keeps the still on any failure or a decoded size past the limits, plays
it only while main reports the window focused, shown and not minimized and no
dialog is open, and omits it under prefers-reduced-motion.
Animated WebP was measured and declined: software-decoded every frame, it cost
11-23 points of CPU vs 3-10 for the same loop as video.
Implements #38363 proposed by @dohaidang.
* Use Cmd+K for search on new Mac profiles
Preserve existing search and terminal clear shortcuts during migration
on desktop and web.
* Preserve legacy Mac shortcuts during web profile migration
* Preserve keybinding resets across profiles and platforms
* Preserve legacy shortcuts and markdown link bindings
* Separate profile shortcut defaults from shared overrides
* Check shortcut conflicts when restoring defaults
* Fix tab shortcuts targeting floating workspace panels
* fix(clipboard): shrink re-encoded paste images to fit size limits instead of rejecting
Pasted images are re-encoded as PNG before saving, and the size cap was applied to
that output, so a retina screenshot or copied photo that grew during re-encoding
failed with "Clipboard image is too large". Downscale until it fits at every
re-encode site (desktop clipboard image, Windows copied files, renderer conversion).
Fixes#37855
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
* fix(clipboard): keep image copies full size; only pastes shrink to fit
Copy image shares the PNG conversion with paste. Copies keep rejecting an
oversized PNG instead of silently copying a smaller image, and canvas
downscales use high smoothing quality.
* test(clipboard): match macOS paste tests to downscale-to-fit
Image mocks gain resize; the over-pixel-cap rejection case is removed since
those pastes now downscale (covered by the handler regression test).
* fix(clipboard): let pasted non-PNG images above the pixel cap downscale in the renderer
* test(clipboard): drop web paste test for the removed over-pixel-cap rejection
* fix(clipboard): let pasted PNGs above the pixel cap downscale instead of rejecting
* fix(clipboard): route web PNG pastes through the fit-to-limit converter
---------
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
* fix(terminal): a host with a window keeps a pane a client moved to a new tab
Dragging a split pane onto the tab strip in a paired worktree whose host runs
the desktop app with a window flickered and snapped back. The client draws the
new tab and calls session.tabs.movePaneToNewTab, but the host answered
not_held whenever it had a window, because the window owns the layout and only
headless hosts committed the move. The client reverted, and even without the
revert the host window's next snapshot would have rebuilt the old tab.
Main now asks its window to make the move and waits for the answer, the way
other client requests reach a window-owned host. The window moves the pane
with its own drag-out path under the tab id the client already drew, at the
client's drop position, mounting a parked source tab first. A pane the window
cannot reach is refused, so the client puts it back; a host without the
window bridge still answers not_held. Older clients and hosts are unaffected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(web): rely on the optional pane-move bridge instead of no-op stubs
The web client never receives host pane-move requests, and the bridge
already skips registration when these members are absent; dropping the
stubs keeps web-ui-api.ts under the line limit after rebasing on main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(e2e): a window host keeps a pane a paired client moved to a new tab
The window-host case asserted the old refusal, where the client drew the new
tab and put the pane back. It now drags a pane out on a paired client and
checks the pane stays in the tab the client drew: the host window holds the
leaf alone under that tab id, no snapshot pulls it back, and the source keeps
one pane. It fails on main, where the tab count falls back after the drop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(terminal): refuse a pane move the host window cannot answer
When a paired client asks a host with a window to move a pane to a new
tab, the host relays the request to that window. If the window is gone,
closes, or does not reply within 10 seconds, the relay rejects. The
host returned that rejection as an RPC error instead of a move result.
The host now answers { status: 'refused', reason: 'pane_unavailable' }
in that case. The client already puts the pane back on a refusal, and
this is the reply the relay timeout was sized for.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(terminal): fence a window's pane move once the client heard it timed out
The relay told the client "refused" after 10s but left the window's move
running, so a late commit moved the pane on the host after the client had
put it back. Main now fences the commit: a move the client was told timed
out commits nothing, and a commit answers the client at once, before the
deadline can turn it into a refusal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(terminal): keep the pane-move fence Electron-free and let a claimed commit answer
The leaf-move IPC is shared with the plain-Node runtime, so the fence moves to its own
Electron-free module. A window that goes away mid-commit no longer refuses a move its commit
landed, and a second move cannot replace a fence a timed-out move still holds.
Fixes#37082
* chore(terminal): follow moved shared modules after rebasing the pane-move relay
Refs #37082
* fix(terminal): hold a client's pane move's source tab mounted until the move settles
The timed split-mount lease could expire while the window was still moving the
pane, so cold parking could unmount the source tab after main had already told
the client the move landed. The move now holds the tab mounted for its whole
duration and releases it when it settles.
Fixes#37082
* fix(terminal): don't toast the host window for a pane move another client asked for
Fixes#37082
* fix(terminal): relay a client's pane move only to a window that owns the layout
Where main owns the layout (the dark ORCA_LAYOUT_OWNER=main path), the window's
move goes back through main, which names its own tab, so the client's tab id
could not be kept. Main answers not_held there, as before this change.
Fixes#37082
* fix(terminal): name the renderer a relayed pane move belongs to
Fixes#37082
---------
Co-authored-by: Seunghwan <hyunsh.cycle@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: organize runtime semantic operations by concrete owner
* refactor: consolidate terminal tab close type imports
* refactor: finish runtime contract owners and mobile catalog snapshot ownership
* feat(browser): save and fill passwords in Orca's browser (experimental)
Off by default under Settings > Experimental > Saved passwords.
* fix(browser): secure saved passwords and fix login lifecycles
* fix(browser): keep the guest preload self-contained so saved passwords load
The shared uuid import split into a chunk the sandboxed preload can't require, so the
whole preload failed on every page. Element tokens now come from crypto.getRandomValues.
* fix(browser): fill a saved login on mouse press so clicking a row works
Pressing Orca's UI blurs the page field, which closed the list before the click landed.
* feat(browser): manage saved passwords from a Passwords section in Settings › Browser
Replaces the Manage passwords dialog with an inline section: save and Touch ID
switches, import menu, a searchable list grouped by site with last-used times and
hover actions in a capped scroll box, delete confirmations, and an Add/Edit dialog.
* feat(browser): turn saved passwords on for everyone instead of behind Experimental
Drops the experimental flag, its Experimental toggle and the badge; the Passwords section in
Settings › Browser now shows on desktop for the local host without opting in. With no toggle,
the guest disable message and the apply-to-open-pages step had nothing left to trigger them.
* fix(browser): refuse protected password actions when Touch ID is set up but unavailable, and keep fills on the matched page
Touch ID lockout or a closed lid made canPromptTouchID() false, which counted as a Mac without
Touch ID, so show, copy and protected fill went through unchecked. Enrollment from bioutil now
tells the two apart. A fill also kept no hold on the page across the Touch ID prompt, so a page
that navigated meanwhile got the password; it now goes only to the frame and origin it was matched for.
* fix(browser): keep a browser profile when its saved logins can't be deleted
Profile deletion removed the profile first and only logged a failed vault delete, so the
encrypted vault could stay on disk with nothing left to retry it. The vault now goes first,
and the profile stays (deletion reports failure) when it can't.
* fix(window): ride out Windows renderer launch refusals instead of prompting after 1s
Windows renderer launch-failed exit 18 is Chromium SBOX_ERROR_CREATE_PROCESS:
CreateProcessAsUser for the sandboxed renderer was refused. Field reports show
the refusal clears within seconds to minutes and the same webContents reloads
fine afterwards, but Windows was kept on the 750ms crash breaker, so three
reloads burned in ~1s and a blank window waited on a Try Again prompt.
Use the same ~2 minute launch-failure backoff on Windows as on POSIX.
* test(window): pin bounded prompt for persistent Windows exit-18 refusal
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Neil <neil@stably.ai>
On Windows, relaunching Orca while the previous process was still quitting
delivered a second-instance activation to the exiting process, which opened a
new main window bound to services already being torn down; the window never
appeared and the exit hung.
focusExistingWindow's canOpenWindow now also returns false once the quit
teardown start gate has fired, and delayed reopen retries re-check
canOpenWindow before constructing a window so a retry scheduled before quit
can't slip through afterwards.
Fixes#37625
Co-authored-by: Claude <noreply@anthropic.com>
* refactor: group workspace creation and preparation owners
* refactor: coalesce concrete owner type imports
* test: preserve IPC default-ref mock at its concrete owner
* refactor: group desktop integration and lifecycle owners
* refactor: preserve runtime literals and mock owner boundaries
* refactor: link historical reliability evidence to current test paths
* refactor: retain the historical lid-sleep baseline path
* feat(updater): let users delay updates until a release has been public for N days
Adds an Update Delay setting (None/1/3/7/14/30 days) for supply-chain safety (#5259).
Release age comes from GitHub's Atom feed timestamps and fails closed when unknown.
Settings shows the held release with a countdown, a one-off Skip Delay Once, and
the updater wakes when a held release clears instead of waiting a day.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(updater): satisfy cast, progress-restyle and wall-clock test gates
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(updater): simplify delay UI and enforce release age across checks
* fix(updater): retain ready updates and recheck changed delay settings
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(fonts): enumerate Windows fonts natively instead of through PowerShell
Read the registered font files' name/fvar/STAT tables so a Group Policy
block on PowerShell no longer collapses the picker to five fonts, and say
so in the picker when the OS list is unreadable.
* fix(fonts): harden sfnt parsing and Windows font file discovery
* fix(fonts): follow STAT naming rules and bound Windows font reads
* test(fonts): type platform timeout cases without assertions
* refactor(fonts): list Windows fonts with Chromium's queryLocalFonts instead of a custom sfnt parser
* fix(fonts): scope the local-fonts grant to the main window and keep the web client quiet