vintage-frames 0.14.0 → 0.15.0

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.
Files changed (55) hide show
  1. package/README.md +16 -0
  2. package/custom-elements.json +271 -27
  3. package/dist/components/vf-button.d.ts +5 -1
  4. package/dist/components/vf-button.js +1 -1
  5. package/dist/components/vf-container.d.ts +3 -1
  6. package/dist/components/vf-container.js +34 -16
  7. package/dist/components/vf-desktop.d.ts +114 -12
  8. package/dist/components/vf-desktop.js +119 -58
  9. package/dist/components/vf-dialog.d.ts +7 -1
  10. package/dist/components/vf-icon-field.d.ts +1 -1
  11. package/dist/components/vf-icon.js +68 -61
  12. package/dist/components/vf-menu-bar.d.ts +5 -0
  13. package/dist/components/vf-menu-bar.js +67 -43
  14. package/dist/components/vf-window.d.ts +13 -0
  15. package/dist/components/vf-window.js +10 -1
  16. package/dist/events.d.ts +3 -2
  17. package/dist/events.js +3 -3
  18. package/dist/index.d.ts +3 -1
  19. package/dist/index.js +44 -43
  20. package/dist/modal-dialog.d.ts +31 -4
  21. package/dist/modal-dialog.js +98 -82
  22. package/dist/pattern-fill.d.ts +11 -8
  23. package/dist/pattern-fill.js +2 -4
  24. package/dist/position.d.ts +25 -7
  25. package/dist/position.js +31 -23
  26. package/dist/shell/alert.d.ts +40 -0
  27. package/dist/shell/alert.js +41 -0
  28. package/dist/shell/catalog.d.ts +168 -0
  29. package/dist/shell/catalog.js +278 -0
  30. package/dist/shell/clock.d.ts +16 -0
  31. package/dist/shell/clock.js +36 -0
  32. package/dist/shell/filing.d.ts +39 -0
  33. package/dist/shell/filing.js +68 -0
  34. package/dist/shell/finder.d.ts +156 -0
  35. package/dist/shell/finder.js +598 -0
  36. package/dist/shell/geometry.d.ts +196 -0
  37. package/dist/shell/geometry.js +241 -0
  38. package/dist/shell/index.d.ts +27 -0
  39. package/dist/shell/index.js +10 -0
  40. package/dist/shell/shell.d.ts +164 -0
  41. package/dist/shell/shell.js +234 -0
  42. package/dist/shell/state.d.ts +88 -0
  43. package/dist/shell/state.js +103 -0
  44. package/dist/shell/storage.d.ts +14 -0
  45. package/dist/shell/storage.js +41 -0
  46. package/dist/shell/windows.d.ts +234 -0
  47. package/dist/shell/windows.js +387 -0
  48. package/dist/text-control.d.ts +6 -0
  49. package/dist/text-control.js +2 -2
  50. package/dist/window-chrome.d.ts +43 -0
  51. package/dist/window-chrome.js +13 -0
  52. package/docs/SPEC.md +17 -16
  53. package/editor/vscode.html-custom-data.json +11 -6
  54. package/editor/web-types.json +55 -14
  55. package/package.json +10 -2
@@ -303,7 +303,7 @@
303
303
  "text": "string"
304
304
  },
305
305
  "default": "''",
306
- "description": "Value submitted under `name` when `type=\"submit\"`.",
306
+ "description": "Value submitted under `name` when `type=\"submit\"`. In a\n`<form method=\"dialog\">` inside a `vf-dialog`, the dialog's answer\n(`returnValue`), with or without a `name`.",
307
307
  "attribute": "value",
308
308
  "reflects": true
309
309
  },
@@ -1140,7 +1140,7 @@
1140
1140
  "text": "string"
1141
1141
  },
1142
1142
  "default": "''",
1143
- "description": "Value submitted under `name` when `type=\"submit\"`.",
1143
+ "description": "Value submitted under `name` when `type=\"submit\"`. In a\n`<form method=\"dialog\">` inside a `vf-dialog`, the dialog's answer\n(`returnValue`), with or without a `name`.",
1144
1144
  "fieldName": "value"
1145
1145
  },
