Skip to content

Changelog

The library is pre-1.0. Treat every release as potentially breaking; the entries below name the breaking changes explicitly.

  • Windows taskbar and Start menu, to the Windows 11 reference. The "bar" dock gains the Windows 11 taskbar pieces, each behind an optional chrome token: showDesktopButton (the thin “Show desktop” sliver in the far corner; one click minimizes every window on the active workspace, a second restores them), taskViewButton (a Task View button beside Start that opens the windows overview), taskbarContextMenu (right-click the empty taskbar for “Taskbar settings”, which jumps straight to a dedicated Taskbar section in Settings via the exported requestSettingsSection), and dockAutoHide (the bar tucks off the edge and slides back only while the pointer is at that edge or over it, reserving no work area). Under center alignment the launcher run now centers Start and Task View with the app icons (Windows 11) instead of pinning them to the corner. The "menu" launcher opens above the Start button, tracking a centered or left-aligned taskbar, and is rebuilt on the Windows 11 layout: a search field, a Pinned grid with an All-apps list toggle, a Recommended section (recently focused apps), and an account-plus-power footer. Windows exposes the alignment and the item toggles in the new Taskbar settings section. Non-breaking: every token is optional and off by default.
  • Per-platform chrome sizing. The taskbar, dock, and top bar were one shared metric set, so Windows and Ubuntu were forced to the same size (Ubuntu’s dock was far too thin). Three optional chrome tokens size each clone to its reference: dockTileSize (a "bar" dock derives its thickness from the tile, so a larger-icon dock is a wider bar), dockIconScale (the icon as a fraction of the tile), and menuBarHeight. macOS keeps its 56px floating tiles, Windows gets 24px icons in a 48px taskbar, and Ubuntu gets ~46px icons in a ~64px dock with a 30px top bar. Non-breaking: un-tokenized themes keep the previous viewport metrics, and compact viewports still shrink the tiles.
  • Theme-aware app and system icons. One app can carry a platform-native icon per clone. App.icons (and SystemWindowDef.icons) map a theme’s chrome.iconStyle ("fluent", "macos", "gnome") to an icon component, resolved by the exported resolveAppIcon; anything missing falls back to the single icon. The example apps ship a Fluent variant for Windows (Microsoft Fluent System Icons, MIT) and keep their Lucide glyphs for macOS; the playground demos the colorful Ubuntu Yaru icons for the "gnome" style; and the built-in Settings window resolves a Lucide gear (macOS), a Fluent gear (Windows), and the Yaru gear (Ubuntu). System windows (Settings, Recents) gain an icon, so they no longer fall back to a letter in the launcher. Non-breaking: App.icons, chrome.iconStyle, and SystemWindowDef.icon / icons are all optional.
  • theme.font token. A theme sets its platform’s UI font stack (the SF / system stack on macOS, Segoe UI on Windows, the Ubuntu font on Ubuntu), applied at the desktop root so the chrome inherits it. The consumer loads any non-system webfont (the playground links the Ubuntu font). Non-breaking: optional, defaults to a neutral system stack.
  • Reduced-motion support across the desktop. Every autonomous animation now honors prefers-reduced-motion, through one shared useReducedMotion hook (built on useSyncExternalStore, so it is correct on the first client render and SSR-safe). Under the setting, the wallpaper dissolve, window open / close / minimize and the maximize-snap glide, the Mission Control spread, the notification toasts and center, the Quick Settings popover, all three launcher presentations, the HUD, and the dock launch bounce collapse to instant. Direct-manipulation feedback (the app-switcher selection, dock hover magnification, sliders and toggles) keeps animating, matching the platform convention that reduced motion targets autonomous motion, not the response to a user’s own input.
  • Light and dark appearance, appearance-aware wallpapers, and a wallpaper that dissolves in. Every theme now ships a light and a dark look from one object: author the base palette and supply the opposite under appearances.{light,dark} (token overrides for palette, elevation, wallpaper). theme.appearance is "auto" (the default, following prefers-color-scheme live), "light", or "dark", resolved by the desktop via the pure applyAppearance(theme, mode). All three reference themes expose an Appearance select in Settings and carry a per-appearance wallpaper (the factories take darkWallpaperSrc / lightWallpaperSrc). The wallpaper no longer pops in: each is decoded off-screen, then dissolves in over the base color, crossfading on change (the GNOME model), and honoring prefers-reduced-motion. Pass wallpaperOptions to a theme factory to expose a “Wallpaper” picker in Settings. Non-breaking: appearances, appearance, and the new factory options are all optional; a theme without them stays single-look.
  • Three OS-clone themes, the macOS Tahoe wallpaper, and an on-canvas theme switcher. The theme set is now exactly three platform clones: macOS (@react-ui-os/theme-macos, now a createMacosTheme({ wallpaperSrc }) factory shipped with the macOS Tahoe wallpaper), Windows (@react-ui-os/theme-windows), and Ubuntu (@react-ui-os/theme-ubuntu). Both playgrounds gain a segmented theme switcher that swaps the whole look with a click, persists the choice, and mirrors it into ?theme=. Breaking (pre-release): @react-ui-os/theme-mintables and @react-ui-os/theme-saas are removed; @react-ui-os/theme-redmond is renamed to @react-ui-os/theme-windows and @react-ui-os/theme-default to @react-ui-os/theme-macos (their createRedmondTheme / createDefaultTheme factories become createWindowsTheme / createMacosTheme).
  • Launcher presentations and the useLauncher hook. The macOS Spotlight palette is now one face of a launcher with a shared brain. chrome.launcher picks the presentation: "spotlight" (the centered palette, default), "grid" (the GNOME Activities app grid, a full-bleed search field over a grid of large app icons), or "menu" (the Windows Start menu, raised from the dock launcher). All three draw the same results (apps, system windows, registerSpotlightSource rows), filter the same way, and share the Cmd/Ctrl+K shortcut; Ubuntu sets "grid" and Windows sets "menu". The shared logic lives in an exported useLauncher() hook, so a consumer can build a fully custom launcher (its own markup, the system’s results and shortcut) by rendering it in place of <Launcher>. <Spotlight> stays exported as an alias of <Launcher>. Non-breaking: chrome.launcher is optional and defaults to "spotlight".
  • GNOME / Ubuntu theme, the "gnome" window-control register, a centered clock, and Quick Settings. The Ubuntu theme, @react-ui-os/theme-ubuntu, is the first to pair a top menu bar with a left dock (the GNOME Shell arrangement): Yaru dark palette, Ubuntu orange, a left bar dock with no fisheye, and the new pieces below. chrome.windowControls gains "gnome": minimize / maximize-restore / close as round symbolic buttons on the right, the neutral Adwaita / Yaru treatment (the close button is not reddened). chrome.menuBarClock places the top-bar clock "right" (the macOS default) or "center" (the GNOME placement, status indicators kept on the right). chrome.quickSettings turns the menu-bar status cluster into one button that opens the new Quick Settings popover (the GNOME system menu, the macOS Control Center, the Windows quick settings flyout): a panel of action buttons, sliders, and toggle tiles, populated by registerQuickSetting(...) on the same imperative-store pattern as status items. The left bar dock now adapts to a top menu bar (it drops its own clock, starts below the bar, and moves the launcher to the trailing edge), and a new chrome.dockAlign ("start" | "center" | "end") packs a bar dock’s icons from the top, the GNOME / Windows 10 placement, instead of always centering them; Ubuntu sets "start" and both Ubuntu and Windows expose it in Settings (Windows 11 calls it Taskbar alignment). The workspace switcher moves to the top-left in the GNOME arrangement, and chrome.menuBarBrand (default true) lets a theme drop the macOS-style brand button so the GNOME top-left is just the workspace switcher (Ubuntu sets false). Non-breaking: chrome.menuBarClock, chrome.quickSettings, chrome.dockAlign, chrome.menuBarBrand, and the new control register are additive with defaults that leave existing themes unchanged.
  • Window-control registers, elevation and dock-magnification tokens, and the Windows theme. theme.chrome.windowControls now renders all three declared registers: macOS "traffic-lights", Windows "windows" (a minimize / maximize-restore / close caption cluster, close turning red on hover, drawn from font-independent SVG glyphs at the Windows 11 metrics), and "minimal" (a single close glyph). Three new structural knobs stop the components hardcoding macOS looks: elevation (focused / unfocused window shadow, optional, falls back to the previous default), motion.dockMagnification (the dock fisheye peak; set it to 1 to turn the fisheye off), and chrome.dockStyle ("floating" macOS pill vs "bar", a flat taskbar flush to the edge, full width on the bottom or full height on the left, with flat brand-colored icon buttons and an accent underline under running apps, matching the Windows 11 48px taskbar). The three existing reference themes expose the window-control style as a Settings select, and the macOS theme exposes dock magnification as a slider, so the look can be mixed live. The Windows theme, @react-ui-os/theme-windows, puts the levers together into a Windows-style desktop: caption buttons, a real bottom taskbar (dockStyle: "bar") with no fisheye, no menu bar, lighter shadows, 8px corners. Non-breaking: elevation, motion.dockMagnification, and chrome.dockStyle are optional with fallbacks, so existing themes are unchanged.
  • App Store: @react-ui-os/cli and an app registry. Apps are distributed, not bundled into the library. A root registry.json lists each app (id, source files, dependencies, accent, category); npx @react-ui-os/cli add <id> copies an app’s source into the consumer’s project under os-apps/<id>/, prints the dependencies to install and how to register it with <Desktop>, and skips existing files unless --force. list shows the registry, build inlines a registry into a single self-contained JSON to host. Third parties host their own registry and install from it with --registry <url>, so the catalog grows without pull requests to this repo. The docs gain a searchable App Store gallery and a publish guide. The seven seed apps (Notes, Calculator, Clock, Calendar, Reminders, Sketch, Terminal) moved into @react-ui-os/example-apps, which the playground and docs both consume.
  • Slider and Toggle form primitives. Themed wrappers over a real <input type="range"> and <button role="switch">: keyboard arrows, Page Up/Down, screen-reader value announcements, aria-checked semantics all work because the native primitives are still doing the heavy lifting; only the visual layer is custom. Exported for consumer windows. Settings panel RangeControl and ToggleControl now render these instead of the prior hand-styled inputs, so the built-in Settings UI matches the rest of the desktop without per-theme styling work.
  • registerStatusItem, menu-bar plug-in slots. Third extension point in the library (after registerSpotlightSource and registerSystemWindow). Items sit between the workspace pips and the clock; they declare icon, optional tooltip, optional badge, optional onClick, and an order for placement. Re-registering the same id updates in place. Live-demo’d in the playground: a green “online” dot and a clickable “ding” widget that fires a toast. The menu bar widget is a <button> with the tooltip text as its aria-label, so keyboard-only users get the same hint as hover users.
  • Tooltip primitive. Replaces the native title="" attribute on every dock tile, workspace pip, and the menu-bar clock; available for consumer code via import { Tooltip } from "@react-ui-os/desktop". Cold show ~480 ms, warm ~60 ms (a tooltip that hid in the last 800 ms makes the next one near-instant, the same cadence the macOS menu bar uses). Auto-flips placement when it would clip; final clamp keeps it on-screen. Optional shortcut hint on the right ("⌘K", "F3"). Honors focus events so keyboard users see the same hint as hover users.
  • Workspaces (virtual desktops). Window manager now tracks an ordered list of workspaces and an active one; every window carries workspaceId. WindowLayer filters by active workspace, so switching is a free O(n) hide rather than mount/unmount churn. Menu bar shows a pip cluster (one per workspace, active one wider and accent-colored). Ctrl + Alt + ←/→ switches; Ctrl + Alt + Shift + ←/→ brings the focused window with you. Window title-bar context menu gains “Move to Workspace N”. Three workspaces ship by default. useWindowManager() exposes switchWorkspace, moveWindowToWorkspace, addWorkspace, removeWorkspace. Opening or focusing a window on another workspace pulls the user there, matching the macOS Cmd+Tab-jumps-spaces behavior. Non-breaking at the call-site level, but OpenWindow now has a required workspaceId: string; persisted snapshots need a fallback. 13 new reducer tests cover the lifecycle.
  • HUD overlay. Transient floating indicator ("Maximized", "Snapped Left") the OS uses to confirm a momentary action. Mounted by <Desktop>, fires automatically on window snap, maximize, restore. showHud({ title, sublabel?, icon?, progress?, accent?, duration? }) is exposed for consumer-driven HUDs (volume / brightness analogues). Re-firing while a HUD is visible replaces it in place so repeated taps coalesce.
  • Mission Control (F3 / Ctrl + ↑). Tile every open window in a grid of large preview cards (window aspect, accent stripe, traffic lights, name). Click any card to focus that window and dismiss; click empty space or press Esc to dismiss without changing focus. Minimized windows are excluded; Mission Control is “what’s on screen now,” not “what’s stashed in the dock.” Cards are representational rather than live thumbnails so the cost stays flat regardless of how much DOM each window owns.
  • App switcher (Cmd/Ctrl + Tab). Centered overlay shows every running app, MRU order from window z-index, large highlighted tile on the selection. Hold the modifier and Tab cycles forward, Shift+Tab back; release commits the focus; Esc cancels. First press selects the second entry; quick tap-and-release flips you to the previous app, the Mac convention. Apps with no open window are omitted (this is a switcher, not a launcher; Cmd+1..9 and Spotlight handle launching).
  • Window snapping (Aero Snap). Drag a window’s title bar toward the viewport edge: a translucent rectangle in the theme accent shows the snap target (left half, right half, top = maximize, four corners = quarters). Release in the zone and the window snaps. Activation thresholds: 24 px from an edge, 48 px from a corner. Keyboard chords: Cmd/Ctrl + ←/→ for halves, Cmd/Ctrl + Shift + ←/→ for top corners, Cmd/Ctrl + ↑/↓ to maximize/restore. Snap math (computeSnapZone, rectForZone) and the store (setSnapPreview, getSnapPreview, subscribeSnapPreview) are exported so custom draggable surfaces (floating panels, tool windows) get the same affordance for free.
  • ContextMenu primitive + DesktopBackdrop. Right-clicking the desktop background now opens a system menu (Spotlight, Notifications, Settings, Show Desktop); right-clicking a dock tile shows the per-app menu (Open / Minimize / Close / Mark notifications as read); right-clicking a window title bar shows Restore / Minimize / Close. Two contribution paths: imperative openContextMenu({ x, y, items }) from any event handler, or <ContextMenuAnchor items={...}> to wrap a region declaratively. Items support label, icon, shortcut, onSelect, disabled, danger, and separator. Keyboard navigation, flip-to-stay-in-viewport positioning, and Esc / scroll / blur dismissal all handled by the renderer. Existing in-FileExplorer right-click menu kept; the new primitive is what every other surface uses.
  • Notification subsystem. New notify({ title, body, appId?, level?, duration?, actions? }) in @react-ui-os/core, plus <NotificationToasts> and <NotificationCenter> in @react-ui-os/desktop. One call drives four surfaces at once: a top-right toast stack, a right-edge Notification Center sheet, per-app dock badges, and a menu-bar accent dot. Levels (info | success | warn | error) carry defaults: errors stick until dismissed, warnings linger, success/info auto-expire. Re-injecting the same id updates a running toast (progress-bar pattern). Snapshot exposed via useNotifications(). Reducer-free, vanilla store, callable from any code.- registerSpotlightSource(id, source): third contribution path for the Cmd-K palette. A source is a function (query) => SpotlightResult[]; each result owns its own onActivate, so activations can navigate, open URLs, or dispatch events without going through openWindow. Sources are called inside a try/catch so a misbehaving source contributes zero rows rather than killing the palette. Use it for docs pages, bookmarks, recents, or anything that is not a window.
  • SystemWindowArgs: the system variant of WindowPayload now accepts an optional args record of string/number/boolean primitives. windowIdOf serializes the args into the id so two system windows with distinct args coexist as distinct windows (one per “Component: Window” / “Component: Spotlight” pair). Backward compatible: omitting args keeps the single-slot behavior.
  • SystemWindowDef.name can be a (args) => string function for per-instance titles. New resolveSystemWindowName(def, args) helper.
  • <Desktop children>: children now render inside the provider alongside the default surfaces. Lets consumers mount headless companions (URL sync, analytics, deep-link activators) with access to useWindowManager().
  • Generated prop / field tables. apps/docs/scripts/extract-types.mjs parses interface and type-alias declarations from each package’s source with the TypeScript Compiler API and emits apps/docs/src/data/types.json. A new <TypeTable name="..." /> Astro component renders the rows with the actual JSDoc comments, the optional flag, and a link back to the source file. Used today on StorageAdapter, SpotlightResult, SpotlightSource, OsTheme, and OsThemeChrome. Runs as part of dev, build, and typecheck so the JSON cannot drift from source.
  • Pivoted apps/docs to Astro Starlight. Standard left-nav, right TOC, search, theme toggle. Replaced the “docs are windows” prototype with proper component reference pages.
  • <LivePreview> Astro component embeds /playground?demo=<key> iframes in component pages so each one opens with the relevant feature focused.
  • Per-feature deep-link demos: ?demo=spotlight, ?demo=settings, ?demo=window, ?demo=dock, ?demo=menubar, ?demo=recents.
  • The playground registers a Spotlight source listing every docs page; activating a row escapes the iframe and navigates the parent docs frame, so Cmd-K finds content as well as windows.
  • OG / Twitter card meta tags so shared URLs render a social preview. Uses the desktop wallpaper as the stand-in card until a branded render replaces it.
  • CI at .github/workflows/ci.yml: typecheck, test, build on every push and PR.
  • Pages deploy at .github/workflows/docs.yml: auto-deploys apps/docs on push to main.

The full repo history lives at github.com/saschb2b/react-ui-os/commits/main. High-level milestones:

  • Phase 4: FileExplorer primitive with full macOS-Finder interaction model (multi-select, Shift-range, right-click context menu, F2 rename, sidebar). State-earned desktop folders via SystemWindowDef.appearsAsDesktopIcon.
  • Phase 3: First branded theme. Chrome variants real (dockPosition: "left", menuBar: "none"). Wallpaper parallax.
  • Phase 2b: Settings as a system window. The customizable schema on OsTheme. Effective-theme overlay.
  • Phase 2a: Resize handles, global keyboard shortcuts, Spotlight.
  • Phase 1: Window manager, default theme, playground demo.
  • Expand <TypeTable> coverage to every remaining hand-written field table (Settings, useWindowManager, useTheme, the customizable field variants).
  • A branded social card render to replace the wallpaper stand-in (title, logotype, accent stripe).
  • First 1.0.0 release once the API stops moving.