Window Manager
Shell chrome and Mission Control UX: [../design/os-shell-design-language.md](../../../product/design/os-shell-design-language.md). State ownership: [state-model
Shell chrome and Mission Control UX: ../design/os-shell-design-language.md. State ownership: state-model.md.
Source Files
| Area | Source file |
|---|---|
| Zustand implementation | apps/os-shell/src/window-manager/store.ts |
| Shell-local window entrypoint | apps/os-shell/src/shell/window-manager/windowStore.ts |
| Selectors and helpers | apps/os-shell/src/shell/window-manager/windowSelectors.ts |
| Recently closed window stack | apps/os-shell/src/window-manager/closed-window-stack.ts |
| Window layer | apps/os-shell/src/shell/window-manager/WindowLayer.tsx |
| Single window component | apps/os-shell/src/shell/window-manager/AppWindow.tsx |
| Renderable layout resolver | apps/os-shell/src/shell/window-manager/useAppWindowModel.ts |
| Titlebar controls | apps/os-shell/src/shell/window-manager/WindowTitlebar.tsx |
| Snap preview overlay | apps/os-shell/src/shell/desktop/SnapPreviewOverlay.tsx |
| Mission Control / Scene overlay | apps/os-shell/src/shell/spaces/MissionControlOverlay.tsx |
| Spaces model | apps/os-shell/src/window-manager/space-model.ts |
| Window model helpers | apps/os-shell/src/window-manager/window-model.ts |
| Shared snap geometry helpers | packages/ui/os-ui/src/shell/windowGeometry.ts |
| Mobile viewport presets | apps/os-shell/src/shell/window-manager/mobilePresets.ts |
| Shell window actions | apps/os-shell/src/shell/state/useWindowActions.ts |
| Global shortcut handler | apps/os-shell/src/shell/GlobalShortcuts.tsx |
| Keyboard Shortcuts app | apps/os-shell/src/apps/keyboard-shortcuts/KeyboardShortcutsApp.tsx |
| Window resize menu | apps/os-shell/src/shell/chrome/WindowResizeMenu.tsx |
WindowInstance
Current shape:
type WindowInstance = {
windowId: string
appId: ShellAppId
title: string
subtitle: string
icon: string
zIndex: number
spaceId: string
layout?: WindowLayout
restoreLayout?: WindowLayout
fullscreenOrigin?: { spaceId: string; layout?: WindowLayout }
fullscreenSpaceId?: string
isMinimized?: boolean
isDesktopMaximized?: boolean
isPinned?: boolean
}WindowLayout is { x, y, width, height }.
isDesktopMaximized means "zoom inside the current desktop Space." It is intentionally separate from fullscreenSpaceId, which means the window has moved into a dedicated fullscreen Space.
Recently Closed Windows
The shell keeps a small in-memory closedWindowStack for user-initiated app
window closes. It is runtime-only state, not persistence, app data, or a
canonical URL resource.
Current conceptual shape:
type ClosedWindowSnapshot = {
appId: ShellAppId
title?: string
subtitle?: string
icon?: string
route?: WindowRouteState
layout?: WindowLayout
snapTarget?: WindowSnapTarget
restoreLayout?: WindowLayout
spaceId?: string
closedAt: number
}Rules:
| Rule | Detail |
|---|---|
| Stack size | The stack is capped at 20 snapshots. |
| Restore order | Restore order is LIFO: the last closed window is restored first. |
| Duplicate app behavior | Restore should recreate an app window even when another window for the same app is already open. |
| Preserved state | Restore should preserve app id, route state, last layout, and originating desktop Space when possible. |
| Visibility | Restore should come back visible and not minimized. |
| Missing manifests | Unknown or removed app manifests are skipped safely. |
| Shortcut | Shift T calls reopenLastClosedWindow. |
| Input safety | The shortcut must not fire while the user is typing into an input, textarea, select, contenteditable region, rich text editor, or while IME composition is active. |
Window-Manager Spaces
The shell store still uses Space as an implementation name for the Desktop
Runtime window manager. User-facing switching is expressed as Runtime/Studio
switching and Scene switching.
type Space = {
id: string
kind: 'desktop' | 'fullscreen'
name: string
order: number
windowIds: string[]
fullscreenWindowId?: string
originSpaceId?: string
createdAt: number
}Main fields:
| Field | Meaning |
|---|---|
spaces | Ordered desktop and fullscreen Spaces. |
activeSpaceId | Currently visible Space. |
previousSpaceId | Previous active Space for transition and restore context. |
missionControlMode | none, spaces, or windows. |
spaceTransitionDirection | Horizontal animation hint for Space switches. |
Desktop window-manager Spaces can hold multiple windows. Fullscreen Spaces hold
one fullscreenWindowId and remember originSpaceId so exiting fullscreen
returns to the desktop where the window started.
Ordering And Focus
| Concept | Current behavior |
|---|---|
| Window order | There is no separate windowOrder array yet; order is implicit through zIndex. |
| Focused window | Stored as activeWindowId. |
| Active Space | Stored as activeSpaceId; visible windows are filtered by spaceId. |
| Focus action | focusWindow raises a window by assigning the next zIndex. |
| Fallback focus | fallbackActiveWindowId chooses the top non-minimized window in the active Space, preferring pinned windows. |
| Pinned windows | Represented by isPinned; rendering maps pinned windows to a higher shell layer. |
App open and focus
User-initiated opens (command palette, desktop icons, shell menus, in-app requestOpenApp) should flow through openShellApp in apps/os-shell/src/shell/open-shell-app.ts. That helper:
- Calls
openApp/openAppWithRecent, which usesresolveExistingWindowForAppinapps/os-shell/src/window-manager/resolve-app-window.tsto focus the best matching window (prefer the active window when it matches the app, otherwise the highestzIndex) instead of creating a duplicate. - Syncs the shell route via
getShellRoutePathso the address bar matches the focused app.
Direct URL navigation still uses useShellRouting, which calls openAppWithRecent when the pathname changes. Shell bar dock icons are now split into persistent Dock apps and temporary running apps. A persistent app without a window calls openShellApp; a running app focuses its best existing window. Temporary apps are derived from open windows and disappear after quit unless the user keeps them in Dock.
Browser before-unload protection is scoped to Studio workspace routes. Public landing and content routes should not show a native "Leave site?" prompt; Studio routes may guard against accidental tab or window closes while workspace state is active.
Singleton App Window
Singleton App Window is separate from the Desktop Runtime window manager. It is
a single lightweight floating surface that can appear on public pages and
non-Desktop Runtime routes for small tasks. It renders the selected app through
AppRuntime, but it does not create a WindowInstance, does not join a
Desktop Runtime Space, and does not persist layout in the window store.
MVP constraints:
| Constraint | Detail |
|---|---|
| Singleton count | Only one singleton app window may be open at a time outside Desktop Runtime. |
| Replacement | Opening another global-capable app replaces the current singleton window. |
| Controls | It can be dragged, closed, reset, and maximized. |
| Supported apps | It is meant for light apps declared with launchModes: ['globalWindow']. |
| Fallback | Apps without global-window support fall back to the normal openShellApp path. |
| Layering | It renders below shell-owned global dialogs, login/sign-out confirmations, and permission prompts. |
This preserves the host distinction: open light apps anywhere, organize multi-window app work in Desktop Runtime, build in Studios, and keep Alma as the companion layer.
Dock Semantics
The shell Dock is local browser OS state, not the app registry itself:
| Dock concept | Behavior |
|---|---|
| Persistent app ids | Live in prox-os.os.dock.pinned-apps and default to a small starter set. |
| Open-at-login app ids | Live in prox-os.os.dock.open-at-login-apps; the shell opens those apps once the workspace is ready. |
| Closed persistent apps | Do not show a running dot while closed and do not render as disabled placeholders. |
| Temporary running apps | Running apps that are not persistent appear after a visual divider. |
| Options submenu | Every Dock app exposes Keep in Dock, Open at Login, and Show in App Store. |
| App actions | The same menu exposes Show all Apps (Control Shift Down), Hide, and Quit; closed persistent apps show Open instead of Quit. |
| Action behavior | Hide minimizes open windows, Quit closes open windows, and Show in App Store opens App Store with a hash target for the selected app. |
| Desktop Runtime presentation | Desktop Runtime renders the Dock as a standalone floating surface controlled by the floating-surface position preference (bottom, top, left, or right). |
Window Actions
Current actions in useShellStore:
| Action | Responsibility |
|---|---|
openApp(appId, bounds?) | Create a new window or focus an existing app window. |
closeWindow(windowId) | Remove one window and pick fallback focus if needed. |
closeOtherWindows(windowId) | Keep only the target window. |
closeAllWindows() | Clear all windows. |
reopenLastClosedWindow(bounds?) | Restore the most recently closed app window from the in-memory stack. |
focusWindow(windowId) | Unminimize and raise a window. |
minimizeWindow(windowId) | Mark minimized and update active fallback. |
saveWindowSize(windowId) | Persist current size to localStorage. |
togglePinWindow(windowId) | Pin exactly one window at a time. |
toggleDesktopMaximizeWindow(windowId, bounds) | Titlebar-double-click zoom inside the current desktop Space. |
toggleFullscreenSpace(windowId, bounds) | Move a window into or out of a dedicated fullscreen Space. |
centerWindow(windowId, bounds) | Restore to centered default layout. |
hydrateWindowLayouts(bounds) | Assign/clamp layouts after workspace bounds are known. |
updateWindowLayout(windowId, layout) | Apply drag/resize result. |
createDesktopSpace(insertIndex?) | Create a desktop Space, capped at 17 desktop Spaces. |
closeDesktopSpace(spaceId) | Remove a desktop Space and move its windows to the nearest remaining desktop Space. |
moveWindowToSpace(windowId, spaceId) | Move a window to an existing desktop Space from Mission Control drag/drop. |
createDesktopSpaceForWindow(windowId, insertIndex) | Create a desktop Space at a Mission Control insertion point and move the dragged window into it. |
switchSpace(spaceId), switchSpaceByDirection(direction) | Manage desktop and fullscreen Space navigation. |
toggleMissionControl(), toggleWindowExpose(), closeMissionControl() | Own Mission Control and current desktop window overview state. |
Shell-level animation and snap behavior currently lives in useWindowActions.
Fullscreen And Zoom Semantics
| Interaction | Behavior |
|---|---|
| Titlebar double-click | Calls toggleDesktopMaximizeWindow, saves restoreLayout, and does not create a Space. |
| Titlebar controls | Render as left-aligned traffic-light dots in close, minimize, fullscreen order. Inactive windows show gray dots by default; hovering any dot reveals all three colors. |
| Maximize control | Calls toggleFullscreenSpace. Entering fullscreen creates a new internal kind: "fullscreen" workspace state, moves the window there, expands its layout to workspace bounds, and records fullscreenOrigin. |
| Fullscreen route boundary | The shell no longer injects /spaces/fullscreen-* URLs for temporary fullscreen presentation state. |
| Resize hover menu | Hovering or right-clicking the active window's fullscreen dot opens WindowResizeMenu, grouped under Move & Resize, Fill & Arrange, and Full Screen. |
| Exiting fullscreen | Removes the internal fullscreen state, moves the window back to its origin desktop Space, and restores its saved layout. |
| Mission Control shrink | Fullscreen Space thumbnails expose a shrink action that returns the app to its origin desktop window. |
| Fullscreen shortcuts | Control Shift Down does nothing in fullscreen Spaces; Control Shift Left/Right still switches between Spaces. |
Snap And Space Dragging
Window dragging supports direct edge targets:
| Target | Behavior |
|---|---|
| Top-left, top-right, bottom-left, and bottom-right corners | Snap to quarter layouts. |
| Left and right edges | Snap to half-screen layouts. |
| Top-half and bottom-half layouts | Available from the resize menu. |
| Bottom edge | Direct bottom-edge drag snapping remains intentionally absent so ordinary vertical resizing does not accidentally arrange a window. |
| Top edge | Toggles desktop maximize, the same state as titlebar double-click zoom, and does not create a fullscreen Space. |
| Drag from desktop-maximized titlebar | Restores the previous layout before continuing the drag. |
Geometry rules:
| Rule | Detail |
|---|---|
| Initial centering | Initial centered windows may use readability padding. Floating drag/resize clamping uses measured workspace edges directly. |
| Exact snap geometry | Snapped windows, quarter windows, desktop-maximized windows, and fullscreen Space windows must render from exact workspace geometry. |
| Frame shape | Desktop-maximized and fullscreen Space windows keep the normal WindowFrame radius and border; they only remove body padding. |
| Odd dimensions | Half and quarter layouts split odd workspace dimensions with floor for the first side and the remaining pixel for the opposite side. |
| Drag activation | Drag activation uses the pointer position, not the last saved snapTarget. |
| Shared geometry source | The shared geometry source is @prox-os/os-ui windowGeometry; the shell adapts it through window-model.ts and windowSnap.ts. |
| Visual regression reference | The Storybook story is Shell/WindowSnapGeometry. |
Dragging a normal desktop window to the top edge and holding for about 1.5 seconds opens Mission Control as a drop target. While the pointer stays in the Spaces strip:
| Pointer state | Result |
|---|---|
| Hovering an existing desktop Space | Marks that Space as the drop target. |
| Hovering the left edge, right edge, or a gap between Spaces | Shows a temporary translucent desktop placeholder with a +. |
| Releasing on an existing desktop | Moves the window into that desktop. |
| Releasing on a placeholder | Creates a new desktop Space at that position and moves the window into it. |
| Hovering fullscreen Spaces | Shows them in the strip, but they are not window drop targets. |
Mission Control Design Contract
Control Shift Up and Control Shift Down are deliberately split:
| Shortcut | Meaning |
|---|---|
Control Shift Up | Prox Mission Control for Studio switching and active Studio Scene switching. |
Control Shift Down | Current desktop window overview; it reuses window transforms and keeps the active Space mental model intact. |
The Mission Control strip is a shell overlay, not app content. It should:
| Contract | Behavior |
|---|---|
| Overlay treatment | Use a single restrained glass panel at the top instead of applying blur to every window. |
| Desktop thumbnail background | Render with --os-desktop-background so the selected user background appears in miniature. |
| Overflow | Keep the Space thumbnail list scrollable when many Spaces exist. |
| Alignment | Center the list when there are only a few Spaces, then let it grow outward before it becomes scrollable. |
| Create control | Pin the + create control to the far right of the strip. |
| Space cap | Cap normal desktop creation at 17 desktop Spaces. |
| New desktop animation | Animate the new-desktop preview from the right edge toward the + hover area before creation. |
| Close action | Show a hover X action on desktop thumbnails when the desktop can be closed. |
| Shrink action | Show a hover shrink action on fullscreen Space thumbnails to return that app to its origin desktop. |
- Show fullscreen Spaces as app-identity thumbnails and desktop Spaces as miniature desktop surfaces with small window previews.
MobileMode Preview
MobileMode layout presets still exist as shell actions and can be re-exposed from a future app/window menu. They are no longer part of the default three-dot titlebar control set.
- Presets update only the current
WindowLayout; they do not persist unless the user explicitly saves the window size. - Apps with
window.maximizable === false, such as Display options, should not expose MobileMode.
Shell vs Window Manager Boundary
Should stay in shell:
- Runtime Command Strip, command palette, drawers, dialogs, notifications.
- App-owned global modal overlays rendered through
@prox-os/os-uiWindowDialog. These look window-like, but they are not window manager instances and do not create route, Dock, z-index, or layout state. - Alma as a shell surface that resizes the measured workspace but does not become a normal window instance.
- Route synchronization with TanStack Router.
- Link interception and external link menu behavior.
- System toasts and overlay placement.
- Screensaver and large cursor.
Should move deeper into window-manager over time:
- Snap target calculation.
- Minimize animation target calculation.
- Mission Control, App Expose, and overview layout transforms.
- Layout persistence.
- Explicit
windowOrderif z-index becomes insufficient. - Per-window route state for app-owned
appPath, search, and hash.
Oversized Or Broad Files
Current files are all below 500 lines, but some functions still carry broad responsibility:
apps/os-shell/src/window-manager/store.ts: core Zustand actions; helpers are split intospace-model.ts,space-state.ts, andwindow-model.ts.apps/os-shell/src/shell/state/useWindowActions.ts: close, snap, minimize animation, clamp, maximize, center.apps/os-shell/src/shell/useDesktopShellController.ts: shell orchestration and model wiring.apps/os-shell/src/shell/GlobalShortcuts.tsx: many shortcut branches.
These are candidates for later behavior-preserving splits.