1146
1146
  {
@@ -2246,7 +2246,7 @@
2246
2246
  "declarations": [
2247
2247
  {
2248
2248
  "kind": "class",
2249
- "description": "`<vf-container>` — a box that is nothing but its declared size.\n\nThe kit's positioned-placement story (src/position.ts) ends with one line of\nCSS it can't write for you: children placed with `top`/`left` need a\npositioned ancestor, and while every kit container is one — a window body, a\nstack, a fieldset — a region of *your own* needs `position: relative` in a\nstylesheet. This component is that region as an element: declare `width` and\n`height` in whole system px, slot anything into it, place children against\nits origin. A DITL's enclosing rectangle, with nothing drawn in it.\n\n```html\n<vf-container width=\"200\" height=\"120\">\n <vf-icon label=\"System\" width=\"64\" top=\"8\" left=\"12\">…</vf-icon>\n <vf-icon label=\"Finder\" width=\"64\" top=\"8\" left=\"104\">…</vf-icon>\n</vf-container>\n```\n\nThe rectangle is the whole API — `width`/`height` here, plus the `top`/`left`\npair nearly every component takes (VfPositioned), so a container is\nitself placeable: inside a window, a desktop, or another container, at whole\nsystem px that keep its box on the device-pixel grid by construction.\n\n**It is not a `vf-stack`.** The stack is a flexbox with opinions — it\ndistributes children along an axis, compiles `fill-width`/`fill-height` into\nflex, defaults a cross-axis alignment. This box has no layout opinion at\nall: in-flow children get normal flow, placed children get a coordinate\nsystem, and that is the whole API. Reach for it when the stack's opinions\nare the thing in the way — a field of placed icons, a fixed stage for\nabsolutely-positioned art, a consumer's own composition that brings its\nlayout with it.\n\n**The declared size is the layout.** `width`/`height` land on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)` (VfSized), so the box scales\nwith the display and sits on the device-pixel grid by construction. Content\nthat outgrows the box overflows it rather than growing it — the number is\nthe layout, and content that doesn't fit is a number to raise. Leave a\ndimension off and that axis shrink-wraps: `fit-content`, not the parent's\nwidth, because a layout box that silently claimed a size nobody declared\nwould be inventing one (the `vf-stack` rule, held here too).\n\n**It paints nothing and means nothing** — unless `pattern` or `rule` say\nwhat to paint. No role, keyboard behavior or selection; what it holds\ndecides what it is.\n\n**`rule` draws the 1px rule on the edges it names** — `rule=\"bottom\"` is\nthe menu bar's anatomy (the box's rows over one row of ink), `rule=\"top\"`\na status strip's, all four a framed box. The rule is drawn inside the\ndeclared size (vfRule), so a 24px `rule=\"bottom\"` strip is 23 rows\nof box over the line; content, `fill-width` children and placed children\nbegin inside it, as a rectangle's interior begins inside FrameRect's line.\n\n**`pattern` fills the box with a 1-bit pattern**: one of the 38 standard\nMacPaint patterns by name (`pattern=\"bricks\"`, `pattern=\"gray-50\"` —\ndocs/PATTERNS.md has the table), or sixteen hex digits stating a custom\n8×8 pattern row by row, the way a PAT resource did. It is painted as the\nbox's own background — black ink on a `--vf-white` ground, anchored at\nthe box's top-left corner, under the content — by the same repeating tile\nas the desktop dither, so it is 1-bit at every density and zoom and a box\nof any size, declared or not, costs nothing more (src/pattern-fill.ts).\nUnder forced colors the pattern goes flat Canvas.\n\n**It holds its box on the device-pixel grid** — with a `GridSnapController`.\nA container's box is itself the consumer's coordinate system, including for\nnon-`vf` content that cannot correct itself, so the box is the thing to\nhold on the grid. The correction lands on the shadow box below, and the\ncoordinate system sits inside it, so the correction moves the whole\ncoordinate system — everything placed against it rides along instead of\nbeing re-corrected child by child. (`vf-stack` shipped\nwithout a controller on the theory that slotted `vf-*` children correct\ntheir own origins; this component is where that theory's gap — consumer\ncontent — became visible, and the stack has since adopted the same\narrangement.)\n\nLike the stack it is **typographically transparent**: `vfBase`'s chrome\ndress is returned to `inherit` on the host, so wrapping content in a sized\nbox changes nothing about how that content reads.\n\n`fill-width` / `fill-height` work here the way they do everywhere: read\nabout the host (be as big as *its* parent allows), and compiled for slotted\nchildren — `width: 100%` in normal flow, so a child filling the cross of a\ndeclared box needs no stylesheet. A height fill needs a declared `height`\nto resolve against; with none it is inert, not an error.",
2249
+ "description": "`<vf-container>` — a box that is nothing but its declared size.\n\nThe kit's positioned-placement story (src/position.ts) ends with one line of\nCSS it can't write for you: children placed with `top`/`left` need a\npositioned ancestor, and while every kit container is one — a window body, a\nstack, a fieldset — a region of *your own* needs `position: relative` in a\nstylesheet. This component is that region as an element: declare `width` and\n`height` in whole system px, slot anything into it, place children against\nits origin. A DITL's enclosing rectangle, with nothing drawn in it.\n\n```html\n<vf-container width=\"200\" height=\"120\">\n <vf-icon label=\"System\" width=\"64\" top=\"8\" left=\"12\">…</vf-icon>\n <vf-icon label=\"Finder\" width=\"64\" top=\"8\" left=\"104\">…</vf-icon>\n</vf-container>\n```\n\nThe rectangle is the whole API — `width`/`height` here, plus the `top`/`left`\npair nearly every component takes (VfPositioned), so a container is\nitself placeable: inside a window, a desktop, or another container, at whole\nsystem px that keep its box on the device-pixel grid by construction.\n\n**It is not a `vf-stack`.** The stack is a flexbox with opinions — it\ndistributes children along an axis, compiles `fill-width`/`fill-height` into\nflex, defaults a cross-axis alignment. This box has no layout opinion at\nall: in-flow children get normal flow, placed children get a coordinate\nsystem, and that is the whole API. Reach for it when the stack's opinions\nare the thing in the way — a field of placed icons, a fixed stage for\nabsolutely-positioned art, a consumer's own composition that brings its\nlayout with it.\n\n**The declared size is the layout.** `width`/`height` land on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)` (VfSized), so the box scales\nwith the display and sits on the device-pixel grid by construction. Content\nthat outgrows the box overflows it rather than growing it — the number is\nthe layout, and content that doesn't fit is a number to raise. Leave a\ndimension off and that axis shrink-wraps: `fit-content`, not the parent's\nwidth, because a layout box that silently claimed a size nobody declared\nwould be inventing one (the `vf-stack` rule, held here too).\n\n**It paints nothing and means nothing** — unless `pattern` or `rule` say\nwhat to paint. No role, keyboard behavior or selection; what it holds\ndecides what it is. A page that gives it a role and a `tabindex` — a\npattern chooser's cells as radios — gets the kit's keyboard focus mark\nrather than the browser's ring: the dashed rule below the box.\n\n**`rule` draws the 1px rule on the edges it names** — `rule=\"bottom\"` is\nthe menu bar's anatomy (the box's rows over one row of ink), `rule=\"top\"`\na status strip's, all four a framed box. The rule is drawn inside the\ndeclared size (vfRule), so a 24px `rule=\"bottom\"` strip is 23 rows\nof box over the line; content, `fill-width` children and placed children\nbegin inside it, as a rectangle's interior begins inside FrameRect's line.\n\n**`pattern` fills the box with a 1-bit pattern**: one of the 38 standard\nMacPaint patterns by name (`pattern=\"bricks\"`, `pattern=\"gray-50\"` —\ndocs/PATTERNS.md has the table), or sixteen hex digits stating a custom\n8×8 pattern row by row, the way a PAT resource did. It is painted as the\nbox's own background — black ink on a `--vf-white` ground, anchored at\nthe box's top-left corner, under the content — by the same repeating tile\nas the desktop dither, so it is 1-bit at every density and zoom and a box\nof any size, declared or not, costs nothing more (src/pattern-fill.ts).\nUnder forced colors the pattern goes flat Canvas.\n\n**It holds its box on the device-pixel grid** — with a `GridSnapController`.\nA container's box is itself the consumer's coordinate system, including for\nnon-`vf` content that cannot correct itself, so the box is the thing to\nhold on the grid. The correction lands on the shadow box below, and the\ncoordinate system sits inside it, so the correction moves the whole\ncoordinate system — everything placed against it rides along instead of\nbeing re-corrected child by child. (`vf-stack` shipped\nwithout a controller on the theory that slotted `vf-*` children correct\ntheir own origins; this component is where that theory's gap — consumer\ncontent — became visible, and the stack has since adopted the same\narrangement.)\n\nLike the stack it is **typographically transparent**: `vfBase`'s chrome\ndress is returned to `inherit` on the host, so wrapping content in a sized\nbox changes nothing about how that content reads.\n\n`fill-width` / `fill-height` work here the way they do everywhere: read\nabout the host (be as big as *its* parent allows), and compiled for slotted\nchildren — `width: 100%` in normal flow, so a child filling the cross of a\ndeclared box needs no stylesheet. A height fill needs a declared `height`\nto resolve against; with none it is inert, not an error.",
2250
2250
  "name": "VfContainer",
2251
2251
  "slots": [
2252
2252
  {
@@ -2563,7 +2563,7 @@
2563
2563
  "declarations": [
2564
2564
  {
2565
2565
  "kind": "class",
2566
- "description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. 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, a repeating tile drawn at the display's resolution, 1-bit at every\ndensity and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. A window asks the same way and draws\nthere with the same pen: its zoom rects when it opens from a rect\n(`vf-window.show({ from })`), its outline under an `outline-drag`. Not the\ntop layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).",
2566
+ "description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the screen-corner mask is in front of it.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\n**Hidden windows.** A window with `hidden` — closed by a page that hides\nits windows rather than removing them, through `vf-window.hide()` or the\nattribute itself — never holds the active state. One slotted hidden waits\nto be shown; `bringToFront()` on one gives it its place in the stack and\nactivates nothing; the holder hiding hands the state to the topmost shown\ndocument window, or to none. A window shown into a tier where none is\nactive, unless the tier was deliberately deactivated, takes it.\nstackingOrder lists the windows as they stand, bottom-most first.\n\n**The work area.** A slotted `vf-menu-bar` along the top takes its band\noff the screen: workArea is what is left, and the gestures on the\nscreen stay in it — a window dragged up stops with its title bar under the\nbar, where it can still be grabbed, and an icon dropped, nudged or walked\non the desktop lands below the bar. `window-top` deepens the area for\nwindows alone (windowArea), for a page with a strip of its own\nunder the bar. A gesture asks for the box it is clamped into with\n`vf-placement-bounds-request` (bubbles, non-composed), the drag surface's\nhandshake, and the desktop answers for the hosts placed on its screen.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, a repeating tile drawn at the display's resolution, 1-bit at every\ndensity and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. A window asks the same way and draws\nthere with the same pen: its zoom rects when it opens from a rect\n(`vf-window.show({ from })`), its outline under an `outline-drag`. Not the\ntop layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).",
2567
2567
  "name": "VfDesktop",
2568
2568
  "cssProperties": [
2569
2569
  {
@@ -2629,6 +2629,33 @@
2629
2629
  "description": "The desktop pattern — System 7's General Controls setting. A library\npattern by name (`gray-50`, the classic dither, by default; `gray-75`,\n`bricks`, … — docs/PATTERNS.md) or sixteen hex digits stating a custom\n8×8 pattern, as on `vf-container`. Painted black on opaque white over\nthe whole screen, 1-bit at every density and zoom. A\n`--vf-desktop-pattern` token override still wins and renders the\nconsumer's tile as a placed grid; an unrecognized value warns once and\nkeeps the dither.",
2630
2630
  "attribute": "pattern"
2631
2631
  },
2632
+ {
2633
+ "kind": "field",
2634
+ "name": "windowTop",
2635
+ "type": {
2636
+ "text": "number | null | undefined"
2637
+ },
2638
+ "description": "The least `top` a window may take, in system px from the screen's top —\nfor a page that keeps a strip of its own under the menu bar, over the\nwindows. A window dragged on this desktop stops there, and\nwindowArea starts there; icons still stop at the menu bar. Below\nthe bar's bottom it changes nothing. Unset, windows stop at the bar.",
2639
+ "attribute": "window-top"
2640
+ },
2641
+ {
2642
+ "kind": "field",
2643
+ "name": "workArea",
2644
+ "type": {
2645
+ "text": "{ left: number; top: number; width: number; height: number }"
2646
+ },
2647
+ "description": "The screen less a slotted `vf-menu-bar` along its top edge, in system\npx from the screen's corner (the bezel excluded, as for\nplacementAt): `{ left, top, width, height }`. The desktop clamps\nthe gestures on its screen into it — an icon dropped, nudged or walked\non the desktop lands below the menu bar, where it can be grabbed again.\nWithout a bar it is the whole screen. Measured at the call.",
2648
+ "readonly": true
2649
+ },
2650
+ {
2651
+ "kind": "field",
2652
+ "name": "windowArea",
2653
+ "type": {
2654
+ "text": "{ left: number; top: number; width: number; height: number }"
2655
+ },
2656
+ "description": "workArea, deepened to windowTop where that is lower: the\narea windows are dragged in. A window's title bar never goes above it.",
2657
+ "readonly": true
2658
+ },
2632
2659
  {
2633
2660
  "kind": "method",
2634
2661
  "name": "fitWithin",
@@ -2807,6 +2834,14 @@
2807
2834
  "default": "false",
2808
2835
  "description": "Whether a one-shot post-upgrade re-normalization is already pending."
2809
2836
  },
2837
+ {
2838
+ "kind": "field",
2839
+ "name": "#visibility",
2840
+ "privacy": "private",
2841
+ "readonly": true,
2842
+ "default": "new MutationObserver(() => this._resolveActive())",
2843
+ "description": "Watches each slotted window's `hidden`, which a page may set directly as\nwell as through `vf-window.hide()`: a hidden window never holds the\nactive state, so the holder hiding hands it on (see _resolveActive).\nRe-pointed at the windows on every slot change."
2844
+ },
2810
2845
  {
2811
2846
  "kind": "field",
2812
2847
  "name": "_pointerGesture",
@@ -2862,7 +2897,7 @@
2862
2897
  }
2863
2898
  }
2864
2899
  ],
2865
- "description": "Whether a slotted window belongs to the floating (utility) tier. The\nattribute is the source of truth here so a not-yet-upgraded element\nstill lands in the right tier (variant reflects, so an upgraded\nproperty-set window agrees)."
2900
+ "description": "Whether a slotted window belongs to the floating (utility) tier: the\n`variant` property where the window has one, else the attribute, so a\nnot-yet-upgraded element still lands in the right tier. Not the\nattribute alone — it reflects in the window's next update, after a\nwindow made by script and appended in the same task has been slotted,\nand that palette would land in the document tier and lose its\n`active`."
2866
2901
  },
2867
2902
  {
2868
2903
  "kind": "method",
@@ -2888,7 +2923,16 @@
2888
2923
  "type": {
2889
2924
  "text": "HTMLElement | null"
2890
2925
  },
2891
- "description": "The active document-tier window, or null while the tier is deactivated\n(or has no windows). Utility windows are never the holder.",
2926
+ "description": "The active document-tier window, or null while the tier is deactivated\n(or has no shown windows). Utility windows and hidden windows are never\nthe holder.",
2927
+ "readonly": true
2928
+ },
2929
+ {
2930
+ "kind": "field",
2931
+ "name": "stackingOrder",
2932
+ "type": {
2933
+ "text": "HTMLElement[]"
2934
+ },
2935
+ "description": "The slotted windows in stacking order, bottom-most first: the document\ntier, then the floating tier above it, each in the order its windows\nwere last raised. Hidden windows are included where they stand. This is\nthe order the eye sees; the windows' light-DOM order catches up with it\nonly once a gesture ends, and a raise by keyboard focus moves no node.",
2892
2936
  "readonly": true
2893
2937
  },
2894
2938
  {
@@ -2918,7 +2962,7 @@
2918
2962
  }
2919
2963
  }
2920
2964
  ],
2921
- "description": "The z/active half of a raise, shared by every path."
2965
+ "description": "The z/active half of a raise, shared by every path. A hidden window\ntakes its place in the stack and nothing else: it activates when it is\nshown and raised."
2922
2966
  },
2923
2967
  {
2924
2968
  "kind": "method",
@@ -2968,6 +3012,23 @@
2968
3012
  "privacy": "private",
2969
3013
  "description": "A dragged icon, or a window opening from a rect, asking for the screen's\ndrag surface (see the class doc). Non-composed, so it reaches here only\nthrough the light-DOM path the asker is slotted on — a window body\nincluded. The nearest desktop answers; an outer one leaves a filled\ndetail alone."
2970
3014
  },
3015
+ {
3016
+ "kind": "field",
3017
+ "name": "_onBoundsRequest",
3018
+ "privacy": "private",
3019
+ "description": "A gesture measuring the box it is clamped into (src/position.ts): for a\nhost placed on this screen — a slotted window, an icon in an unplaced\nfield — the screen, bezel excluded, and the work area's top as the\nfloor: windowArea's for a window, workArea's for\nanything else. The floor is rounded up to the host's placement lattice,\nso a snapped landing never rounds above it. A host placed in a box of its\nown (a window body, a placed field) is that box's; an outer desktop\nleaves a filled detail alone."
3020
+ },
3021
+ {
3022
+ "kind": "method",
3023
+ "name": "_menuBarFloor",
3024
+ "privacy": "private",
3025
+ "return": {
3026
+ "type": {
3027
+ "text": "number"
3028
+ }
3029
+ },
3030
+ "description": "The bottom of a slotted, shown menu bar along the screen's top edge, system px; else 0."
3031
+ },
2971
3032
  {
2972
3033
  "kind": "field",
2973
3034
  "name": "_onFocusIn",
@@ -3004,6 +3065,28 @@
3004
3065
  },
3005
3066
  "description": "Wire up newly slotted windows: seed z-indices, normalize `active`."
3006
3067
  },
3068
+ {
3069
+ "kind": "method",
3070
+ "name": "_resolveActive",
3071
+ "privacy": "private",
3072
+ "return": {
3073
+ "type": {
3074
+ "text": "void"
3075
+ }
3076
+ },
3077
+ "description": "Resolve who should hold `active` after the window set or a window's\nvisibility changed: a holder still slotted and shown keeps it, a\ndeliberate deactivation is preserved (never promote a survivor over the\nuser's \"clicked the Finder\"), and only then does the topmost shown\nwindow inherit — the active window left the DOM or was hidden, or a\nwindow was shown into a tier with none active. Applied even when null,\nso a stray `active` attribute on slotted markup, or on a hidden window,\nis normalized in the deactivated state too."
3078
+ },
3079
+ {
3080
+ "kind": "method",
3081
+ "name": "_observeWindows",
3082
+ "privacy": "private",
3083
+ "return": {
3084
+ "type": {
3085
+ "text": "void"
3086
+ }
3087
+ },
3088
+ "description": "Watch the `hidden` attribute of every slotted window, and no other."
3089
+ },
3007
3090
  {
3008
3091
  "kind": "method",
3009
3092
  "name": "_normalizeAfterUpgrade",
@@ -3098,7 +3181,32 @@
3098
3181
  "text": "void"
3099
3182
  }
3100
3183
  },
