vintage-frames 0.12.1 → 0.12.3

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.
@@ -231,7 +231,7 @@
231
231
  },
232
232
  {
233
233
  "name": "vf-desktop",
234
- "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. 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\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, one whole-surface raster at one image px per system px, 1-bit at\nevery density 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. Not the top 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 with none 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).",
234
+ "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. 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\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, one whole-surface raster at one image px per system px, 1-bit at\nevery density 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 with none 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).",
235
235
  "attributes": [
236
236
  {
237
237
  "name": "width",
@@ -476,7 +476,7 @@
476
476
  },
477
477
  {
478
478
  "name": "vf-icon",
479
- "description": "`<vf-icon>` — a Finder icon: pixel art in a reserved cell with its name on a\nplate below, selectable, movable and renameable in place.\n\nIt is the one composite in the kit whose parts already existed separately —\n`vf-img` draws the art, `vf-list-item` inverts on selection, `vf-window`\ndrags — and the reason to have it anyway is that the *icon* is the unit the\nFinder actually manipulates. A picture and a caption that select together,\nmove together and rename together is a control, not a layout.\n\n### The art arrives by slot\n\nTwo slots, one per resource size — `large` is the 32×32 `ICN#` and `small`\nthe 16×16 `ics#` — each holding a `vf-img` around the consumer's own `<img>`:\n\n```html\n<vf-icon label=\"Macintosh HD\" selectable movable editable>\n <vf-img slot=\"large\"><img src=\"hd-32.png\" alt=\"\"></vf-img>\n <vf-img slot=\"small\"><img src=\"hd-16.png\" alt=\"\"></vf-img>\n</vf-icon>\n```\n\nSlots rather than `src` properties, for the reason `vf-img` exists at all:\nthe kit ships no raster files and never builds an `<img>` on a consumer's\nbehalf, so the graphic stays a real element in the light DOM with its own\n`alt`, `srcset`, loading behavior and asset URLs. A `src` string can express\nnone of that, and it could not hold an inline `<svg>` or a `<canvas>` either.\n`vf-list-item`'s `icon` slot makes the same trade.\n\nThe cost is that both files fetch even though one paints — pay it with two\ndata URIs, or slot only the size that view uses.\n\n### The cell is reserved, the art is registered in it\n\n`size` picks which slot paints *and* the cell it paints in: 32×32 or 16×16,\nheld whether or not there is art for it. A folder is 32×20 of ink and a\ndocument 25×32, and a row of them keeps one baseline only because the cell —\nnot the ink — is the unit, which is what an icon resource always was.\n\n### Everything centered lands on a whole pixel, by parity\n\nThe frame centers two things over one axis — the art cell and the name — and\na centered child sits at `(box − child) / 2`. That is a whole number exactly\nwhen the box and the child have the **same parity**. Half a system pixel is\nwhat fringes 1-bit art: the glyph stems smear across two device columns and\ngo gray, while the plate behind them stays sharp, because backgrounds are\npixel-snapped by the compositor and glyphs are not. A crisp plate under a\ngrey name is the signature of exactly this.\n\nSo the component makes the parities agree instead of correcting afterwards.\nThe cell is 32 or 16, and #measurePlate sizes the name plate to a\nwhole **even** number of system px — measured from the text, rounded up. Both\nchildren are then even, and so is every offset, at every display density and\nwhether or not a `width` is declared. Nothing is snapped, nothing leans on\nthe rasterizer, and `verify:icon` asserts both the geometry and that a field\nof names renders without a single gray pixel — with the kit's normal\nantialiasing left on, since a run on whole pixels gives it nothing to smooth.\n\nYour side of it is one rule: **a declared `width` must be even**, since the\ncomponent cannot round a number you chose. Supply art at the cell size too —\nwhich \"one image pixel is one system pixel\" already implies — because art\ndiffering from its cell by an odd number centers the same way.\n\n### Selection inverts, because the art is a 1-bit mask\n\nA System 7 icon is black ink and opaque white on a transparent surround —\nprecisely an image plus its mask — so inverting it flips ink and fill and\nleaves the surround alone, which is the whole of the classic selected\nappearance. `filter: invert(1)` reproduces it exactly for 1-bit art. Color\nart would invert into a photographic negative, which was never what\nSystem 7 showed: declare `color` and selection **darkens** the art\ninstead — Icon Utilities' ttSelected transform, every color blended\nhalfway toward black (`brightness(0.5)`), whites going gray, the\ntransparent surround still untouched. The declaration is the consumer's\nbecause the component cannot tell color art from a mask by looking at a\nslotted image. It covers whatever the cell shows, the derived open ghost\nincluded, so a color icon has one selected treatment open or closed. The\nlabel plate inverts to the `--vf-highlight` pair either way, sharing one\nselection color with `vf-list-item` — the selected name was inverted\nwhatever the art.\n\n### Open is derived, not shipped\n\nWith `open`, the art redraws as the Finder's open ghost: the outline held\nin solid black, the interior re-filled with the kit's loose 25% dither (the\nscrollbar trough's lattice), the transparent surround untouched. There is\nno second raster and no second fetch — the same alpha channel that makes\nselection an inversion makes the ghost derivable, and `src/open-art.ts`\nderives it from the slotted art by canvas compositing alone. No pixels are\never read back, so a cross-origin image that taints its canvas still works:\ntaint forbids reading, not drawing or displaying.\n\nThe ghost keeps the shape selection expects — ink and opaque white on a\ntransparent surround — so a selected open icon inverts exactly as a closed\none does, with no second treatment. The slot stays in the tree while the\nghost paints, hidden (it is where the art loads, and re-loads, from), and\nart the pipeline cannot draw — nothing slotted yet, a failed load, an\ninline `<svg>` — keeps rendering as itself rather than vanishing behind a\nstate it cannot show.\n\n### `movable`, not `draggable`\n\n`draggable` is a global HTML attribute *and* an `HTMLElement` accessor, so\ndeclaring it would both shadow a platform member and hand the element to the\nbrowser's own drag-and-drop machinery. This is the `align` trap from\n`vf-stack` in a second costume — `align`, `hidden`, `dir`, `draggable` and\n`title` all carry behavior a custom element never asked for. The kit already\nspells this parameter `movable` on `vf-window`, so the icon does too, and it\nmoves the same way: `DragController` tracks the gesture and\n`PlacementController` writes the result into `left`/`top` in whole system px,\nthe art's own unit — the same pair markup places an icon with, so a moved\nicon is still where it was dropped after a zoom.\n\nDragging is a pointer gesture with no keyboard equivalent, which is the kind\nof gap the kit closes rather than inherits (SPEC §1): a focused movable icon\nalso moves under the arrow keys, one system px at a time and eight with\nShift. Focus is what `selectable` grants, so the keyboard half of `movable`\nand `editable` presupposes it — see the role section below.\n\n### The drag is an outline; the icon stays put\n\nThe Finder dragged an icon as a dotted outline — the mask's boundary and\nthe name's rectangle — drawn over everything with the XOR pen, the icon\nitself staying put until the drop. The kit draws it from what it already\nhas: deriveDragOutline finds the ring the way the open ghost does\n(the silhouette from the art's alpha, the plate's rectangle united with\nit, the whole inset by one pixel as `FrameRgn` inset a region, the ring\nthe inset removes — so where the art and the plate abut, the outline\nwraps around both), and dotOutline dots it against a 2×2 checker\n— compositing only, so cross-origin art derives too, and art the\npipeline cannot draw falls back to the cell's rectangle. The canvas\ncovers the frame widened to the plate, since a name wider than a\ndeclared `width` overhangs the frame.\nThe canvas paints with the cursor's XOR recipe, `filter: invert(1)` under\n`mix-blend-mode: difference`: a dotted black line over a white window\nbody, and over the desktop dither a black line, the composition\nQuickDraw's pattern pen gave — the checker's phase is taken from where\nthe outline lands on the *screen* (penPhase), so its dots fall on\nthe pixels the dither leaves white wherever it goes, and the dither's own\nink fills in between them.\n\nIt draws on the desktop's drag surface: a layer in the screen's shadow,\nover windows, palettes and the menu bar alike, clipped at the raster's\nedge like everything else, reached through the `vf-drag-surface-request`\nhandshake (bubbles, non-composed — it travels up the slots the icon is\nassigned to, so it works from inside a window body). With no desktop the\ncanvas lives in the icon's own frame instead, translated by the delta,\nclipped by whatever clips the icon; the gesture and its events are the\nsame either way. Its origin is a whole count of system px from the\nsurface's, written as a live `sysLength()`, so a scale change mid-gesture\ncannot smear it.\n\n### The drag reports, the page files\n\nThe kit ships no filing semantics — folders are the page's own catalog —\nso a drag is a stream of events and a cancelable commit, on the icon:\n\n- `vf-drag-start` `{ left, top }` on the first lattice step away from the\n press (a plain click never starts one, and never fires a drop);\n- `vf-drag` `{ clientX, clientY, left, top, x, y }` on every step that\n changed the snapped proposal;\n- `vf-drop`, the same shape at the release, **cancelable**: its default\n action is the move, the proposal written through `left`/`top` and\n clamped whole in the container measured at the press — held at the\n origin only in a scrolling plane, whose rails reach the rest. `preventDefault()`\n writes nothing — the page re-parents or places the icon itself, the\n outline's origin converted with the destination's `placementAt`;\n- `vf-drag-cancel` `{}` on Escape or a `pointercancel`: nothing written.\n\n`left`/`top` in the detail are the *proposal*, unclamped — the origin plus\nthe lattice-snapped delta — and only the default action clamps, so a page\nthat cancels gets the raw gesture. `x`/`y` are the outline's top-left in\nviewport CSS px: the frame's box at the press translated by the delta,\nthe press offset already inside it, so an icon placed at the\ndestination's `placementAt(x, y)` lands exactly where the outline was.\n\n### The selection travels\n\nA drag that begins on a *selected* icon takes every other selected,\nmovable icon of its field with it — the Finder outlined the whole\nselection under a drag on any member. Each follower draws its own outline\n(followDrag), moved by the same lattice delta as the leader's, and\nthe leader's events carry the set as `detail.icons`, itself first. The\ndefault action of the drop clamps the delta once for the whole group —\nthe arrangement is kept, and the group stops when its outermost member\nmeets the container's edge — then writes each member's origin plus that\ndelta. A cancel takes every outline down and writes nothing. A press on\nan unselected icon makes it the selection first (the outside listeners\nclear the others), so it drags alone; a Shift press that deselects the\npressed icon drags it alone too, the rest staying where they are.\n\nFor the selection to travel, a plain press on one of its members must not\ncollapse it: the other icons' outside-press listeners leave a selection\nalone when the press landed on an icon already selected in the same\nfield. A *click* on such an icon — released with no drag — collapses the\nselection to it on the release, so a click still single-selects and the\nsame press could have dragged them all.\n\nEscape is heard by a document listener scoped to the gesture rather than\nby the icon's own key handler, because a `movable`-only icon never takes\nfocus — an addition in the kit's idiom (a pointer-only gesture with no way\nout is the kind of gap SPEC §1 closes), not a claim about the Finder.\n\n`target` is the destination highlight the page sets on the folder under a\ndrag: the selected treatment of the art alone — inverted, or darkened\nunder `color` — with none of `selected`'s semantics: no event, no outside\nlistener, no `aria-selected`, the plate untouched.\n\n### The page drags it too\n\nThe Finder's Clean Up walked a container's icons one at a time, each\nicon's outline travelling from where it sat to its cell and the icon\nlanding when the outline arrived — the drag's own vocabulary, with the\nFinder for a hand. dragTo is that move for one icon: the outline\nbuilt and placed as a drag's is, on the desktop's surface or in the frame,\nstepped to the landing at the walk's cadence (runOutlineTravel,\nsrc/motion.ts — the kit's, no knobs), then the outline down and the icon\nwritten where a drop would write it. The landing is resolved before the\noutline sets off, so the outline goes where the icon will land rather\nthan where a pair past the container's edge pointed. moveTo is\nthat write alone: the drop's clamp and snap, one `vf-placement-change` —\nwhere a property set is the authored pair, unclamped. Neither fires a\ndrag event (the page is the mover and already knows) and neither needs\n`movable`, which says the hand may drag: the page may always place its\nicon. `vf-icon-field.dragIcons` walks a set through `dragTo` with the\nFinder's beat between landings.\n\nA walk is finished, never cancelled: a press anywhere, or Escape, lands\nthe icon at once — the user wants the machine back, and the move was the\npage's to make. A second `dragTo`, or a `moveTo`, finishes the one in\nflight the same way; leaving the DOM takes the outline down and writes\nnothing. Under `prefers-reduced-motion` the icon lands at once with no\noutline, the blink's posture — as does one being renamed, or one under\nthe hand's own press.\n\nOpening gets the same treatment. The double-click is the pointer gesture,\nand its keyboard route is ⌘O / ⌘↓ — the System 7 Open shortcuts, with Ctrl\nstanding in for ⌘ off the Mac. Return is deliberately not one of them: the\nFinder's Return renamed, never opened, so on an editable icon it starts the\nedit and on a non-editable one it does nothing at all.\n\n### The name and the art are one target, and the second click decides\n\nA double-click opens the icon *wherever it lands* — the name is as much the\nicon as the picture is, and the Finder never made you aim at the 32 pixels of\nart. But the name is also where a single click renames, so the two gestures\nbegin with the same press and only the second one tells them apart.\n\nSo the rename waits for it. A press on the plate of an already-selected icon\narms the field rather than opening it, and the next press inside\nRENAME_DELAY_MS calls it off — leaving the double-click to open, with\nno rename box flashing up behind it. Nothing needs to *undo* an edit that\nbegan: the press that starts one and the press that opens are the same\npress, so the only thing that can be got right is not committing early.\n\nThe window is generous in the one direction that is cheap. Reading a lone\nclick as a pair costs a wait before the box appears; reading a pair as a lone\nclick renames when the user asked to open. The same reasoning covers a press\nthat turns into a drag, a press elsewhere, and any key — each calls the\npending rename off, because none of them is the click it is waiting for.\n\n### An icon alone is a picture; an icon in a field is an option\n\n`role=\"option\"` is only meaningful inside a `listbox` that owns it. Written\nunconditionally it is not merely untidy — the browser *drops* it, and\n`aria-selected` with it, so a `selectable` icon announced as a bare generic\nand its selection state reached assistive tech nowhere at all.\n\nSo the role follows the container. Owned, the icon is an `option` that names\nitself from its plate and publishes `aria-selected`. Unowned, it degrades to\n`role=\"img\"` with a name — the same vocabulary the derived open ghost uses,\nand true of what it is. Deliberately not `button`: that would promise Enter\nand Space activate, and here Return *renames* while the open route is ⌘O / ⌘↓.\n\nThe owner is `vf-icon-field`, the container a field of icons sits in, and\nit is what buys the selection state back:\n\n```html\n<vf-icon-field label=\"Desktop\">\n <vf-icon label=\"Macintosh HD\" selectable movable editable>…</vf-icon>\n <vf-icon label=\"Trash\" selectable movable editable>…</vf-icon>\n</vf-icon-field>\n```\n\nA `vf-desktop` cannot be that container itself: it also holds windows and a\nmenu bar, and a non-`option` child of a listbox is invalid the same way the\norphaned option was. The field is layout-neutral — placed icons anchor to\nthe nearest *positioned* ancestor, which is still the desktop's raster. An\nelement of your own carrying `role=\"listbox\"` still owns an icon the same\nway. One divergence from the APG listbox is deliberate: its options share\na single roving tab stop, while these stay one stop each, the way a Finder\nicon is reached on its own.\n\n**`selectable` is what makes an icon focusable**, and `movable`/`editable`\npresuppose it. That is the Finder's own model — you cannot move or rename\nwhat you have not selected — and the pointer path already assumed it: the\nrename opens on a press on the plate of an *already-selected* icon. A\n`movable`-only icon is a picture you can drag, not a widget.\n\n### The label is a property, because it is editable\n\nThe caption is `label` rather than slotted content: renaming means the\ncomponent owns the string and hands it back on `vf-change`, and it cannot own\ntext that lives in the consumer's DOM. An empty `label` draws no plate at\nall — that *is* the \"no label\" parameter, in preference to a second attribute\nthat could disagree with it. `editable` then lets a click on the plate of an\nalready-selected icon open the rename box a moment later, as the Finder's\ndoes (see above), with Return committing, Escape reverting, and the plate\nwidening as you type.\n\n### A name is never abbreviated, and never folded\n\nThere is no ellipsis, no clipping and no wrapping: one line, always. System 7\nsolved the long-name problem at the *other* end — HFS capped a filename at 31\ncharacters — so the Finder could afford to always draw the name in full, and\ndid. A name wider than its cell simply overflows it, centered, the way a name\nwider than a 32-pixel icon always did.\n\nStaying on one line is also what keeps the name on the grid: a single run has\na single measured width, so the even-plate rule above is one number rather\nthan one per line, each with its own parity. `width` is therefore the cell —\nthe grid pitch — not a bound on the name.\n\n`maxlength` (31) bounds the rename field rather than the `label` property:\nthe name belongs to the consumer's model, and truncating one handed to us\nwould lose data.\n\nThe box hugs its text while you type it, not only once you are done, which is\nwhat keeps the name from moving when the edit commits: the plate is the same\nwidth either side of it, so the glyphs stay exactly where they were.\n\nA file also has to be called *something*, so a rename committed empty — or\nas nothing but spaces — is refused rather than applied, and the previous name\ncomes back, which is what System 7 did. An empty `label` is still a perfectly\ngood state to *start* in, though: a freshly made icon has no name until it is\ngiven one. Such an icon draws no plate but stays selectable, focusable and\nrenameable — Return opens a field, and an edit with nothing in it yet is the\none time the box stops hugging and reserves a cell's width, since a field you\ncannot see is one you cannot type into.\n---\n\n\n### **Events:**\n - **vf-select** - Selection changed by user interaction. `detail: { selected: boolean }`.\n- **vf-change** - The name was committed. `detail: { label: string, previous: string }`.\n- **vf-open** - The icon was opened — double-clicked anywhere on it, its name included, or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`.\n- **vf-name-too-long** - A rename was typed or pasted past `maxlength`, and the field refused the excess. `detail: { attempted, accepted, limit }` — enough to raise the alert System 7 raised rather than drop the characters silently. Not fired for a `label` set from your own code, which is never truncated in the first place.\n- **vf-name-rejected** - A rename was committed with no name in it, so the edit was dropped and the old name put back. `detail: { attempted, kept, reason: 'empty' }`. A `vf-change` is *not* fired alongside it — nothing changed.\n- **vf-drag-start** - A drag began: the first lattice step away from the press. `detail: { left, top, icons }` — the origin, whole system px in the icon's container, and every icon the drag carries (this one first).\n- **vf-drag** - A step that changed the proposal. `detail: { clientX, clientY, left, top, x, y, icons }` — the pointer in viewport CSS px, the proposed origin in the container (whole system px on the lattice), the outline's top-left in viewport CSS px, and the icons travelling.\n- **vf-drop** - The release; cancelable. The same detail as `vf-drag`. Its default action writes the proposal through `left`/`top` — every icon in `icons` by the same delta, clamped whole in the container as a group; `preventDefault()` writes nothing.\n- **vf-drag-cancel** - The drag was abandoned — Escape, or a `pointercancel` — and nothing was written. `detail: {}`.\n\n### **Slots:**\n - **large** - The 32×32 art, normally a `vf-img` around an `<img>`.\n- **small** - The 16×16 art, shown under `size=\"small\"`.\n\n### **CSS Properties:**\n - **--vf-icon-gap** - Space between the art cell and the name plate; 0 by default, the plate directly under the cell as the Finder drew it _(default: undefined)_\n- **--vf-icon-label-height** - The name plate's line box _(default: undefined)_\n\n### **CSS Parts:**\n - **frame** - The icon and its label plate, stacked.\n- **icon** - The reserved art cell.\n- **label** - The name block; its lines are centered under the art.\n- **plate** - The inked run behind the name, which each wrapped line gets its own of (inverts when selected).\n- **input** - The rename field, while editing.",
479
+ "description": "`<vf-icon>` — a Finder icon: pixel art in a reserved cell with its name on a\nplate below, selectable, movable and renameable in place.\n\nIt is the one composite in the kit whose parts already existed separately —\n`vf-img` draws the art, `vf-list-item` inverts on selection, `vf-window`\ndrags — and the reason to have it anyway is that the *icon* is the unit the\nFinder actually manipulates. A picture and a caption that select together,\nmove together and rename together is a control, not a layout.\n\n### The art arrives by slot\n\nTwo slots, one per resource size — `large` is the 32×32 `ICN#` and `small`\nthe 16×16 `ics#` — each holding a `vf-img` around the consumer's own `<img>`:\n\n```html\n<vf-icon label=\"Macintosh HD\" selectable movable editable>\n <vf-img slot=\"large\"><img src=\"hd-32.png\" alt=\"\"></vf-img>\n <vf-img slot=\"small\"><img src=\"hd-16.png\" alt=\"\"></vf-img>\n</vf-icon>\n```\n\nSlots rather than `src` properties, for the reason `vf-img` exists at all:\nthe kit ships no raster files and never builds an `<img>` on a consumer's\nbehalf, so the graphic stays a real element in the light DOM with its own\n`alt`, `srcset`, loading behavior and asset URLs. A `src` string can express\nnone of that, and it could not hold an inline `<svg>` or a `<canvas>` either.\n`vf-list-item`'s `icon` slot makes the same trade.\n\nThe cost is that both files fetch even though one paints — pay it with two\ndata URIs, or slot only the size that view uses.\n\n### The cell is reserved, the art is registered in it\n\n`size` picks which slot paints *and* the cell it paints in: 32×32 or 16×16,\nheld whether or not there is art for it. A folder is 32×20 of ink and a\ndocument 25×32, and a row of them keeps one baseline only because the cell —\nnot the ink — is the unit, which is what an icon resource always was.\n\n### Everything centered lands on a whole pixel, by parity\n\nThe frame centers two things over one axis — the art cell and the name — and\na centered child sits at `(box − child) / 2`. That is a whole number exactly\nwhen the box and the child have the **same parity**. Half a system pixel is\nwhat fringes 1-bit art: the glyph stems smear across two device columns and\ngo gray, while the plate behind them stays sharp, because backgrounds are\npixel-snapped by the compositor and glyphs are not. A crisp plate under a\ngrey name is the signature of exactly this.\n\nSo the component makes the parities agree instead of correcting afterwards.\nThe cell is 32 or 16, and #measurePlate sizes the name plate to a\nwhole **even** number of system px — measured from the text, rounded up. Both\nchildren are then even, and so is every offset, at every display density and\nwhether or not a `width` is declared. Nothing is snapped, nothing leans on\nthe rasterizer, and `verify:icon` asserts both the geometry and that a field\nof names renders without a single gray pixel — with the kit's normal\nantialiasing left on, since a run on whole pixels gives it nothing to smooth.\n\nYour side of it is one rule: **a declared `width` must be even**, since the\ncomponent cannot round a number you chose. Supply art at the cell size too —\nwhich \"one image pixel is one system pixel\" already implies — because art\ndiffering from its cell by an odd number centers the same way.\n\n### Selection inverts, because the art is a 1-bit mask\n\nA System 7 icon is black ink and opaque white on a transparent surround —\nprecisely an image plus its mask — so inverting it flips ink and fill and\nleaves the surround alone, which is the whole of the classic selected\nappearance. `filter: invert(1)` reproduces it exactly for 1-bit art. Color\nart would invert into a photographic negative, which was never what\nSystem 7 showed: declare `color` and selection **darkens** the art\ninstead — Icon Utilities' ttSelected transform, every color blended\nhalfway toward black (`brightness(0.5)`), whites going gray, the\ntransparent surround still untouched. The declaration is the consumer's\nbecause the component cannot tell color art from a mask by looking at a\nslotted image. It covers whatever the cell shows, the derived open ghost\nincluded, so a color icon has one selected treatment open or closed. The\nlabel plate inverts to the `--vf-highlight` pair either way, sharing one\nselection color with `vf-list-item` — the selected name was inverted\nwhatever the art.\n\n### Open is derived, not shipped\n\nWith `open`, the art redraws as the Finder's open ghost: the outline held\nin solid black, the interior re-filled with the kit's loose 25% dither (the\nscrollbar trough's lattice), the transparent surround untouched. There is\nno second raster and no second fetch — the same alpha channel that makes\nselection an inversion makes the ghost derivable, and `src/open-art.ts`\nderives it from the slotted art by canvas compositing alone. No pixels are\never read back, so a cross-origin image that taints its canvas still works:\ntaint forbids reading, not drawing or displaying.\n\nThe ghost keeps the shape selection expects — ink and opaque white on a\ntransparent surround — so a selected open icon inverts exactly as a closed\none does, with no second treatment. The slot stays in the tree while the\nghost paints, hidden (it is where the art loads, and re-loads, from), and\nart the pipeline cannot draw — nothing slotted yet, a failed load, an\ninline `<svg>` — keeps rendering as itself rather than vanishing behind a\nstate it cannot show.\n\n### `movable`, not `draggable`\n\n`draggable` is a global HTML attribute *and* an `HTMLElement` accessor, so\ndeclaring it would both shadow a platform member and hand the element to the\nbrowser's own drag-and-drop machinery. This is the `align` trap from\n`vf-stack` in a second costume — `align`, `hidden`, `dir`, `draggable` and\n`title` all carry behavior a custom element never asked for. The kit already\nspells this parameter `movable` on `vf-window`, so the icon does too, and it\nmoves the same way: `DragController` tracks the gesture and\n`PlacementController` writes the result into `left`/`top` in whole system px,\nthe art's own unit — the same pair markup places an icon with, so a moved\nicon is still where it was dropped after a zoom.\n\nDragging is a pointer gesture with no keyboard equivalent, which is the kind\nof gap the kit closes rather than inherits (SPEC §1): a focused movable icon\nalso moves under the arrow keys, one system px at a time and eight with\nShift. Focus is what `selectable` grants, so the keyboard half of `movable`\nand `editable` presupposes it — see the role section below.\n\n### The drag is an outline; the icon stays put\n\nThe Finder dragged an icon as a dotted outline — the mask's boundary and\nthe name's rectangle — drawn over everything with the XOR pen, the icon\nitself staying put until the drop. The kit draws it from what it already\nhas: deriveDragOutline finds the ring the way the open ghost does\n(the silhouette from the art's alpha, the plate's rectangle united with\nit, the whole inset by one pixel as `FrameRgn` inset a region, the ring\nthe inset removes — so where the art and the plate abut, the outline\nwraps around both), and dotOutline dots it against a 2×2 checker\n— compositing only, so cross-origin art derives too, and art the\npipeline cannot draw falls back to the cell's rectangle. The canvas\ncovers the frame widened to the plate, since a name wider than a\ndeclared `width` overhangs the frame.\nThe canvas paints with the cursor's XOR recipe, `filter: invert(1)` under\n`mix-blend-mode: difference`: a dotted black line over a white window\nbody, and over the desktop dither a black line, the composition\nQuickDraw's pattern pen gave — the checker's phase is taken from where\nthe outline lands on the *screen* (penPhase), so its dots fall on\nthe pixels the dither leaves white wherever it goes, and the dither's own\nink fills in between them.\n\nIt draws on the desktop's drag surface: a layer in the screen's shadow,\nover windows, palettes and the menu bar alike, clipped at the raster's\nedge like everything else, reached through the `vf-drag-surface-request`\nhandshake (bubbles, non-composed — it travels up the slots the icon is\nassigned to, so it works from inside a window body). With no desktop the\ncanvas lives in the icon's own frame instead, translated by the delta,\nclipped by whatever clips the icon; the gesture and its events are the\nsame either way. Its origin is a whole count of system px from the\nsurface's, written as a live `sysLength()`, so a scale change mid-gesture\ncannot smear it.\n\n### The drag reports, the page files\n\nThe kit ships no filing semantics — folders are the page's own catalog —\nso a drag is a stream of events and a cancelable commit, on the icon:\n\n- `vf-drag-start` `{ left, top }` on the first lattice step past the drag\n slop (a click or a tap never starts one, and never fires a drop — see\n \"Mouse, finger and pen\" below);\n- `vf-drag` `{ clientX, clientY, left, top, x, y }` on every step that\n changed the snapped proposal;\n- `vf-drop`, the same shape at the release, **cancelable**: its default\n action is the move, the proposal written through `left`/`top` and\n clamped whole in the container measured at the press — held at the\n origin only in a scrolling plane, whose rails reach the rest. `preventDefault()`\n writes nothing — the page re-parents or places the icon itself, the\n outline's origin converted with the destination's `placementAt`;\n- `vf-drag-cancel` `{}` on Escape or a `pointercancel`: nothing written.\n\n`left`/`top` in the detail are the *proposal*, unclamped — the origin plus\nthe lattice-snapped delta — and only the default action clamps, so a page\nthat cancels gets the raw gesture. `x`/`y` are the outline's top-left in\nviewport CSS px: the frame's box at the press translated by the delta,\nthe press offset already inside it, so an icon placed at the\ndestination's `placementAt(x, y)` lands exactly where the outline was.\n\n### The selection travels\n\nA drag that begins on a *selected* icon takes every other selected,\nmovable icon of its field with it — the Finder outlined the whole\nselection under a drag on any member. Each follower draws its own outline\n(followDrag), moved by the same lattice delta as the leader's, and\nthe leader's events carry the set as `detail.icons`, itself first. The\ndefault action of the drop clamps the delta once for the whole group —\nthe arrangement is kept, and the group stops when its outermost member\nmeets the container's edge — then writes each member's origin plus that\ndelta. A cancel takes every outline down and writes nothing. A press on\nan unselected icon makes it the selection first (the outside listeners\nclear the others), so it drags alone; a Shift press that deselects the\npressed icon drags it alone too, the rest staying where they are.\n\nFor the selection to travel, a plain press on one of its members must not\ncollapse it: the other icons' outside-press listeners leave a selection\nalone when the press landed on an icon already selected in the same\nfield. A *click* on such an icon — released with no drag — collapses the\nselection to it on the release, so a click still single-selects and the\nsame press could have dragged them all.\n\nEscape is heard by a document listener scoped to the gesture rather than\nby the icon's own key handler, because a `movable`-only icon never takes\nfocus — an addition in the kit's idiom (a pointer-only gesture with no way\nout is the kind of gap SPEC §1 closes), not a claim about the Finder.\n\n`target` is the destination highlight the page sets on the folder under a\ndrag: the selected treatment of the art alone — inverted, or darkened\nunder `color` — with none of `selected`'s semantics: no event, no outside\nlistener, no `aria-selected`, the plate untouched.\n\n### The page drags it too\n\nThe Finder's Clean Up walked a container's icons one at a time, each\nicon's outline travelling from where it sat to its cell and the icon\nlanding when the outline arrived — the drag's own vocabulary, with the\nFinder for a hand. dragTo is that move for one icon: the outline\nbuilt and placed as a drag's is, on the desktop's surface or in the frame,\nstepped to the landing at the walk's cadence (runOutlineTravel,\nsrc/motion.ts — the kit's, no knobs), then the outline down and the icon\nwritten where a drop would write it. The landing is resolved before the\noutline sets off, so the outline goes where the icon will land rather\nthan where a pair past the container's edge pointed. moveTo is\nthat write alone: the drop's clamp and snap, one `vf-placement-change` —\nwhere a property set is the authored pair, unclamped. Neither fires a\ndrag event (the page is the mover and already knows) and neither needs\n`movable`, which says the hand may drag: the page may always place its\nicon. `vf-icon-field.dragIcons` walks a set through `dragTo` with the\nFinder's beat between landings.\n\nA walk is finished, never cancelled: a press anywhere, or Escape, lands\nthe icon at once — the user wants the machine back, and the move was the\npage's to make. A second `dragTo`, or a `moveTo`, finishes the one in\nflight the same way; leaving the DOM takes the outline down and writes\nnothing. Under `prefers-reduced-motion` the icon lands at once with no\noutline, the blink's posture — as does one being renamed, or one under\nthe hand's own press.\n\nOpening gets the same treatment. The double-click is the pointer gesture,\nand its keyboard route is ⌘O / ⌘↓ — the System 7 Open shortcuts, with Ctrl\nstanding in for ⌘ off the Mac. Return is deliberately not one of them: the\nFinder's Return renamed, never opened, so on an editable icon it starts the\nedit and on a non-editable one it does nothing at all.\n\n### Mouse, finger and pen\n\nThe instrument never changes what the icon means: a tap is a click, two\ntaps are a double-click, a drag is a drag. Two things are measured per\ninstrument so that holds, both added by the kit — System 7 had the mouse\nalone.\n\n**The open gesture.** The mouse opens on the platform's `dblclick`, which\nfollows the user's own double-click setting. A finger or a pen opens on a\n*tap pair* the icon classifies from its own pointer events: two taps on\nthe icon, the second pressed within `TAP_PAIR_MS` (500) of the first and\nno further than `TAP_PAIR_SLOP_PX` (24 CSS px) from it, `vf-open` firing\non the second release. A platform reserves the double-tap for its zoom\nand may deliver no `dblclick` for one, and a movable icon's press is\n`preventDefault()`ed for the drag, which can suppress it too; the pair\ndepends on neither. A tap is a press released where it landed — one that\nbecame a drag, or that the platform cancelled, is not half of a pair —\nand a pair does not chain: a third tap starts the next one. Each press\ntakes exactly one of the two routes, so a platform that does deliver a\n`dblclick` for a double-tap opens the icon once. The frame sets\n`touch-action: manipulation`, which takes the double-tap zoom off the\nicon and leaves panning and pinching to the page.\n\n**The drag slop.** A drag begins once the pointer has travelled from its\npress by `DRAG_SLOP_PX` (4 CSS px) for the mouse or `DRAG_SLOP_COARSE_PX`\n(10) for a finger or pen, which never lands still. Inside it the press is\na click or a tap: the icon stays on its spot, a rename the press armed\nstays armed, and no drag event fires. The delta is measured from the\npress all the same, so the outline begins under the pointer. The numbers\nare CSS px rather than system px because they measure the hand, not the\nart (src/motion.ts).\n\n### The name and the art are one target, and the second click decides\n\nA double-click opens the icon *wherever it lands* — the name is as much the\nicon as the picture is, and the Finder never made you aim at the 32 pixels of\nart. But the name is also where a single click renames, so the two gestures\nbegin with the same press and only the second one tells them apart.\n\nSo the rename waits for it. A press on the plate of an already-selected icon\narms the field rather than opening it, and the next press inside\nRENAME_DELAY_MS calls it off — leaving the double-click, or a\nfinger's tap pair, to open, with no rename box flashing up behind it. Nothing needs to *undo* an edit that\nbegan: the press that starts one and the press that opens are the same\npress, so the only thing that can be got right is not committing early.\n\nThe window is generous in the one direction that is cheap. Reading a lone\nclick as a pair costs a wait before the box appears; reading a pair as a lone\nclick renames when the user asked to open. The same reasoning covers a press\nthat turns into a drag, a press elsewhere, and any key — each calls the\npending rename off, because none of them is the click it is waiting for.\n\n### An icon alone is a picture; an icon in a field is an option\n\n`role=\"option\"` is only meaningful inside a `listbox` that owns it. Written\nunconditionally it is not merely untidy — the browser *drops* it, and\n`aria-selected` with it, so a `selectable` icon announced as a bare generic\nand its selection state reached assistive tech nowhere at all.\n\nSo the role follows the container. Owned, the icon is an `option` that names\nitself from its plate and publishes `aria-selected`. Unowned, it degrades to\n`role=\"img\"` with a name — the same vocabulary the derived open ghost uses,\nand true of what it is. Deliberately not `button`: that would promise Enter\nand Space activate, and here Return *renames* while the open route is ⌘O / ⌘↓.\n\nThe owner is `vf-icon-field`, the container a field of icons sits in, and\nit is what buys the selection state back:\n\n```html\n<vf-icon-field label=\"Desktop\">\n <vf-icon label=\"Macintosh HD\" selectable movable editable>…</vf-icon>\n <vf-icon label=\"Trash\" selectable movable editable>…</vf-icon>\n</vf-icon-field>\n```\n\nA `vf-desktop` cannot be that container itself: it also holds windows and a\nmenu bar, and a non-`option` child of a listbox is invalid the same way the\norphaned option was. The field is layout-neutral — placed icons anchor to\nthe nearest *positioned* ancestor, which is still the desktop's raster. An\nelement of your own carrying `role=\"listbox\"` still owns an icon the same\nway. One divergence from the APG listbox is deliberate: its options share\na single roving tab stop, while these stay one stop each, the way a Finder\nicon is reached on its own.\n\n**`selectable` is what makes an icon focusable**, and `movable`/`editable`\npresuppose it. That is the Finder's own model — you cannot move or rename\nwhat you have not selected — and the pointer path already assumed it: the\nrename opens on a press on the plate of an *already-selected* icon. A\n`movable`-only icon is a picture you can drag, not a widget.\n\n### The label is a property, because it is editable\n\nThe caption is `label` rather than slotted content: renaming means the\ncomponent owns the string and hands it back on `vf-change`, and it cannot own\ntext that lives in the consumer's DOM. An empty `label` draws no plate at\nall — that *is* the \"no label\" parameter, in preference to a second attribute\nthat could disagree with it. `editable` then lets a click on the plate of an\nalready-selected icon open the rename box a moment later, as the Finder's\ndoes (see above), with Return committing, Escape reverting, and the plate\nwidening as you type.\n\n### A name is never abbreviated, and never folded\n\nThere is no ellipsis, no clipping and no wrapping: one line, always. System 7\nsolved the long-name problem at the *other* end — HFS capped a filename at 31\ncharacters — so the Finder could afford to always draw the name in full, and\ndid. A name wider than its cell simply overflows it, centered, the way a name\nwider than a 32-pixel icon always did.\n\nStaying on one line is also what keeps the name on the grid: a single run has\na single measured width, so the even-plate rule above is one number rather\nthan one per line, each with its own parity. `width` is therefore the cell —\nthe grid pitch — not a bound on the name.\n\n`maxlength` (31) bounds the rename field rather than the `label` property:\nthe name belongs to the consumer's model, and truncating one handed to us\nwould lose data.\n\nThe box hugs its text while you type it, not only once you are done, which is\nwhat keeps the name from moving when the edit commits: the plate is the same\nwidth either side of it, so the glyphs stay exactly where they were.\n\nA file also has to be called *something*, so a rename committed empty — or\nas nothing but spaces — is refused rather than applied, and the previous name\ncomes back, which is what System 7 did. An empty `label` is still a perfectly\ngood state to *start* in, though: a freshly made icon has no name until it is\ngiven one. Such an icon draws no plate but stays selectable, focusable and\nrenameable — Return opens a field, and an edit with nothing in it yet is the\none time the box stops hugging and reserves a cell's width, since a field you\ncannot see is one you cannot type into.\n---\n\n\n### **Events:**\n - **vf-select** - Selection changed by user interaction. `detail: { selected: boolean }`.\n- **vf-change** - The name was committed. `detail: { label: string, previous: string }`.\n- **vf-open** - The icon was opened — double-clicked anywhere on it, its name included; tapped twice with a finger or pen; or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`.\n- **vf-name-too-long** - A rename was typed or pasted past `maxlength`, and the field refused the excess. `detail: { attempted, accepted, limit }` — enough to raise the alert System 7 raised rather than drop the characters silently. Not fired for a `label` set from your own code, which is never truncated in the first place.\n- **vf-name-rejected** - A rename was committed with no name in it, so the edit was dropped and the old name put back. `detail: { attempted, kept, reason: 'empty' }`. A `vf-change` is *not* fired alongside it — nothing changed.\n- **vf-drag-start** - A drag began: the first lattice step past the drag slop, 4 CSS px from the press for the mouse and 10 for a finger or pen. `detail: { left, top, icons }` — the origin, whole system px in the icon's container, and every icon the drag carries (this one first).\n- **vf-drag** - A step that changed the proposal. `detail: { clientX, clientY, left, top, x, y, icons }` — the pointer in viewport CSS px, the proposed origin in the container (whole system px on the lattice), the outline's top-left in viewport CSS px, and the icons travelling.\n- **vf-drop** - The release; cancelable. The same detail as `vf-drag`. Its default action writes the proposal through `left`/`top` — every icon in `icons` by the same delta, clamped whole in the container as a group; `preventDefault()` writes nothing.\n- **vf-drag-cancel** - The drag was abandoned — Escape, or a `pointercancel` — and nothing was written. `detail: {}`.\n\n### **Slots:**\n - **large** - The 32×32 art, normally a `vf-img` around an `<img>`.\n- **small** - The 16×16 art, shown under `size=\"small\"`.\n\n### **CSS Properties:**\n - **--vf-icon-gap** - Space between the art cell and the name plate; 0 by default, the plate directly under the cell as the Finder drew it _(default: undefined)_\n- **--vf-icon-label-height** - The name plate's line box _(default: undefined)_\n\n### **CSS Parts:**\n - **frame** - The icon and its label plate, stacked.\n- **icon** - The reserved art cell.\n- **label** - The name block; its lines are centered under the art.\n- **plate** - The inked run behind the name, which each wrapped line gets its own of (inverts when selected).\n- **input** - The rename field, while editing.",
480
480
  "attributes": [
481
481
  {
482
482
  "name": "label",
@@ -1679,7 +1679,7 @@
1679
1679
  },
1680
1680
  {
1681
1681
  "name": "vf-window",
1682
- "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\" 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\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close' }` (shape- compatible with vf-dialog's `vf-close`). 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).",
1682
+ "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\" 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' }` (shape- compatible with vf-dialog's `vf-close`). 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).",
1683
1683
  "attributes": [
1684
1684
  {
1685
1685
  "name": "variant",
@@ -1711,6 +1711,11 @@
1711
1711
  "description": "Allow dragging the window by its title bar.",
1712
1712
  "values": []
1713
1713
  },
1714
+ {
1715
+ "name": "outline-drag",
1716
+ "description": "Drag by the title bar as an outline: a press draws a dotted one-pixel\nrectangle around the window on the desktop's drag surface, with the\nicon drag outline's pen; the drag moves it, held where the window can\nland, and the window moves there on the release, in one write. Escape\ncancels and writes nothing, as does a release that never moved it.\nUnset, the window moves live under the pointer; with no `vf-desktop` on\nits path it moves live either way. Only matters with `movable`.",
1717
+ "values": []
1718
+ },
1714
1719
  {
1715
1720
  "name": "resizable",
1716
1721
  "description": "Show a grow box at the bottom-right corner for resizing. The drag is\nbounded per axis by the sizeRect below.",
@@ -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.12.1",
4
+ "version": "0.12.3",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -537,7 +537,7 @@
537
537
  },
538
538
  {
539
539
  "name": "vf-desktop",
540
- "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. 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\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, one whole-surface raster at one image px per system px, 1-bit at\nevery density 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. Not the top 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 with none 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).",
540
+ "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. 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\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, one whole-surface raster at one image px per system px, 1-bit at\nevery density 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 with none 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).",
541
541
  "doc-url": "",
542
542
  "attributes": [
543
543
  {
@@ -1075,7 +1075,7 @@
1075
1075
  },
1076
1076
  {
1077
1077
  "name": "vf-icon",
1078
- "description": "`<vf-icon>` — a Finder icon: pixel art in a reserved cell with its name on a\nplate below, selectable, movable and renameable in place.\n\nIt is the one composite in the kit whose parts already existed separately —\n`vf-img` draws the art, `vf-list-item` inverts on selection, `vf-window`\ndrags — and the reason to have it anyway is that the *icon* is the unit the\nFinder actually manipulates. A picture and a caption that select together,\nmove together and rename together is a control, not a layout.\n\n### The art arrives by slot\n\nTwo slots, one per resource size — `large` is the 32×32 `ICN#` and `small`\nthe 16×16 `ics#` — each holding a `vf-img` around the consumer's own `<img>`:\n\n```html\n<vf-icon label=\"Macintosh HD\" selectable movable editable>\n <vf-img slot=\"large\"><img src=\"hd-32.png\" alt=\"\"></vf-img>\n <vf-img slot=\"small\"><img src=\"hd-16.png\" alt=\"\"></vf-img>\n</vf-icon>\n```\n\nSlots rather than `src` properties, for the reason `vf-img` exists at all:\nthe kit ships no raster files and never builds an `<img>` on a consumer's\nbehalf, so the graphic stays a real element in the light DOM with its own\n`alt`, `srcset`, loading behavior and asset URLs. A `src` string can express\nnone of that, and it could not hold an inline `<svg>` or a `<canvas>` either.\n`vf-list-item`'s `icon` slot makes the same trade.\n\nThe cost is that both files fetch even though one paints — pay it with two\ndata URIs, or slot only the size that view uses.\n\n### The cell is reserved, the art is registered in it\n\n`size` picks which slot paints *and* the cell it paints in: 32×32 or 16×16,\nheld whether or not there is art for it. A folder is 32×20 of ink and a\ndocument 25×32, and a row of them keeps one baseline only because the cell —\nnot the ink — is the unit, which is what an icon resource always was.\n\n### Everything centered lands on a whole pixel, by parity\n\nThe frame centers two things over one axis — the art cell and the name — and\na centered child sits at `(box − child) / 2`. That is a whole number exactly\nwhen the box and the child have the **same parity**. Half a system pixel is\nwhat fringes 1-bit art: the glyph stems smear across two device columns and\ngo gray, while the plate behind them stays sharp, because backgrounds are\npixel-snapped by the compositor and glyphs are not. A crisp plate under a\ngrey name is the signature of exactly this.\n\nSo the component makes the parities agree instead of correcting afterwards.\nThe cell is 32 or 16, and #measurePlate sizes the name plate to a\nwhole **even** number of system px — measured from the text, rounded up. Both\nchildren are then even, and so is every offset, at every display density and\nwhether or not a `width` is declared. Nothing is snapped, nothing leans on\nthe rasterizer, and `verify:icon` asserts both the geometry and that a field\nof names renders without a single gray pixel — with the kit's normal\nantialiasing left on, since a run on whole pixels gives it nothing to smooth.\n\nYour side of it is one rule: **a declared `width` must be even**, since the\ncomponent cannot round a number you chose. Supply art at the cell size too —\nwhich \"one image pixel is one system pixel\" already implies — because art\ndiffering from its cell by an odd number centers the same way.\n\n### Selection inverts, because the art is a 1-bit mask\n\nA System 7 icon is black ink and opaque white on a transparent surround —\nprecisely an image plus its mask — so inverting it flips ink and fill and\nleaves the surround alone, which is the whole of the classic selected\nappearance. `filter: invert(1)` reproduces it exactly for 1-bit art. Color\nart would invert into a photographic negative, which was never what\nSystem 7 showed: declare `color` and selection **darkens** the art\ninstead — Icon Utilities' ttSelected transform, every color blended\nhalfway toward black (`brightness(0.5)`), whites going gray, the\ntransparent surround still untouched. The declaration is the consumer's\nbecause the component cannot tell color art from a mask by looking at a\nslotted image. It covers whatever the cell shows, the derived open ghost\nincluded, so a color icon has one selected treatment open or closed. The\nlabel plate inverts to the `--vf-highlight` pair either way, sharing one\nselection color with `vf-list-item` — the selected name was inverted\nwhatever the art.\n\n### Open is derived, not shipped\n\nWith `open`, the art redraws as the Finder's open ghost: the outline held\nin solid black, the interior re-filled with the kit's loose 25% dither (the\nscrollbar trough's lattice), the transparent surround untouched. There is\nno second raster and no second fetch — the same alpha channel that makes\nselection an inversion makes the ghost derivable, and `src/open-art.ts`\nderives it from the slotted art by canvas compositing alone. No pixels are\never read back, so a cross-origin image that taints its canvas still works:\ntaint forbids reading, not drawing or displaying.\n\nThe ghost keeps the shape selection expects — ink and opaque white on a\ntransparent surround — so a selected open icon inverts exactly as a closed\none does, with no second treatment. The slot stays in the tree while the\nghost paints, hidden (it is where the art loads, and re-loads, from), and\nart the pipeline cannot draw — nothing slotted yet, a failed load, an\ninline `<svg>` — keeps rendering as itself rather than vanishing behind a\nstate it cannot show.\n\n### `movable`, not `draggable`\n\n`draggable` is a global HTML attribute *and* an `HTMLElement` accessor, so\ndeclaring it would both shadow a platform member and hand the element to the\nbrowser's own drag-and-drop machinery. This is the `align` trap from\n`vf-stack` in a second costume — `align`, `hidden`, `dir`, `draggable` and\n`title` all carry behavior a custom element never asked for. The kit already\nspells this parameter `movable` on `vf-window`, so the icon does too, and it\nmoves the same way: `DragController` tracks the gesture and\n`PlacementController` writes the result into `left`/`top` in whole system px,\nthe art's own unit — the same pair markup places an icon with, so a moved\nicon is still where it was dropped after a zoom.\n\nDragging is a pointer gesture with no keyboard equivalent, which is the kind\nof gap the kit closes rather than inherits (SPEC §1): a focused movable icon\nalso moves under the arrow keys, one system px at a time and eight with\nShift. Focus is what `selectable` grants, so the keyboard half of `movable`\nand `editable` presupposes it — see the role section below.\n\n### The drag is an outline; the icon stays put\n\nThe Finder dragged an icon as a dotted outline — the mask's boundary and\nthe name's rectangle — drawn over everything with the XOR pen, the icon\nitself staying put until the drop. The kit draws it from what it already\nhas: deriveDragOutline finds the ring the way the open ghost does\n(the silhouette from the art's alpha, the plate's rectangle united with\nit, the whole inset by one pixel as `FrameRgn` inset a region, the ring\nthe inset removes — so where the art and the plate abut, the outline\nwraps around both), and dotOutline dots it against a 2×2 checker\n— compositing only, so cross-origin art derives too, and art the\npipeline cannot draw falls back to the cell's rectangle. The canvas\ncovers the frame widened to the plate, since a name wider than a\ndeclared `width` overhangs the frame.\nThe canvas paints with the cursor's XOR recipe, `filter: invert(1)` under\n`mix-blend-mode: difference`: a dotted black line over a white window\nbody, and over the desktop dither a black line, the composition\nQuickDraw's pattern pen gave — the checker's phase is taken from where\nthe outline lands on the *screen* (penPhase), so its dots fall on\nthe pixels the dither leaves white wherever it goes, and the dither's own\nink fills in between them.\n\nIt draws on the desktop's drag surface: a layer in the screen's shadow,\nover windows, palettes and the menu bar alike, clipped at the raster's\nedge like everything else, reached through the `vf-drag-surface-request`\nhandshake (bubbles, non-composed — it travels up the slots the icon is\nassigned to, so it works from inside a window body). With no desktop the\ncanvas lives in the icon's own frame instead, translated by the delta,\nclipped by whatever clips the icon; the gesture and its events are the\nsame either way. Its origin is a whole count of system px from the\nsurface's, written as a live `sysLength()`, so a scale change mid-gesture\ncannot smear it.\n\n### The drag reports, the page files\n\nThe kit ships no filing semantics — folders are the page's own catalog —\nso a drag is a stream of events and a cancelable commit, on the icon:\n\n- `vf-drag-start` `{ left, top }` on the first lattice step away from the\n press (a plain click never starts one, and never fires a drop);\n- `vf-drag` `{ clientX, clientY, left, top, x, y }` on every step that\n changed the snapped proposal;\n- `vf-drop`, the same shape at the release, **cancelable**: its default\n action is the move, the proposal written through `left`/`top` and\n clamped whole in the container measured at the press — held at the\n origin only in a scrolling plane, whose rails reach the rest. `preventDefault()`\n writes nothing — the page re-parents or places the icon itself, the\n outline's origin converted with the destination's `placementAt`;\n- `vf-drag-cancel` `{}` on Escape or a `pointercancel`: nothing written.\n\n`left`/`top` in the detail are the *proposal*, unclamped — the origin plus\nthe lattice-snapped delta — and only the default action clamps, so a page\nthat cancels gets the raw gesture. `x`/`y` are the outline's top-left in\nviewport CSS px: the frame's box at the press translated by the delta,\nthe press offset already inside it, so an icon placed at the\ndestination's `placementAt(x, y)` lands exactly where the outline was.\n\n### The selection travels\n\nA drag that begins on a *selected* icon takes every other selected,\nmovable icon of its field with it — the Finder outlined the whole\nselection under a drag on any member. Each follower draws its own outline\n(followDrag), moved by the same lattice delta as the leader's, and\nthe leader's events carry the set as `detail.icons`, itself first. The\ndefault action of the drop clamps the delta once for the whole group —\nthe arrangement is kept, and the group stops when its outermost member\nmeets the container's edge — then writes each member's origin plus that\ndelta. A cancel takes every outline down and writes nothing. A press on\nan unselected icon makes it the selection first (the outside listeners\nclear the others), so it drags alone; a Shift press that deselects the\npressed icon drags it alone too, the rest staying where they are.\n\nFor the selection to travel, a plain press on one of its members must not\ncollapse it: the other icons' outside-press listeners leave a selection\nalone when the press landed on an icon already selected in the same\nfield. A *click* on such an icon — released with no drag — collapses the\nselection to it on the release, so a click still single-selects and the\nsame press could have dragged them all.\n\nEscape is heard by a document listener scoped to the gesture rather than\nby the icon's own key handler, because a `movable`-only icon never takes\nfocus — an addition in the kit's idiom (a pointer-only gesture with no way\nout is the kind of gap SPEC §1 closes), not a claim about the Finder.\n\n`target` is the destination highlight the page sets on the folder under a\ndrag: the selected treatment of the art alone — inverted, or darkened\nunder `color` — with none of `selected`'s semantics: no event, no outside\nlistener, no `aria-selected`, the plate untouched.\n\n### The page drags it too\n\nThe Finder's Clean Up walked a container's icons one at a time, each\nicon's outline travelling from where it sat to its cell and the icon\nlanding when the outline arrived — the drag's own vocabulary, with the\nFinder for a hand. dragTo is that move for one icon: the outline\nbuilt and placed as a drag's is, on the desktop's surface or in the frame,\nstepped to the landing at the walk's cadence (runOutlineTravel,\nsrc/motion.ts — the kit's, no knobs), then the outline down and the icon\nwritten where a drop would write it. The landing is resolved before the\noutline sets off, so the outline goes where the icon will land rather\nthan where a pair past the container's edge pointed. moveTo is\nthat write alone: the drop's clamp and snap, one `vf-placement-change` —\nwhere a property set is the authored pair, unclamped. Neither fires a\ndrag event (the page is the mover and already knows) and neither needs\n`movable`, which says the hand may drag: the page may always place its\nicon. `vf-icon-field.dragIcons` walks a set through `dragTo` with the\nFinder's beat between landings.\n\nA walk is finished, never cancelled: a press anywhere, or Escape, lands\nthe icon at once — the user wants the machine back, and the move was the\npage's to make. A second `dragTo`, or a `moveTo`, finishes the one in\nflight the same way; leaving the DOM takes the outline down and writes\nnothing. Under `prefers-reduced-motion` the icon lands at once with no\noutline, the blink's posture — as does one being renamed, or one under\nthe hand's own press.\n\nOpening gets the same treatment. The double-click is the pointer gesture,\nand its keyboard route is ⌘O / ⌘↓ — the System 7 Open shortcuts, with Ctrl\nstanding in for ⌘ off the Mac. Return is deliberately not one of them: the\nFinder's Return renamed, never opened, so on an editable icon it starts the\nedit and on a non-editable one it does nothing at all.\n\n### The name and the art are one target, and the second click decides\n\nA double-click opens the icon *wherever it lands* — the name is as much the\nicon as the picture is, and the Finder never made you aim at the 32 pixels of\nart. But the name is also where a single click renames, so the two gestures\nbegin with the same press and only the second one tells them apart.\n\nSo the rename waits for it. A press on the plate of an already-selected icon\narms the field rather than opening it, and the next press inside\nRENAME_DELAY_MS calls it off — leaving the double-click to open, with\nno rename box flashing up behind it. Nothing needs to *undo* an edit that\nbegan: the press that starts one and the press that opens are the same\npress, so the only thing that can be got right is not committing early.\n\nThe window is generous in the one direction that is cheap. Reading a lone\nclick as a pair costs a wait before the box appears; reading a pair as a lone\nclick renames when the user asked to open. The same reasoning covers a press\nthat turns into a drag, a press elsewhere, and any key — each calls the\npending rename off, because none of them is the click it is waiting for.\n\n### An icon alone is a picture; an icon in a field is an option\n\n`role=\"option\"` is only meaningful inside a `listbox` that owns it. Written\nunconditionally it is not merely untidy — the browser *drops* it, and\n`aria-selected` with it, so a `selectable` icon announced as a bare generic\nand its selection state reached assistive tech nowhere at all.\n\nSo the role follows the container. Owned, the icon is an `option` that names\nitself from its plate and publishes `aria-selected`. Unowned, it degrades to\n`role=\"img\"` with a name — the same vocabulary the derived open ghost uses,\nand true of what it is. Deliberately not `button`: that would promise Enter\nand Space activate, and here Return *renames* while the open route is ⌘O / ⌘↓.\n\nThe owner is `vf-icon-field`, the container a field of icons sits in, and\nit is what buys the selection state back:\n\n```html\n<vf-icon-field label=\"Desktop\">\n <vf-icon label=\"Macintosh HD\" selectable movable editable>…</vf-icon>\n <vf-icon label=\"Trash\" selectable movable editable>…</vf-icon>\n</vf-icon-field>\n```\n\nA `vf-desktop` cannot be that container itself: it also holds windows and a\nmenu bar, and a non-`option` child of a listbox is invalid the same way the\norphaned option was. The field is layout-neutral — placed icons anchor to\nthe nearest *positioned* ancestor, which is still the desktop's raster. An\nelement of your own carrying `role=\"listbox\"` still owns an icon the same\nway. One divergence from the APG listbox is deliberate: its options share\na single roving tab stop, while these stay one stop each, the way a Finder\nicon is reached on its own.\n\n**`selectable` is what makes an icon focusable**, and `movable`/`editable`\npresuppose it. That is the Finder's own model — you cannot move or rename\nwhat you have not selected — and the pointer path already assumed it: the\nrename opens on a press on the plate of an *already-selected* icon. A\n`movable`-only icon is a picture you can drag, not a widget.\n\n### The label is a property, because it is editable\n\nThe caption is `label` rather than slotted content: renaming means the\ncomponent owns the string and hands it back on `vf-change`, and it cannot own\ntext that lives in the consumer's DOM. An empty `label` draws no plate at\nall — that *is* the \"no label\" parameter, in preference to a second attribute\nthat could disagree with it. `editable` then lets a click on the plate of an\nalready-selected icon open the rename box a moment later, as the Finder's\ndoes (see above), with Return committing, Escape reverting, and the plate\nwidening as you type.\n\n### A name is never abbreviated, and never folded\n\nThere is no ellipsis, no clipping and no wrapping: one line, always. System 7\nsolved the long-name problem at the *other* end — HFS capped a filename at 31\ncharacters — so the Finder could afford to always draw the name in full, and\ndid. A name wider than its cell simply overflows it, centered, the way a name\nwider than a 32-pixel icon always did.\n\nStaying on one line is also what keeps the name on the grid: a single run has\na single measured width, so the even-plate rule above is one number rather\nthan one per line, each with its own parity. `width` is therefore the cell —\nthe grid pitch — not a bound on the name.\n\n`maxlength` (31) bounds the rename field rather than the `label` property:\nthe name belongs to the consumer's model, and truncating one handed to us\nwould lose data.\n\nThe box hugs its text while you type it, not only once you are done, which is\nwhat keeps the name from moving when the edit commits: the plate is the same\nwidth either side of it, so the glyphs stay exactly where they were.\n\nA file also has to be called *something*, so a rename committed empty — or\nas nothing but spaces — is refused rather than applied, and the previous name\ncomes back, which is what System 7 did. An empty `label` is still a perfectly\ngood state to *start* in, though: a freshly made icon has no name until it is\ngiven one. Such an icon draws no plate but stays selectable, focusable and\nrenameable — Return opens a field, and an edit with nothing in it yet is the\none time the box stops hugging and reserves a cell's width, since a field you\ncannot see is one you cannot type into.\n---\n\n\n### **Events:**\n - **vf-select** - Selection changed by user interaction. `detail: { selected: boolean }`.\n- **vf-change** - The name was committed. `detail: { label: string, previous: string }`.\n- **vf-open** - The icon was opened — double-clicked anywhere on it, its name included, or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`.\n- **vf-name-too-long** - A rename was typed or pasted past `maxlength`, and the field refused the excess. `detail: { attempted, accepted, limit }` — enough to raise the alert System 7 raised rather than drop the characters silently. Not fired for a `label` set from your own code, which is never truncated in the first place.\n- **vf-name-rejected** - A rename was committed with no name in it, so the edit was dropped and the old name put back. `detail: { attempted, kept, reason: 'empty' }`. A `vf-change` is *not* fired alongside it — nothing changed.\n- **vf-drag-start** - A drag began: the first lattice step away from the press. `detail: { left, top, icons }` — the origin, whole system px in the icon's container, and every icon the drag carries (this one first).\n- **vf-drag** - A step that changed the proposal. `detail: { clientX, clientY, left, top, x, y, icons }` — the pointer in viewport CSS px, the proposed origin in the container (whole system px on the lattice), the outline's top-left in viewport CSS px, and the icons travelling.\n- **vf-drop** - The release; cancelable. The same detail as `vf-drag`. Its default action writes the proposal through `left`/`top` — every icon in `icons` by the same delta, clamped whole in the container as a group; `preventDefault()` writes nothing.\n- **vf-drag-cancel** - The drag was abandoned — Escape, or a `pointercancel` — and nothing was written. `detail: {}`.\n\n### **Slots:**\n - **large** - The 32×32 art, normally a `vf-img` around an `<img>`.\n- **small** - The 16×16 art, shown under `size=\"small\"`.\n\n### **CSS Properties:**\n - **--vf-icon-gap** - Space between the art cell and the name plate; 0 by default, the plate directly under the cell as the Finder drew it _(default: undefined)_\n- **--vf-icon-label-height** - The name plate's line box _(default: undefined)_\n\n### **CSS Parts:**\n - **frame** - The icon and its label plate, stacked.\n- **icon** - The reserved art cell.\n- **label** - The name block; its lines are centered under the art.\n- **plate** - The inked run behind the name, which each wrapped line gets its own of (inverts when selected).\n- **input** - The rename field, while editing.",
1078
+ "description": "`<vf-icon>` — a Finder icon: pixel art in a reserved cell with its name on a\nplate below, selectable, movable and renameable in place.\n\nIt is the one composite in the kit whose parts already existed separately —\n`vf-img` draws the art, `vf-list-item` inverts on selection, `vf-window`\ndrags — and the reason to have it anyway is that the *icon* is the unit the\nFinder actually manipulates. A picture and a caption that select together,\nmove together and rename together is a control, not a layout.\n\n### The art arrives by slot\n\nTwo slots, one per resource size — `large` is the 32×32 `ICN#` and `small`\nthe 16×16 `ics#` — each holding a `vf-img` around the consumer's own `<img>`:\n\n```html\n<vf-icon label=\"Macintosh HD\" selectable movable editable>\n <vf-img slot=\"large\"><img src=\"hd-32.png\" alt=\"\"></vf-img>\n <vf-img slot=\"small\"><img src=\"hd-16.png\" alt=\"\"></vf-img>\n</vf-icon>\n```\n\nSlots rather than `src` properties, for the reason `vf-img` exists at all:\nthe kit ships no raster files and never builds an `<img>` on a consumer's\nbehalf, so the graphic stays a real element in the light DOM with its own\n`alt`, `srcset`, loading behavior and asset URLs. A `src` string can express\nnone of that, and it could not hold an inline `<svg>` or a `<canvas>` either.\n`vf-list-item`'s `icon` slot makes the same trade.\n\nThe cost is that both files fetch even though one paints — pay it with two\ndata URIs, or slot only the size that view uses.\n\n### The cell is reserved, the art is registered in it\n\n`size` picks which slot paints *and* the cell it paints in: 32×32 or 16×16,\nheld whether or not there is art for it. A folder is 32×20 of ink and a\ndocument 25×32, and a row of them keeps one baseline only because the cell —\nnot the ink — is the unit, which is what an icon resource always was.\n\n### Everything centered lands on a whole pixel, by parity\n\nThe frame centers two things over one axis — the art cell and the name — and\na centered child sits at `(box − child) / 2`. That is a whole number exactly\nwhen the box and the child have the **same parity**. Half a system pixel is\nwhat fringes 1-bit art: the glyph stems smear across two device columns and\ngo gray, while the plate behind them stays sharp, because backgrounds are\npixel-snapped by the compositor and glyphs are not. A crisp plate under a\ngrey name is the signature of exactly this.\n\nSo the component makes the parities agree instead of correcting afterwards.\nThe cell is 32 or 16, and #measurePlate sizes the name plate to a\nwhole **even** number of system px — measured from the text, rounded up. Both\nchildren are then even, and so is every offset, at every display density and\nwhether or not a `width` is declared. Nothing is snapped, nothing leans on\nthe rasterizer, and `verify:icon` asserts both the geometry and that a field\nof names renders without a single gray pixel — with the kit's normal\nantialiasing left on, since a run on whole pixels gives it nothing to smooth.\n\nYour side of it is one rule: **a declared `width` must be even**, since the\ncomponent cannot round a number you chose. Supply art at the cell size too —\nwhich \"one image pixel is one system pixel\" already implies — because art\ndiffering from its cell by an odd number centers the same way.\n\n### Selection inverts, because the art is a 1-bit mask\n\nA System 7 icon is black ink and opaque white on a transparent surround —\nprecisely an image plus its mask — so inverting it flips ink and fill and\nleaves the surround alone, which is the whole of the classic selected\nappearance. `filter: invert(1)` reproduces it exactly for 1-bit art. Color\nart would invert into a photographic negative, which was never what\nSystem 7 showed: declare `color` and selection **darkens** the art\ninstead — Icon Utilities' ttSelected transform, every color blended\nhalfway toward black (`brightness(0.5)`), whites going gray, the\ntransparent surround still untouched. The declaration is the consumer's\nbecause the component cannot tell color art from a mask by looking at a\nslotted image. It covers whatever the cell shows, the derived open ghost\nincluded, so a color icon has one selected treatment open or closed. The\nlabel plate inverts to the `--vf-highlight` pair either way, sharing one\nselection color with `vf-list-item` — the selected name was inverted\nwhatever the art.\n\n### Open is derived, not shipped\n\nWith `open`, the art redraws as the Finder's open ghost: the outline held\nin solid black, the interior re-filled with the kit's loose 25% dither (the\nscrollbar trough's lattice), the transparent surround untouched. There is\nno second raster and no second fetch — the same alpha channel that makes\nselection an inversion makes the ghost derivable, and `src/open-art.ts`\nderives it from the slotted art by canvas compositing alone. No pixels are\never read back, so a cross-origin image that taints its canvas still works:\ntaint forbids reading, not drawing or displaying.\n\nThe ghost keeps the shape selection expects — ink and opaque white on a\ntransparent surround — so a selected open icon inverts exactly as a closed\none does, with no second treatment. The slot stays in the tree while the\nghost paints, hidden (it is where the art loads, and re-loads, from), and\nart the pipeline cannot draw — nothing slotted yet, a failed load, an\ninline `<svg>` — keeps rendering as itself rather than vanishing behind a\nstate it cannot show.\n\n### `movable`, not `draggable`\n\n`draggable` is a global HTML attribute *and* an `HTMLElement` accessor, so\ndeclaring it would both shadow a platform member and hand the element to the\nbrowser's own drag-and-drop machinery. This is the `align` trap from\n`vf-stack` in a second costume — `align`, `hidden`, `dir`, `draggable` and\n`title` all carry behavior a custom element never asked for. The kit already\nspells this parameter `movable` on `vf-window`, so the icon does too, and it\nmoves the same way: `DragController` tracks the gesture and\n`PlacementController` writes the result into `left`/`top` in whole system px,\nthe art's own unit — the same pair markup places an icon with, so a moved\nicon is still where it was dropped after a zoom.\n\nDragging is a pointer gesture with no keyboard equivalent, which is the kind\nof gap the kit closes rather than inherits (SPEC §1): a focused movable icon\nalso moves under the arrow keys, one system px at a time and eight with\nShift. Focus is what `selectable` grants, so the keyboard half of `movable`\nand `editable` presupposes it — see the role section below.\n\n### The drag is an outline; the icon stays put\n\nThe Finder dragged an icon as a dotted outline — the mask's boundary and\nthe name's rectangle — drawn over everything with the XOR pen, the icon\nitself staying put until the drop. The kit draws it from what it already\nhas: deriveDragOutline finds the ring the way the open ghost does\n(the silhouette from the art's alpha, the plate's rectangle united with\nit, the whole inset by one pixel as `FrameRgn` inset a region, the ring\nthe inset removes — so where the art and the plate abut, the outline\nwraps around both), and dotOutline dots it against a 2×2 checker\n— compositing only, so cross-origin art derives too, and art the\npipeline cannot draw falls back to the cell's rectangle. The canvas\ncovers the frame widened to the plate, since a name wider than a\ndeclared `width` overhangs the frame.\nThe canvas paints with the cursor's XOR recipe, `filter: invert(1)` under\n`mix-blend-mode: difference`: a dotted black line over a white window\nbody, and over the desktop dither a black line, the composition\nQuickDraw's pattern pen gave — the checker's phase is taken from where\nthe outline lands on the *screen* (penPhase), so its dots fall on\nthe pixels the dither leaves white wherever it goes, and the dither's own\nink fills in between them.\n\nIt draws on the desktop's drag surface: a layer in the screen's shadow,\nover windows, palettes and the menu bar alike, clipped at the raster's\nedge like everything else, reached through the `vf-drag-surface-request`\nhandshake (bubbles, non-composed — it travels up the slots the icon is\nassigned to, so it works from inside a window body). With no desktop the\ncanvas lives in the icon's own frame instead, translated by the delta,\nclipped by whatever clips the icon; the gesture and its events are the\nsame either way. Its origin is a whole count of system px from the\nsurface's, written as a live `sysLength()`, so a scale change mid-gesture\ncannot smear it.\n\n### The drag reports, the page files\n\nThe kit ships no filing semantics — folders are the page's own catalog —\nso a drag is a stream of events and a cancelable commit, on the icon:\n\n- `vf-drag-start` `{ left, top }` on the first lattice step past the drag\n slop (a click or a tap never starts one, and never fires a drop — see\n \"Mouse, finger and pen\" below);\n- `vf-drag` `{ clientX, clientY, left, top, x, y }` on every step that\n changed the snapped proposal;\n- `vf-drop`, the same shape at the release, **cancelable**: its default\n action is the move, the proposal written through `left`/`top` and\n clamped whole in the container measured at the press — held at the\n origin only in a scrolling plane, whose rails reach the rest. `preventDefault()`\n writes nothing — the page re-parents or places the icon itself, the\n outline's origin converted with the destination's `placementAt`;\n- `vf-drag-cancel` `{}` on Escape or a `pointercancel`: nothing written.\n\n`left`/`top` in the detail are the *proposal*, unclamped — the origin plus\nthe lattice-snapped delta — and only the default action clamps, so a page\nthat cancels gets the raw gesture. `x`/`y` are the outline's top-left in\nviewport CSS px: the frame's box at the press translated by the delta,\nthe press offset already inside it, so an icon placed at the\ndestination's `placementAt(x, y)` lands exactly where the outline was.\n\n### The selection travels\n\nA drag that begins on a *selected* icon takes every other selected,\nmovable icon of its field with it — the Finder outlined the whole\nselection under a drag on any member. Each follower draws its own outline\n(followDrag), moved by the same lattice delta as the leader's, and\nthe leader's events carry the set as `detail.icons`, itself first. The\ndefault action of the drop clamps the delta once for the whole group —\nthe arrangement is kept, and the group stops when its outermost member\nmeets the container's edge — then writes each member's origin plus that\ndelta. A cancel takes every outline down and writes nothing. A press on\nan unselected icon makes it the selection first (the outside listeners\nclear the others), so it drags alone; a Shift press that deselects the\npressed icon drags it alone too, the rest staying where they are.\n\nFor the selection to travel, a plain press on one of its members must not\ncollapse it: the other icons' outside-press listeners leave a selection\nalone when the press landed on an icon already selected in the same\nfield. A *click* on such an icon — released with no drag — collapses the\nselection to it on the release, so a click still single-selects and the\nsame press could have dragged them all.\n\nEscape is heard by a document listener scoped to the gesture rather than\nby the icon's own key handler, because a `movable`-only icon never takes\nfocus — an addition in the kit's idiom (a pointer-only gesture with no way\nout is the kind of gap SPEC §1 closes), not a claim about the Finder.\n\n`target` is the destination highlight the page sets on the folder under a\ndrag: the selected treatment of the art alone — inverted, or darkened\nunder `color` — with none of `selected`'s semantics: no event, no outside\nlistener, no `aria-selected`, the plate untouched.\n\n### The page drags it too\n\nThe Finder's Clean Up walked a container's icons one at a time, each\nicon's outline travelling from where it sat to its cell and the icon\nlanding when the outline arrived — the drag's own vocabulary, with the\nFinder for a hand. dragTo is that move for one icon: the outline\nbuilt and placed as a drag's is, on the desktop's surface or in the frame,\nstepped to the landing at the walk's cadence (runOutlineTravel,\nsrc/motion.ts — the kit's, no knobs), then the outline down and the icon\nwritten where a drop would write it. The landing is resolved before the\noutline sets off, so the outline goes where the icon will land rather\nthan where a pair past the container's edge pointed. moveTo is\nthat write alone: the drop's clamp and snap, one `vf-placement-change` —\nwhere a property set is the authored pair, unclamped. Neither fires a\ndrag event (the page is the mover and already knows) and neither needs\n`movable`, which says the hand may drag: the page may always place its\nicon. `vf-icon-field.dragIcons` walks a set through `dragTo` with the\nFinder's beat between landings.\n\nA walk is finished, never cancelled: a press anywhere, or Escape, lands\nthe icon at once — the user wants the machine back, and the move was the\npage's to make. A second `dragTo`, or a `moveTo`, finishes the one in\nflight the same way; leaving the DOM takes the outline down and writes\nnothing. Under `prefers-reduced-motion` the icon lands at once with no\noutline, the blink's posture — as does one being renamed, or one under\nthe hand's own press.\n\nOpening gets the same treatment. The double-click is the pointer gesture,\nand its keyboard route is ⌘O / ⌘↓ — the System 7 Open shortcuts, with Ctrl\nstanding in for ⌘ off the Mac. Return is deliberately not one of them: the\nFinder's Return renamed, never opened, so on an editable icon it starts the\nedit and on a non-editable one it does nothing at all.\n\n### Mouse, finger and pen\n\nThe instrument never changes what the icon means: a tap is a click, two\ntaps are a double-click, a drag is a drag. Two things are measured per\ninstrument so that holds, both added by the kit — System 7 had the mouse\nalone.\n\n**The open gesture.** The mouse opens on the platform's `dblclick`, which\nfollows the user's own double-click setting. A finger or a pen opens on a\n*tap pair* the icon classifies from its own pointer events: two taps on\nthe icon, the second pressed within `TAP_PAIR_MS` (500) of the first and\nno further than `TAP_PAIR_SLOP_PX` (24 CSS px) from it, `vf-open` firing\non the second release. A platform reserves the double-tap for its zoom\nand may deliver no `dblclick` for one, and a movable icon's press is\n`preventDefault()`ed for the drag, which can suppress it too; the pair\ndepends on neither. A tap is a press released where it landed — one that\nbecame a drag, or that the platform cancelled, is not half of a pair —\nand a pair does not chain: a third tap starts the next one. Each press\ntakes exactly one of the two routes, so a platform that does deliver a\n`dblclick` for a double-tap opens the icon once. The frame sets\n`touch-action: manipulation`, which takes the double-tap zoom off the\nicon and leaves panning and pinching to the page.\n\n**The drag slop.** A drag begins once the pointer has travelled from its\npress by `DRAG_SLOP_PX` (4 CSS px) for the mouse or `DRAG_SLOP_COARSE_PX`\n(10) for a finger or pen, which never lands still. Inside it the press is\na click or a tap: the icon stays on its spot, a rename the press armed\nstays armed, and no drag event fires. The delta is measured from the\npress all the same, so the outline begins under the pointer. The numbers\nare CSS px rather than system px because they measure the hand, not the\nart (src/motion.ts).\n\n### The name and the art are one target, and the second click decides\n\nA double-click opens the icon *wherever it lands* — the name is as much the\nicon as the picture is, and the Finder never made you aim at the 32 pixels of\nart. But the name is also where a single click renames, so the two gestures\nbegin with the same press and only the second one tells them apart.\n\nSo the rename waits for it. A press on the plate of an already-selected icon\narms the field rather than opening it, and the next press inside\nRENAME_DELAY_MS calls it off — leaving the double-click, or a\nfinger's tap pair, to open, with no rename box flashing up behind it. Nothing needs to *undo* an edit that\nbegan: the press that starts one and the press that opens are the same\npress, so the only thing that can be got right is not committing early.\n\nThe window is generous in the one direction that is cheap. Reading a lone\nclick as a pair costs a wait before the box appears; reading a pair as a lone\nclick renames when the user asked to open. The same reasoning covers a press\nthat turns into a drag, a press elsewhere, and any key — each calls the\npending rename off, because none of them is the click it is waiting for.\n\n### An icon alone is a picture; an icon in a field is an option\n\n`role=\"option\"` is only meaningful inside a `listbox` that owns it. Written\nunconditionally it is not merely untidy — the browser *drops* it, and\n`aria-selected` with it, so a `selectable` icon announced as a bare generic\nand its selection state reached assistive tech nowhere at all.\n\nSo the role follows the container. Owned, the icon is an `option` that names\nitself from its plate and publishes `aria-selected`. Unowned, it degrades to\n`role=\"img\"` with a name — the same vocabulary the derived open ghost uses,\nand true of what it is. Deliberately not `button`: that would promise Enter\nand Space activate, and here Return *renames* while the open route is ⌘O / ⌘↓.\n\nThe owner is `vf-icon-field`, the container a field of icons sits in, and\nit is what buys the selection state back:\n\n```html\n<vf-icon-field label=\"Desktop\">\n <vf-icon label=\"Macintosh HD\" selectable movable editable>…</vf-icon>\n <vf-icon label=\"Trash\" selectable movable editable>…</vf-icon>\n</vf-icon-field>\n```\n\nA `vf-desktop` cannot be that container itself: it also holds windows and a\nmenu bar, and a non-`option` child of a listbox is invalid the same way the\norphaned option was. The field is layout-neutral — placed icons anchor to\nthe nearest *positioned* ancestor, which is still the desktop's raster. An\nelement of your own carrying `role=\"listbox\"` still owns an icon the same\nway. One divergence from the APG listbox is deliberate: its options share\na single roving tab stop, while these stay one stop each, the way a Finder\nicon is reached on its own.\n\n**`selectable` is what makes an icon focusable**, and `movable`/`editable`\npresuppose it. That is the Finder's own model — you cannot move or rename\nwhat you have not selected — and the pointer path already assumed it: the\nrename opens on a press on the plate of an *already-selected* icon. A\n`movable`-only icon is a picture you can drag, not a widget.\n\n### The label is a property, because it is editable\n\nThe caption is `label` rather than slotted content: renaming means the\ncomponent owns the string and hands it back on `vf-change`, and it cannot own\ntext that lives in the consumer's DOM. An empty `label` draws no plate at\nall — that *is* the \"no label\" parameter, in preference to a second attribute\nthat could disagree with it. `editable` then lets a click on the plate of an\nalready-selected icon open the rename box a moment later, as the Finder's\ndoes (see above), with Return committing, Escape reverting, and the plate\nwidening as you type.\n\n### A name is never abbreviated, and never folded\n\nThere is no ellipsis, no clipping and no wrapping: one line, always. System 7\nsolved the long-name problem at the *other* end — HFS capped a filename at 31\ncharacters — so the Finder could afford to always draw the name in full, and\ndid. A name wider than its cell simply overflows it, centered, the way a name\nwider than a 32-pixel icon always did.\n\nStaying on one line is also what keeps the name on the grid: a single run has\na single measured width, so the even-plate rule above is one number rather\nthan one per line, each with its own parity. `width` is therefore the cell —\nthe grid pitch — not a bound on the name.\n\n`maxlength` (31) bounds the rename field rather than the `label` property:\nthe name belongs to the consumer's model, and truncating one handed to us\nwould lose data.\n\nThe box hugs its text while you type it, not only once you are done, which is\nwhat keeps the name from moving when the edit commits: the plate is the same\nwidth either side of it, so the glyphs stay exactly where they were.\n\nA file also has to be called *something*, so a rename committed empty — or\nas nothing but spaces — is refused rather than applied, and the previous name\ncomes back, which is what System 7 did. An empty `label` is still a perfectly\ngood state to *start* in, though: a freshly made icon has no name until it is\ngiven one. Such an icon draws no plate but stays selectable, focusable and\nrenameable — Return opens a field, and an edit with nothing in it yet is the\none time the box stops hugging and reserves a cell's width, since a field you\ncannot see is one you cannot type into.\n---\n\n\n### **Events:**\n - **vf-select** - Selection changed by user interaction. `detail: { selected: boolean }`.\n- **vf-change** - The name was committed. `detail: { label: string, previous: string }`.\n- **vf-open** - The icon was opened — double-clicked anywhere on it, its name included; tapped twice with a finger or pen; or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`.\n- **vf-name-too-long** - A rename was typed or pasted past `maxlength`, and the field refused the excess. `detail: { attempted, accepted, limit }` — enough to raise the alert System 7 raised rather than drop the characters silently. Not fired for a `label` set from your own code, which is never truncated in the first place.\n- **vf-name-rejected** - A rename was committed with no name in it, so the edit was dropped and the old name put back. `detail: { attempted, kept, reason: 'empty' }`. A `vf-change` is *not* fired alongside it — nothing changed.\n- **vf-drag-start** - A drag began: the first lattice step past the drag slop, 4 CSS px from the press for the mouse and 10 for a finger or pen. `detail: { left, top, icons }` — the origin, whole system px in the icon's container, and every icon the drag carries (this one first).\n- **vf-drag** - A step that changed the proposal. `detail: { clientX, clientY, left, top, x, y, icons }` — the pointer in viewport CSS px, the proposed origin in the container (whole system px on the lattice), the outline's top-left in viewport CSS px, and the icons travelling.\n- **vf-drop** - The release; cancelable. The same detail as `vf-drag`. Its default action writes the proposal through `left`/`top` — every icon in `icons` by the same delta, clamped whole in the container as a group; `preventDefault()` writes nothing.\n- **vf-drag-cancel** - The drag was abandoned — Escape, or a `pointercancel` — and nothing was written. `detail: {}`.\n\n### **Slots:**\n - **large** - The 32×32 art, normally a `vf-img` around an `<img>`.\n- **small** - The 16×16 art, shown under `size=\"small\"`.\n\n### **CSS Properties:**\n - **--vf-icon-gap** - Space between the art cell and the name plate; 0 by default, the plate directly under the cell as the Finder drew it _(default: undefined)_\n- **--vf-icon-label-height** - The name plate's line box _(default: undefined)_\n\n### **CSS Parts:**\n - **frame** - The icon and its label plate, stacked.\n- **icon** - The reserved art cell.\n- **label** - The name block; its lines are centered under the art.\n- **plate** - The inked run behind the name, which each wrapped line gets its own of (inverts when selected).\n- **input** - The rename field, while editing.",
1079
1079
  "doc-url": "",
1080
1080
  "attributes": [
1081
1081
  {
@@ -1175,7 +1175,7 @@
1175
1175
  },
1176
1176
  {
1177
1177
  "name": "vf-open",
1178
- "description": "The icon was opened — double-clicked anywhere on it, its name included, or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`."
1178
+ "description": "The icon was opened — double-clicked anywhere on it, its name included; tapped twice with a finger or pen; or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`."
1179
1179
  },
1180
1180
  {
1181
1181
  "name": "vf-name-too-long",
@@ -1187,7 +1187,7 @@
1187
1187
  },
1188
1188
  {
1189
1189
  "name": "vf-drag-start",
1190
- "description": "A drag began: the first lattice step away from the press. `detail: { left, top, icons }` — the origin, whole system px in the icon's container, and every icon the drag carries (this one first)."
1190
+ "description": "A drag began: the first lattice step past the drag slop, 4 CSS px from the press for the mouse and 10 for a finger or pen. `detail: { left, top, icons }` — the origin, whole system px in the icon's container, and every icon the drag carries (this one first)."
1191
1191
  },
1192
1192
  {
1193
1193
  "name": "vf-drag",
@@ -1291,7 +1291,7 @@
1291
1291
  },
1292
1292
  {
1293
1293
  "name": "vf-open",
1294
- "description": "The icon was opened — double-clicked anywhere on it, its name included, or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`."
1294
+ "description": "The icon was opened — double-clicked anywhere on it, its name included; tapped twice with a finger or pen; or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`."
1295
1295
  },
1296
1296
  {
1297
1297
  "name": "vf-name-too-long",
@@ -1303,7 +1303,7 @@
1303
1303
  },
1304
1304
  {
1305
1305
  "name": "vf-drag-start",
1306
- "description": "A drag began: the first lattice step away from the press. `detail: { left, top, icons }` — the origin, whole system px in the icon's container, and every icon the drag carries (this one first)."
1306
+ "description": "A drag began: the first lattice step past the drag slop, 4 CSS px from the press for the mouse and 10 for a finger or pen. `detail: { left, top, icons }` — the origin, whole system px in the icon's container, and every icon the drag carries (this one first)."
1307
1307
  },
1308
1308
  {
1309
1309
  "name": "vf-drag",
@@ -3989,7 +3989,7 @@
3989
3989
  },
3990
3990
  {
3991
3991
  "name": "vf-window",
3992
- "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\" 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\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close' }` (shape- compatible with vf-dialog's `vf-close`). 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).",
3992
+ "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\" 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' }` (shape- compatible with vf-dialog's `vf-close`). 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).",
3993
3993
  "doc-url": "",
3994
3994
  "attributes": [
3995
3995
  {
@@ -4022,6 +4022,11 @@
4022
4022
  "description": "Allow dragging the window by its title bar.",
4023
4023
  "value": { "type": "boolean", "default": "false" }
4024
4024
  },
4025
+ {
4026
+ "name": "outline-drag",
4027
+ "description": "Drag by the title bar as an outline: a press draws a dotted one-pixel\nrectangle around the window on the desktop's drag surface, with the\nicon drag outline's pen; the drag moves it, held where the window can\nland, and the window moves there on the release, in one write. Escape\ncancels and writes nothing, as does a release that never moved it.\nUnset, the window moves live under the pointer; with no `vf-desktop` on\nits path it moves live either way. Only matters with `movable`.",
4028
+ "value": { "type": "boolean", "default": "false" }
4029
+ },
4025
4030
  {
4026
4031
  "name": "resizable",
4027
4032
  "description": "Show a grow box at the bottom-right corner for resizing. The drag is\nbounded per axis by the sizeRect below.",
@@ -4147,6 +4152,11 @@
4147
4152
  "description": "Allow dragging the window by its title bar.",
4148
4153
  "type": "boolean"
4149
4154
  },
4155
+ {
4156
+ "name": "outlineDrag",
4157
+ "description": "Drag by the title bar as an outline: a press draws a dotted one-pixel\nrectangle around the window on the desktop's drag surface, with the\nicon drag outline's pen; the drag moves it, held where the window can\nland, and the window moves there on the release, in one write. Escape\ncancels and writes nothing, as does a release that never moved it.\nUnset, the window moves live under the pointer; with no `vf-desktop` on\nits path it moves live either way. Only matters with `movable`.",
4158
+ "type": "boolean"
4159
+ },
4150
4160
  {
4151
4161
  "name": "resizable",
4152
4162
  "description": "Show a grow box at the bottom-right corner for resizing. The drag is\nbounded per axis by the sizeRect below.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vintage-frames",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
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",