State
Logical (intent) state is one plain, JSON-serializable object. It describes what the user asked for: which windows exist, where they belong, what layout each workspace uses and who has focus. It holds no pixels for tiled windows and no DOM. update produces new states from old ones; derive turns a state into a presentation. Source: src/state/create.mjs.
State shape
Section titled “State shape”createState() returns (with STATE_VERSION 2):
{ version: 2, // STATE_VERSION; see versioning.md config: { ...DEFAULT_CONFIG }, // see "Configuration" below windows: { [id]: WindowRecord }, workspaces: { [id]: WorkspaceRecord }, workspaceOrder: ["main"], // every workspace id, in creation order activeWorkspace: "main", // mirrors outputs[focusedOutput].activeWorkspace outputs: { primary: OutputRecord }, outputOrder: ["primary"], focusedOutput: "primary", // the output with input focus focus: { window: null, history: [] }, // focused id (or null); history oldest → newest, no duplicates stack: { background: [], normal: [], top: [], modal: [], popover: [], notification: [], system: [] }, lastScratchpad: null, // default target of `scratchpad/toggle {}` urgent: [], // urgent window ids, oldest first}| Field | Meaning |
|---|---|
version |
The shape version. migrate upgrades older states. See Versioning. |
config |
Policy settings. See Configuration. |
windows |
Window records keyed by id. See Window record. |
workspaces |
Workspace records keyed by id. |
workspaceOrder |
All workspace ids across all outputs, in creation order. |
activeWorkspace |
The active workspace of the focused output. It always equals outputs[focusedOutput].activeWorkspace, and it is kept so that single-output code never has to think about outputs. |
outputs |
Output records keyed by id. Every workspace belongs to exactly one output, and every output has at least one workspace. |
outputOrder |
Output ids in order. focus/next/focus/previous walk outputs in this order. |
focusedOutput |
The output that has input focus. |
focus.window |
The focused window id, or null. |
focus.history |
Focus history, oldest first, each id at most once. Used to pick the next window when the focused one disappears (close, minimize, move away, scratchpad, pop-out, unstick). |
stack |
Stacking order within each layer, bottom to top. Every window appears in exactly one layer’s list (its layer). A tiled window’s position here does not affect painting (see paintOrder in Queries). |
lastScratchpad |
The most recently hidden or shown scratchpad window, used when scratchpad/toggle has no id. |
urgent |
Ids with an urgency hint, oldest first. |
Window record
Section titled “Window record”window/create normalizes its payload into this record (via createWindowRecord, below):
| Field | Type | Default | Notes |
|---|---|---|---|
id |
string | required | Non-empty and unique. |
title |
string | "" |
Used for aria-label, tab text, announcements. |
role |
one of ROLES |
"window" |
Semantic role. derive decides presentation from it. |
parent |
id | null |
null |
Parent window. Children close, move and raise with their parent. |
modal |
boolean | false |
An open (not minimized, not popped-out) modal child blocks its parent. |
mode |
"tiled" | "floating" |
"tiled" for role window, else "floating" |
Only role: "window" can take part in a tiled layout. |
placement |
{ x, y, width, height } |
config.defaultPlacement merged with the given placement |
Requested geometry. derive honours it only for floating windows. x/y may be numbers (px) or CSS strings; "center" centres (used by the scratchpad). |
constraints |
object | {} |
minWidth, minHeight, maxWidth, maxHeight, aspectRatio (number, or { min, max }, as width / height), widthIncrement, heightIncrement, baseWidth, baseHeight. See constrainSize in Geometry. |
status |
one of STATUSES |
"normal" |
Set by the status commands. |
layer |
one of LAYERS |
by role (see below) | Stacking layer. |
anchor |
object | null |
null |
Anchor options for popover-like roles: to (defaults to the parent) plus side, align, offset, inside, x, y, gravity, flip, slide, resize. See anchor. |
workspace |
id | null |
the parent’s workspace, else the active workspace | null only while hidden in the scratchpad. |
data |
any | absent | Stored only when given. Application data, never read by the library. |
draggable |
false |
absent | Only the false exception is stored. A pinned window cannot be dragged or swapped away. |
app |
string | absent | Stored only when given. An application or window-class id (like WM_CLASS), matched by rules. |
sticky |
true |
absent | Set by window/set-sticky. The window is visible on every workspace of its output. |
scratchpad |
true |
absent | Set by window/to-scratchpad. Cleared when the window is moved to a workspace directly. |
Default layer by role: notification → "notification"; popover, menu, tooltip → "popover"; any other role with modal: true → "modal"; panel → "top"; everything else → "normal".
The initial placement is not clamped to the create command’s own constraints. Rule-supplied constraints are clamped; see window/create. window/resize and window/set-constraints clamp.
Workspace record
Section titled “Workspace record”| Field | Type | Notes |
|---|---|---|
id |
string | |
windows |
id[] | The workspace order. Order-based layouts read the tiled subset of it. Drops and swaps rewrite it. |
layout |
layout spec | A plain object { type, ...options, modifiers? } or a function (spec, ids, context) → tree. Defaults to { type: "master-stack", ratio: 0.5 }. See Layouts. |
output |
id | The output this workspace belongs to. |
toggleLayouts |
[a, b] |
Present after the first layout/toggle. |
Output record
Section titled “Output record”| Field | Type | Notes |
|---|---|---|
id |
string | |
workspaces |
id[] | The workspaces on this output, in order. Never empty. |
activeWorkspace |
id | The workspace this output currently shows. |
A window is visible when its workspace is the active workspace of its own output, whichever output has focus. So every output’s stage can be rendered at the same time with derive(state, { output }).
Constants
Section titled “Constants”| Export | Value |
|---|---|
LAYERS |
["background", "normal", "top", "modal", "popover", "notification", "system"], bottom to top. Frozen. |
ROLES |
["window", "dialog", "sheet", "popover", "menu", "tooltip", "panel", "notification"]. Frozen. |
STATUSES |
["normal", "minimized", "maximized", "fullscreen", "popped-out"]. Frozen. |
STATE_VERSION |
2 |
DEFAULT_OUTPUT |
"primary", the id of the output every fresh state starts with. |
DEFAULT_CONFIG |
The frozen default configuration below. |
How each role is presented (by derive, for windows not in the tiled base):
| Role | Presentation |
|---|---|
window, panel |
Floating at placement (floating({ x, y, width, height })). A tiled window is laid out by the workspace layout instead. |
dialog |
Anchored to the centre of anchor.to or the visible parent; centred in the stage with no anchor. |
sheet |
Anchored inside the top of its parent, centred, with content height; centred in the stage with no anchor. |
popover, menu, tooltip |
Anchored by side (default side: "bottom", align: "start", offset: 4) to anchor.to or the visible parent. Every other anchor option passes through. With no anchor, floating at placement. |
notification |
Stacked in the bottom-right corner (right: 16, each successive one height + 8 px higher). |
Any window with status: "maximized" fills the stage (place({ top: 0, right: 0, bottom: 0, left: 0 })). A "fullscreen" window is presented with its descendants only. "minimized" and "popped-out" windows are not presented.
Configuration (config)
Section titled “Configuration (config)”createState({ config }) merges the given keys over DEFAULT_CONFIG; drag, urgency and snap merge one level deep. At runtime, change it with config/set, which also merges plain-object values one level deep and validates drag, rules, urgency and snap.
| Key | Default | Meaning | Validated by config/set |
|---|---|---|---|
direction |
"ltr" |
Reading direction: "ltr" or "rtl". Under "rtl" every horizontal arrangement mirrors (see Layouts › Right-to-left). May be missing on a saved state: read it as "ltr" (directionOf(state)). |
"ltr" | "rtl" |
focusRaises |
true |
Focusing a window also raises it (and its descendants) within its layer. | no |
gap |
0 |
Gap in px between tiled siblings. It wraps every container in the tiled base, unless a smart-gaps/no-gaps modifier suppresses it. |
no |
inset |
0 |
Padding in px around the tiled base, suppressed the same way. | no |
defaultPlacement |
{ x: 40, y: 40, width: 480, height: 320 } |
Placement for new windows that give none. | no |
drag.tiled |
"swap-or-insert" |
Which drops a tiled window may make: "swap-or-insert", "swap", "insert" or "off" (DRAG_MODES). A layout spec’s own drag overrides it for that workspace. |
must be in DRAG_MODES |
drag.edgeZone |
0.25 |
The fraction of a target’s width/height that counts as an edge zone. | 0 ≤ n ≤ 0.5 |
drag.preview |
true |
Draw the ghost preview while dragging. | no |
drag.tooSmall |
"allow" |
"reject" refuses a drop whose resulting slot violates the dragged or target window’s min/max constraints (needs the geometry estimate the input adapter sends). |
"allow" | "reject" |
drag.toFloating |
"modifier" |
How a dragged tiled window detaches as floating: "modifier" (modifier key held), "threshold" (dragged outside the stage) or "off". "off" also makes window/detach reject with drag-disabled. |
one of the three |
drag.toTiled |
"modifier" |
When a dragged floating window may drop into the layout: "modifier", "always" or "off". The pure window/drop accepts a floating window unless this is "off". |
one of the three |
drag.crossWorkspace |
true |
Dropping on a [data-wm-workspace-target] element moves the window to that workspace. |
no |
drag.follow |
false |
A cross-workspace drop also activates the target workspace (follow: true). |
no |
rules |
[] |
Declarative window rules, applied at window/create. See rules/set and Queries › Rules. |
validRules |
urgency.clearOnFocus |
true |
Focusing an urgent window clears its hint. | boolean |
snap.edges |
true |
While dragging a floating window, approaching a stage edge or corner previews a half, quarter or maximize placement and applies it on release. | boolean |
snap.threshold |
16 |
How close in px the pointer must be to a stage edge or corner. | ≥ 0 |
snap.magnet |
8 |
During a floating move or resize, edges within this many px snap onto other visible windows’ edges and the stage. 0 disables magnetism. |
≥ 0 |
snap.zones |
"halves-quarters" |
"halves-quarters", "halves" (edges only), "quarters" (corners only) or "off". |
one of the four |
Keys config/set does not validate are stored as given. Unknown keys are kept (and serialized) but ignored by the library.
Constructors
Section titled “Constructors”createState(options?)
Section titled “createState(options?)”createState({ workspaces = ["main"], layout, config = {} } = {}) → Stateworkspaces: workspace ids, or{ id, layout?, windows? }objects. At least one is required. ThrowsTypeErroron an empty list.layout: the default layout spec for workspaces given by id (and for objects without their ownlayout). When omitted,createWorkspace’s default applies ({ type: "master-stack", ratio: 0.5 }).config: overrides forDEFAULT_CONFIG.drag,urgencyandsnapmerge one level deep; every other key replaces.
All workspaces go on one output, DEFAULT_OUTPUT, and the first workspace is active. Create more outputs with output/create.
createWorkspace({ id, layout?, windows?, output? })
Section titled “createWorkspace({ id, layout?, windows?, output? })”Returns a workspace record. Defaults: layout = { type: "master-stack", ratio: 0.5 }, windows = [] (copied), output = DEFAULT_OUTPUT. It does not add the workspace to any state. Use workspace/create or output/create for that.
createOutput({ id, workspaces?, activeWorkspace? })
Section titled “createOutput({ id, workspaces?, activeWorkspace? })”Returns an output record. activeWorkspace defaults to the first workspace. It does not add the output to any state.
createWindowRecord(description, state)
Section titled “createWindowRecord(description, state)”Normalizes a window/create payload into a window record using state.config.defaultPlacement, the parent’s workspace and state.activeWorkspace. It applies the role defaults above, but not rules. update uses it internally. It is exported for tools that want to preview a record.