3101
- "description": "Re-order the slotted windows in the light DOM to match their z-order\n(bottom-most first), so sequential focus navigation walks the stack the\nway the eye does — and Shift+Tab is its exact mirror. The utility band\nsorts the floating tier after every document window by construction.\n\nMinimal-move: windows already in relative order are never touched (the\ncommon case — after one raise, one window moves). Non-window siblings\n(a menu bar, page content) keep their positions; only a window that must\ncross the stack moves past them. Moving a node containing the focused\nelement drops focus to `<body>`, so it is restored afterwards — behind\n`_restoringFocus`, because the restore re-fires focusin (see\n_onFocusIn) on an element that may sit in a background window.\nReached only through _requestDomSync's task — after a pointer\ngesture or a programmatic raise, never from a focus-driven one (see\n_raise for why)."
3184
+ "description": "Re-order the slotted windows in the light DOM to match their z-order\n(bottom-most first), so sequential focus navigation walks the stack the\nway the eye does — and Shift+Tab is its exact mirror. The utility band\nsorts the floating tier after every document window by construction.\n\nMinimal-move: windows already in relative order are never touched (the\ncommon case — after one raise, one window moves). Non-window siblings\n(a menu bar, page content) keep their positions; only a window that must\ncross the stack moves past them. `moveBefore()` keeps the focus inside a\nmoved window where the browser has it (_move); re-inserting a\nnode containing the focused element drops focus to `<body>`, so it is\nrestored afterwards — behind `_restoringFocus`, because the restore\nre-fires focusin (see _onFocusIn) on an element that may sit in a\nbackground window.\nReached only through _requestDomSync's task — after a pointer\ngesture or a programmatic raise, never from a focus-driven one (see\n_raise for why)."
3185
+ },
3186
+ {
3187
+ "kind": "method",
3188
+ "name": "_move",
3189
+ "privacy": "private",
3190
+ "return": {
3191
+ "type": {
3192
+ "text": "void"
3193
+ }
3194
+ },
3195
+ "parameters": [
3196
+ {
3197
+ "name": "win",
3198
+ "type": {
3199
+ "text": "HTMLElement"
3200
+ }
3201
+ },
3202
+ {
3203
+ "name": "before",
3204
+ "type": {
3205
+ "text": "Node | null"
3206
+ }
3207
+ }
3208
+ ],
3209
+ "description": "Move a slotted window before `before`: with `Element.moveBefore()` where\nthe browser has it, which keeps the focus inside the window — a rename\nfield, a text field's caret — through the move; else by re-inserting it,\nand _syncDomOrder puts the focus back."
3102
3210
  },
3103
3211
  {
3104
3212
  "kind": "field",
@@ -3158,7 +3266,7 @@
3158
3266
  ],
3159
3267
  "events": [
3160
3268
  {
3161
- "description": "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.",
3269
+ "description": "The active document-tier window changed. Detail `{ window: HTMLElement | null }` — the new holder, or `null` when the document tier deactivated (a {@link clearActive} call, or the active window leaving the DOM or hiding with no shown window behind it). Fired once per change of holder, never for a re-assertion of the same one.",
3162
3270
  "name": "vf-activate"
3163
3271
  }
3164
3272
  ],
@@ -3193,6 +3301,14 @@
3193
3301
  "description": "The desktop pattern — System 7's General Controls setting. A library\npattern by name (`gray-50`, the classic dither, by default; `gray-75`,\n`bricks`, … — docs/PATTERNS.md) or sixteen hex digits stating a custom\n8×8 pattern, as on `vf-container`. Painted black on opaque white over\nthe whole screen, 1-bit at every density and zoom. A\n`--vf-desktop-pattern` token override still wins and renders the\nconsumer's tile as a placed grid; an unrecognized value warns once and\nkeeps the dither.",
3194
3302
  "fieldName": "pattern"
3195
3303
  },
3304
+ {
3305
+ "name": "window-top",
3306
+ "type": {
3307
+ "text": "number | null | undefined"
3308
+ },
3309
+ "description": "The least `top` a window may take, in system px from the screen's top —\nfor a page that keeps a strip of its own under the menu bar, over the\nwindows. A window dragged on this desktop stops there, and\nwindowArea starts there; icons still stop at the menu bar. Below\nthe bar's bottom it changes nothing. Unset, windows stop at the bar.",
3310
+ "fieldName": "windowTop"
3311
+ },
3196
3312
  {
3197
3313
  "name": "top",
3198
3314
  "type": {
@@ -3282,7 +3398,7 @@
3282
3398
  "declarations": [
3283
3399
  {
3284
3400
  "kind": "class",
3285
- "description": "`<vf-dialog>` — the System 7 modal dialog shell.\n\nTwo chromes, one modal lifecycle (native `<dialog>` for top-layer rendering\nand focus trapping, with a fully transparent backdrop — no dimming). Both\nare the same dBoxProc double frame — 1px outer rule, 2px gap, 2px inner\nband, no shadow (vfModalFrame):\n\n- **Default:** the movable modal (movableDBoxProc) — the striped title bar\n set into the top of that frame, with a centered title over a white body.\n Drag the title bar to move it. `closable` adds the standard close box\n (left of the bar) — the HIG's own figures disagree on whether a movable\n modal carries one (Figure 5-1 says yes, Figure 6-1 and the Chapter 6 text\n say no), so the component enables either reading rather than enforcing\n one.\n- **`frame=\"plain\"`:** the modal dialog box — the bare frame, no title bar —\n and immovable, like the original. With no bar, `heading` only names the\n dialog and `closable` is ignored.\n\nOpen it with `show()` (or set the `open` attribute/property); close with\n`close()`. Escape closes it and fires `vf-close` with\n`{ reason: 'escape' }`; the close box and programmatic closing fire\n`{ reason: 'close' }`. With `light-dismiss`, a click outside the frame\ncloses it too, with `{ reason: 'outside' }` — for the About box; off by\ndefault, since the classic modal ignored an outside click.\n\nKeyboard, the classic Dialog Manager's two rules (VfModalDialog):\non open, focus goes to the first text-entry control — a slotted control\nwith `autofocus` first — or, with none, to the default button\n(`vf-button variant=\"default\"`). Return or Enter activates the default\nbutton from anywhere in the dialog, a focused Cancel included; Space\npresses the focused control. A link keeps its own Enter, and in a\nmulti-line editor Return inserts the newline while the keypad's Enter\nactivates the button.\n\nSlotted children placed with `top`/`left` measure from the content region's\ncorner — the frame's inner edge, below the title bar — and flow content\nstarts at the same corner.",
3401
+ "description": "`<vf-dialog>` — the System 7 modal dialog shell.\n\nTwo chromes, one modal lifecycle (native `<dialog>` for top-layer rendering\nand focus trapping, with a fully transparent backdrop — no dimming). Both\nare the same dBoxProc double frame — 1px outer rule, 2px gap, 2px inner\nband, no shadow (vfModalFrame):\n\n- **Default:** the movable modal (movableDBoxProc) — the striped title bar\n set into the top of that frame, with a centered title over a white body.\n Drag the title bar to move it. `closable` adds the standard close box\n (left of the bar) — the HIG's own figures disagree on whether a movable\n modal carries one (Figure 5-1 says yes, Figure 6-1 and the Chapter 6 text\n say no), so the component enables either reading rather than enforcing\n one.\n- **`frame=\"plain\"`:** the modal dialog box — the bare frame, no title bar —\n and immovable, like the original. With no bar, `heading` only names the\n dialog and `closable` is ignored.\n\nOpen it with `show()` (or set the `open` attribute/property); close with\n`close()`. Escape closes it and fires `vf-close` with\n`{ reason: 'escape' }`; the close box and programmatic closing fire\n`{ reason: 'close' }`. With `light-dismiss`, a click outside the frame\ncloses it too, with `{ reason: 'outside' }` — for the About box; off by\ndefault, since the classic modal ignored an outside click.\n\nAnswers: a `<form method=\"dialog\">` inside it closes it with the\nsubmitting button's `value` as `returnValue`, and `close(value)` does the\nsame. `vf-close` carries it as `detail.returnValue`, null when the close\ncarried none (Escape, a click outside, the close box).\n\nKeyboard, the classic Dialog Manager's two rules (VfModalDialog):\non open, focus goes to the first text-entry control — a slotted control\nwith `autofocus` first — or, with none, to the default button\n(`vf-button variant=\"default\"`). Return or Enter activates the default\nbutton from anywhere in the dialog, a focused Cancel included; Space\npresses the focused control. A link keeps its own Enter, and in a\nmulti-line editor Return inserts the newline while the keypad's Enter\nactivates the button.\n\nSlotted children placed with `top`/`left` measure from the content region's\ncorner — the frame's inner edge, below the title bar — and flow content\nstarts at the same corner.",
3286
3402
  "name": "VfDialog",
3287
3403
  "cssProperties": [
3288
3404
  {
@@ -3474,6 +3590,19 @@
3474
3590
  "module": "src/modal-dialog.ts"
3475
3591
  }
3476
3592
  },
3593
+ {
3594
+ "kind": "field",
3595
+ "name": "returnValue",
3596
+ "type": {
3597
+ "text": "string"
3598
+ },
3599
+ "default": "''",
3600
+ "description": "The modal's answer, as on `<dialog>`: set by `close(value)` and by a\nsubmit from a `<form method=\"dialog\">` inside it (the submitting\nbutton's `value`). Cleared on each open, so a dialog asked again doesn't\nstart with its last answer.",
3601
+ "inheritedFrom": {
3602
+ "name": "VfModalDialog",
3603
+ "module": "src/modal-dialog.ts"
3604
+ }
3605
+ },
3477
3606
  {
3478
3607
  "kind": "field",
3479
3608
  "name": "width",
@@ -3610,6 +3739,30 @@
3610
3739
  "module": "src/modal-dialog.ts"
3611
3740
  }
3612
3741
  },
3742
+ {
3743
+ "kind": "field",
3744
+ "name": "#closeValue",
3745
+ "privacy": "private",
3746
+ "type": {
3747
+ "text": "string | null"
3748
+ },
3749
+ "default": "null",
3750
+ "description": "The value `close(value)` gave, pending for the next native `close` event.",
3751
+ "inheritedFrom": {
3752
+ "name": "VfModalDialog",
3753
+ "module": "src/modal-dialog.ts"
3754
+ }
3755
+ },
3756
+ {
3757
+ "kind": "field",
3758
+ "name": "#onSubmit",
3759
+ "privacy": "private",
3760
+ "description": "A submit from a form whose nearest modal is this one, by `method=\"dialog\"`\nor the submitter's `formmethod=\"dialog\"`, closes it with the submitter's\n`value` — at the end of the submit's path, and not if anything cancelled\nit, as the platform runs a form's submission.",
3761
+ "inheritedFrom": {
3762
+ "name": "VfModalDialog",
3763
+ "module": "src/modal-dialog.ts"
3764
+ }
3765
+ },
3613
3766
  {
3614
3767
  "kind": "field",
3615
3768
  "name": "#outsidePress",
@@ -3754,7 +3907,16 @@
3754
3907
  "text": "void"
3755
3908
  }
3756
3909
  },
3757
- "description": "Close the modal. Fires `vf-close` with `{ reason: 'close' }`.",
3910
+ "parameters": [
3911
+ {
3912
+ "name": "returnValue",
3913
+ "optional": true,
3914
+ "type": {
3915
+ "text": "string"
3916
+ }
3917
+ }
3918
+ ],
3919
+ "description": "Close the modal, with `returnValue` as its answer when one is given.\nFires `vf-close` with `{ reason: 'close', returnValue }`.",
3758
3920
  "inheritedFrom": {
3759
3921
  "name": "VfModalDialog",
3760
3922
  "module": "src/modal-dialog.ts"
@@ -3988,7 +4150,7 @@
3988
4150
  "text": "void"
3989
4151
  }
3990
4152
  },
3991
- "description": "Native `close` — the single teardown funnel for every close path. Drops\nthe written origin so the next open re-derives it from the box it will\nactually have (a stated `top`/`left` is the author's or the user's and\nsurvives, and is re-applied by that open), syncs `open`, and fires\n`vf-close` with the reason.",
4153
+ "description": "Native `close` — the single teardown funnel for every close path. Drops\nthe written origin so the next open re-derives it from the box it will\nactually have (a stated `top`/`left` is the author's or the user's and\nsurvives, and is re-applied by that open), syncs `open`, and fires\n`vf-close` with the reason and the value the close carried.",
3992
4154
  "inheritedFrom": {
3993
4155
  "name": "VfModalDialog",
3994
4156
  "module": "src/modal-dialog.ts"
@@ -3997,7 +4159,7 @@
3997
4159
  ],
3998
4160
  "events": [
3999
4161
  {
4000
- "description": "Dialog closed. Detail `{ reason: 'escape' | 'close' | 'outside' }` — `'outside'` only under `light-dismiss`.",
4162
+ "description": "Dialog closed. Detail `{ reason: 'escape' | 'close' | 'outside', returnValue: string | null }` — `'outside'` only under `light-dismiss`.",
4001
4163
  "name": "vf-close",
4002
4164
  "inheritedFrom": {
4003
4165
  "name": "VfModalDialog",
@@ -4708,7 +4870,7 @@
4708
4870
  "declarations": [
4709
4871
  {
4710
4872
  "kind": "class",
4711
- "description": "`<vf-icon-field>` — the container a field of `vf-icon`s sits in: a desktop's\nicons, a folder window's contents.\n\nEvery other set in the kit has its container — `vf-list` owns\n`vf-list-item`, `vf-radio-group` owns `vf-radio`, `vf-menu` owns\n`vf-menu-item`, `vf-select` owns `vf-option` — and `vf-icon` was the one\nitem whose container the consumer wrote by hand: a bare\n`<div role=\"listbox\" aria-multiselectable=\"true\">`, because an `option` is\nonly valid inside a `listbox` that owns it, and every kit container is a\npositioning anchor by design, so nothing in the kit was neutral enough to\nstand in. A consumer who forgot the role got icons that silently degraded\nto pictures. This is that container, as kit furniture:\n\n```html\n<vf-desktop width=\"512\" height=\"342\">\n <vf-icon-field label=\"Desktop\" fill-width fill-height>\n <vf-icon label=\"Macintosh HD\" width=\"64\" selectable movable editable left=\"16\" top=\"24\">…</vf-icon>\n <vf-icon label=\"Trash\" width=\"64\" selectable movable editable left=\"16\" top=\"280\">…</vf-icon>\n </vf-icon-field>\n <vf-window heading=\"Documents\" width=\"320\" height=\"201\" scrollbars=\"both\" top=\"40\" left=\"120\">\n <vf-icon-field label=\"Documents\">\n <vf-icon label=\"Read Me\" width=\"64\" selectable movable editable left=\"16\" top=\"16\">…</vf-icon>\n </vf-icon-field>\n </vf-window>\n</vf-desktop>\n```\n\n### Semantics through internals\n\n`role=\"listbox\"` and `aria-multiselectable=\"true\"` always — the Finder was\nalways multi-select, so there is no attribute for it — and `label` as the\naccessible name, the way `vf-scroll-area` takes one. Internals values are\ndefaults (SPEC §2): a consumer's own `role`/`aria-*` on the tag still wins.\nA `selectable` icon inside becomes a real `option` carrying `aria-selected`;\n`vf-icon` matches this tag directly, since a role written through internals\nnever lands as an attribute for `closest()` to find.\n\n### Layout-neutral, exactly what the div was\n\nAn in-flow block with no size, no inset and no position of its own,\npainting nothing — the `vf-stack` posture. A field whose icons are all\nplaced is a zero-height block, so the icons keep anchoring to the desktop's\nraster or the window's plane, stored coordinates mean what they meant, and\na press on the bare dither still reaches the desktop. It takes `top`/`left`\nand `width`/`height` like the other declared-box containers, so a page that\nwants the field to be a box places and sizes it, at which point it becomes\nthe anchor — the placement working, as documented for everything.\n`fill-width`/`fill-height` are the other way to give it a box, read about\nthe host the way `vf-stack` and `vf-container` read them: a filled field\nstays in flow and static, so its icons keep their anchor, and it has a\nsurface to press — which is what the rubber band needs.\n\n### The rubber band\n\nA press on the field's own background — not on an icon — dragged across\nthe field draws the Finder's dotted selection rectangle from the press to\nthe pointer, and the selection follows it live by the Finder's one rule:\n**an icon is selected exactly when it was selected at the press XOR the\nrectangle touches it.** The press fixes that anchor: a plain press has\nalready cleared everything (the way any press outside an icon does), so\nthe rectangle simply selects what it touches and releases what it leaves;\nwith Shift or ⌘ held the selection survives the press and the rectangle\n*toggles* against it — an icon already selected deselects while the\nrectangle covers it and comes back when it leaves, one that was not\nselects. Each change reports `vf-select` as a press would. \"Touches\"\nmeans the icon's art cell or its name plate (VfIcon.touches), not\nthe empty cell beside them, so a rectangle through the gap between two\nicons selects neither. The release keeps the result; Escape or a\n`pointercancel` cancels, the rectangle vanishes and the selection goes\nback to the anchor. A press that never leaves its system pixel draws\nnothing.\n\nThe rectangle is drawn with the drag outline's pen (* paintSelectionRect: the dotted one-pixel outline, `filter: invert(1)`\nunder `mix-blend-mode: difference`) at the field's own level, which is\nwhere the Finder drew its marquee — on the desktop under the windows, in a\nwindow body over that window's content and under any window above it —\npositioned against the viewport from the field's own shadow, since a\nlayout-neutral field has no box of its own to anchor to. It reaches no\nfurther than the field, inside every clip above the field (a window body,\na scroll viewport, the raster's edge), so it never paints outside the\nregion the icons are visible in. Its dots take the pen's phase on the\n*screen* — the desktop's, found by the `vf-drag-surface-request`\nhandshake the drag outline uses (penPhase) — so over the desktop\ndither the rectangle reads as a black line wherever the field sits; with\nno desktop, the field's own box is the reference.\n\nOnly a field with a box can be pressed: fill it, or place and size it. An\nunplaced, unfilled field of placed icons is zero-height and draws no band.\n`touch-action` is left alone, so on a touch screen a drag on the\nbackground pans a scrolling window as it always did; the band is a mouse\nand pen gesture.\n\n### The walk\n\nThe Finder's Clean Up moved a container's icons one at a time, each\nicon's dotted outline travelling to its cell and the icon landing when\nthe outline arrived. dragIcons is that walk: `{ icon, left, top }`\nmoves in the page's order — which icon goes first is a statement about\nthe page's lattice, and the kit ships none — each through the icon's own\n`dragTo`, with a beat (`WALK_BEAT_MS`, src/motion.ts) after every landing\nthat showed. It resolves when the last icon has landed. One walk per\nfield: a second call finishes the first. A press anywhere, or Escape,\nfinishes it too — every icon still to go lands at its target at once, the\nFinder never made you wait — and so does the field leaving the DOM; an\nicon gone from the DOM by its turn is skipped. Under\n`prefers-reduced-motion` every icon lands at once, no outline, no beat.\n\n### `size` is the view's\n\n`large` or `small`, written onto every icon in the field when set and\nwhenever it changes, so View → by Small Icon is one attribute instead of a\nloop over every icon. Unset, each icon keeps its own.\n\n### The desktop uses it, and renders none\n\nA desktop's icons sit in a field slotted into `vf-desktop` beside the\nwindows, written by the page; a folder window's icons sit in one slotted\ninto the window body. `vf-desktop` builds no field of its own: its furniture\nis slotted light DOM and the page decides what it is, a desktop with no\nicons should carry no empty listbox, and a field inside the desktop's\nshadow is one `closest()` could not reach.",
4873
+ "description": "`<vf-icon-field>` — the container a field of `vf-icon`s sits in: a desktop's\nicons, a folder window's contents.\n\nEvery other set in the kit has its container — `vf-list` owns\n`vf-list-item`, `vf-radio-group` owns `vf-radio`, `vf-menu` owns\n`vf-menu-item`, `vf-select` owns `vf-option` — and `vf-icon` was the one\nitem whose container the consumer wrote by hand: a bare\n`<div role=\"listbox\" aria-multiselectable=\"true\">`, because an `option` is\nonly valid inside a `listbox` that owns it, and every kit container is a\npositioning anchor by design, so nothing in the kit was neutral enough to\nstand in. A consumer who forgot the role got icons that silently degraded\nto pictures. This is that container, as kit furniture:\n\n```html\n<vf-desktop width=\"512\" height=\"342\">\n <vf-icon-field label=\"Desktop\" fill-width fill-height>\n <vf-icon label=\"Macintosh HD\" width=\"64\" selectable movable editable left=\"16\" top=\"24\">…</vf-icon>\n <vf-icon label=\"Trash\" width=\"64\" selectable movable editable left=\"16\" top=\"280\">…</vf-icon>\n </vf-icon-field>\n <vf-window heading=\"Documents\" width=\"320\" height=\"201\" scrollbars=\"both\" top=\"40\" left=\"120\">\n <vf-icon-field label=\"Documents\">\n <vf-icon label=\"Read Me\" width=\"64\" selectable movable editable left=\"16\" top=\"16\">…</vf-icon>\n </vf-icon-field>\n </vf-window>\n</vf-desktop>\n```\n\n### Semantics through internals\n\n`role=\"listbox\"` and `aria-multiselectable=\"true\"` always — the Finder was\nalways multi-select, so there is no attribute for it — and `label` as the\naccessible name, the way `vf-scroll-area` takes one. Internals values are\ndefaults (SPEC §2): a consumer's own `role`/`aria-*` on the tag still wins.\nA `selectable` icon inside becomes a real `option` carrying `aria-selected`;\n`vf-icon` matches this tag directly, since a role written through internals\nnever lands as an attribute for `closest()` to find.\n\n### Layout-neutral, exactly what the div was\n\nAn in-flow block with no size, no inset and no position of its own,\npainting nothing — the `vf-stack` posture. A field whose icons are all\nplaced is a zero-height block, so the icons keep anchoring to the desktop's\nraster or the window's plane, stored coordinates mean what they meant, and\na press on the bare dither still reaches the desktop. It takes `top`/`left`\nand `width`/`height` like the other declared-box containers, so a page that\nwants the field to be a box places and sizes it, at which point it becomes\nthe anchor — the placement working, as documented for everything.\n`fill-width`/`fill-height` are the other way to give it a box, read about\nthe host the way `vf-stack` and `vf-container` read them: a filled field\nstays in flow and static, so its icons keep their anchor, and it has a\nsurface to press — which is what the rubber band needs.\n\n### The rubber band\n\nA press on the field's own background — not on an icon — dragged across\nthe field draws the Finder's dotted selection rectangle from the press to\nthe pointer, and the selection follows it live by the Finder's one rule:\n**an icon is selected exactly when it was selected at the press XOR the\nrectangle touches it.** The press fixes that anchor: a plain press has\nalready cleared everything (a press on a field's background does), so\nthe rectangle simply selects what it touches and releases what it leaves;\nwith Shift or ⌘ held the selection survives the press and the rectangle\n*toggles* against it — an icon already selected deselects while the\nrectangle covers it and comes back when it leaves, one that was not\nselects. Each change reports `vf-select` as a press would. \"Touches\"\nmeans the icon's art cell or its name plate (VfIcon.touches), not\nthe empty cell beside them, so a rectangle through the gap between two\nicons selects neither. The release keeps the result; Escape or a\n`pointercancel` cancels, the rectangle vanishes and the selection goes\nback to the anchor. A press that never leaves its system pixel draws\nnothing.\n\nThe rectangle is drawn with the drag outline's pen (* paintSelectionRect: the dotted one-pixel outline, `filter: invert(1)`\nunder `mix-blend-mode: difference`) at the field's own level, which is\nwhere the Finder drew its marquee — on the desktop under the windows, in a\nwindow body over that window's content and under any window above it —\npositioned against the viewport from the field's own shadow, since a\nlayout-neutral field has no box of its own to anchor to. It reaches no\nfurther than the field, inside every clip above the field (a window body,\na scroll viewport, the raster's edge), so it never paints outside the\nregion the icons are visible in. Its dots take the pen's phase on the\n*screen* — the desktop's, found by the `vf-drag-surface-request`\nhandshake the drag outline uses (penPhase) — so over the desktop\ndither the rectangle reads as a black line wherever the field sits; with\nno desktop, the field's own box is the reference.\n\nOnly a field with a box can be pressed: fill it, or place and size it. An\nunplaced, unfilled field of placed icons is zero-height and draws no band.\n`touch-action` is left alone, so on a touch screen a drag on the\nbackground pans a scrolling window as it always did; the band is a mouse\nand pen gesture.\n\n### The walk\n\nThe Finder's Clean Up moved a container's icons one at a time, each\nicon's dotted outline travelling to its cell and the icon landing when\nthe outline arrived. dragIcons is that walk: `{ icon, left, top }`\nmoves in the page's order — which icon goes first is a statement about\nthe page's lattice, and the kit ships none — each through the icon's own\n`dragTo`, with a beat (`WALK_BEAT_MS`, src/motion.ts) after every landing\nthat showed. It resolves when the last icon has landed. One walk per\nfield: a second call finishes the first. A press anywhere, or Escape,\nfinishes it too — every icon still to go lands at its target at once, the\nFinder never made you wait — and so does the field leaving the DOM; an\nicon gone from the DOM by its turn is skipped. Under\n`prefers-reduced-motion` every icon lands at once, no outline, no beat.\n\n### `size` is the view's\n\n`large` or `small`, written onto every icon in the field when set and\nwhenever it changes, so View → by Small Icon is one attribute instead of a\nloop over every icon. Unset, each icon keeps its own.\n\n### The desktop uses it, and renders none\n\nA desktop's icons sit in a field slotted into `vf-desktop` beside the\nwindows, written by the page; a folder window's icons sit in one slotted\ninto the window body. `vf-desktop` builds no field of its own: its furniture\nis slotted light DOM and the page decides what it is, a desktop with no\nicons should carry no empty listbox, and a field inside the desktop's\nshadow is one `closest()` could not reach.",
4712
4874
  "name": "VfIconField",
4713
4875
  "slots": [
4714
4876
  {
@@ -5664,13 +5826,13 @@
5664
5826
  "privacy": "private",
5665
5827
  "readonly": true,
5666
5828
  "default": "new DocumentListenersController(this, () => [ [document, 'pointerdown', this.#onOutsidePointerDown, true], ])",
5667
- "description": "Clicking elsewhere deselects, the way it does on a real desktop. Attached\nonly while this icon is both selectable and selected, so an unselected\nfield of icons costs nothing. Capture phase, so a handler that stops the\npress still deselects — and so the icon being clicked *into* selects after\nthis runs, which is what makes plain clicks single-select with no container\nmanaging the set. A container that wants multi-select suppresses it by\nowning `selected` and leaving `selectable` off."
5829
+ "description": "Clicking elsewhere deselects, the way it does on a real desktop: anywhere\noutside an icon alone, and on another icon or a field's background for an\nicon in a field, whose selection outlives a press on the menu bar, a\ndialog or another window (see #onOutsidePointerDown). Attached\nonly while this icon is both selectable and selected, so an unselected\nfield of icons costs nothing. Capture phase, so a handler that stops the\npress still deselects — and so the icon being clicked *into* selects after\nthis runs, which is what makes plain clicks single-select with no container\nmanaging the set. A container that wants multi-select suppresses it by\nowning `selected` and leaving `selectable` off."
5668
5830
  },
5669
5831
  {
5670
5832
  "kind": "field",
5671
5833
  "name": "#keepWhole",
5672
5834
  "privacy": "private",
5673
- "description": "Where a moved icon is allowed to end up, in system px. Unlike `vf-window`,\nwhich only keeps a grabbable strip on screen, an icon is small enough to\nhold whole — losing half of one to an edge reads as a bug rather than as a\nwindow pushed aside. Except in a box that scrolls (#anchorScrolls):\nthe rails reach anything placed past such a box, so only its origin holds\n— the Finder put an icon below a window's fold and scrolled to it."
5835
+ "description": "Where a moved icon is allowed to end up, in system px. Unlike `vf-window`,\nwhich only keeps a grabbable strip on screen, an icon is small enough to\nhold whole — losing half of one to an edge reads as a bug rather than as a\nwindow pushed aside. On a desktop's screen it holds below the menu bar\n(the bounds' `top`). Except in a box that scrolls (#anchorScrolls):\nthe rails reach anything placed past such a box, so only its origin holds\n— the Finder put an icon below a window's fold and scrolled to it."
5674
5836
  },
5675
5837
  {
5676
5838
  "kind": "method",
@@ -6111,6 +6273,12 @@
6111
6273
  },
6112
6274
  "description": "Keep the typed name and close the field — unless there is no name left, in\nwhich case the edit is refused and the old one comes back.\n\nA file has to be called something: System 7 would not let you commit an\nempty name, and this does the same, treating a run of nothing but spaces\nas empty too (the plate would read as blank either way). The refusal is\nreported as `vf-name-rejected` for the same reason `vf-name-too-long` is —\nso a host can say why instead of leaving the name to silently snap back."
6113
6275
  },
6276
+ {
6277
+ "kind": "field",
6278
+ "name": "#onRenameBlur",
6279
+ "privacy": "private",
6280
+ "description": "The rename field lost focus, which commits the name — unless the focus\nis back on the field before the task is out. That is a node move, not\nthe user leaving: a desktop re-inserting the window this icon sits in, to\nkeep the DOM in stacking order, takes the focus off the field and puts it\nstraight back. A microtask, so the check runs after that task's own\ncode and before anything else."
6281
+ },
6114
6282
  {
6115
6283
  "kind": "method",
6116
6284
  "name": "cancelEditing",
@@ -8145,6 +8313,10 @@
8145
8313
  {
8146
8314
  "description": "`vf-menu` elements.",
8147
8315
  "name": ""
8316
+ },
8317
+ {
8318
+ "description": "The bar's right end: a clock, say. Held 9px in from the end, and a `vf-label` there rides the bar's line, so its baseline is the menu titles'.",
8319
+ "name": "end"
8148
8320
  }
8149
8321
  ],
8150
8322
  "members": [
@@ -8203,6 +8375,16 @@
8203
8375
  },
8204
8376
  "privacy": "private"
8205
8377
  },
8378
+ {
8379
+ "kind": "field",
8380
+ "name": "_hasEnd",
8381
+ "type": {
8382
+ "text": "boolean"
8383
+ },
8384
+ "privacy": "private",
8385
+ "default": "false",
8386
+ "description": "Whether the `end` slot has assigned content (drives its `.empty` gate)."
8387
+ },
8206
8388
  {
8207
8389
  "kind": "field",
8208
8390
  "name": "#openMenu",
@@ -8279,6 +8461,25 @@
8279
8461
  "privacy": "private",
8280
8462
  "description": "Focus left the bar entirely while a menu was open: close it, so an open\npanel can't outlive the focus that operates it and go on capturing every\narrow key on the page. Modeled on `VfSelect.handleHostFocusOut`; the\nrelatedTarget is retargeted to light-DOM scope, so slotted menus (and\ntheir panels' items) read as contained."
8281
8463
  },
8464
+ {
8465
+ "kind": "method",
8466
+ "name": "#onEndSlotChange",
8467
+ "privacy": "private",
8468
+ "return": {
8469
+ "type": {
8470
+ "text": "void"
8471
+ }
8472
+ },
8473
+ "parameters": [
8474
+ {
8475
+ "name": "event",
8476
+ "type": {
8477
+ "text": "Event"
8478
+ }
8479
+ }
8480
+ ],
8481
+ "description": "The `.empty` gate: the end slot takes part in the row only while populated."
8482
+ },
8282
8483
  {
8283
8484
  "kind": "method",
8284
8485
  "name": "#onSlotChange",
@@ -10163,7 +10364,7 @@
10163
10364
  "text": "boolean"
10164
10365
  }
10165
10366
  },
10166
- "description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched.",
10367
+ "description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nOne departure: a submit `vf-button variant=\"default\"`, the button with\nthe ring, is the default button when the form has one, ahead of the\nfirst in tree order. A Cancel/OK row puts Cancel first, and Return\nshould press the button the ring marks — the one a modal's own Return\nrule presses (`VfModalDialog`).\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched.",
10167
10368
  "inheritedFrom": {
10168
10369
  "name": "VfTextControlBase",
10169
10370
  "module": "src/text-control.ts"
@@ -18109,7 +18310,7 @@
18109
18310
  "text": "boolean"
18110
18311
  }
18111
18312
  },
18112
- "description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched.",
18313
+ "description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nOne departure: a submit `vf-button variant=\"default\"`, the button with\nthe ring, is the default button when the form has one, ahead of the\nfirst in tree order. A Cancel/OK row puts Cancel first, and Return\nshould press the button the ring marks — the one a modal's own Return\nrule presses (`VfModalDialog`).\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched.",
18113
18314
  "inheritedFrom": {
18114
18315
  "name": "VfTextControlBase",
18115
18316
  "module": "src/text-control.ts"
@@ -19261,7 +19462,7 @@
19261
19462
  "text": "boolean"
19262
19463
  }
19263
19464
  },
19264
- "description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched.",
19465
+ "description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nOne departure: a submit `vf-button variant=\"default\"`, the button with\nthe ring, is the default button when the form has one, ahead of the\nfirst in tree order. A Cancel/OK row puts Cancel first, and Return\nshould press the button the ring marks — the one a modal's own Return\nrule presses (`VfModalDialog`).\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched.",
19265
19466
  "inheritedFrom": {
19266
19467
  "name": "VfTextControlBase",
19267
19468
  "module": "src/text-control.ts"
@@ -20297,6 +20498,15 @@
20297
20498
  "description": "See minWidth. Unbounded by default.",
20298
20499
  "attribute": "max-height"
20299
20500
  },
20501
+ {
20502
+ "kind": "field",
20503
+ "name": "sizeLimits",
20504
+ "type": {
20505
+ "text": "{ minWidth: number; minHeight: number; maxWidth: number; maxHeight: number }"
20506
+ },
20507
+ "description": "The sizeRect as it applies, in whole system px: `min-width` and\n`min-height`, or the 80×54 floor where unset, and `max-width` and\n`max-height`, `Infinity` where unbounded — the range the grow box drags\nthe window in. A page that sizes the window itself, re-fitting it to a\nsmaller screen say, reads it to stay inside the same range.",
20508
+ "readonly": true
20509
+ },
20300
20510
  {
20301
20511
  "kind": "field",
20302
20512
  "name": "scrollbars",
@@ -20413,7 +20623,7 @@
20413
20623
  "kind": "field",
20414
20624
  "name": "#keepGrabbable",
20415
20625
  "privacy": "private",
20416
- "description": "Where a dragged window is allowed to end up, in system px: clamped against\nthe positioning parent (the desktop, usually) so it can't be pushed fully\npast an edge and lost. Only a grabbable strip has to stay in — a window\npushed mostly off-screen is a thing System 7 let you do.\n\nThe box comes from the placement controller, measured once when the drag\nbegan — see PlacementController.moveTo for why a fresh measurement\nper move is the wrong one."
20626
+ "description": "Where a dragged window is allowed to end up, in system px: clamped against\nthe positioning parent (the desktop, usually) so it can't be pushed fully\npast an edge and lost. Only a grabbable strip has to stay in — a window\npushed mostly off-screen is a thing System 7 let you do — and the title\nbar never goes above the bounds' top: on a desktop, the bottom of its\nmenu bar (or `window-top`), where it would be out of reach.\n\nThe box comes from the placement controller, measured once when the drag\nbegan — see PlacementController.moveTo for why a fresh measurement\nper move is the wrong one."
20417
20627
  },
20418
20628
  {
20419
20629
  "kind": "field",
@@ -22391,7 +22601,7 @@
22391
22601
  "text": "boolean"
22392
22602
  }
22393
22603
  },
22394
- "description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched."
22604
+ "description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nOne departure: a submit `vf-button variant=\"default\"`, the button with\nthe ring, is the default button when the form has one, ahead of the\nfirst in tree order. A Cancel/OK row puts Cancel first, and Return\nshould press the button the ring marks — the one a modal's own Return\nrule presses (`VfModalDialog`).\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched."
22395
22605
  },
22396
22606
  {
22397
22607
  "kind": "method",
@@ -23331,7 +23541,7 @@
23331
23541
  },
23332
23542
  {
23333
23543
  "kind": "class",
23334
- "description": "Base class for the modal shell `vf-dialog` — and for a consumer's own\nmodal (an alert box, say) authored against the kit.\n\nOwns the native `<dialog>` lifecycle every modal shares: `open` sync, `show()` /\n`close()`, the top/left placement (stated or centered) and the\nsingle `close` funnel that drops the written origin and fires `vf-close` with\nthe reason. Because every close path — Escape, `close()`, the opt-in\nlightDismiss click outside — routes through the native `close`\nevent, an Escape-close no longer leaves a stale origin behind, so the next\nopen re-derives it.\n\nIt also owns the two keyboard rules every classic dialog followed (see\ndefaultButton and initialFocusTarget): Return or Enter\nactivates the default button from anywhere in the dialog, and the box opens\nwith the insertion point in its first text field — or, with none, focus on\nthe default button.\n\nRemoving an open modal from the DOM is a close path too. HTML's dialog\n*removing steps* take the element out of the top layer **without** running\nthe close algorithm — no `close` event, no focus restoration — which is\nexactly what the standard framework pattern of unmounting a dialog instead\nof calling `close()` does. `disconnectedCallback` routes that path through\nthe same funnel: the removed element still fires `vf-close` (heard by\nlisteners on the element itself — it has left the tree, so nothing\nbubbles), `open` and the pinned margins reconcile so a re-append mounts it\nclosed and re-centered, and focus returns to the element that was focused\nwhen the modal opened.\n\nSubclasses supply only the frame chrome: a `render()` returning\n`<dialog",
23544
+ "description": "Base class for the modal shell `vf-dialog` — and for a consumer's own\nmodal (an alert box, say) authored against the kit.\n\nOwns the native `<dialog>` lifecycle every modal shares: `open` sync, `show()` /\n`close()`, the top/left placement (stated or centered) and the\nsingle `close` funnel that drops the written origin and fires `vf-close` with\nthe reason. Because every close path — Escape, `close()`, the opt-in\nlightDismiss click outside — routes through the native `close`\nevent, an Escape-close no longer leaves a stale origin behind, so the next\nopen re-derives it.\n\nIt also owns the two keyboard rules every classic dialog followed (see\ndefaultButton and initialFocusTarget): Return or Enter\nactivates the default button from anywhere in the dialog, and the box opens\nwith the insertion point in its first text field — or, with none, focus on\nthe default button.\n\nRemoving an open modal from the DOM is a close path too. HTML's dialog\n*removing steps* take the element out of the top layer **without** running\nthe close algorithm — no `close` event, no focus restoration — which is\nexactly what the standard framework pattern of unmounting a dialog instead\nof calling `close()` does. `disconnectedCallback` routes that path through\nthe same funnel: the removed element still fires `vf-close` (heard by\nlisteners on the element itself — it has left the tree, so nothing\nbubbles), `open` and the pinned margins reconcile so a re-append mounts it\nclosed and re-centered, and focus returns to the element that was focused\nwhen the modal opened.\n\nA `<form method=\"dialog\">` inside the modal closes it with the submitting\nbutton's `value` as returnValue, as a form inside a native\n`<dialog>` does. The platform's own rule looks for a `<dialog>` around the\nform, and this one is in the shadow root, so the modal listens for the\nsubmit itself.\n\nSubclasses supply only the frame chrome: a `render()` returning\n`<dialog",
23335
23545
  "name": "VfModalDialog",
23336
23546
  "members": [
23337
23547
  {
@@ -23353,6 +23563,15 @@
23353
23563
  "attribute": "open",
23354
23564
  "reflects": true
23355
23565
  },
23566
+ {
23567
+ "kind": "field",
23568
+ "name": "returnValue",
23569
+ "type": {
23570
+ "text": "string"
23571
+ },
23572
+ "default": "''",
23573
+ "description": "The modal's answer, as on `<dialog>`: set by `close(value)` and by a\nsubmit from a `<form method=\"dialog\">` inside it (the submitting\nbutton's `value`). Cleared on each open, so a dialog asked again doesn't\nstart with its last answer."
23574
+ },
23356
23575
  {
23357
23576
  "kind": "field",
23358
23577
  "name": "width",
@@ -23449,6 +23668,22 @@
23449
23668
  "default": "null",
23450
23669
  "description": "Close reason pending for the next native `close` event."
23451
23670
  },
23671
+ {
23672
+ "kind": "field",
23673
+ "name": "#closeValue",
23674
+ "privacy": "private",
23675
+ "type": {
23676
+ "text": "string | null"
23677
+ },
23678
+ "default": "null",
23679
+ "description": "The value `close(value)` gave, pending for the next native `close` event."
23680
+ },
23681
+ {
23682
+ "kind": "field",
23683
+ "name": "#onSubmit",
23684
+ "privacy": "private",
23685
+ "description": "A submit from a form whose nearest modal is this one, by `method=\"dialog\"`\nor the submitter's `formmethod=\"dialog\"`, closes it with the submitter's\n`value` — at the end of the submit's path, and not if anything cancelled\nit, as the platform runs a form's submission."
23686
+ },
23452
23687
  {
23453
23688
  "kind": "field",
23454
23689
  "name": "#outsidePress",
@@ -23549,7 +23784,16 @@
23549
23784
  "text": "void"
23550
23785
  }
23551
23786
  },
23552
- "description": "Close the modal. Fires `vf-close` with `{ reason: 'close' }`."
23787
+ "parameters": [
23788
+ {
23789
+ "name": "returnValue",
23790
+ "optional": true,
23791
+ "type": {
23792
+ "text": "string"
23793
+ }
23794
+ }
23795
+ ],
23796
+ "description": "Close the modal, with `returnValue` as its answer when one is given.\nFires `vf-close` with `{ reason: 'close', returnValue }`."
23553
23797
  },
23554
23798
  {
23555
23799
  "kind": "method",
@@ -23727,12 +23971,12 @@
23727
23971
  "text": "void"
23728
23972
  }
23729
23973
  },
23730
- "description": "Native `close` — the single teardown funnel for every close path. Drops\nthe written origin so the next open re-derives it from the box it will\nactually have (a stated `top`/`left` is the author's or the user's and\nsurvives, and is re-applied by that open), syncs `open`, and fires\n`vf-close` with the reason."
23974
+ "description": "Native `close` — the single teardown funnel for every close path. Drops\nthe written origin so the next open re-derives it from the box it will\nactually have (a stated `top`/`left` is the author's or the user's and\nsurvives, and is re-applied by that open), syncs `open`, and fires\n`vf-close` with the reason and the value the close carried."
23731
23975
  }
23732
23976
  ],
23733
23977
  "events": [
23734
23978
  {
23735
- "description": "The modal closed. `detail: { reason: 'escape' | 'close' | 'outside' }`.",
23979
+ "description": "The modal closed. `detail: { reason: 'escape' | 'close' | 'outside', returnValue: string | null }`.",
23736
23980
  "name": "vf-close"
23737
23981
  }
23738
23982
  ],
@@ -23868,7 +24112,7 @@
23868
24112
  },
23869
24113
  {
23870
24114
  "kind": "mixin",
23871
- "description": "Explicit placement — `top`/`left` in whole system px, on **every** component.\n\nNo exceptions, deliberately. These are web components, and a consumer may put\none wherever they like; the kit does not get to decide that a `vf-option` is\nonly ever a popup row. The rows a container normally owns (`vf-option`,\n`vf-menu-item`, `vf-list-item`) and a bar's `vf-menu` take the pair on the\nsame terms as anything else — and stating an origin on one *inside* its\nmanaging parent does take it out of that parent's flow layout, which is the\nplacement working, not failing. Each of those components documents what its\ncontainer stops doing for it. `vf-dialog` takes the pair in viewport\ncoordinates, the one difference the platform forces (see modal-dialog.ts).\n\nA DITL resource laid a dialog out as a list of items, each with a rectangle\nin the window's own coordinates; arranging controls by stating where they go\nis as native to System 7 as stacking them. This mixin is that mechanism:\ndeclaring `top` or `left` takes the element out of normal flow and\nabsolutely positions it within its parent, both coordinates in the art's own\nunit so the position scales with the display like every other metric. A\nwindow or dialog body can be laid out either way — a tree of `vf-stack`s, or\npositioned children — with no stylesheet in either case.\n\nMechanics, mirroring how `vf-stack` writes its declared size:\n\n- Setting either property writes `position: absolute` and both offsets onto\n the host's inline style as `calc(var(--vf-scale, 1) * Npx)` — live against\n the display, resolved at paint time. The coordinate left unset is 0.\n- `right`/`bottom` are released to `auto` and `margin` zeroed, so the stated\n offsets are the whole story — an auto-width box with both edges set would\n stretch rather than sit (the `vf-icon` seed logic, generalized).\n- Unsetting both returns the element to normal flow: every inline\n declaration this wrote is removed, and stylesheet values resume.\n\nThe anchor is CSS's own: the nearest positioned ancestor's padding box. The\nkit's containers are all deliberate anchors — a desktop's raster, a window's\nor a dialog's content region (the frame's inner edge, below any title bar,\nwith no inset — flow content starts there too, exactly the DITL\nconvention), a stack's box, a fieldset's border box, a scroll area's\nscrolled plane. In a non-kit parent, give the parent `position: relative`,\nthe one line of CSS this feature can't write for you.\n\nThe writing rides a ReactiveController (`hostUpdated`) rather than\nan `updated()` override, for a lifecycle reason worth keeping in the source:\nthe mixin sits *under* each component class, and most components override\n`updated()` without calling `super.updated()` — Lit's base is a no-op, so\nnothing ever forced the call — which would silently shadow a mixin-level\noverride. A controller is invoked by ReactiveElement itself, after every\nupdate, no matter what the subclass does.\n\nA gesture writes **through these properties**: `vf-window`'s title-bar drag\nand `vf-icon`'s drag and arrow nudge hand their new origin to a\nPlacementController, which snaps it onto the placement lattice and\nsets `left`/`top`. A moved element is therefore placed exactly the way an\nauthored one is — a live `calc()` in the art's own unit — and stays where it\nwas dropped when the zoom or the display changes what a system px costs.\n\nIt did not always. Writing the resolved CSS px straight to the inline style\nfroze the coordinate in the wrong unit: `--vf-scale` moved under it at every\nzoom step and the same constant read back as a different number of system px,\nso moved windows and icons slid off the grid the rest of the kit stayed on\n(by `3z / round(3z)` — 10% at 110% zoom, where nothing else moves at all).\n\nSetting a property yourself is still the deliberate way to re-place a moved\nelement. The controller re-applies **only when the values changed**, so an\nunrelated update — a heading change, a desktop toggling `active` — never\nre-asserts a coordinate and costs nothing.\n\n**Every write it makes is announced** as `vf-placement-change` (bubbles,\n`composed: false`, detail `{ left, top }` — nulls on a return to flow). It\nis an internal coordination event on the terms of the menu handshakes:\nparent and child share one light tree, and a kit protocol must not leak out\nof a consumer's shadow boundary. `vf-scroll-area` listens for it and\nre-measures, because a placed child moved through `top`/`left` changes the\nscroll range with no box changing anywhere a ResizeObserver can see — the\nplane's box is never grown by an absolutely positioned child. One funnel\ncovers a drop, an arrow nudge and a page's own `icon.left = …` alike.\n\n**`origin`** names which point of the host's own box the pair places —\nnine keywords, vertical then horizontal (`top center`, `bottom right`,\n`center`), `top left` the default. A title stays centered on its `left`\nwhatever its text; a button group keeps its bottom-right corner in from\nthe dialog's however long its labels run. The point is measured, not\ntransformed: the controller reads the host's border box in whole system px\n(w × h) and writes `left` as the stated `L` less ⌈w·fx⌉ (`fx` 0, ½ or 1),\n`top` the same way — the ceiling sends an odd size's leftover half toward\nthe start, the tie rule the kit's other centering uses. A ResizeObserver\nkeeps the offset current through a relabel or a late font, and each such\nrewrite is announced like any other write. A zoom step changes CSS px, not\nsystem px, so it recomputes the same offset. `translate: -50% 0` would have\nbeen the no-JavaScript version, and fails twice: a percentage of an odd\nwidth is half a pixel, which smears the 1-bit art, and a transform makes\nthe host the containing block for `position: fixed` descendants — a placed\ngroup holding a `vf-select` would trap its list. `top left` is the plain\ncode path: no observer, no measurement, the same inline style as before\nthe attribute existed. An unknown value places as `top left` and warns once\nper element. On its own the attribute places nothing: it changes what the\npair means, and is inert on a host in flow.\n\n**`fixed`** holds the placement against the *visible* region of the nearest\nscrolling ancestor instead of its scrolled plane — a tool strip over a\ndocument, a column header over rows — and the flag alone places at (0,0).\nThe engine underneath is `position: sticky`, the one way the platform holds\na box against a scrollport from inside it on the compositor (a scroll\nlistener counter-translating lags a frame; a layer outside the scroller\nstops wheel and touch over it). Sticky boxes stay in flow, which is not\nwhat a placement is, so the controller erases the footprint: the host is\nblockified (no line box), shrink-wrapped, and given a negative right/bottom\nmargin equal to its own box — a 0×0 margin box that flow content lays out\nas if it weren't there, measured by a ResizeObserver so a relabel or a zoom\nstep keeps it exact. `z-index: 1` puts it over the plane's placed children,\nwhich is where a fixed strip belongs. Two facts of the engine that the\ndocs state as rules: sticky only ever pushes a box *down* from where the\nflow put it, so a fixed child comes before the flow content in its parent;\nand a scroll container that never scrolls (a plain window body is\n`overflow: hidden`) holds it exactly where placement would.",
24115
+ "description": "Explicit placement — `top`/`left` in whole system px, on **every** component.\n\nNo exceptions, deliberately. These are web components, and a consumer may put\none wherever they like; the kit does not get to decide that a `vf-option` is\nonly ever a popup row. The rows a container normally owns (`vf-option`,\n`vf-menu-item`, `vf-list-item`) and a bar's `vf-menu` take the pair on the\nsame terms as anything else — and stating an origin on one *inside* its\nmanaging parent does take it out of that parent's flow layout, which is the\nplacement working, not failing. Each of those components documents what its\ncontainer stops doing for it. `vf-dialog` takes the pair in viewport\ncoordinates, the one difference the platform forces (see modal-dialog.ts).\n\nA DITL resource laid a dialog out as a list of items, each with a rectangle\nin the window's own coordinates; arranging controls by stating where they go\nis as native to System 7 as stacking them. This mixin is that mechanism:\ndeclaring `top` or `left` takes the element out of normal flow and\nabsolutely positions it within its parent, both coordinates in the art's own\nunit so the position scales with the display like every other metric. A\nwindow or dialog body can be laid out either way — a tree of `vf-stack`s, or\npositioned children — with no stylesheet in either case.\n\nMechanics, mirroring how `vf-stack` writes its declared size:\n\n- Setting either property writes `position: absolute` and both offsets onto\n the host's inline style as `calc(var(--vf-scale, 1) * Npx)` — live against\n the display, resolved at paint time. The coordinate left unset is 0.\n- `right`/`bottom` are released to `auto` and `margin` zeroed, so the stated\n offsets are the whole story — an auto-width box with both edges set would\n stretch rather than sit (the `vf-icon` seed logic, generalized).\n- Unsetting both returns the element to normal flow: every inline\n declaration this wrote is removed, and stylesheet values resume.\n\nThe anchor is CSS's own: the nearest positioned ancestor's padding box. The\nkit's containers are all deliberate anchors — a desktop's raster, a window's\nor a dialog's content region (the frame's inner edge, below any title bar,\nwith no inset — flow content starts there too, exactly the DITL\nconvention), a stack's box, a fieldset's border box, a scroll area's\nscrolled plane. In a non-kit parent, give the parent `position: relative`,\nthe one line of CSS this feature can't write for you.\n\nThe writing rides a ReactiveController (`hostUpdated`) rather than\nan `updated()` override, for a lifecycle reason worth keeping in the source:\nthe mixin sits *under* each component class, and most components override\n`updated()` without calling `super.updated()` — Lit's base is a no-op, so\nnothing ever forced the call — which would silently shadow a mixin-level\noverride. A controller is invoked by ReactiveElement itself, after every\nupdate, no matter what the subclass does.\n\nA gesture writes **through these properties**: `vf-window`'s title-bar drag\nand `vf-icon`'s drag and arrow nudge hand their new origin to a\nPlacementController, which snaps it onto the placement lattice and\nsets `left`/`top`. A moved element is therefore placed exactly the way an\nauthored one is — a live `calc()` in the art's own unit — and stays where it\nwas dropped when the zoom or the display changes what a system px costs.\n\nIt did not always. Writing the resolved CSS px straight to the inline style\nfroze the coordinate in the wrong unit: `--vf-scale` moved under it at every\nzoom step and the same constant read back as a different number of system px,\nso moved windows and icons slid off the grid the rest of the kit stayed on\n(by `3z / round(3z)` — 10% at 110% zoom, where nothing else moves at all).\n\nSetting a property yourself is still the deliberate way to re-place a moved\nelement. The controller re-applies **only when the values changed**, so an\nunrelated update — a heading change, a desktop toggling `active` — never\nre-asserts a coordinate and costs nothing.\n\n**Every write it makes is announced** as `vf-placement-change` (bubbles,\n`composed: false`, detail `{ left, top }` — nulls on a return to flow), so\na page hears every move the one way: a title-bar drag's steps and its\nrelease (an outline drag's single write), an arrow nudge, a drop, a walk's\nlanding, an `origin` re-measure, and its own property writes. Non-composed,\nbecause parent and child share one light tree and the event has no reason\nto leave a consumer's shadow root. It is dispatched from the host's update,\nwith the new pair already on its style: a handler that writes the pair\nagain takes effect on the next update. `vf-scroll-area` listens for it and\nre-measures, because a placed child moved through `top`/`left` changes the\nscroll range with no box changing anywhere a ResizeObserver can see — the\nplane's box is never grown by an absolutely positioned child.\n\n**`origin`** names which point of the host's own box the pair places —\nnine keywords, vertical then horizontal (`top center`, `bottom right`,\n`center`), `top left` the default. A title stays centered on its `left`\nwhatever its text; a button group keeps its bottom-right corner in from\nthe dialog's however long its labels run. The point is measured, not\ntransformed: the controller reads the host's border box in whole system px\n(w × h) and writes `left` as the stated `L` less ⌈w·fx⌉ (`fx` 0, ½ or 1),\n`top` the same way — the ceiling sends an odd size's leftover half toward\nthe start, the tie rule the kit's other centering uses. A ResizeObserver\nkeeps the offset current through a relabel or a late font, and each such\nrewrite is announced like any other write. A zoom step changes CSS px, not\nsystem px, so it recomputes the same offset. `translate: -50% 0` would have\nbeen the no-JavaScript version, and fails twice: a percentage of an odd\nwidth is half a pixel, which smears the 1-bit art, and a transform makes\nthe host the containing block for `position: fixed` descendants — a placed\ngroup holding a `vf-select` would trap its list. `top left` is the plain\ncode path: no observer, no measurement, the same inline style as before\nthe attribute existed. An unknown value places as `top left` and warns once\nper element. On its own the attribute places nothing: it changes what the\npair means, and is inert on a host in flow.\n\n**`fixed`** holds the placement against the *visible* region of the nearest\nscrolling ancestor instead of its scrolled plane — a tool strip over a\ndocument, a column header over rows — and the flag alone places at (0,0).\nThe engine underneath is `position: sticky`, the one way the platform holds\na box against a scrollport from inside it on the compositor (a scroll\nlistener counter-translating lags a frame; a layer outside the scroller\nstops wheel and touch over it). Sticky boxes stay in flow, which is not\nwhat a placement is, so the controller erases the footprint: the host is\nblockified (no line box), shrink-wrapped, and given a negative right/bottom\nmargin equal to its own box — a 0×0 margin box that flow content lays out\nas if it weren't there, measured by a ResizeObserver so a relabel or a zoom\nstep keeps it exact. `z-index: 1` puts it over the plane's placed children,\nwhich is where a fixed strip belongs. Two facts of the engine that the\ndocs state as rules: sticky only ever pushes a box *down* from where the\nflow put it, so a fixed child comes before the flow content in its parent;\nand a scroll container that never scrolls (a plain window body is\n`overflow: hidden`) holds it exactly where placement would.",
23872
24116
  "name": "VfPositioned",
23873
24117
  "members": [
23874
24118
  {
@@ -24137,7 +24381,7 @@
24137
24381
  "text": "PlacementBounds"
24138
24382
  }
24139
24383
  },
24140
- "description": "The positioning parent's content box, in system px.\n\nA parent with no box at all falls back to the viewport, the same as no\nparent — that is the *other* contract failure (a `position: relative`\ncontainer nobody gave a size), and clamping into nothing would leave the\nhost unable to move at all. The viewport keeps the gesture usable while\nwarnMovableContract does the teaching."
24384
+ "description": "The positioning parent's content box, in system px.\n\nA desktop answers for a host placed on its screen\n(`vf-placement-bounds-request`, the drag surface's handshake idiom): the\nscreen, bezel excluded, with the menu bar's floor as `top`. Any other\nparent is measured.\n\nA parent with no box at all falls back to the viewport, the same as no\nparent — that is the *other* contract failure (a `position: relative`\ncontainer nobody gave a size), and clamping into nothing would leave the\nhost unable to move at all. The viewport keeps the gesture usable while\nwarnMovableContract does the teaching."
24141
24385
  }
24142
24386
  ]
24143
24387
  },