Skip to content

window-algebra

@johnhenry/window-algebra is a functional window manager for browser applications. It is not a drop-in desktop replacement. It is a small set of parts you can put together into a floating, tiling, tabbed, docking or hybrid window manager.

command → update (pure) → state → derive (pure) → layout tree → compile (pure) → render tree → DOM
  • State is immutable data. update(state, command) returns { state, events, effects } and never touches the DOM, timers, the clock or randomness. A bad command is rejected with an event, never thrown.
  • Layout is an algebra. Eleven primitives build a JSON tree. Named layouts such as masterStack, bsp and the docking tree are ordinary functions that return those primitives.
  • CSS is the layout engine. Tiled windows get flex weights, grid tracks and anchor relationships instead of pixel rectangles. The browser works out the geometry, and every window is a container-query container.
  • The DOM is an effectful backend, not the source of truth. The renderer reconciles a keyed render tree, so a window keeps its element, and its mounted surface, when the layout changes around it.

Zero dependencies. ESM only. The core runs in Node, workers and browsers. Every entry point ships TypeScript declarations.

import { createState, update, derive, compile, presentationContext, toHTML } from "@johnhenry/window-algebra";
let state = createState({ layout: { type: "master-stack", ratio: 0.6 }, config: { gap: 8 } });
for (const id of ["editor", "terminal", "browser"]) state = update(state, { type: "window/create", id }).state;
const tree = derive(state); // a JSON layout-algebra tree
const html = toHTML(compile(tree, presentationContext(state))); // flex: 0.6 1 0 …, no pixels

Provenance: a new package, never published under another name. 0.0.0 is its first version under any name, not a sign of immaturity. Published on 2026-10-03; the source is at github.com/johnhenry/window-algebra.

Guides

API reference: every public export, verified against the source and tests: state, commands, events and effects, queries, the layout algebra, layouts and modifiers, drag and drop, compile and CSS, theming, geometry and interaction, the manager, browser adapters, the command palette, cross-tab sync, framework bindings, versioning and migration, TypeScript types and errors.

Source: github.com/johnhenry/window-algebra. MIT licensed.

window-algebra is one of the family’s browser-first libraries. None of them depends on another. They fit together at the page level:

  • html-modules: declarative HTML modules. <html-import src="./ui.html" as="ui"> turns each <html-export> in an ordinary HTML file into a native custom element (<ui--card>). Those elements are exactly what window-algebra’s surfaces host: htmlSurface(document.createElement("ui--card")) (or a lazySurface that creates one on first mount) puts an HTML-module component in a window. window-algebra only ever calls mount(target)/unmount(), so it needs no knowledge of how the element was defined. Neither package depends on the other. See Surfaces here and html-modules’ Lazy loading for how a lazy import behaves inside a window.
  • mport: routes JavaScript imports across CDNs and compiles the result to a standard import map. A no-build page using window-algebra needs an import-map entry for each entry point it imports (see Getting started), and for anything it loads alongside, such as React for the /react binding, which the repository’s demo/react.html currently fetches from esm.sh by a hard-coded URL. mport can produce that map with fallback across mirrors, instead of hand-written URLs. There is no dependency in either direction; the browser only sees the resulting import map. See mport’s Import maps, lockfiles and the CLI.
  • safe-fragment: Web Components that render untrusted HTML through versioned security profiles. A <safe-fragment> is an ordinary element, so htmlSurface(safeFragmentEl) puts sanitized, less-trusted content in a window; window-algebra only calls mount/unmount. Prefer its default scope="light" in windows that may pop out: the rendered content moves with the element into the pop-out document (scope="shadow" is not isolation, and its content does not receive the copied page stylesheets). Neither package depends on the other.
  • Untrusted Desk: the Orrery planet whose desk is window-algebra, with html-modules components and safe-fragment notes.