You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add desktop-only Space Tabs so users can work in multiple Spaces without replacing the current Space's canvas, conversation or preview state. Each window continues to use one Server and one active Workspace; browser deployments retain the existing single-page experience.
Introduce an Electron shell with persistent Home and independent, sandboxed WebContentsViews for Spaces, immutable renderer identities, deduplicated navigation and lazy session restoration.
Keep the Tab strip minimal: icon-only Home, neutral Space titles and close controls, and persistent Settings/Workspace actions. Reuse the existing Home setup page for opening Workspaces.
Isolate Space storage and Chromium connection pools to avoid cross-Space state overwrites and SSE connection starvation; synchronize application preferences across partitions.
Coordinate acknowledged saves before Tab close, reload, Workspace changes, window close, application quit and update installation. Save failures retain usable pages instead of discarding them.
Route native menus, updater state and remote Basic Auth through the multi-view architecture; update the architecture documentation.
Independent review fixes included
Repeated quit/window-close requests share the same save preparation and cannot bypass pending saves, regardless of which action starts first.
Workspace restoration uses Server-confirmed identity, not stale client state after a successful activation followed by a failed query. An unconfirmed identity restores no Space pages; registration during a Workspace transition is rejected rather than queued.
A synchronous renderer input freeze covers global Canvas shortcuts, native commands and portalled controls until resume or destruction, including after an individual save acknowledgement.
Retained Home refreshes after Space creation/import and reactivation; request-generation and Workspace-identity checks prevent stale list responses.
Desktop unit suite: 75 tests passed, 1 existing todo
Real Electron integration: five concurrent Spaces, draft/document/selection/viewport isolation and retention, undo/redo, preference synchronization, failed and delayed saves, both quit-first and close-first shutdown sequences, frozen shortcuts/portals, retained Home creation/list refresh, lazy restart restoration, Workspace menu/setup routing and Workspace switching
Formatting and git diff --check
Boundaries / remaining validation
One active Workspace per window; no split views, detached Tabs or automatic Tab suspension.
Unsent drafts and in-memory undo/selection survive Tab switching, not page destruction or application restart.
Closing a Tab does not send Agent Stop; quitting a local application still shuts down its Server.
Windows real-device behavior, actual update installer execution and background Agent execution with a configured model remain unverified.
Add retained per-Space content views, workspace-aware session restoration, isolated storage and synchronized preferences. Coordinate save acknowledgements across tab/window/quit/update lifecycles and retain the minimal desktop tab and workspace navigation UI.
Include regression coverage for repeated shutdown requests, authoritative Workspace recovery, renderer input freezing and retained Home refreshes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Preserve save ownership during competing quits, recover shutdown from unavailable pages, avoid preference echo loops, and retain keyboard Tab focus. Add unit and real Electron regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Shutdown can explicitly discard unavailable pages after saving healthy pages; cancellation keeps the application open. Workspace changes remain strict.
A rejected competing quit no longer releases another operation's save barrier.
Incoming World and language preference updates hydrate local state without invoking the outbound writer; explicit Settings language selections still persist.
Keyboard Tab activation requests Shell-native focus and restores the selected button after IPC acknowledgement. Ordinary clicks still focus content.
Validation: 80 desktop tests passed (1 existing TODO), 118 focused web tests passed, repository typecheck/format/lint passed, and expanded isolated real-Electron smoke passed (competing quit ownership, rapid preference writes with exact outbound counts, continuous keyboard navigation, crashed Home shutdown cancellation/confirmation). Native OS-focus assertions were explicitly skipped because this automation environment did not activate any fixture window; native focus invocation is covered by unit tests. Architecture documentation updated. Please re-review the new commit.
Expose workspace save-barrier failures in the Store error
apps/web/src/store/workspaceStore.ts:172
[P2] Publish save-barrier failures in the Workspace Store. This await is outside the error-handling block, so a rejected save leaves the Store's error unchanged. WorkspaceSetupPage catches activation rejections and relies on that error for feedback (lines 112–136 and 201–207). When a background Space cannot save, selecting a Workspace from /setup therefore fails without showing the reason there. Record the barrier error before rethrowing, while retaining the current Workspace identity.
Ignore stale preference notifications after newer local writes
apps/web/src/utils/desktopStorage.ts:58
[P2] Ignore preference notifications older than an acknowledged local write. If A's zh-CN notification is queued while B synchronously commits en, this listener later replaces B's newer local value with zh-CN. Main remains on en; DesktopTabs.setPreference excludes the writer from broadcasts, so B receives no corrective update. This can leave language or input-mode settings inconsistent across Spaces. Include per-key revisions in snapshots, write acknowledgements and notifications, and ignore older revisions. Add a regression test that delivers A's notification after B's write succeeds.
Treat preference notifications as invalidations to avoid applying queued stale values. Surface Workspace save-barrier failures with Store errors and setup toasts. Add focused and real Electron regression coverage.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Preference event handling can restore stale state, and native Close can target an unrelated Space from an auxiliary window.
No open issues 🎉
2 previously missed issues
In code that hasn't changed since last review
Cmd+W closes the wrong target in auxiliary windows
apps/desktop/src/mac-menu.ts:293
Replacing Electron's focused-window Close role with an unconditional Tab close also changes Cmd+W in auxiliary windows. For example, in the remote Basic Auth window it targets the main window's active Space (or does nothing on Home) instead of dismissing sign-in. Use the callback's focusedWindow argument: delegate to closeActiveTab only for the main window, and close other focused windows normally.
Native event rehydrates stale preference over newer value
apps/web/src/hooks/useDesktopContent.ts:204
Home and Shell share localStorage. Shell's preference hydration can queue a native storage event for Home; if Home commits a newer toggle before that event runs, these assignments restore the old Store value even though storage and main hold the newer value. The IPC snapshot check does not cover native events. Read the current localStorage value here, as the rehydrate calls above do. Add a regression test where event.newValue differs from the current stored value.
Route native Close to the focused auxiliary window instead of the active Space. Hydrate native storage events from current values for World, MiniMap and language, with regression coverage.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Release only failed Home-owned Workspace transitions and recheck Home readiness before destroying Space views. Invalidate list refreshes after deletion to prevent stale cards and counts. Add unit and real Electron recovery regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prevent inactive Spaces from marking chat results as viewed
apps/desktop/src/main.ts:1053
Retaining each Space's App also retains ChatPanel's result-acknowledgement effect (apps/web/src/components/Panels/ChatPanel/index.tsx:186–207). Its active-view predicate checks only the inner Preview tab and panel expansion, not desktop Tab activation. Open a Question conversation in Space A, switch to B, and let A finish: the hidden page can mark the result as viewed before the user returns. Track desktop activation in renderer state and include it in both active-view predicates in useActivelyViewingQuestion.ts, without unmounting the retained preview. Test that background completion stays unread until the Space becomes active.
Surface underlying Canvas save errors in submission feedback
apps/web/src/hooks/useDesktopContent.ts:67
saveCanvasForSubmission wraps failures in CanvasSubmissionSaveError, whose message is only “Cannot submit before Canvas is saved”; the underlying save failure is in cause (canvasStore.ts:1053–1087). Close and quit feedback therefore reports a submission error instead of the disk, network, or conflict error that explains the failed save. Use the cause's message when it is an Error, falling back to the existing message otherwise.
Remove deleted preferences from closed Space partitions
apps/web/src/utils/desktopStorage.ts:53
If a preference is deleted while a Space is closed, its persistent partition misses the notification and retains the old value. On reopening, the snapshot omits that key, so this loop leaves it in localStorage and the Store hydrates stale settings. Remove recognized preference keys absent from the snapshot before hydrating Space Stores, while preserving Home's legacy migration. Add a regression test that reopens a partition containing a deleted preference.
Track desktop activation using existing Tab snapshots, expose underlying save errors, and clear deleted preferences when reopening persistent partitions. Add focused and real Electron regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Cross-process save and shutdown coordination needs human sign-off, particularly while Windows behavior and actual update installation remain unverified.
Tell users to close an open Space Tab before deleting it from Home. Recheck current Tabs at confirmation and guard the deletion against concurrent Tab or Workspace operations. Cover deletion failures, lock recovery, and the close-then-delete flow in real Electron.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
docs/architecture/web-architecture.md:42–46 prohibits utilities from importing components. This import makes the shared preference persistence adapter depend on UI presentation for both write and hydration failures. Move error presentation to a UI-facing layer, or inject an error reporter wired there, while preserving the existing failure feedback and write-before-notify behavior.
Register content preference error feedback before Store initialization while keeping persistence utilities independent of UI components. Preserve both architecture updates when resolving the main merge.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Synchronize the recent conversational Agent through the existing application preference adapter, preserving Built-In selection across partitions and restarts. Use validated single-key IPC for routine reads, retain legacy snapshots only for bootstrap, and skip preference access for already observed pen input. Add migration, error, synchronization, bounded-payload and real Electron regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
[P2] Reconcile preferences after subscribing. These Stores hydrate during module initialization, but this listener is installed only after React mounts. If another Space changes World, MiniMap, tool, or chat preferences between those steps, the invalidation can arrive before the listener exists. The new or reloaded page then keeps stale settings until another refresh. Subscribe first, then reread or rehydrate all four preferences. Add a regression case where main's value changes after Store initialization but before this effect mounts.
Reuse preference invalidation hydration to reconcile World, MiniMap, tool and chat values after the content subscription is installed. Preserve transient tool state, report read failures without losing the listener, and cover pre-mount changes, deletions, recovery, cleanup and browser compatibility with real Stores.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Focus Settings WebContents before forwarding the open-settings command
apps/desktop/src/mac-menu.ts:157
After keyboard Tab activation or dismissing the Workspace menu, native keyboard focus remains in Shell. Cmd+, forwards the Settings command to the active content renderer without focusing its WebContents, so the modal opens but typing, Tab and Escape still reach Shell until the user clicks the content view. Focus the target before sending the command, matching DesktopTabs.command('open-settings').
Drain pending conversation title renames before acknowledging close
apps/web/src/hooks/useDesktopContent.ts:113
This barrier does not wait for existing Chat title renames. renameConversationTitle sends a separate request (conversationTitleStore.ts:233) outside the canvas save queues, and normal durable renames are not stored in the persisted pending record. Closing or reloading while that request is delayed can acknowledge success and destroy the renderer before the rename completes or its failure is reported. Track and drain these title writes before acknowledging close, including renames triggered by editor blur, and retain the page if saving fails.
Wait for conversation-title writes in the shared close preparation barrier, preserve failed latest intent for explicit retry, and transfer native Settings focus to content. Cover both paths with Store, lifecycle, menu and Electron regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
[P2] Limit mandatory language persistence to desktop Tabs. In browsers, writeApplicationPreference calls localStorage.setItem directly; a quota error now skips i18n.changeLanguage, so users cannot change even the in-memory language. Keep the existing i18next caching path for browsers and require persistence before changing language only when the Tab bridge exists. Update the tests to distinguish browser fallback from desktop write failure.
Expose title retry through persistent save-error feedback after inner Chat tabs close, retry current failed intent without overwriting newer edits, and preserve browser language fallback when localStorage is unavailable. Add Store, hook, settings and Electron regression coverage.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A failed title query also sets entries[key].error in refreshConversationTitles(). If a pre-send title is already persisted in pending, this predicate treats that read failure as a failed write even though no title PUT was attempted. The save barrier then blocks Tab close, Workspace switching, and quit unnecessarily. Track query errors separately from write failures, and add a regression test showing that a failed query does not block closing a safely persisted draft title.
Track local pending-title persistence failures at the storage boundary and retry the latest snapshot before allowing close. Separate server-write errors from read feedback so failed queries do not block safely persisted drafts or hide failed writes. Cover quota recovery, creation races, and close preparation in unit and Electron regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reject . and .. as Space identities. Both pass this predicate, but tabUrl() normalizes their URLs to /canvas/ and /, respectively. Opening or restoring either value therefore creates a Space renderer that loads a non-Space route. Exclude these dot segments in this shared predicate so both navigation and session restoration reject them, and add regression cases for both values.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add desktop-only Space Tabs so users can work in multiple Spaces without replacing the current Space's canvas, conversation or preview state. Each window continues to use one Server and one active Workspace; browser deployments retain the existing single-page experience.
Independent review fixes included
Validation
pnpm typecheckpnpm formatpnpm lint:fix(0 errors; existing warnings remain)git diff --checkBoundaries / remaining validation
Architecture reference: Desktop Space Tabs.