Skip to content

[KS] API: window workspaces (#191) - #448

Merged
sethwebster merged 5 commits into
seth/kitchen-sink-gatefrom
seth/ks-191-workspaces
Oct 13, 2026
Merged

sethwebster merged 5 commits into
seth/kitchen-sink-gatefrom
seth/ks-191-workspaces

Conversation

@sethwebster

@sethwebster sethwebster commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Refs #191

Window workspaces in @legendapp/spark/windows: named window layouts built on navigator sessions (#188), saved in the app's data directory and restored onto the displays connected now.

  • createWindowWorkspaces(navigator, { menu?, onError? }): list, save, restore, rename, delete, clear, subscribe, remove. getWindowWorkspaceAvailability().
  • Storage: <getDirectory('data')>/workspaces.json, { version: 1, workspaces }, written atomically. Every read checks the whole file. JSON that does not parse, an unknown version or a malformed entry rejects E_INVALID_DATA with the path, and the file is never overwritten until clear().
  • Restore checks the saved session first (a removed entry rejects before anything closes). It then closes the navigator's windows through their guards (a veto gives { restored: false, reason, windowId }) and reopens the windows adapted to the current displays. Each window is placed exact (same display), moved (a display of the same size) or scaled (proportional within the work area). Windows saved in fullscreen enter fullscreen again.
  • Optional Workspaces menu (its own menu before Window, so the system's localized Window title is never replaced; [KS][MENU] Location-targeted menus replace the system's localized menu title #449): Save Workspace… and one item per workspace, with shortcuts; setMenuLabels for language changes.
  • Spaces / Stage Manager: there is no public API, and no private API is used. Windows restore on the active Space, Stage Manager treats them as new windows, macos.spaces entry options apply again, and fullscreen windows get their own Spaces. The contract spells this out.
  • Windows: unsupported-platform; createWindowWorkspaces rejects E_UNSUPPORTED_PLATFORM before any native call ([KS][MW] Windows: window workspaces #445). There is no new native code: this uses the existing window, display, files and menu modules.
  • Kitchen Sink: multiwindow/workspaces (en + ar, titles: { ar }), checks MW-WORKSPACE-01…07, and flows workspaces-*.yaml (save/restore, relaunch, displays, menu, corrupt file, Windows unsupported, manual Spaces/Stage Manager/fullscreen). The screen is added to the multiwindow visual and accessibility flows.

Review fixes

  • Pooled windows: restoreSession releases a parked pooled window that holds a saved ID (fails E_ALREADY_EXISTS without it; test in tests/windows-pool.test.ts, session and workspace).
  • Restore skips closed windows and counts natively gone windows as closed (instance close() on a gone window now ends it with E_CLOSED). A veto or a failure after closing reopens what was closed (rolledBack); failures resolve { restored: false, reason: "failed", error, rolledBack }.
  • Every operation re-reads the file; writes use writeTextIfUnchanged (compare and swap; a missing file is created with createNew), retried, then E_BUSY.
  • Limits: file size (stat before read), 100 workspaces, 200 windows each, duplicate fullscreen IDs, negative savedAt.
  • Menu item IDs are per controller and per name, so a click names the workspace its item showed.
  • Displays without persistent IDs match by display ID.
  • Docs: attachments are not restored; a fullscreen window's saved frame is its fullscreen frame.
  • Kitchen Sink: menu labels from the screen messages (ar), relabelled on locale change; Arabic shortcuts in LTR isolates; goldens re-approved after viewing.

Verification (run in this session)

  • bun run typecheck: passes.
  • bun run test (after rebasing on bc15fee): 1995 passed, 2 failed, both accepted: node-runtime (asdf shim) and motion-native frames (display asleep). tests/windows-workspaces.test.ts 14/14, tests/windows-pool.test.ts 18/18.
  • bun run e2e:validate / e2e:lint: 0 problems.
  • Driver (ks-lock native … --backend driver):
    • workspaces-save-restore, -relaunch, -displays, -menu and -corrupt: 5/5 passed.
    • multiwindow/visual (4 matrix runs), accessibility/audit-multiwindow (4), infra/catalog-driver (4; catalog goldens unchanged) and internationalization/native-strings (3): passed. The final run of the five workspace flows plus these was 20/20.
    • native-strings failed for ar and fr while the menu overlaid Window (it replaced the localized title); it passes with the menu as its own menu.
    • The whole e2e/flows/multiwindow directory: the only failures are the flows that use focusWindow (black-box only), as before.
  • workspaces-manual and workspaces-unsupported (Windows) were not run.
  • ⌃⌘S got no key equivalent in the driver's menu snapshot, so Save Workspace… uses ⌥⌘S. Filed as [KS][MENU] Menu item shortcut ⌃⌘S silently has no key equivalent #446.

Goldens (new)

multiwindow-workspaces light/dark × en/ar.

light
dark
light ar

Follow-ups: #445 (Windows), #446 (menu shortcut silently dropped), #449 (location-targeted menus replace localized menu titles).

@sethwebster
sethwebster force-pushed the seth/ks-191-workspaces branch from 860b99e to 308800b Compare October 13, 2026 04:41
createWindowWorkspaces saves a navigator's session and the connected displays
as named workspaces in the app's data directory (versioned workspaces.json,
validated on every read; a corrupt file rejects E_INVALID_DATA until clear()).
restore() checks the saved session, closes the navigator's windows through
their guards and reopens the workspace adapted to the displays connected now
(exact, moved or scaled), and puts fullscreen windows back into fullscreen.
An optional Window > Workspaces menu saves and restores with shortcuts.
macOS only; Windows reports unsupported-platform (#445). Spaces and Stage
Manager have no public API; the contract says what is and isn't possible.
- restoreSession releases parked pooled windows that hold saved IDs.
- Restore skips closed windows, counts natively gone ones as closed, and
  reopens what it closed when it stops or fails (typed failed result).
- Every operation re-reads the file; writes use compare and swap.
- Load limits: file size, workspace and window counts, duplicate fullscreen
  IDs, negative savedAt.
- Menu item IDs are per controller and per name; setMenuLabels relabels.
- Displays without persistent IDs match by display ID.
- A navigator instance close on a natively gone window ends it (E_CLOSED).
…Window (#191)

A location-targeted overlay sets the merged menu's title, which replaced the
system's localized Window title (#449). Re-approved the workspace goldens:
Arabic shortcuts are in left-to-right isolates.
@sethwebster
sethwebster force-pushed the seth/ks-191-workspaces branch from 308800b to 2f0b0af Compare October 13, 2026 05:39
@sethwebster
sethwebster merged commit 32ea832 into seth/kitchen-sink-gate Oct 13, 2026
2 checks passed
@sethwebster
sethwebster deleted the seth/ks-191-workspaces branch October 13, 2026 06:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant