vintage-frames 0.20.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2563,7 +2563,7 @@
2563
2563
  "declarations": [
2564
2564
  {
2565
2565
  "kind": "class",
2566
- "description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. A press on a title bar with ⌘ held\nraises nothing, so a background window is dragged where it stands in the\nstack. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the screen-corner mask is in front of it.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\n**Hidden windows.** A window with `hidden` — closed by a page that hides\nits windows rather than removing them, through `vf-window.hide()` or the\nattribute itself — never holds the active state. One slotted hidden waits\nto be shown; `bringToFront()` on one gives it its place in the stack and\nactivates nothing; the holder hiding hands the state to the topmost shown\ndocument window, or to none. A window shown into a tier where none is\nactive, unless the tier was deliberately deactivated, takes it.\nstackingOrder lists the windows as they stand, bottom-most first.\n\n**The work area.** A slotted `vf-menu-bar` along the top takes its band\noff the screen: workArea is what is left, and the gestures on the\nscreen stay in it — a window dragged up stops with its title bar under the\nbar, where it can still be grabbed, and an icon dropped, nudged or walked\non the desktop lands below the bar. `window-top` deepens the area for\nwindows alone (windowArea), for a page with a strip of its own\nunder the bar. A gesture asks for the box it is clamped into with\n`vf-placement-bounds-request` (bubbles, non-composed), the drag surface's\nhandshake, and the desktop answers for the hosts placed on its screen.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, a repeating tile drawn at the display's resolution, 1-bit at every\ndensity and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. A window asks the same way and draws\nthere with the same pen: its zoom rects when it opens from a rect\n(`vf-window.show({ from })`), its outline under an `outline-drag`. Not the\ntop layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).",
2566
+ "description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. A press on a title bar with ⌘ held\nraises nothing, so a background window is dragged where it stands in the\nstack. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the drag outline, the full-screen tier and the screen-corner\nmask are in front of it.\n\n**The full-screen tier.** fullScreenWindow puts one slotted window\nabove everything else the screen shows — both window tiers, the menu bar\nand its dropped menus, the drag outline and the rects — and under the\ncorner mask alone: the tier a window takes the whole screen on. The page\nsets it and clears it, and sizes, draws and activates the window itself\n(`vf-window`'s `frameless`, the screen's `width` and `height`,\nbringToFront); cleared, the window goes back to its tier where it\nstood.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\n**Hidden windows.** A window with `hidden` — closed by a page that hides\nits windows rather than removing them, through `vf-window.hide()` or the\nattribute itself — never holds the active state. One slotted hidden waits\nto be shown; `bringToFront()` on one gives it its place in the stack and\nactivates nothing; the holder hiding hands the state to the topmost shown\ndocument window, or to none. A window shown into a tier where none is\nactive, unless the tier was deliberately deactivated, takes it.\nstackingOrder lists the windows as they stand, bottom-most first.\n\n**The work area.** A slotted `vf-menu-bar` along the top takes its band\noff the screen: workArea is what is left, and the gestures on the\nscreen stay in it — a window dragged up stops with its title bar under the\nbar, where it can still be grabbed, and an icon dropped, nudged or walked\non the desktop lands below the bar. `window-top` deepens the area for\nwindows alone (windowArea), for a page with a strip of its own\nunder the bar. A gesture asks for the box it is clamped into with\n`vf-placement-bounds-request` (bubbles, non-composed), the drag surface's\nhandshake, and the desktop answers for the hosts placed on its screen.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, a repeating tile drawn at the display's resolution, 1-bit at every\ndensity and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. A window asks the same way and draws\nthere with the same pen: its zoom rects when it opens from a rect\n(`vf-window.show({ from })`), its outline under an `outline-drag`. Not the\ntop layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).",
2567
2567
  "name": "VfDesktop",
2568
2568
  "cssProperties": [
2569
2569
  {
@@ -2933,7 +2933,7 @@
2933
2933
  "type": {
2934
2934
  "text": "HTMLElement[]"
2935
2935
  },
2936
- "description": "The slotted windows in stacking order, bottom-most first: the document\ntier, then the floating tier above it, each in the order its windows\nwere last raised. Hidden windows are included where they stand. This is\nthe order the eye sees; the windows' light-DOM order catches up with it\nonly once a gesture ends, and a raise by keyboard focus moves no node.",
2936
+ "description": "The slotted windows in stacking order, bottom-most first: the document\ntier, then the floating tier above it, each in the order its windows\nwere last raised, and the window on the full-screen tier last. Hidden\nwindows are included where they stand. This is the order the eye sees;\nthe windows' light-DOM order catches up with it only once a gesture\nends, a raise by keyboard focus moves no node, and the full-screen\nwindow keeps the light-DOM place of where it stood.",
2937
2937
  "readonly": true
2938
2938
  },
2939
2939
  {
@@ -2946,6 +2946,45 @@
2946
2946
  },
2947
2947
  "description": "Deactivate the whole document tier — clear `active` from every document\nwindow, leaving none the holder. Pages route \"the user clicked the\nFinder\" presses — on the bare dither, on their own desktop furniture\n(an icon layer) — through here; the deactivated state holds until a\ndocument window is pressed, focused, brought to front, or newly\nslotted."
2948
2948
  },
2949
+ {
2950
+ "kind": "field",
2951
+ "name": "fullScreenWindow",
2952
+ "type": {
2953
+ "text": "HTMLElement | null"
2954
+ },
2955
+ "description": "The window on the full-screen tier, or null. A slotted window set here\nstacks above everything else the screen shows — both window tiers, the\nmenu bar and any dropped menu, the drag outline and the window rects —\nand under the corner mask alone, so the screen keeps its rounded\ncorners: the tier for a window that takes the whole screen. Sizing it to\nthe screen and drawing it without chrome (`vf-window`'s `frameless`) are\nthe page's, and so is bringing it forward: setting this activates\nnothing. One window holds the tier at a time: setting another puts the\nfirst back, and null puts the holder back in its own tier where it\nstood — a raise meanwhile moves that place, as it would have moved the\nwindow. A holder that leaves the desktop gives the tier up. Anything but\na window slotted here warns and changes nothing."
2956
+ },
2957
+ {
2958
+ "kind": "field",
2959
+ "name": "#screenHolder",
2960
+ "privacy": "private",
2961
+ "type": {
2962
+ "text": "HTMLElement | null"
2963
+ },
2964
+ "default": "null",
2965
+ "description": "The window on the full-screen tier, or null (see fullScreenWindow)."
2966
+ },
2967
+ {
2968
+ "kind": "field",
2969
+ "name": "#screenPlace",
2970
+ "privacy": "private",
2971
+ "type": {
2972
+ "text": "string"
2973
+ },
2974
+ "default": "''",
2975
+ "description": "The holder's z-index in its own tier: the place it goes back to."
2976
+ },
2977
+ {
2978
+ "kind": "method",
2979
+ "name": "#leaveScreenTier",
2980
+ "privacy": "private",
2981
+ "return": {
2982
+ "type": {
2983
+ "text": "void"
2984
+ }
2985
+ },
2986
+ "description": "The holder back in its own tier, where it stood."
2987
+ },
2949
2988
  {
2950
2989
  "kind": "method",
2951
2990
  "name": "_restack",
@@ -2963,7 +3002,7 @@
2963
3002
  }
2964
3003
  }
2965
3004
  ],
2966
- "description": "The z/active half of a raise, shared by every path. A hidden window\ntakes its place in the stack and nothing else: it activates when it is\nshown and raised."
3005
+ "description": "The z/active half of a raise, shared by every path. A hidden window\ntakes its place in the stack and nothing else: it activates when it is\nshown and raised. The full-screen window stays on its tier, and the\nraise moves the place it goes back to."
2967
3006
  },
2968
3007
  {
2969
3008
  "kind": "method",
@@ -3182,7 +3221,7 @@
3182
3221
  "text": "void"
3183
3222
  }
3184
3223
  },
3185
- "description": "Re-order the slotted windows in the light DOM to match their z-order\n(bottom-most first), so sequential focus navigation walks the stack the\nway the eye does — and Shift+Tab is its exact mirror. The utility band\nsorts the floating tier after every document window by construction.\n\nMinimal-move: windows already in relative order are never touched (the\ncommon case — after one raise, one window moves). Non-window siblings\n(a menu bar, page content) keep their positions; only a window that must\ncross the stack moves past them. `moveBefore()` keeps the focus inside a\nmoved window where the browser has it (_move); re-inserting a\nnode containing the focused element drops focus to `<body>`, so it is\nrestored afterwards — behind `_restoringFocus`, because the restore\nre-fires focusin (see _onFocusIn) on an element that may sit in a\nbackground window.\nReached only through _requestDomSync's task — after a pointer\ngesture or a programmatic raise, never from a focus-driven one (see\n_raise for why)."
3224
+ "description": "Re-order the slotted windows in the light DOM to match their z-order\n(bottom-most first), so sequential focus navigation walks the stack the\nway the eye does — and Shift+Tab is its exact mirror. The utility band\nsorts the floating tier after every document window by construction.\n\nMinimal-move: windows already in relative order are never touched (the\ncommon case — after one raise, one window moves). Non-window siblings\n(a menu bar, page content) keep their positions; only a window that must\ncross the stack moves past them. `moveBefore()` keeps the focus inside a\nmoved window where the browser has it (_move); re-inserting a\nnode containing the focused element drops focus to `<body>`, so it is\nrestored afterwards — behind `_restoringFocus`, because the restore\nre-fires focusin (see _onFocusIn) on an element that may sit in a\nbackground window. The full-screen window is ordered by the place it\ngoes back to, so taking and leaving the tier move no node.\nReached only through _requestDomSync's task — after a pointer\ngesture or a programmatic raise, never from a focus-driven one (see\n_raise for why)."
3186
3225
  },
3187
3226
  {
3188
3227
  "kind": "method",
@@ -21144,7 +21183,7 @@
21144
21183
  "declarations": [
21145
21184
  {
21146
21185
  "kind": "class",
21147
- "description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" closable movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n\n**Opening from a rect.** show clears `hidden`, and given `from` — a\nbox in viewport CSS px, an icon's `cellRect()` or a button's rect — opens\nthe window from it: dotted rectangles grow from that box toward the\nwindow's frame, four on screen at a time, bunched at the box and opening\nout toward the window, drawn with the icon drag outline's XOR pen on the\ndesktop's drag surface; the window draws once they have drained, eighteen\nsteps later. Until then it matches `:state(opening)` and paints nothing —\nstill laid out, focusable and a hit target, so a page can place it, raise\nit and focus into it at once, and a press finishes the rects and lands on\nthe window. Under `prefers-reduced-motion`, with no `vf-desktop` on its\npath, or without `from`, the window shows at once.\n\n**Closing to a rect.** hide sets `hidden`, and given `to` closes\nthe window to it: the same rectangles run the other way, from where the\nframe was in toward the box, the window gone from the first step. A press\nfinishes them, and removing the window leaves them running. Under\n`prefers-reduced-motion`, with no `vf-desktop` on its path, or without\n`to`, the window hides at once. The window knows nothing about icons:\n`from` and `to` are any rect.\n\n**Dragging as an outline.** A `movable` window moves live under the\npointer. With `outline-drag` a press on the title bar draws a dotted\noutline around the window on the desktop's drag surface instead, with the\nsame pen; the drag moves the outline, held where the window can land, and\nthe window moves there on the release. Escape cancels.",
21186
+ "description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" closable movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n\n**Opening from a rect.** show clears `hidden`, and given `from` — a\nbox in viewport CSS px, an icon's `cellRect()` or a button's rect — opens\nthe window from it: dotted rectangles grow from that box toward the\nwindow's frame, four on screen at a time, bunched at the box and opening\nout toward the window, drawn with the icon drag outline's XOR pen on the\ndesktop's drag surface; the window draws once they have drained, eighteen\nsteps later. Until then it matches `:state(opening)` and paints nothing —\nstill laid out, focusable and a hit target, so a page can place it, raise\nit and focus into it at once, and a press finishes the rects and lands on\nthe window. Under `prefers-reduced-motion`, with no `vf-desktop` on its\npath, or without `from`, the window shows at once.\n\n**Closing to a rect.** hide sets `hidden`, and given `to` closes\nthe window to it: the same rectangles run the other way, from where the\nframe was in toward the box, the window gone from the first step. A press\nfinishes them, and removing the window leaves them running. Under\n`prefers-reduced-motion`, with no `vf-desktop` on its path, or without\n`to`, the window hides at once. The window knows nothing about icons:\n`from` and `to` are any rect.\n\n**Dragging as an outline.** A `movable` window moves live under the\npointer. With `outline-drag` a press on the title bar draws a dotted\noutline around the window on the desktop's drag surface instead, with the\nsame pen; the drag moves the outline, held where the window can land, and\nthe window moves there on the release. Escape cancels.\n\n**Frameless.** With `frameless` the window draws no chrome — no title bar\nor widgets, no frame or shadow, no grow box, no header or status strip —\nand the body is the whole box: a window that takes the whole screen, say,\non `vf-desktop`'s full-screen tier. The chrome is hidden, not torn down, so\nthe switch moves no node and the focus in the body stays where it is.",
21148
21187
  "name": "VfWindow",
21149
21188
  "cssProperties": [
21150
21189
  {
@@ -21238,6 +21277,17 @@
21238
21277
  "attribute": "variant",
21239
21278
  "reflects": true
21240
21279
  },
21280
+ {
21281
+ "kind": "field",
21282
+ "name": "frameless",
21283
+ "type": {
21284
+ "text": "boolean"
21285
+ },
21286
+ "default": "false",
21287
+ "description": "Draw no chrome: no title bar, so no close or zoom box, no frame or\nshadow, no grow box, and no header or status strip, whatever their slots\nhold. The body is the whole box, `pattern` filling it and a placed child\nmeasured from its corner; under `scrollbars` the built-in scroll area\nfills the box, its own frame line on the box's edge. `heading` still\nnames the window. The chrome is hidden rather than left out of the\nrender, so switching this on a connected window moves no node and the\nfocus in the body stays where it is. In a `vf-desktop` the window keeps\nits tier for activation and `hidden`, and `show()` and `hide()` run\ntheir rects from and to its box.",
21288
+ "attribute": "frameless",
21289
+ "reflects": true
21290
+ },
21241
21291
  {
21242
21292
  "kind": "field",
21243
21293
  "name": "heading",
@@ -22154,6 +22204,15 @@
22154
22204
  "description": "Chrome variant. Omit for the standard 18px striped bar; `'utility'` for\nthe slim windoid bar (dot-grid dither, 7×7 widgets, no title patch — the\nheading still names the widgets). Inside a `vf-desktop`, utility windows\nfloat above the document tier and never take the single-active state.",
22155
22205
  "fieldName": "variant"
22156
22206
  },
22207
+ {
22208
+ "name": "frameless",
22209
+ "type": {
22210
+ "text": "boolean"
22211
+ },
22212
+ "default": "false",
22213
+ "description": "Draw no chrome: no title bar, so no close or zoom box, no frame or\nshadow, no grow box, and no header or status strip, whatever their slots\nhold. The body is the whole box, `pattern` filling it and a placed child\nmeasured from its corner; under `scrollbars` the built-in scroll area\nfills the box, its own frame line on the box's edge. `heading` still\nnames the window. The chrome is hidden rather than left out of the\nrender, so switching this on a connected window moves no node and the\nfocus in the body stays where it is. In a `vf-desktop` the window keeps\nits tier for activation and `hidden`, and `show()` and `hide()` run\ntheir rects from and to its box.",
22214
+ "fieldName": "frameless"
22215
+ },
22157
22216
  {
22158
22217
  "name": "heading",
22159
22218
  "type": {
@@ -22,7 +22,17 @@ declare const VfDesktop_base: (new (...args: any[]) => import("../position.js").
22
22
  * System 7's floating windoids behaved while their application was frontmost.
23
23
  * A slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier
24
24
  * above both, so its dropped menus cover palettes and document windows
25
- * alike; only the screen-corner mask is in front of it.
25
+ * alike; only the drag outline, the full-screen tier and the screen-corner
26
+ * mask are in front of it.
27
+ *
28
+ * **The full-screen tier.** {@link fullScreenWindow} puts one slotted window
29
+ * above everything else the screen shows — both window tiers, the menu bar
30
+ * and its dropped menus, the drag outline and the rects — and under the
31
+ * corner mask alone: the tier a window takes the whole screen on. The page
32
+ * sets it and clears it, and sizes, draws and activates the window itself
33
+ * (`vf-window`'s `frameless`, the screen's `width` and `height`,
34
+ * {@link bringToFront}); cleared, the window goes back to its tier where it
35
+ * stood.
26
36
  *
27
37
  * **Deactivation.** On a real System 7 machine clicking the desktop clicked
28
38
  * the *Finder* — the frontmost application's windows lost their stripes.
@@ -310,9 +320,11 @@ export declare class VfDesktop extends VfDesktop_base {
310
320
  /**
311
321
  * The slotted windows in stacking order, bottom-most first: the document
312
322
  * tier, then the floating tier above it, each in the order its windows
313
- * were last raised. Hidden windows are included where they stand. This is
314
- * the order the eye sees; the windows' light-DOM order catches up with it
315
- * only once a gesture ends, and a raise by keyboard focus moves no node.
323
+ * were last raised, and the window on the full-screen tier last. Hidden
324
+ * windows are included where they stand. This is the order the eye sees;
325
+ * the windows' light-DOM order catches up with it only once a gesture
326
+ * ends, a raise by keyboard focus moves no node, and the full-screen
327
+ * window keeps the light-DOM place of where it stood.
316
328
  */
317
329
  get stackingOrder(): HTMLElement[];
318
330
  /**
@@ -324,10 +336,27 @@ export declare class VfDesktop extends VfDesktop_base {
324
336
  * slotted.
325
337
  */
326
338
  clearActive(): void;
339
+ /**
340
+ * The window on the full-screen tier, or null. A slotted window set here
341
+ * stacks above everything else the screen shows — both window tiers, the
342
+ * menu bar and any dropped menu, the drag outline and the window rects —
343
+ * and under the corner mask alone, so the screen keeps its rounded
344
+ * corners: the tier for a window that takes the whole screen. Sizing it to
345
+ * the screen and drawing it without chrome (`vf-window`'s `frameless`) are
346
+ * the page's, and so is bringing it forward: setting this activates
347
+ * nothing. One window holds the tier at a time: setting another puts the
348
+ * first back, and null puts the holder back in its own tier where it
349
+ * stood — a raise meanwhile moves that place, as it would have moved the
350
+ * window. A holder that leaves the desktop gives the tier up. Anything but
351
+ * a window slotted here warns and changes nothing.
352
+ */
353
+ get fullScreenWindow(): HTMLElement | null;
354
+ set fullScreenWindow(win: HTMLElement | null);
327
355
  /**
328
356
  * The z/active half of a raise, shared by every path. A hidden window
329
357
  * takes its place in the stack and nothing else: it activates when it is
330
- * shown and raised.
358
+ * shown and raised. The full-screen window stays on its tier, and the
359
+ * raise moves the place it goes back to.
331
360
  */
332
361
  private _restack;
333
362
  /**
@@ -466,7 +495,8 @@ export declare class VfDesktop extends VfDesktop_base {
466
495
  * node containing the focused element drops focus to `<body>`, so it is
467
496
  * restored afterwards — behind `_restoringFocus`, because the restore
468
497
  * re-fires focusin (see {@link _onFocusIn}) on an element that may sit in a
469
- * background window.
498
+ * background window. The full-screen window is ordered by the place it
499
+ * goes back to, so taking and leaving the tier move no node.
470
500
  * Reached only through {@link _requestDomSync}'s task — after a pointer
471
501
  * gesture or a programmatic raise, never from a focus-driven one (see
472
502
  * {@link _raise} for why).
@@ -14,9 +14,9 @@ import { SCREEN_CORNER as C, steppedCornerClip as w } from "../pixel-frame.js";
14
14
  import { LitElement as T, css as E, html as D, unsafeCSS as O } from "lit";
15
15
  import { property as k, query as A, queryAssignedElements as j } from "lit/decorators.js";
16
16
  //#region src/components/vf-desktop.ts
17
- var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L], "#000000", "#ffffff"), z = u(R.width, R.height, d(R.rects)), B = f(R.width), V = (e) => Number(e.style.zIndex) || 0, H = (e) => e.composedPath().some((e) => e instanceof Element && e.classList.contains("vf-title-bar")), U = class extends s(T) {
17
+ var M = 1e6, N = 2 * M, P = 2000001, F = 3e6, I = 512, L = 342, R = "gray-50", z = v(g[R], "#000000", "#ffffff"), B = u(z.width, z.height, d(z.rects)), V = f(z.width), H = (e) => Number(e.style.zIndex) || 0, U = (e) => e.composedPath().some((e) => e instanceof Element && e.classList.contains("vf-title-bar")), W = class extends s(T) {
18
18
  constructor(...e) {
19
- super(...e), this.width = F, this.height = I, this.bezel = 0, this.pattern = L, this.scale = new n(this), this.gridSnap = new x(this), this._token = "", this._desktopPattern = g[L], this.#e = !1, this.patternFill = new y(this, {
19
+ super(...e), this.width = I, this.height = L, this.bezel = 0, this.pattern = R, this.scale = new n(this), this.gridSnap = new x(this), this._token = "", this._desktopPattern = g[R], this.#e = !1, this.patternFill = new y(this, {
20
20
  getBox: () => this.screen,
21
21
  getPattern: () => this._token ? null : this._desktopPattern
22
22
  }), this._zCounter = 0, this._activeWindow = null, this._deactivated = !1, this._awaitingUpgrade = !1, this.#t = new MutationObserver(() => this._resolveActive()), this._pointerGesture = !1, this._restoringFocus = !1, this._domSyncTimer = 0, this._gestureEnd = new S(this, () => [[
@@ -29,10 +29,10 @@ var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L]
29
29
  "pointercancel",
30
30
  this._onGestureEnd,
31
31
  !0
32
- ]]), this._onPointerDown = (e) => {
32
+ ]]), this.#n = null, this.#r = "", this._onPointerDown = (e) => {
33
33
  this._pointerGesture || (this._pointerGesture = !0, this._gestureEnd.attach());
34
34
  let t = this._windowFromEvent(e);
35
- t && e.metaKey && H(e) || t && this._raise(t);
35
+ t && e.metaKey && U(e) || t && this._raise(t);
36
36
  }, this._onGestureEnd = () => {
37
37
  this._gestureEnd.detach(), this._pointerGesture = !1, this._requestDomSync();
38
38
  }, this._onDragSurfaceRequest = (e) => {
@@ -119,7 +119,7 @@ var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L]
119
119
  position: absolute;
120
120
  inset: 0;
121
121
  z-index: -1;
122
- --_vf-tile-image: var(--vf-desktop-pattern, ${O(z)});
122
+ --_vf-tile-image: var(--vf-desktop-pattern, ${O(B)});
123
123
  /* Forced colors: hidden with the pattern, same posture. */
124
124
  @media (forced-colors: active) {
125
125
  display: none;
@@ -208,7 +208,7 @@ var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L]
208
208
  ];
209
209
  }
210
210
  get workArea() {
211
- let e = this.width ?? F, t = this.height ?? I, n = Math.min(t, this._menuBarFloor());
211
+ let e = this.width ?? I, t = this.height ?? L, n = Math.min(t, this._menuBarFloor());
212
212
  return {
213
213
  left: 0,
214
214
  top: n,
@@ -252,14 +252,32 @@ var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L]
252
252
  return this._activeWindow;
253
253
  }
254
254
  get stackingOrder() {
255
- return [...this._windows].sort((e, t) => V(e) - V(t));
255
+ return [...this._windows].sort((e, t) => H(e) - H(t));
256
256
  }
257
257
  clearActive() {
258
258
  this._deactivated = !0, this._setActive(null);
259
259
  }
260
+ get fullScreenWindow() {
261
+ return this.#n;
262
+ }
263
+ set fullScreenWindow(e) {
264
+ if (e !== this.#n) {
265
+ if (e && !this._windows.includes(e)) {
266
+ console.warn("vf-desktop: fullScreenWindow takes a window slotted in this desktop, or null.", e);
267
+ return;
268
+ }
269
+ this.#i(), e && (this.#n = e, this.#r = e.style.zIndex, e.style.zIndex = String(F));
270
+ }
271
+ }
272
+ #n;
273
+ #r;
274
+ #i() {
275
+ let e = this.#n;
276
+ e && (this.#n = null, e.style.zIndex = this.#r);
277
+ }
260
278
  _restack(e) {
261
- let t = this._isUtility(e);
262
- e.style.zIndex = String(++this._zCounter + (t ? M : 0)), !t && !e.hidden && this._setActive(e);
279
+ let t = this._isUtility(e), n = String(++this._zCounter + (t ? M : 0));
280
+ e === this.#n ? this.#r = n : e.style.zIndex = n, !t && !e.hidden && this._setActive(e);
263
281
  }
264
282
  _requestDomSync() {
265
283
  this._pointerGesture || this._domSyncTimer || (this._domSyncTimer = window.setTimeout(() => {
@@ -286,7 +304,7 @@ var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L]
286
304
  }
287
305
  _onSlotChange() {
288
306
  let e = this._windows;
289
- this._observeWindows();
307
+ this.#n && !e.includes(this.#n) && this.#i(), this._observeWindows();
290
308
  let t = null;
291
309
  for (let n of e) if (!n.style.zIndex) {
292
310
  let e = this._isUtility(n);
@@ -324,7 +342,7 @@ var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L]
324
342
  _topmost(e) {
325
343
  let t = null, n = -Infinity;
326
344
  for (let r of e) {
327
- let e = V(r);
345
+ let e = H(r);
328
346
  e >= n && (n = e, t = r);
329
347
  }
330
348
  return t;
@@ -338,9 +356,9 @@ var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L]
338
356
  if (!this.isConnected) return;
339
357
  let e = this._windows;
340
358
  if (e.length < 2) return;
341
- let t = [...e].sort((e, t) => V(e) - V(t)), n = this._deepActiveElement(), r = !1, i = null;
342
- for (let e of t) i && i.compareDocumentPosition(e) & Node.DOCUMENT_POSITION_PRECEDING && (this._move(e, i.nextSibling), r = !0), i = e;
343
- r && n instanceof HTMLElement && n.isConnected && this._deepActiveElement() !== n && (this._restoringFocus = !0, n.focus({ preventScroll: !0 }), this._restoringFocus = !1);
359
+ let t = (e) => e === this.#n ? Number(this.#r) || 0 : H(e), n = [...e].sort((e, n) => t(e) - t(n)), r = this._deepActiveElement(), i = !1, a = null;
360
+ for (let e of n) a && a.compareDocumentPosition(e) & Node.DOCUMENT_POSITION_PRECEDING && (this._move(e, a.nextSibling), i = !0), a = e;
361
+ i && r instanceof HTMLElement && r.isConnected && this._deepActiveElement() !== r && (this._restoringFocus = !0, r.focus({ preventScroll: !0 }), this._restoringFocus = !1);
344
362
  }
345
363
  _move(e, t) {
346
364
  let n = this;
@@ -353,21 +371,21 @@ var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L]
353
371
  willUpdate(e) {
354
372
  if (super.willUpdate(e), this._token = p(this, "--vf-desktop-pattern"), e.has("pattern")) {
355
373
  let e = _(this.pattern);
356
- e === null && this.pattern?.trim() && !this.#e && (this.#e = !0, console.warn(`vf-desktop: unknown pattern "${this.pattern}" — a library name (docs/PATTERNS.md) or sixteen hex digits. Keeping ${L}.`)), this._desktopPattern = e ?? g[L];
374
+ e === null && this.pattern?.trim() && !this.#e && (this.#e = !0, console.warn(`vf-desktop: unknown pattern "${this.pattern}" — a library name (docs/PATTERNS.md) or sixteen hex digits. Keeping ${R}.`)), this._desktopPattern = e ?? g[R];
357
375
  }
358
376
  }
359
377
  updated(e) {
360
378
  if (e.has("bezel") && (this.bezel > 0 ? this.style.setProperty("--vf-desktop-bezel", i(this.bezel)) : this.style.removeProperty("--vf-desktop-bezel")), e.has("width") || e.has("height") || e.has("bezel")) {
361
- let e = this.width ?? F, t = this.height ?? I;
379
+ let e = this.width ?? I, t = this.height ?? L;
362
380
  this.style.width = i(e + 2 * this.bezel), this.style.height = i(t + 2 * this.bezel);
363
381
  }
364
382
  }
365
383
  render() {
366
- let e = this.width ?? F, t = this.height ?? I, n = this._token ? D`<div class="vf-tile-grid">
384
+ let e = this.width ?? I, t = this.height ?? L, n = this._token ? D`<div class="vf-tile-grid">
367
385
  ${m({
368
- cols: Math.ceil(e / B),
369
- rows: Math.ceil(t / B),
370
- tile: B
386
+ cols: Math.ceil(e / V),
387
+ rows: Math.ceil(t / V),
388
+ tile: V
371
389
  })}
372
390
  </div>` : null;
373
391
  return D`
@@ -388,9 +406,9 @@ var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L]
388
406
  `;
389
407
  }
390
408
  };
391
- o([k({ type: Number })], U.prototype, "width", void 0), o([k({ type: Number })], U.prototype, "height", void 0), o([k({ type: Number })], U.prototype, "bezel", void 0), o([k({ reflect: !0 })], U.prototype, "pattern", void 0), o([k({
409
+ o([k({ type: Number })], W.prototype, "width", void 0), o([k({ type: Number })], W.prototype, "height", void 0), o([k({ type: Number })], W.prototype, "bezel", void 0), o([k({ reflect: !0 })], W.prototype, "pattern", void 0), o([k({
392
410
  type: Number,
393
411
  attribute: "window-top"
394
- })], U.prototype, "windowTop", void 0), o([j({ selector: "vf-window" })], U.prototype, "_windows", void 0), o([A(".screen")], U.prototype, "screen", void 0), o([A(".drag-surface")], U.prototype, "dragSurface", void 0), U = o([t("vf-desktop")], U);
412
+ })], W.prototype, "windowTop", void 0), o([j({ selector: "vf-window" })], W.prototype, "_windows", void 0), o([A(".screen")], W.prototype, "screen", void 0), o([A(".drag-surface")], W.prototype, "dragSurface", void 0), W = o([t("vf-desktop")], W);
395
413
  //#endregion
396
- export { U as VfDesktop };
414
+ export { W as VfDesktop };
@@ -80,6 +80,12 @@ declare const VfWindow_base: (new (...args: any[]) => import("../size.js").VfSiz
80
80
  * same pen; the drag moves the outline, held where the window can land, and
81
81
  * the window moves there on the release. Escape cancels.
82
82
  *
83
+ * **Frameless.** With `frameless` the window draws no chrome — no title bar
84
+ * or widgets, no frame or shadow, no grow box, no header or status strip —
85
+ * and the body is the whole box: a window that takes the whole screen, say,
86
+ * on `vf-desktop`'s full-screen tier. The chrome is hidden, not torn down, so
87
+ * the switch moves no node and the focus in the body stays where it is.
88
+ *
83
89
  * @slot - Default slot: window body content.
84
90
  * @slot header - Optional header content — a band between the title bar and
85
91
  * the body, the full width of the window (the Finder window's header
@@ -141,6 +147,19 @@ export declare class VfWindow extends VfWindow_base {
141
147
  * float above the document tier and never take the single-active state.
142
148
  */
143
149
  variant?: 'utility';
150
+ /**
151
+ * Draw no chrome: no title bar, so no close or zoom box, no frame or
152
+ * shadow, no grow box, and no header or status strip, whatever their slots
153
+ * hold. The body is the whole box, `pattern` filling it and a placed child
154
+ * measured from its corner; under `scrollbars` the built-in scroll area
155
+ * fills the box, its own frame line on the box's edge. `heading` still
156
+ * names the window. The chrome is hidden rather than left out of the
157
+ * render, so switching this on a connected window moves no node and the
158
+ * focus in the body stays where it is. In a `vf-desktop` the window keeps
159
+ * its tier for activation and `hidden`, and `show()` and `hide()` run
160
+ * their rects from and to its box.
161
+ */
162
+ frameless: boolean;
144
163
  /** Title text shown centered in the title bar. */
145
164
  heading: string;
146
165
  /**
@@ -32,7 +32,7 @@ function Z(e, t, n) {
32
32
  }
33
33
  var Q = 24, $ = class extends j(l(H)) {
34
34
  constructor(...e) {
35
- super(...e), this.heading = "", this.active = !0, this.closable = !1, this.zoomable = !1, this.movable = !1, this.outlineDrag = !1, this.resizable = !1, this._pattern = null, this.#e = !1, this._hasHeader = !1, this._hasStatus = !1, this.scale = new n(this), this.gridSnap = new k(this), this.titleCenter = new re(this), this._dotsPattern = "", this.#t = new p(this, {
35
+ super(...e), this.frameless = !1, this.heading = "", this.active = !0, this.closable = !1, this.zoomable = !1, this.movable = !1, this.outlineDrag = !1, this.resizable = !1, this._pattern = null, this.#e = !1, this._hasHeader = !1, this._hasStatus = !1, this.scale = new n(this), this.gridSnap = new k(this), this.titleCenter = new re(this), this._dotsPattern = "", this.#t = new p(this, {
36
36
  getBox: () => this.renderRoot?.querySelector(".vf-dots"),
37
37
  getArt: () => this.variant === "utility" && !this._dotsPattern ? _ : null,
38
38
  property: "--_vf-dots-tile"
@@ -214,6 +214,33 @@ var Q = 24, $ = class extends j(l(H)) {
214
214
  height: calc(var(--vf-scale, 1) * 3px);
215
215
  }
216
216
 
217
+ /* --- Frameless ------------------------------------------------- */
218
+ /* No chrome: the frame's stroke and shadow go, and the bars and the
219
+ grow box with them, so the body is the whole box. Hidden rather than
220
+ left out of the render, so switching moves no node and the focus in
221
+ the body stays; the title patch stays in the tree to name the window
222
+ (AccName reads a hidden aria-labelledby target, as the utility
223
+ variant's does). The border is the stroke's forced-colors form. */
224
+ :host([frameless]) .vf-frame {
225
+ padding: 0;
226
+ border: 0;
227
+ box-shadow: none;
228
+ }
229
+ :host([frameless]) .vf-title-bar,
230
+ :host([frameless]) .header,
231
+ :host([frameless]) .status,
232
+ :host([frameless]) .grow {
233
+ display: none;
234
+ }
235
+ /* The edge rails' overhang lands the scroll area's frame on the
236
+ window's; with no frame to land on, the area fills the box instead,
237
+ its own frame line on the box's edge. */
238
+ :host([frameless]) .edge-scroll {
239
+ width: 100%;
240
+ height: 100%;
241
+ margin: 0;
242
+ }
243
+
217
244
  /* --- Body ------------------------------------------------------ */
218
245
  /* Clipped at the frame, the way the classic content region was: the
219
246
  window is a fixed box (width and height are both declared), so content
@@ -636,7 +663,7 @@ var Q = 24, $ = class extends j(l(H)) {
636
663
  <vf-scroll-area
637
664
  class="edge-scroll"
638
665
  axis=${this.scrollbars}
639
- ?corner=${this.resizable && !this._hasStatus}
666
+ ?corner=${this.resizable && !this._hasStatus && !this.frameless}
640
667
  label=${this.heading || G}
641
668
  pattern=${this._pattern ? this.pattern : G}
642
669
  exportparts="viewport"
@@ -663,7 +690,10 @@ var Q = 24, $ = class extends j(l(H)) {
663
690
  `;
664
691
  }
665
692
  };
666
- s([K({ reflect: !0 })], $.prototype, "variant", void 0), s([K()], $.prototype, "heading", void 0), s([K({
693
+ s([K({ reflect: !0 })], $.prototype, "variant", void 0), s([K({
694
+ type: Boolean,
695
+ reflect: !0
696
+ })], $.prototype, "frameless", void 0), s([K()], $.prototype, "heading", void 0), s([K({
667
697
  type: Boolean,
668
698
  reflect: !0
669
699
  })], $.prototype, "active", void 0), s([K({
package/docs/SPEC.md CHANGED
@@ -197,13 +197,14 @@ Every recipe also declares its size — `width`, and `height` where the window i
197
197
  #### `vf-desktop` (`VfDesktop`, vf-desktop.ts)
198
198
  Full-bleed classic desktop container.
199
199
  - **Attributes/props:** `width`, `height: number` (system px, defaults 512×342 — the compact Mac's raster) — the screen's own size, the way a WIND resource declared a window's. The host box renders at exactly `width + 2·bezel` per axis (a live `sysLength` written as host inline size on every update), always a whole number of system px. **Pure CSS sizing is not supported**: the inline size wins over any stylesheet, so the page sets the numbers — directly or via `fitWithin` — and positions the explicitly sized desktop with its own CSS, keeping any sub-system-pixel slack in the page's own layout. `bezel: number` (system px, default 0) — the black screen surround, the CRT's unlit margin between raster and case, added onto the declared screen on every side. The screen owns flow, an absolute window's containing block and the drag clip, so a dragged window crops at the raster's edge. Its four corners wear the `SCREEN_CORNER` mask (the bottom pair mirrored vertically — `steppedCornerClip`'s `edge`), painted above everything like the hardware mask — the top pair lands over a slotted menu bar's corners, so the bar needs no `rounded` of its own inside a bezeled desktop. The bezel width is written onto the host as `--vf-desktop-bezel` (self-set geometry, like `--vf-scale` — component-owned, not a theming token). `pattern: string` (default `'gray-50'`, reflected) — the desktop pattern, System 7's General Controls setting: a library name (docs/PATTERNS.md) or sixteen hex digits stating a custom 8×8; an unrecognized value warns once and keeps the dither. A `--vf-desktop-pattern` token override wins over it. It reflects so a page can observe a change on the attribute; system-online-sdk's shell saves it that way. `window-top: number` (`windowTop`, system px from the screen's top, unset by default) — the least `top` a window may take, for a page that keeps a strip of its own under the menu bar, over the windows: a window dragged on the desktop stops there and `windowArea` starts there; icons still stop at the menu bar; below the bar's bottom it changes nothing.
200
- - **Methods:** `fitWithin(maxWidth, maxHeight)` (CSS px) → sets `width`/ `height` to the largest whole-system-px raster whose host box — bezel included — fits the bound, per the current effective scale, and returns `{ width, height }`. The page's half of the sizing contract: call it on `resize` and `onScaleChange` (zoom and density moves change what a system px costs in CSS px), as a full-screen faux desktop does. `clearActive()` → deactivate the whole document tier (see *Deactivation* under Behavior). `activeWindow: HTMLElement | null` (read-only) → the active document-tier window, or `null` while the tier is deactivated or has no shown window; utility windows and hidden windows are never the holder. `stackingOrder: HTMLElement[]` (read-only) → the slotted windows in stacking order, bottom-most first — the document tier, then the floating tier, each by its last raise — hidden windows included where they stand; a fresh array per read. The light-DOM order catches up with it only after a gesture, and a raise by keyboard focus moves no node, so a page that saves window depths or picks the window behind another reads this rather than the DOM. `workArea` (read-only) → `{ left, top, width, height }` in system px from the screen's corner, bezel excluded: the screen less a slotted, shown `vf-menu-bar` along its top edge (measured at the call; the whole screen without one). `windowArea` (read-only) → the work area deepened to `window-top` where that is lower — the area windows are dragged in. `placementAt(clientX, clientY, child?)` → a viewport point as a placement on the screen, `{ left, top }` in whole system px on the placement lattice, measured from the raster's corner with the bezel excluded — the pair a child dropped onto the desktop is written with; with `child`, the pair to write to that element, its `origin` offset folded in (`placementIn`, src/position.ts; the anchor sits in shadow, which is why it is a method).
200
+ - **Methods:** `fitWithin(maxWidth, maxHeight)` (CSS px) → sets `width`/ `height` to the largest whole-system-px raster whose host box — bezel included — fits the bound, per the current effective scale, and returns `{ width, height }`. The page's half of the sizing contract: call it on `resize` and `onScaleChange` (zoom and density moves change what a system px costs in CSS px), as a full-screen faux desktop does. `clearActive()` → deactivate the whole document tier (see *Deactivation* under Behavior). `activeWindow: HTMLElement | null` (read-only) → the active document-tier window, or `null` while the tier is deactivated or has no shown window; utility windows and hidden windows are never the holder. `stackingOrder: HTMLElement[]` (read-only) → the slotted windows in stacking order, bottom-most first — the document tier, then the floating tier, each by its last raise — hidden windows included where they stand; a fresh array per read. The light-DOM order catches up with it only after a gesture, and a raise by keyboard focus moves no node, so a page that saves window depths or picks the window behind another reads this rather than the DOM. The window on the full-screen tier is listed last. `fullScreenWindow: HTMLElement | null` → the window on the full-screen tier, or `null`; set it to a slotted window to put it there, `null` to put it back (see *Full-screen tier* under Behavior). `workArea` (read-only) → `{ left, top, width, height }` in system px from the screen's corner, bezel excluded: the screen less a slotted, shown `vf-menu-bar` along its top edge (measured at the call; the whole screen without one). `windowArea` (read-only) → the work area deepened to `window-top` where that is lower — the area windows are dragged in. `placementAt(clientX, clientY, child?)` → a viewport point as a placement on the screen, `{ left, top }` in whole system px on the placement lattice, measured from the raster's corner with the bezel excluded — the pair a child dropped onto the desktop is written with; with `child`, the pair to write to that element, its `origin` offset folded in (`placementIn`, src/position.ts; the anchor sits in shadow, which is why it is a method).
201
201
  - **Visual:** `display: block; position: relative;` — the paint lives on an inner screen surface (part `desktop`, `overflow: hidden` — the whole-system-px raster, inset by `bezel` when one is set). Screen surface = the `pattern` (the classic 50% dither, `gray-50`, by default), painted as the screen's own background by the pattern fill (*Patterns* under *Tiled fills*): black ink on the fill's opaque `--vf-white` paper, a repeating tile — the authentic black-on-white dither. A consumer `--vf-desktop-pattern` renders instead as a placed tile grid on the token's 30-system-px tile with no paper, so `var(--vf-desktop, #808080)` beneath shows only under a custom tile with transparent cells (or `none`). Forced colors: flat Canvas.
202
202
  - **Slots:** default (menu bar, windows, anything).
203
203
  - **Behavior:** manages stacking of slotted `vf-window` children: `pointerdown` *or `focusin`* on a window brings it to front (incrementing z-index counter) and sets its `active` attribute, clearing `active` on the others — the `focusin` half is the keyboard route to activation: Tab landing anywhere in a background window (its undrawn-but-focusable widgets included) raises it. A press on a title bar with ⌘ held (`metaKey`) raises nothing and activates nothing, so a background window is dragged where it stands in the stack. `npm run verify:desktop-activate` (⌘-DRAG). Listens via delegated pointerdown/focusin listeners + `slotchange`. Windows slotted before `vf-window` is defined are re-normalized once `customElements.whenDefined('vf-window')` settles, since the upgrade reflects each window's `active = true` default back out and upgrading a slotted node doesn't re-fire `slotchange`.
204
204
  - **DOM order follows z-order** (bottom-most first), so sequential focus order matches the visual stack and Shift+Tab mirrors Tab exactly. The sync runs in a task after a pointer gesture ends, or after a programmatic `bringToFront` — never mid-gesture (a node move would clear the pointer capture a title-bar drag or grow-box resize holds), never between a press's release and its `click` (Chromium drops a click whose mousedown node was re-inserted first, which used to cost a control in a background window its click), and never from a focus-driven raise (moving the window focus just entered would re-order the tab sequence mid-traversal); the move is `Element.moveBefore()` where the browser has it, which keeps the focus inside the moved window — a rename field, a caret — and otherwise a re-insert, after which the focus is restored without re-raising that window (a `vf-icon` rename whose field blurred in the move and has its focus back in the same task commits nothing). Non-window children (a menu bar, page content) keep their positions. `npm run verify:window-a11y`.
205
205
  - **Floating tier:** `vf-window[variant="utility"]` children stack in a z band `1_000_000` above the document tier (one shared monotonic counter, so a palette stays above every document window), restack only among themselves, and stand outside the single-active invariant both ways: clicking a palette doesn't deactivate the active document window, and activating a document window never clears a palette's `active` — System 7 windoid behavior while the app is frontmost. The tier test reads the `variant` property of an upgraded window and the attribute of one not yet upgraded, so both land right — and so does a palette made by script and appended at once, which is slotted before its attribute reflects. `npm run verify:desktop-activate` (SCRIPTED).
206
- - **Menu tier:** a slotted `vf-menu-bar` — or a free-standing `vf-menu` placed on the desktop — is restated at `z-index: 2_000_000` by the desktop's own `::slotted(vf-menu-bar), ::slotted(vf-menu)` rule (an outer-tree `::slotted` declaration beats the bar's inner `:host { z-index: 1000 }`), a band above the floating tier, so a dropped menu paints over palettes and document windows alike — the Menu Manager drew menus over every window. Only the drag outline, a window's opening rects and the screen-corner mask are in front. The full stack inside the screen's isolated context: consumer tile grid (−1) < document windows (counter) < utility windows (counter + 1_000_000) < menu tier (2_000_000) < drag outline and window rects (2_000_001) < corner mask (max). A popup *inside* a window (`vf-select`'s fixed panel) is a different case: it stacks within its window's context, so a palette can still cover it — KNOWN-BUGS.md. `npm run verify:archetypes` (MENU TIER).
206
+ - **Menu tier:** a slotted `vf-menu-bar` — or a free-standing `vf-menu` placed on the desktop — is restated at `z-index: 2_000_000` by the desktop's own `::slotted(vf-menu-bar), ::slotted(vf-menu)` rule (an outer-tree `::slotted` declaration beats the bar's inner `:host { z-index: 1000 }`), a band above the floating tier, so a dropped menu paints over palettes and document windows alike — the Menu Manager drew menus over every window. Only the drag outline, a window's opening rects, the full-screen tier and the screen-corner mask are in front. The full stack inside the screen's isolated context: consumer tile grid (−1) < document windows (counter) < utility windows (counter + 1_000_000) < menu tier (2_000_000) < drag outline and window rects (2_000_001) < the full-screen window (3_000_000) < corner mask (max). A popup *inside* a window (`vf-select`'s fixed panel) is a different case: it stacks within its window's context, so a palette can still cover it — KNOWN-BUGS.md. `npm run verify:archetypes` (MENU TIER).
207
+ - **Full-screen tier:** `fullScreenWindow` puts one slotted window on a tier of its own, `z-index: 3_000_000`, above everything else the screen shows — both window tiers, the menu bar and any dropped menu, the drag outline and the window rects, which belong to the windows and icons it covers — and under the corner mask alone, so the screen keeps its rounded corners: the tier a window takes the whole screen on, the bar covered rather than hidden, so its band stays in `workArea` and nothing else's geometry moves. The tier stacks and nothing more: the page sizes the window to the screen (`width` × `height`, at `0, 0`), draws it without chrome (`vf-window frameless`) and brings it forward (`bringToFront`); setting it activates nothing. One window holds it: setting another puts the first back, and `null` puts the holder back in its own tier where it stood — its z-index there is kept, and a raise while it holds the tier (`bringToFront`, a press) moves that kept place, as it would have moved the window, and activates as ever. The DOM-order sync orders the holder by its kept place, so taking and leaving the tier move no node and the focus inside it stays put. A holder that leaves the desktop gives the tier up, its kept place written back, so one put back in lands in its own tier. Anything but a window slotted in the desktop warns and changes nothing. `npm run verify:archetypes` (FULL-SCREEN TIER).
207
208
  - **The drag surface:** the Finder drew a dragged icon's outline on the *screen*, clipped at its edge and phase-locked to its raster, so the screen renders the layer that outline draws on — `.drag-surface`, a full-bleed child of `.screen` after the slot, `pointer-events: none` (never a hit for the page's `elementsFromPoint`), inside the raster's clip. It is deliberately no stacking context (no z-index of its own): a stacking context is an isolated group, and the outline's `mix-blend-mode` would then composite against the empty layer instead of the windows and dither beneath; the tier goes on its children (`.drag-surface > * { z-index: 2_000_001 }`), which places the outline in `.screen`'s own context, above the menu bar and under the corner mask. An icon reaches it through the `vf-drag-surface-request` handshake (§5 vf-icon): the desktop listens on its host, and the nearest desktop fills a still-null `detail.surface`. A window asks the same way and draws there with the same pen: its zoom rects when it opens from or closes to a rect (§5 vf-window, `show` and `hide`), its outline under `outline-drag`. Not the top layer: stable Safari cannot blend an element there against the page (`XOR_UNAVAILABLE`, src/cursor.ts). `npm run verify:icon-drag` (OUTLINE).
208
209
  - **Deactivation:** on a real System 7 machine clicking the desktop clicked the *Finder* — the frontmost application's windows lost their stripes. `clearActive()` is that gesture's handler: it clears `active` from the whole document tier, and **zero active windows is a legal state**, held until a press or `focusin` re-enters a document window or a new one is slotted (opening a window brings its application forward — a newly slotted window ends a deliberate deactivation, where a mere survivor never does: removing a *background* window while deactivated promotes nothing, while removing the holder outside a deactivation promotes the topmost survivor). The desktop never takes the decision itself — a press on its own bare dither changes nothing; desktop furniture is slotted light DOM, so only the page knows which of its children mean "the Finder". Utility windows keep their `active` through a deactivation (their dots stay drawn). **A hidden window never holds the active state.** A page that closes windows by hiding them (`vf-window.hide()`, or the `hidden` attribute set directly — the desktop watches the attribute on every slotted window) gets no activation from the hidden ones: a window slotted hidden waits to be shown, `bringToFront()` on one gives it its place in the stack and activates nothing, and the holder hiding hands the state to the topmost shown document window, or to none. A window shown into a tier where none is active takes the state, unless the tier was deliberately deactivated. Every change of holder — window to window, window to none, none to window — fires `vf-activate`, once per change; re-asserting the current holder is silent. `npm run verify:desktop-activate`.
209
210
  - **The work area:** a slotted `vf-menu-bar` along the screen's top takes its band off the screen (`workArea`), and the gestures on the screen stay below it: a window dragged up stops with its title bar under the bar — or at `window-top`, for windows alone (`windowArea`) — where it can still be grabbed, and an icon dropped, nudged, moved (`moveTo`, `dragTo`) or carried in a group on the desktop lands below the bar. A gesture measures the box it is clamped into once, at its start (`PlacementController.seed()`), by dispatching `vf-placement-bounds-request` (bubbles, non-composed — the drag surface's handshake); the desktop answers for a host placed on its screen (`offsetParent` the desktop: a slotted window, an icon in an unplaced field) with the screen's size, bezel excluded, and the area's top as the floor, rounded up to the host's placement lattice so a snapped landing never rounds above it (21 on a lattice of 3). A host placed in a box of its own — a window body, a placed field — is clamped in that box, from 0. `npm run verify:window` (WORK AREA), `npm run verify:icon-drag` (WORK AREA).
@@ -212,7 +213,7 @@ Full-bleed classic desktop container.
212
213
 
213
214
  #### `vf-window` (`VfWindow`, vf-window.ts)
214
215
  The desktop-window shell: the classic document window (see DragThing screenshot), parameterized down to the windoid (see the Group A recipe table).
215
- - **Attributes/props:** `heading: string` (title text), `width: number` / `height: number` (**declare them both** — whole system px, so the window keeps its proportion to the chrome inside it at every density. A window is a fixed box in both axes, the way a WIND resource was: one that grows with its body is one the user can neither predict nor control via the grow box. Each missing dimension falls back to something different — width to block layout, height to the content — and the window names whichever are missing, once, in the console), `active: boolean` (default **true**; reflect), `closable: boolean` (default false, like every boolean attribute, so markup can leave it out; the document window and modeless dialog box recipes state it), `zoomable: boolean` (default false), `movable: boolean` (default false), `outline-drag: boolean` (`outlineDrag`, default false — the title-bar drag moves an outline and the release moves the window; see Behavior), `resizable: boolean` (default false), `min-width` / `min-height: number` (default 80 / 54) and `max-width` / `max-height: number` (unbounded) — the grow box's sizeRect, whole system px (see Behavior), `variant?: 'utility'` (the slim windoid chrome), `scrollbars?: 'vertical' | 'horizontal' | 'both'` (edge scroll rails), `header-height: number` (`headerHeight` — the header's height in whole system px, rule included; unset, the header is as tall as its content plus the rule), `pattern?: string` (a 1-bit fill for the body as on `vf-container`, under the content and anchored at the content region's corner; under `scrollbars` the built-in scroll area's plane carries it, so it scrolls with the content — docs/PATTERNS.md). Read-only: `sizeLimits` → `{ minWidth, minHeight, maxWidth, maxHeight }`, the sizeRect as it applies in whole system px — the declared mins or the 80×54 floor, the declared maxes or `Infinity` — for a page that sizes the window itself to stay in the grow box's range. The chrome around the content is `windowChrome()` (§2, src/window-chrome.ts): the insets in system px between the outer box and the body, or under `scrollbars` the scrolled plane's viewport, for a set of declared options — `{ variant, scrollbars, headerHeight, status }` — at the default tokens. `npm run verify:window` (CHROME).
216
+ - **Attributes/props:** `heading: string` (title text), `width: number` / `height: number` (**declare them both** — whole system px, so the window keeps its proportion to the chrome inside it at every density. A window is a fixed box in both axes, the way a WIND resource was: one that grows with its body is one the user can neither predict nor control via the grow box. Each missing dimension falls back to something different — width to block layout, height to the content — and the window names whichever are missing, once, in the console), `active: boolean` (default **true**; reflect), `closable: boolean` (default false, like every boolean attribute, so markup can leave it out; the document window and modeless dialog box recipes state it), `zoomable: boolean` (default false), `movable: boolean` (default false), `outline-drag: boolean` (`outlineDrag`, default false — the title-bar drag moves an outline and the release moves the window; see Behavior), `resizable: boolean` (default false), `min-width` / `min-height: number` (default 80 / 54) and `max-width` / `max-height: number` (unbounded) — the grow box's sizeRect, whole system px (see Behavior), `variant?: 'utility'` (the slim windoid chrome), `frameless: boolean` (default false, reflected — no chrome at all, the body the whole box; see Visual), `scrollbars?: 'vertical' | 'horizontal' | 'both'` (edge scroll rails), `header-height: number` (`headerHeight` — the header's height in whole system px, rule included; unset, the header is as tall as its content plus the rule), `pattern?: string` (a 1-bit fill for the body as on `vf-container`, under the content and anchored at the content region's corner; under `scrollbars` the built-in scroll area's plane carries it, so it scrolls with the content — docs/PATTERNS.md). Read-only: `sizeLimits` → `{ minWidth, minHeight, maxWidth, maxHeight }`, the sizeRect as it applies in whole system px — the declared mins or the 80×54 floor, the declared maxes or `Infinity` — for a page that sizes the window itself to stay in the grow box's range. The chrome around the content is `windowChrome()` (§2, src/window-chrome.ts): the insets in system px between the outer box and the body, or under `scrollbars` the scrolled plane's viewport, for a set of declared options — `{ variant, scrollbars, headerHeight, status }` — at the default tokens. `npm run verify:window` (CHROME).
216
217
  - **Visual:** `vfChromeFrame` + `vfTitleBar` (§4), plus a full-size flex-column layout on the frame — title bar, header, body, status strip. `display: block`. Sets `--vf-surface: var(--vf-white, #fff)` on itself.
217
218
  - Title bar: from `vfTitleBar` — height `var(--vf-titlebar-height, 18px)`, white bg, bottom `1px solid black`, contains `.vf-stripes` layer (only when `active`). `touch-action: none` only when `[movable]`.
218
219
  - Title: centered, bold, on a white patch (`padding: 0 6px`; cap band on interior rows 4..12, 7px of white between ink and stripes — see §4 `vfTitleBar` for the traced geometry and the lattice hold) above the stripes, with `--vf-title-inset: 60px` of clearance so it ellipsizes before reaching the widgets. Inactive: no stripes, widgets undrawn (transparent ink — they keep their tab stops; see A11y below), the grow box's nested squares hidden, and every managed scroll rail inside the window blanked (see "always-a-rail" §5 vf-scroll-area) — but the title text stays black (System 7 never grayed the window title).
@@ -224,6 +225,7 @@ The desktop-window shell: the classic document window (see DragThing screenshot)
224
225
  - Status bar (`status` slot): the classic bottom readout strip ("40px x 40px") — a 1px black rule (`vfRule`'s `.vf-rule-top`) over a white interior, `--vf-status-bar-height` (15px) in all, the grow box's own height, so a `resizable` window's grow box sits flush in the strip's right end (its top/left borders take over the rule there; the strip reserves 21px of right padding under `resizable` so text clears the cell). Slotted content rides the body face's native 12px line (`--vf-line-height`), whole-pixel centered in the 14px interior, `padding-inline: 6px`, nowrap + clipped — vertically by the slot, below the rule. Takes **no space** until the slot is populated, and collapses again when it empties. Composes with `scrollbars`: the edge rails' bottom overhang lands its frame line exactly on the strip's rule, so the two never double up — though the grow box then sits in the strip rather than the rail corner cell.
225
226
  - Edge scroll rails (if `scrollbars`): the body slot renders inside a shadow `vf-scroll-area` (its `axis` = the attribute's value, `label` = the heading, `viewport` part re-exported) carrying the TeachText composition internally — `calc(100% + 2px·scale)` with `margin: -1px·scale`, one system px under the frame on every side, so the area's own frame border repaints the window's border lines and a `resizable` window's grow box (z-index 1) lands exactly over the rail-corner cell. the area's viewport carries no inset, so content and the (0,0) of placed children sit at the content region's corner; a `resizable` window sets the area's `corner`, so a single-axis rail stops at the corner cell too — unless the status strip is populated, when the grow box sits in the strip and the rail runs edge to edge onto the strip's rule. Same caveats as the slotted composition (see vf-scroll-area §5).
226
227
  - Utility variant (`variant="utility"`): the slim windoid bar traced from `Windows/utility-window.png` — `--vf-titlebar-height-utility` (12px = 11px interior + 1px rule), the `vfDots` dither instead of stripes (flush to the side borders — see §4 vfDots), 7×7 widgets (`top: 2px`; close `left: 7px`, zoom `right: 8px` — the art is asymmetric by that pixel) with a 2px patch ring where the striped bar's is 1px (`--vf-widget-ring`, internal geometry: the windoid sheet clears two px of dither beside its widgets), and the nested zoom square shrunk so its edges land at sprite col/row 3. No title patch: the display face's 16px line box can't sit in an 11px interior, so `.vf-title` is `display: none` under the variant (a retheming consumer can re-show it via `::part(title)`) and the heading names the widgets. A pressed windoid widget inverts whole — black interior under a white (invisible) borderline — rather than flashing the big bar's 9×9 sunburst, which can't land on a 5×5 interior.
228
+ - Frameless (`frameless`): no chrome — no title bar, so no close or zoom box, no frame stroke or hard shadow, no grow box, and no header or status strip whatever their slots hold. The body is the whole box: `pattern` fills it edge to edge, and a placed child's (0,0) is the box's corner. Under `scrollbars` the built-in scroll area fills the box, `margin: 0` and no overhang — there is no frame for its frame line to land on, so that line sits on the box's edge — and keeps no corner cell, there being no grow box: the scrolled viewport sits 1px in, and a rail's 15px more on its side. `windowChrome()` describes the framed window; a frameless one has no chrome to state. The chrome is hidden (`display: none`, the stroke and shadow off), not left out of the render, so switching it on a connected window moves no node and the focus in the body stays where it is; the title patch stays in the tree, so `heading` still names the group, as the utility variant's hidden patch does. In a `vf-desktop` the window keeps its tier for activation and `hidden`, and `show({ from })` and `hide({ to })` run their rects from and to its box, the host's. The window that takes the screen on `vf-desktop`'s full-screen tier draws this way. `npm run verify:window` (FRAMELESS).
227
229
  - **A11y:** the close/zoom `aria-label`s are qualified by the title when there is one (`Close ${heading}` / `Zoom ${heading}`, falling back to bare `Close` / `Zoom`) — several windows are open at once by design, so a bare repeated "Close" gives an AT user no way to tell which window a widget belongs to. The frame is `role="group"`, named by the title patch via `aria-labelledby` when there is a heading (the utility variant's hidden patch still names it — AccName resolves hidden labelledby targets) — `group` rather than `region` deliberately, so a desktop of windows doesn't pollute landmark navigation; the title bar is a `<div>`, never a `<header>`, which would map to an unnamed `banner` landmark even inside the shadow root. An inactive window's widgets stay in the tree and the tab order but paint no ink (transparent border/background/patch ring — the bare System 7 bar): a background window whose body holds nothing focusable is still reachable, activated by `vf-desktop`'s `focusin` raise the moment Tab lands on a widget, and never drops focus to `<body>` when it deactivates. `npm run verify:window-a11y`.
228
230
  - **Behavior:** close box click → `vf-close` (does NOT remove itself; consumer decides). Zoom box click → `vf-zoom`. If `movable`: dragging the title bar moves the window — the drag seeds its origin from the current offset position (once, converting to system px) and then states `left`/`top` via pointer capture, in whole system px like any authored placement. With `outline-drag` the drag moves an outline instead: the press draws a one-pixel ring around the window's border box (the frame, shadow excluded) on the desktop's drag surface, with the drag outline's pen (`xorPenCanvas`, dotted at the screen phase — black over the dither), and each step moves it to the landing the live drag would write (`PlacementController.resolve()`: the grabbable-strip clamp, snapped), so the outline stops where the window can go; the release writes the last landing once (one `vf-placement-change`) and a release with no step writes nothing, and Escape — a gesture-scoped document capture listener armed at the press, the key stopped there — or a `pointercancel` takes the outline down and writes nothing. With no `vf-desktop` on its path the drag moves the window live. `npm run verify:window` (DRAG OUTLINE). If `resizable`: dragging the grow box states `width`/`height` the same way, each axis clamped into the sizeRect (`min-width`/`max-width`, `min-height`/`max-height` — GrowWindow's: the app stated the rectangle of sizes and the Window Manager clamped the drag to it; the mins default to the 80×54 floor a window can still be worked at, the maxes are unbounded, and a min equal to its max locks the axis — the strip that scrolls sideways and never grows taller). The clamp runs after the lattice snap, so a bound lands exactly the way an authored size does even off the lattice (an odd height at 2×); where a min and max cross the max wins, so a window authored under the floor and held there never jumps to it. The rect bounds the gesture only: a size authored outside it renders as declared, and the first grow-box move brings it inside. The drag fires `vf-resize` (detail `{ width, height, commit }`, sizes in whole system px) — one event per size the drag actually writes (`commit: false`), fired *after* the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles (release or cancel), only when it changed the size. Window content can follow the grow box from this event alone, no ResizeObserver. Fired by the gesture only: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.
229
231
  - **Methods:** `placementAt(clientX, clientY, child?)` → a viewport point as a placement in the content region — `{ left, top }` in whole system px on the placement lattice: the body's corner, or under `scrollbars` the scrolled plane with the scroll offset already in it (the built-in area's own `placementAt`). The pair a child dropped into the window is written with; with `child`, the pair to write to that element, its `origin` offset folded in. `measure()` → the built-in scroll area's `measure()`, a no-op without `scrollbars`; the area re-measures by itself on its content's box, its slot and every placement write (`vf-placement-change`, §2), so this is the escape hatch for a range that changes with none of those. `show(options?)` → `Promise<boolean>`: clears `hidden`, and with `options.from` — a box in viewport CSS px (`{ left, top, right, bottom }`; a `DOMRect` fits, a zero-size box is a point), such as `vf-icon.cellRect()` — opens the window from it with the zoom rects. The window knows nothing about icons: `from` is any box. 14 rects, rect k at the fraction 0.7^(14−k) of the way from the box to the window's border box — each edge `from + trunc((frame − from) · t)`, so they bunch at the box and the last stops at 0.7 — four on screen at a time, 17 ms per step, then four steps draining; the window draws after the eighteenth (`WINDOW_RECT_*`, `windowRectFraction`, src/motion.ts). Each rect is a canvas of its own on the desktop's drag surface (the `vf-drag-surface-request` handshake), framed once with the drag outline's pen (`xorPenCanvas`, `paintSelectionRect`, src/open-art.ts) and dotted at the screen phase (`penPhase` — black lines over the dither), and a Web Animation (`element.animate()`) shows it for its four steps, every one padded with `endDelay` to the run's length so all of them finish when the window draws. Where two rects share a pixel the later one inverts it back: each canvas blends over those beneath it. Same-task writes to `left`/`top`/`width`/`height`, before or after the call, land first, so the rects end on the frame the page set. Until the rects drain the window matches `:state(opening)` and has `opacity: 0` — laid out, focusable and a hit target, so a page can focus into it at once and a press lands on it — never `visibility`, which would drop focus and pass the press through. A press anywhere or Escape (consumed in the capture phase) finishes the rects' animations (`finish()`), as they finish the walk; a second `show()` finishes the first, as it finishes a `hide()`'s; a removal, or a `hide()` before the window draws, cancels them (`cancel()`) and resolves `false`, while a move — `vf-desktop` re-inserting a raised window — is not a removal (the disconnect checks `isConnected` a microtask later, and the reconnect re-attaches the finishers). Resolves once the window draws: `true` when the rects ran, `false` when it showed at once — no `from`, `prefers-reduced-motion`, no `vf-desktop` on its path, nothing of the rects on the screen, or not connected. Raises and activates nothing; a window already on screen goes blank for the run, so a page brings an open window forward with `bringToFront` instead. `npm run verify:window` (OPEN). `hide(options?)` → `Promise<boolean>`: sets `hidden`, and with `options.to` — a box as for `show` — closes the window to it: the window's border box is read at the call, the window hidden, and the same 14 rects run the other way, rect k shown from step 13 − k, so the trail starts at the rect nearest the frame, closes in on the box and drains into it, eighteen steps in all. A press anywhere or Escape finishes them, a `show()` finishes them and shows the window, and a second `hide()` finishes the first. Removing the window leaves them running — they are the desktop's, and the window is already gone — so a page can remove a closed window straight after the call. A `show()` still opening is taken down (cancelled, so it resolves `false`) and the window hides at once. Resolves once the rects are done: `true` when they ran, `false` when the window hid at once — no `to`, `prefers-reduced-motion`, no `vf-desktop` on its path, nothing of the rects on the screen, not connected, already hidden, or still opening. `npm run verify:window` (CLOSE).
@@ -251,7 +251,7 @@
251
251
  },
252
252
  {
253
253
  "name": "vf-desktop",
254
- "description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. A press on a title bar with ⌘ held\nraises nothing, so a background window is dragged where it stands in the\nstack. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the screen-corner mask is in front of it.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\n**Hidden windows.** A window with `hidden` — closed by a page that hides\nits windows rather than removing them, through `vf-window.hide()` or the\nattribute itself — never holds the active state. One slotted hidden waits\nto be shown; `bringToFront()` on one gives it its place in the stack and\nactivates nothing; the holder hiding hands the state to the topmost shown\ndocument window, or to none. A window shown into a tier where none is\nactive, unless the tier was deliberately deactivated, takes it.\nstackingOrder lists the windows as they stand, bottom-most first.\n\n**The work area.** A slotted `vf-menu-bar` along the top takes its band\noff the screen: workArea is what is left, and the gestures on the\nscreen stay in it — a window dragged up stops with its title bar under the\nbar, where it can still be grabbed, and an icon dropped, nudged or walked\non the desktop lands below the bar. `window-top` deepens the area for\nwindows alone (windowArea), for a page with a strip of its own\nunder the bar. A gesture asks for the box it is clamped into with\n`vf-placement-bounds-request` (bubbles, non-composed), the drag surface's\nhandshake, and the desktop answers for the hosts placed on its screen.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, a repeating tile drawn at the display's resolution, 1-bit at every\ndensity and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. A window asks the same way and draws\nthere with the same pen: its zoom rects when it opens from a rect\n(`vf-window.show({ from })`), its outline under an `outline-drag`. Not the\ntop layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).\n---\n\n\n### **Events:**\n - **vf-activate** - The active document-tier window changed. Detail `{ window: HTMLElement | null }` — the new holder, or `null` when the document tier deactivated (a {@link clearActive} call, or the active window leaving the DOM or hiding with no shown window behind it). Fired once per change of holder, never for a re-assertion of the same one.\n\n### **Slots:**\n - _default_ - Default slot: menu bar, windows, anything.\n\n### **CSS Properties:**\n - **--vf-desktop** - base color under the desktop pattern — occluded by the pattern's opaque paper, so it only shows through a custom `--vf-desktop-pattern` _(default: #808080)_\n- **--vf-desktop-pattern** - a consumer's own desktop tile, in place of `pattern` — the kit's default is the 50% checker drawn as opaque black-on-white rects on a 30-system-px tile. Override the whole tile; consumer art renders as a placed tile grid at that same geometry (raster art magnifies nearest-neighbor, the `vf-img` idiom) _(default: undefined)_\n\n### **CSS Parts:**\n - **desktop** - The patterned screen surface — the whole-system-px raster (inset by `bezel` when one is set).",
254
+ "description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. A press on a title bar with ⌘ held\nraises nothing, so a background window is dragged where it stands in the\nstack. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the drag outline, the full-screen tier and the screen-corner\nmask are in front of it.\n\n**The full-screen tier.** fullScreenWindow puts one slotted window\nabove everything else the screen shows — both window tiers, the menu bar\nand its dropped menus, the drag outline and the rects — and under the\ncorner mask alone: the tier a window takes the whole screen on. The page\nsets it and clears it, and sizes, draws and activates the window itself\n(`vf-window`'s `frameless`, the screen's `width` and `height`,\nbringToFront); cleared, the window goes back to its tier where it\nstood.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\n**Hidden windows.** A window with `hidden` — closed by a page that hides\nits windows rather than removing them, through `vf-window.hide()` or the\nattribute itself — never holds the active state. One slotted hidden waits\nto be shown; `bringToFront()` on one gives it its place in the stack and\nactivates nothing; the holder hiding hands the state to the topmost shown\ndocument window, or to none. A window shown into a tier where none is\nactive, unless the tier was deliberately deactivated, takes it.\nstackingOrder lists the windows as they stand, bottom-most first.\n\n**The work area.** A slotted `vf-menu-bar` along the top takes its band\noff the screen: workArea is what is left, and the gestures on the\nscreen stay in it — a window dragged up stops with its title bar under the\nbar, where it can still be grabbed, and an icon dropped, nudged or walked\non the desktop lands below the bar. `window-top` deepens the area for\nwindows alone (windowArea), for a page with a strip of its own\nunder the bar. A gesture asks for the box it is clamped into with\n`vf-placement-bounds-request` (bubbles, non-composed), the drag surface's\nhandshake, and the desktop answers for the hosts placed on its screen.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, a repeating tile drawn at the display's resolution, 1-bit at every\ndensity and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. A window asks the same way and draws\nthere with the same pen: its zoom rects when it opens from a rect\n(`vf-window.show({ from })`), its outline under an `outline-drag`. Not the\ntop layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).\n---\n\n\n### **Events:**\n - **vf-activate** - The active document-tier window changed. Detail `{ window: HTMLElement | null }` — the new holder, or `null` when the document tier deactivated (a {@link clearActive} call, or the active window leaving the DOM or hiding with no shown window behind it). Fired once per change of holder, never for a re-assertion of the same one.\n\n### **Slots:**\n - _default_ - Default slot: menu bar, windows, anything.\n\n### **CSS Properties:**\n - **--vf-desktop** - base color under the desktop pattern — occluded by the pattern's opaque paper, so it only shows through a custom `--vf-desktop-pattern` _(default: #808080)_\n- **--vf-desktop-pattern** - a consumer's own desktop tile, in place of `pattern` — the kit's default is the 50% checker drawn as opaque black-on-white rects on a 30-system-px tile. Override the whole tile; consumer art renders as a placed tile grid at that same geometry (raster art magnifies nearest-neighbor, the `vf-img` idiom) _(default: undefined)_\n\n### **CSS Parts:**\n - **desktop** - The patterned screen surface — the whole-system-px raster (inset by `bezel` when one is set).",
255
255
  "attributes": [
256
256
  {
257
257
  "name": "width",
@@ -1734,13 +1734,18 @@
1734
1734
  },
1735
1735
  {
1736
1736
  "name": "vf-window",
1737
- "description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" closable movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n\n**Opening from a rect.** show clears `hidden`, and given `from` — a\nbox in viewport CSS px, an icon's `cellRect()` or a button's rect — opens\nthe window from it: dotted rectangles grow from that box toward the\nwindow's frame, four on screen at a time, bunched at the box and opening\nout toward the window, drawn with the icon drag outline's XOR pen on the\ndesktop's drag surface; the window draws once they have drained, eighteen\nsteps later. Until then it matches `:state(opening)` and paints nothing —\nstill laid out, focusable and a hit target, so a page can place it, raise\nit and focus into it at once, and a press finishes the rects and lands on\nthe window. Under `prefers-reduced-motion`, with no `vf-desktop` on its\npath, or without `from`, the window shows at once.\n\n**Closing to a rect.** hide sets `hidden`, and given `to` closes\nthe window to it: the same rectangles run the other way, from where the\nframe was in toward the box, the window gone from the first step. A press\nfinishes them, and removing the window leaves them running. Under\n`prefers-reduced-motion`, with no `vf-desktop` on its path, or without\n`to`, the window hides at once. The window knows nothing about icons:\n`from` and `to` are any rect.\n\n**Dragging as an outline.** A `movable` window moves live under the\npointer. With `outline-drag` a press on the title bar draws a dotted\noutline around the window on the desktop's drag surface instead, with the\nsame pen; the drag moves the outline, held where the window can land, and\nthe window moves there on the release. Escape cancels.\n---\n\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close', altKey, shiftKey, metaKey, ctrlKey }`, the modifier keys held on the click (shape-compatible with vf-dialog's `vf-close`), so an Option-click can mean more than a click. The window does NOT remove itself; the consumer decides what closing means.\n- **vf-zoom** - Zoom box clicked. Detail `{}`.\n- **vf-resize** - The grow box resized the window. Detail `{ width, height, commit }`, sizes in whole system px: one event per size the drag writes (`commit: false`), fired after the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles — only when it changed the size. Fired by the gesture alone: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.\n\n### **Slots:**\n - _default_ - Default slot: window body content.\n- **header** - Optional header content — a band between the title bar and the body, the full width of the window (the Finder window's header line): a white interior over a 1px rule, no inset of its own, a positioning anchor for placed children. As tall as its content unless `header-height` states it. Under `scrollbars` the vertical rail begins below it, so the header spans the rail's column. Takes no space until populated.\n- **status** - Optional status-bar content — the classic bottom readout strip (\"40px x 40px\"): a 1px rule over a 15px white band under the body, body-face text on its native line. Takes no space until populated; a `resizable` window's grow box sits flush in its right end.\n\n### **CSS Properties:**\n - **--vf-dots-pattern** - the windoid bar's dot-grid dither — a 2×2 motif, one black pixel at its origin, on a 30-system-px tile (`vfDots`; override the whole tile like `--vf-desktop-pattern` — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n- **--vf-titlebar-height** - window/dialog title bars _(default: undefined)_\n- **--vf-titlebar-height-utility** - the slim `vf-window[variant=\"utility\"]` (windoid) bar — 11px interior + 1px bottom rule, traced from `Windows/utility-window.png` _(default: 12px)_\n- **--vf-status-bar-height** - the status strip: 1px rule + 14px interior — the grow box's own height, so the two compose flush _(default: 15px)_\n- **--vf-line-height** - the body face's native line, which the status strip's text rides (whole-pixel centered in the 14px interior) _(default: 12px)_\n\n### **CSS Parts:**\n - **frame** - The outer chrome frame.\n- **title-bar** - The striped (or dithered) title bar.\n- **title** - The centered title patch (hidden on the utility bar).\n- **close-box** - The close widget (left).\n- **zoom-box** - The zoom widget (right).\n- **body** - The content area.\n- **header** - The header strip between the title bar and the body (when the `header` slot is populated).\n- **status-bar** - The bottom status strip (when the `status` slot is populated).\n- **grow-box** - The resize widget (bottom-right, when `resizable`).\n- **viewport** - The built-in scroll area's viewport (when `scrollbars`; re-exported from vf-scroll-area).",
1737
+ "description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" closable movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n\n**Opening from a rect.** show clears `hidden`, and given `from` — a\nbox in viewport CSS px, an icon's `cellRect()` or a button's rect — opens\nthe window from it: dotted rectangles grow from that box toward the\nwindow's frame, four on screen at a time, bunched at the box and opening\nout toward the window, drawn with the icon drag outline's XOR pen on the\ndesktop's drag surface; the window draws once they have drained, eighteen\nsteps later. Until then it matches `:state(opening)` and paints nothing —\nstill laid out, focusable and a hit target, so a page can place it, raise\nit and focus into it at once, and a press finishes the rects and lands on\nthe window. Under `prefers-reduced-motion`, with no `vf-desktop` on its\npath, or without `from`, the window shows at once.\n\n**Closing to a rect.** hide sets `hidden`, and given `to` closes\nthe window to it: the same rectangles run the other way, from where the\nframe was in toward the box, the window gone from the first step. A press\nfinishes them, and removing the window leaves them running. Under\n`prefers-reduced-motion`, with no `vf-desktop` on its path, or without\n`to`, the window hides at once. The window knows nothing about icons:\n`from` and `to` are any rect.\n\n**Dragging as an outline.** A `movable` window moves live under the\npointer. With `outline-drag` a press on the title bar draws a dotted\noutline around the window on the desktop's drag surface instead, with the\nsame pen; the drag moves the outline, held where the window can land, and\nthe window moves there on the release. Escape cancels.\n\n**Frameless.** With `frameless` the window draws no chrome — no title bar\nor widgets, no frame or shadow, no grow box, no header or status strip —\nand the body is the whole box: a window that takes the whole screen, say,\non `vf-desktop`'s full-screen tier. The chrome is hidden, not torn down, so\nthe switch moves no node and the focus in the body stays where it is.\n---\n\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close', altKey, shiftKey, metaKey, ctrlKey }`, the modifier keys held on the click (shape-compatible with vf-dialog's `vf-close`), so an Option-click can mean more than a click. The window does NOT remove itself; the consumer decides what closing means.\n- **vf-zoom** - Zoom box clicked. Detail `{}`.\n- **vf-resize** - The grow box resized the window. Detail `{ width, height, commit }`, sizes in whole system px: one event per size the drag writes (`commit: false`), fired after the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles — only when it changed the size. Fired by the gesture alone: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.\n\n### **Slots:**\n - _default_ - Default slot: window body content.\n- **header** - Optional header content — a band between the title bar and the body, the full width of the window (the Finder window's header line): a white interior over a 1px rule, no inset of its own, a positioning anchor for placed children. As tall as its content unless `header-height` states it. Under `scrollbars` the vertical rail begins below it, so the header spans the rail's column. Takes no space until populated.\n- **status** - Optional status-bar content — the classic bottom readout strip (\"40px x 40px\"): a 1px rule over a 15px white band under the body, body-face text on its native line. Takes no space until populated; a `resizable` window's grow box sits flush in its right end.\n\n### **CSS Properties:**\n - **--vf-dots-pattern** - the windoid bar's dot-grid dither — a 2×2 motif, one black pixel at its origin, on a 30-system-px tile (`vfDots`; override the whole tile like `--vf-desktop-pattern` — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n- **--vf-titlebar-height** - window/dialog title bars _(default: undefined)_\n- **--vf-titlebar-height-utility** - the slim `vf-window[variant=\"utility\"]` (windoid) bar — 11px interior + 1px bottom rule, traced from `Windows/utility-window.png` _(default: 12px)_\n- **--vf-status-bar-height** - the status strip: 1px rule + 14px interior — the grow box's own height, so the two compose flush _(default: 15px)_\n- **--vf-line-height** - the body face's native line, which the status strip's text rides (whole-pixel centered in the 14px interior) _(default: 12px)_\n\n### **CSS Parts:**\n - **frame** - The outer chrome frame.\n- **title-bar** - The striped (or dithered) title bar.\n- **title** - The centered title patch (hidden on the utility bar).\n- **close-box** - The close widget (left).\n- **zoom-box** - The zoom widget (right).\n- **body** - The content area.\n- **header** - The header strip between the title bar and the body (when the `header` slot is populated).\n- **status-bar** - The bottom status strip (when the `status` slot is populated).\n- **grow-box** - The resize widget (bottom-right, when `resizable`).\n- **viewport** - The built-in scroll area's viewport (when `scrollbars`; re-exported from vf-scroll-area).",
1738
1738
  "attributes": [
1739
1739
  {
1740
1740
  "name": "variant",
1741
1741
  "description": "Chrome variant. Omit for the standard 18px striped bar; `'utility'` for\nthe slim windoid bar (dot-grid dither, 7×7 widgets, no title patch — the\nheading still names the widgets). Inside a `vf-desktop`, utility windows\nfloat above the document tier and never take the single-active state.",
1742
1742
  "values": [{ "name": "utility" }]
1743
1743
  },
1744
+ {
1745
+ "name": "frameless",
1746
+ "description": "Draw no chrome: no title bar, so no close or zoom box, no frame or\nshadow, no grow box, and no header or status strip, whatever their slots\nhold. The body is the whole box, `pattern` filling it and a placed child\nmeasured from its corner; under `scrollbars` the built-in scroll area\nfills the box, its own frame line on the box's edge. `heading` still\nnames the window. The chrome is hidden rather than left out of the\nrender, so switching this on a connected window moves no node and the\nfocus in the body stays where it is. In a `vf-desktop` the window keeps\nits tier for activation and `hidden`, and `show()` and `hide()` run\ntheir rects from and to its box.",
1747
+ "values": []
1748
+ },
1744
1749
  {
1745
1750
  "name": "heading",
1746
1751
  "description": "Title text shown centered in the title bar.",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
3
  "name": "vintage-frames",
4
- "version": "0.20.0",
4
+ "version": "0.20.1",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -577,7 +577,7 @@
577
577
  },
578
578
  {
579
579
  "name": "vf-desktop",
580
- "description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. A press on a title bar with ⌘ held\nraises nothing, so a background window is dragged where it stands in the\nstack. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the screen-corner mask is in front of it.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\n**Hidden windows.** A window with `hidden` — closed by a page that hides\nits windows rather than removing them, through `vf-window.hide()` or the\nattribute itself — never holds the active state. One slotted hidden waits\nto be shown; `bringToFront()` on one gives it its place in the stack and\nactivates nothing; the holder hiding hands the state to the topmost shown\ndocument window, or to none. A window shown into a tier where none is\nactive, unless the tier was deliberately deactivated, takes it.\nstackingOrder lists the windows as they stand, bottom-most first.\n\n**The work area.** A slotted `vf-menu-bar` along the top takes its band\noff the screen: workArea is what is left, and the gestures on the\nscreen stay in it — a window dragged up stops with its title bar under the\nbar, where it can still be grabbed, and an icon dropped, nudged or walked\non the desktop lands below the bar. `window-top` deepens the area for\nwindows alone (windowArea), for a page with a strip of its own\nunder the bar. A gesture asks for the box it is clamped into with\n`vf-placement-bounds-request` (bubbles, non-composed), the drag surface's\nhandshake, and the desktop answers for the hosts placed on its screen.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, a repeating tile drawn at the display's resolution, 1-bit at every\ndensity and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. A window asks the same way and draws\nthere with the same pen: its zoom rects when it opens from a rect\n(`vf-window.show({ from })`), its outline under an `outline-drag`. Not the\ntop layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).\n---\n\n\n### **Events:**\n - **vf-activate** - The active document-tier window changed. Detail `{ window: HTMLElement | null }` — the new holder, or `null` when the document tier deactivated (a {@link clearActive} call, or the active window leaving the DOM or hiding with no shown window behind it). Fired once per change of holder, never for a re-assertion of the same one.\n\n### **Slots:**\n - _default_ - Default slot: menu bar, windows, anything.\n\n### **CSS Properties:**\n - **--vf-desktop** - base color under the desktop pattern — occluded by the pattern's opaque paper, so it only shows through a custom `--vf-desktop-pattern` _(default: #808080)_\n- **--vf-desktop-pattern** - a consumer's own desktop tile, in place of `pattern` — the kit's default is the 50% checker drawn as opaque black-on-white rects on a 30-system-px tile. Override the whole tile; consumer art renders as a placed tile grid at that same geometry (raster art magnifies nearest-neighbor, the `vf-img` idiom) _(default: undefined)_\n\n### **CSS Parts:**\n - **desktop** - The patterned screen surface — the whole-system-px raster (inset by `bezel` when one is set).",
580
+ "description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. A press on a title bar with ⌘ held\nraises nothing, so a background window is dragged where it stands in the\nstack. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the drag outline, the full-screen tier and the screen-corner\nmask are in front of it.\n\n**The full-screen tier.** fullScreenWindow puts one slotted window\nabove everything else the screen shows — both window tiers, the menu bar\nand its dropped menus, the drag outline and the rects — and under the\ncorner mask alone: the tier a window takes the whole screen on. The page\nsets it and clears it, and sizes, draws and activates the window itself\n(`vf-window`'s `frameless`, the screen's `width` and `height`,\nbringToFront); cleared, the window goes back to its tier where it\nstood.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\n**Hidden windows.** A window with `hidden` — closed by a page that hides\nits windows rather than removing them, through `vf-window.hide()` or the\nattribute itself — never holds the active state. One slotted hidden waits\nto be shown; `bringToFront()` on one gives it its place in the stack and\nactivates nothing; the holder hiding hands the state to the topmost shown\ndocument window, or to none. A window shown into a tier where none is\nactive, unless the tier was deliberately deactivated, takes it.\nstackingOrder lists the windows as they stand, bottom-most first.\n\n**The work area.** A slotted `vf-menu-bar` along the top takes its band\noff the screen: workArea is what is left, and the gestures on the\nscreen stay in it — a window dragged up stops with its title bar under the\nbar, where it can still be grabbed, and an icon dropped, nudged or walked\non the desktop lands below the bar. `window-top` deepens the area for\nwindows alone (windowArea), for a page with a strip of its own\nunder the bar. A gesture asks for the box it is clamped into with\n`vf-placement-bounds-request` (bubbles, non-composed), the drag surface's\nhandshake, and the desktop answers for the hosts placed on its screen.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, a repeating tile drawn at the display's resolution, 1-bit at every\ndensity and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. A window asks the same way and draws\nthere with the same pen: its zoom rects when it opens from a rect\n(`vf-window.show({ from })`), its outline under an `outline-drag`. Not the\ntop layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).\n---\n\n\n### **Events:**\n - **vf-activate** - The active document-tier window changed. Detail `{ window: HTMLElement | null }` — the new holder, or `null` when the document tier deactivated (a {@link clearActive} call, or the active window leaving the DOM or hiding with no shown window behind it). Fired once per change of holder, never for a re-assertion of the same one.\n\n### **Slots:**\n - _default_ - Default slot: menu bar, windows, anything.\n\n### **CSS Properties:**\n - **--vf-desktop** - base color under the desktop pattern — occluded by the pattern's opaque paper, so it only shows through a custom `--vf-desktop-pattern` _(default: #808080)_\n- **--vf-desktop-pattern** - a consumer's own desktop tile, in place of `pattern` — the kit's default is the 50% checker drawn as opaque black-on-white rects on a 30-system-px tile. Override the whole tile; consumer art renders as a placed tile grid at that same geometry (raster art magnifies nearest-neighbor, the `vf-img` idiom) _(default: undefined)_\n\n### **CSS Parts:**\n - **desktop** - The patterned screen surface — the whole-system-px raster (inset by `bezel` when one is set).",
581
581
  "doc-url": "",
582
582
  "attributes": [
583
583
  {
@@ -685,9 +685,14 @@
685
685
  },
686
686
  {
687
687
  "name": "stackingOrder",
688
- "description": "The slotted windows in stacking order, bottom-most first: the document\ntier, then the floating tier above it, each in the order its windows\nwere last raised. Hidden windows are included where they stand. This is\nthe order the eye sees; the windows' light-DOM order catches up with it\nonly once a gesture ends, and a raise by keyboard focus moves no node.",
688
+ "description": "The slotted windows in stacking order, bottom-most first: the document\ntier, then the floating tier above it, each in the order its windows\nwere last raised, and the window on the full-screen tier last. Hidden\nwindows are included where they stand. This is the order the eye sees;\nthe windows' light-DOM order catches up with it only once a gesture\nends, a raise by keyboard focus moves no node, and the full-screen\nwindow keeps the light-DOM place of where it stood.",
689
689
  "type": "HTMLElement[]"
690
690
  },
691
+ {
692
+ "name": "fullScreenWindow",
693
+ "description": "The window on the full-screen tier, or null. A slotted window set here\nstacks above everything else the screen shows — both window tiers, the\nmenu bar and any dropped menu, the drag outline and the window rects —\nand under the corner mask alone, so the screen keeps its rounded\ncorners: the tier for a window that takes the whole screen. Sizing it to\nthe screen and drawing it without chrome (`vf-window`'s `frameless`) are\nthe page's, and so is bringing it forward: setting this activates\nnothing. One window holds the tier at a time: setting another puts the\nfirst back, and null puts the holder back in its own tier where it\nstood — a raise meanwhile moves that place, as it would have moved the\nwindow. A holder that leaves the desktop gives the tier up. Anything but\na window slotted here warns and changes nothing.",
694
+ "type": "HTMLElement | null"
695
+ },
691
696
  {
692
697
  "name": "top",
693
698
  "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
@@ -4159,7 +4164,7 @@
4159
4164
  },
4160
4165
  {
4161
4166
  "name": "vf-window",
4162
- "description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" closable movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n\n**Opening from a rect.** show clears `hidden`, and given `from` — a\nbox in viewport CSS px, an icon's `cellRect()` or a button's rect — opens\nthe window from it: dotted rectangles grow from that box toward the\nwindow's frame, four on screen at a time, bunched at the box and opening\nout toward the window, drawn with the icon drag outline's XOR pen on the\ndesktop's drag surface; the window draws once they have drained, eighteen\nsteps later. Until then it matches `:state(opening)` and paints nothing —\nstill laid out, focusable and a hit target, so a page can place it, raise\nit and focus into it at once, and a press finishes the rects and lands on\nthe window. Under `prefers-reduced-motion`, with no `vf-desktop` on its\npath, or without `from`, the window shows at once.\n\n**Closing to a rect.** hide sets `hidden`, and given `to` closes\nthe window to it: the same rectangles run the other way, from where the\nframe was in toward the box, the window gone from the first step. A press\nfinishes them, and removing the window leaves them running. Under\n`prefers-reduced-motion`, with no `vf-desktop` on its path, or without\n`to`, the window hides at once. The window knows nothing about icons:\n`from` and `to` are any rect.\n\n**Dragging as an outline.** A `movable` window moves live under the\npointer. With `outline-drag` a press on the title bar draws a dotted\noutline around the window on the desktop's drag surface instead, with the\nsame pen; the drag moves the outline, held where the window can land, and\nthe window moves there on the release. Escape cancels.\n---\n\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close', altKey, shiftKey, metaKey, ctrlKey }`, the modifier keys held on the click (shape-compatible with vf-dialog's `vf-close`), so an Option-click can mean more than a click. The window does NOT remove itself; the consumer decides what closing means.\n- **vf-zoom** - Zoom box clicked. Detail `{}`.\n- **vf-resize** - The grow box resized the window. Detail `{ width, height, commit }`, sizes in whole system px: one event per size the drag writes (`commit: false`), fired after the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles — only when it changed the size. Fired by the gesture alone: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.\n\n### **Slots:**\n - _default_ - Default slot: window body content.\n- **header** - Optional header content — a band between the title bar and the body, the full width of the window (the Finder window's header line): a white interior over a 1px rule, no inset of its own, a positioning anchor for placed children. As tall as its content unless `header-height` states it. Under `scrollbars` the vertical rail begins below it, so the header spans the rail's column. Takes no space until populated.\n- **status** - Optional status-bar content — the classic bottom readout strip (\"40px x 40px\"): a 1px rule over a 15px white band under the body, body-face text on its native line. Takes no space until populated; a `resizable` window's grow box sits flush in its right end.\n\n### **CSS Properties:**\n - **--vf-dots-pattern** - the windoid bar's dot-grid dither — a 2×2 motif, one black pixel at its origin, on a 30-system-px tile (`vfDots`; override the whole tile like `--vf-desktop-pattern` — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n- **--vf-titlebar-height** - window/dialog title bars _(default: undefined)_\n- **--vf-titlebar-height-utility** - the slim `vf-window[variant=\"utility\"]` (windoid) bar — 11px interior + 1px bottom rule, traced from `Windows/utility-window.png` _(default: 12px)_\n- **--vf-status-bar-height** - the status strip: 1px rule + 14px interior — the grow box's own height, so the two compose flush _(default: 15px)_\n- **--vf-line-height** - the body face's native line, which the status strip's text rides (whole-pixel centered in the 14px interior) _(default: 12px)_\n\n### **CSS Parts:**\n - **frame** - The outer chrome frame.\n- **title-bar** - The striped (or dithered) title bar.\n- **title** - The centered title patch (hidden on the utility bar).\n- **close-box** - The close widget (left).\n- **zoom-box** - The zoom widget (right).\n- **body** - The content area.\n- **header** - The header strip between the title bar and the body (when the `header` slot is populated).\n- **status-bar** - The bottom status strip (when the `status` slot is populated).\n- **grow-box** - The resize widget (bottom-right, when `resizable`).\n- **viewport** - The built-in scroll area's viewport (when `scrollbars`; re-exported from vf-scroll-area).",
4167
+ "description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" closable movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n\n**Opening from a rect.** show clears `hidden`, and given `from` — a\nbox in viewport CSS px, an icon's `cellRect()` or a button's rect — opens\nthe window from it: dotted rectangles grow from that box toward the\nwindow's frame, four on screen at a time, bunched at the box and opening\nout toward the window, drawn with the icon drag outline's XOR pen on the\ndesktop's drag surface; the window draws once they have drained, eighteen\nsteps later. Until then it matches `:state(opening)` and paints nothing —\nstill laid out, focusable and a hit target, so a page can place it, raise\nit and focus into it at once, and a press finishes the rects and lands on\nthe window. Under `prefers-reduced-motion`, with no `vf-desktop` on its\npath, or without `from`, the window shows at once.\n\n**Closing to a rect.** hide sets `hidden`, and given `to` closes\nthe window to it: the same rectangles run the other way, from where the\nframe was in toward the box, the window gone from the first step. A press\nfinishes them, and removing the window leaves them running. Under\n`prefers-reduced-motion`, with no `vf-desktop` on its path, or without\n`to`, the window hides at once. The window knows nothing about icons:\n`from` and `to` are any rect.\n\n**Dragging as an outline.** A `movable` window moves live under the\npointer. With `outline-drag` a press on the title bar draws a dotted\noutline around the window on the desktop's drag surface instead, with the\nsame pen; the drag moves the outline, held where the window can land, and\nthe window moves there on the release. Escape cancels.\n\n**Frameless.** With `frameless` the window draws no chrome — no title bar\nor widgets, no frame or shadow, no grow box, no header or status strip —\nand the body is the whole box: a window that takes the whole screen, say,\non `vf-desktop`'s full-screen tier. The chrome is hidden, not torn down, so\nthe switch moves no node and the focus in the body stays where it is.\n---\n\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close', altKey, shiftKey, metaKey, ctrlKey }`, the modifier keys held on the click (shape-compatible with vf-dialog's `vf-close`), so an Option-click can mean more than a click. The window does NOT remove itself; the consumer decides what closing means.\n- **vf-zoom** - Zoom box clicked. Detail `{}`.\n- **vf-resize** - The grow box resized the window. Detail `{ width, height, commit }`, sizes in whole system px: one event per size the drag writes (`commit: false`), fired after the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles — only when it changed the size. Fired by the gesture alone: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.\n\n### **Slots:**\n - _default_ - Default slot: window body content.\n- **header** - Optional header content — a band between the title bar and the body, the full width of the window (the Finder window's header line): a white interior over a 1px rule, no inset of its own, a positioning anchor for placed children. As tall as its content unless `header-height` states it. Under `scrollbars` the vertical rail begins below it, so the header spans the rail's column. Takes no space until populated.\n- **status** - Optional status-bar content — the classic bottom readout strip (\"40px x 40px\"): a 1px rule over a 15px white band under the body, body-face text on its native line. Takes no space until populated; a `resizable` window's grow box sits flush in its right end.\n\n### **CSS Properties:**\n - **--vf-dots-pattern** - the windoid bar's dot-grid dither — a 2×2 motif, one black pixel at its origin, on a 30-system-px tile (`vfDots`; override the whole tile like `--vf-desktop-pattern` — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n- **--vf-titlebar-height** - window/dialog title bars _(default: undefined)_\n- **--vf-titlebar-height-utility** - the slim `vf-window[variant=\"utility\"]` (windoid) bar — 11px interior + 1px bottom rule, traced from `Windows/utility-window.png` _(default: 12px)_\n- **--vf-status-bar-height** - the status strip: 1px rule + 14px interior — the grow box's own height, so the two compose flush _(default: 15px)_\n- **--vf-line-height** - the body face's native line, which the status strip's text rides (whole-pixel centered in the 14px interior) _(default: 12px)_\n\n### **CSS Parts:**\n - **frame** - The outer chrome frame.\n- **title-bar** - The striped (or dithered) title bar.\n- **title** - The centered title patch (hidden on the utility bar).\n- **close-box** - The close widget (left).\n- **zoom-box** - The zoom widget (right).\n- **body** - The content area.\n- **header** - The header strip between the title bar and the body (when the `header` slot is populated).\n- **status-bar** - The bottom status strip (when the `status` slot is populated).\n- **grow-box** - The resize widget (bottom-right, when `resizable`).\n- **viewport** - The built-in scroll area's viewport (when `scrollbars`; re-exported from vf-scroll-area).",
4163
4168
  "doc-url": "",
4164
4169
  "attributes": [
4165
4170
  {
@@ -4167,6 +4172,11 @@
4167
4172
  "description": "Chrome variant. Omit for the standard 18px striped bar; `'utility'` for\nthe slim windoid bar (dot-grid dither, 7×7 widgets, no title patch — the\nheading still names the widgets). Inside a `vf-desktop`, utility windows\nfloat above the document tier and never take the single-active state.",
4168
4173
  "value": { "type": "'utility' | undefined" }
4169
4174
  },
4175
+ {
4176
+ "name": "frameless",
4177
+ "description": "Draw no chrome: no title bar, so no close or zoom box, no frame or\nshadow, no grow box, and no header or status strip, whatever their slots\nhold. The body is the whole box, `pattern` filling it and a placed child\nmeasured from its corner; under `scrollbars` the built-in scroll area\nfills the box, its own frame line on the box's edge. `heading` still\nnames the window. The chrome is hidden rather than left out of the\nrender, so switching this on a connected window moves no node and the\nfocus in the body stays where it is. In a `vf-desktop` the window keeps\nits tier for activation and `hidden`, and `show()` and `hide()` run\ntheir rects from and to its box.",
4178
+ "value": { "type": "boolean", "default": "false" }
4179
+ },
4170
4180
  {
4171
4181
  "name": "heading",
4172
4182
  "description": "Title text shown centered in the title bar.",
@@ -4302,6 +4312,11 @@
4302
4312
  "description": "Chrome variant. Omit for the standard 18px striped bar; `'utility'` for\nthe slim windoid bar (dot-grid dither, 7×7 widgets, no title patch — the\nheading still names the widgets). Inside a `vf-desktop`, utility windows\nfloat above the document tier and never take the single-active state.",
4303
4313
  "type": "'utility' | undefined"
4304
4314
  },
4315
+ {
4316
+ "name": "frameless",
4317
+ "description": "Draw no chrome: no title bar, so no close or zoom box, no frame or\nshadow, no grow box, and no header or status strip, whatever their slots\nhold. The body is the whole box, `pattern` filling it and a placed child\nmeasured from its corner; under `scrollbars` the built-in scroll area\nfills the box, its own frame line on the box's edge. `heading` still\nnames the window. The chrome is hidden rather than left out of the\nrender, so switching this on a connected window moves no node and the\nfocus in the body stays where it is. In a `vf-desktop` the window keeps\nits tier for activation and `hidden`, and `show()` and `hide()` run\ntheir rects from and to its box.",
4318
+ "type": "boolean"
4319
+ },
4305
4320
  {
4306
4321
  "name": "heading",
4307
4322
  "description": "Title text shown centered in the title bar.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vintage-frames",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "Lit web components that emulate the look and feel of classic Mac OS System 7",
5
5
  "type": "module",
6
6
  "license": "MIT",