vintage-frames 0.12.2 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +340 -4
- package/dist/components/vf-desktop.d.ts +9 -5
- package/dist/components/vf-icon-field.js +12 -14
- package/dist/components/vf-icon.d.ts +11 -0
- package/dist/components/vf-icon.js +45 -44
- package/dist/components/vf-window.d.ts +90 -0
- package/dist/components/vf-window.js +248 -77
- package/dist/index.d.ts +2 -1
- package/dist/index.js +43 -43
- package/dist/motion.d.ts +21 -0
- package/dist/motion.js +5 -1
- package/dist/open-art.d.ts +17 -4
- package/dist/open-art.js +12 -6
- package/docs/SPEC.md +7 -7
- package/editor/vscode.html-custom-data.json +7 -2
- package/editor/web-types.json +13 -3
- package/package.json +1 -1
package/dist/open-art.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Finder "open" ghost — an icon's art redrawn as outline plus dither,
|
|
3
3
|
* derived from the art itself in the client — and, on the same terms, the
|
|
4
|
-
* dotted outline an icon drags as ({@link deriveDragOutline})
|
|
4
|
+
* dotted outline an icon drags as ({@link deriveDragOutline}), and the XOR pen
|
|
5
|
+
* that outline, the rubber band and a window's zoom rects and drag outline
|
|
6
|
+
* draw with ({@link xorPenCanvas}).
|
|
5
7
|
*
|
|
6
8
|
* When a folder or application is open, the Finder redraws its icon as a
|
|
7
9
|
* ghost: the outline stays, and everything inside it becomes pattern. The kit
|
|
@@ -68,6 +70,16 @@ export interface DragOutlineGeometry {
|
|
|
68
70
|
/** The name plate, or null with no name. */
|
|
69
71
|
plate: DragOutlineBox | null;
|
|
70
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* A canvas that paints with the XOR pen — the cursor's recipe: its ink is
|
|
75
|
+
* turned white by `filter: invert(1)`, then differenced against whatever is
|
|
76
|
+
* beneath it (`mix-blend-mode: difference`), so a black pixel of the raster
|
|
77
|
+
* inverts the pixel under it and a transparent one leaves it alone.
|
|
78
|
+
* Nearest-neighbor at one image px per system px, and never a hit. The
|
|
79
|
+
* caller positions and sizes it: the drag outline, the rubber band, and a
|
|
80
|
+
* window's zoom rects and drag outline all draw with one.
|
|
81
|
+
*/
|
|
82
|
+
export declare function xorPenCanvas(className: string): HTMLCanvasElement;
|
|
71
83
|
/**
|
|
72
84
|
* Derive the outline an icon drags as: the boundary of the mask united with
|
|
73
85
|
* the name plate's rectangle — one shape, the one-pixel ring a 3×3 box
|
|
@@ -106,9 +118,10 @@ export declare function deriveDragOutline(art: HTMLImageElement | HTMLCanvasElem
|
|
|
106
118
|
export declare function penPhase(x: number, y: number): number;
|
|
107
119
|
/**
|
|
108
120
|
* Paint a selection rectangle — the rubber band `vf-icon-field` drags on its
|
|
109
|
-
* background
|
|
110
|
-
*
|
|
111
|
-
* a one-pixel line
|
|
121
|
+
* background, and each of a window's zoom rects and its drag outline — into
|
|
122
|
+
* `into`: a one-pixel outline `width` × `height` system px, dotted at `phase`
|
|
123
|
+
* the way the drag outline is. A degenerate axis paints as a one-pixel line
|
|
124
|
+
* rather than nothing.
|
|
112
125
|
*/
|
|
113
126
|
export declare function paintSelectionRect(into: HTMLCanvasElement, width: number, height: number, phase: number): void;
|
|
114
127
|
/**
|
package/dist/open-art.js
CHANGED
|
@@ -41,7 +41,13 @@ var i = null, a = () => {
|
|
|
41
41
|
}, o = (e, t) => {
|
|
42
42
|
t.width <= 0 || t.height <= 0 || (e.fillStyle = "#000", e.fillRect(t.x, t.y, t.width, t.height), t.width > 2 && t.height > 2 && e.clearRect(t.x + 1, t.y + 1, t.width - 2, t.height - 2));
|
|
43
43
|
};
|
|
44
|
-
function s(
|
|
44
|
+
function s(e) {
|
|
45
|
+
let t = document.createElement("canvas");
|
|
46
|
+
t.className = e;
|
|
47
|
+
let n = t.style;
|
|
48
|
+
return n.display = "block", n.pointerEvents = "none", n.filter = "invert(1)", n.mixBlendMode = "difference", n.imageRendering = "pixelated", t;
|
|
49
|
+
}
|
|
50
|
+
function c(t, n) {
|
|
45
51
|
let { width: r, height: i } = n;
|
|
46
52
|
if (!r || !i) return null;
|
|
47
53
|
let a = e(r, i), o = e(r, i), s = e(r, i);
|
|
@@ -63,19 +69,19 @@ function s(t, n) {
|
|
|
63
69
|
]) (e || t) && o.ctx.drawImage(a.canvas, e, t);
|
|
64
70
|
return s.ctx.drawImage(a.canvas, 0, 0), s.ctx.globalCompositeOperation = "destination-out", s.ctx.drawImage(o.canvas, 0, 0), s.ctx.globalCompositeOperation = "source-over", s.canvas;
|
|
65
71
|
}
|
|
66
|
-
function
|
|
72
|
+
function l(e, t) {
|
|
67
73
|
return +(((e + t) % 2 + 2) % 2 == 0);
|
|
68
74
|
}
|
|
69
|
-
function
|
|
75
|
+
function u(t, n, r, i) {
|
|
70
76
|
let a = Math.max(1, Math.round(n)), s = Math.max(1, Math.round(r)), c = e(a, s);
|
|
71
77
|
c && (o(c.ctx, {
|
|
72
78
|
x: 0,
|
|
73
79
|
y: 0,
|
|
74
80
|
width: a,
|
|
75
81
|
height: s
|
|
76
|
-
}),
|
|
82
|
+
}), d(c.canvas, t, i));
|
|
77
83
|
}
|
|
78
|
-
function
|
|
84
|
+
function d(e, t, n) {
|
|
79
85
|
let r = a(), i = t.getContext("2d");
|
|
80
86
|
if (!r || !i) return;
|
|
81
87
|
t.width = e.width, t.height = e.height, i.imageSmoothingEnabled = !1, i.drawImage(e, 0, 0);
|
|
@@ -83,4 +89,4 @@ function u(e, t, n) {
|
|
|
83
89
|
o && (i.globalCompositeOperation = "destination-in", i.fillStyle = o, i.translate(n & 1, 0), i.fillRect(-2, 0, e.width + 2, e.height), i.setTransform(1, 0, 0, 1, 0, 0), i.globalCompositeOperation = "source-over");
|
|
84
90
|
}
|
|
85
91
|
//#endregion
|
|
86
|
-
export {
|
|
92
|
+
export { c as deriveDragOutline, r as deriveOpenArt, d as dotOutline, u as paintSelectionRect, l as penPhase, s as xorPenCanvas };
|
package/docs/SPEC.md
CHANGED
|
@@ -13,7 +13,7 @@ The System 7 look, distilled:
|
|
|
13
13
|
- **Racing-stripe title bars** — 6 horizontal 1px black pinstripes on the white title bar; the title text and window widgets sit on solid white patches that interrupt the stripes.
|
|
14
14
|
- **Chicago-style type** — bold, dark, tight. One size for almost everything.
|
|
15
15
|
- **1-bit monochrome palette** — black and white only, plus two grays: light gray (`#C0C0C0`, the kit's dim tone) for dimmed/disabled chrome, and mid gray (`#808080`) as the base tone under the desktop's 50% dither. Scroll troughs use a looser 25% black-on-white dot dither. Surfaces are flat solid white — no bevels.
|
|
16
|
-
- **No gradients, no border-radius, no CSS transitions** — interactions are instant. The sanctioned animations are the classic menu-item "blink" on selection, the indeterminate progress stripes,
|
|
16
|
+
- **No gradients, no border-radius, no CSS transitions** — interactions are instant. The sanctioned animations are the classic menu-item "blink" on selection, the indeterminate progress stripes, the walk — an icon's drag outline travelling to where a page sends it (`vf-icon.dragTo`, `vf-icon-field.dragIcons`), the Finder's own Clean Up — and the zoom rects a window opens and closes with (`vf-window.show({ from })`, `hide({ to })`). The blink, the walk and the rects are suppressed under `prefers-reduced-motion: reduce` (the blink selects immediately, the walk lands immediately, the window shows or hides immediately). Even the button's rounded corners are not `border-radius` arcs: they are stepped `clip-path` silhouettes traced pixel-for-pixel from the reference sheet (`src/pixel-frame.ts`), so the 1-bit staircase renders with no antialiasing.
|
|
17
17
|
- **Selection inverts** — selected/active states are white-on-black inversion, not tinted highlights.
|
|
18
18
|
|
|
19
19
|
Modern requirements that we deliberately keep (accessibility over purity):
|
|
@@ -202,15 +202,15 @@ Full-bleed classic desktop container.
|
|
|
202
202
|
- **Behavior:** manages stacking of slotted `vf-window` children: `pointerdown` *or `focusin`* on a window brings it to front (incrementing z-index counter) and sets its `active` attribute, clearing `active` on the others — the `focusin` half is the keyboard route to activation: Tab landing anywhere in a background window (its undrawn-but-focusable widgets included) raises it. Listens via delegated pointerdown/focusin listeners + `slotchange`. Windows slotted before `vf-window` is defined are re-normalized once `customElements.whenDefined('vf-window')` settles, since the upgrade reflects each window's `active = true` default back out and upgrading a slotted node doesn't re-fire `slotchange`.
|
|
203
203
|
- **DOM order follows z-order** (bottom-most first), so sequential focus order matches the visual stack and Shift+Tab mirrors Tab exactly. The sync runs in a task after a pointer gesture ends, or after a programmatic `bringToFront` — never mid-gesture (a node move would clear the pointer capture a title-bar drag or grow-box resize holds), never between a press's release and its `click` (Chromium drops a click whose mousedown node was re-inserted first, which used to cost a control in a background window its click), and never from a focus-driven raise (moving the window focus just entered would re-order the tab sequence mid-traversal); focus surviving its own window's move is restored without re-raising that window. Non-window children (a menu bar, page content) keep their positions. `npm run verify:window-a11y`.
|
|
204
204
|
- **Floating tier:** `vf-window[variant="utility"]` children stack in a z band `1_000_000` above the document tier (one shared monotonic counter, so a palette stays above every document window), restack only among themselves, and stand outside the single-active invariant both ways: clicking a palette doesn't deactivate the active document window, and activating a document window never clears a palette's `active` — System 7 windoid behavior while the app is frontmost. The tier test reads the `variant` *attribute*, so a not-yet-upgraded element still lands right.
|
|
205
|
-
- **Menu tier:** a slotted `vf-menu-bar` — or a free-standing `vf-menu` placed on the desktop — is restated at `z-index: 2_000_000` by the desktop's own `::slotted(vf-menu-bar), ::slotted(vf-menu)` rule (an outer-tree `::slotted` declaration beats the bar's inner `:host { z-index: 1000 }`), a band above the floating tier, so a dropped menu paints over palettes and document windows alike — the Menu Manager drew menus over every window. Only the drag outline and the screen-corner mask are in front. The full stack inside the screen's isolated context: consumer tile grid (−1) < document windows (counter) < utility windows (counter + 1_000_000) < menu tier (2_000_000) < drag outline (2_000_001) < corner mask (max). A popup *inside* a window (`vf-select`'s fixed panel) is a different case: it stacks within its window's context, so a palette can still cover it — KNOWN-BUGS.md. `npm run verify:archetypes` (MENU TIER).
|
|
206
|
-
- **The drag surface:** the Finder drew a dragged icon's outline on the *screen*, clipped at its edge and phase-locked to its raster, so the screen renders the layer that outline draws on — `.drag-surface`, a full-bleed child of `.screen` after the slot, `pointer-events: none` (never a hit for the page's `elementsFromPoint`), inside the raster's clip. It is deliberately no stacking context (no z-index of its own): a stacking context is an isolated group, and the outline's `mix-blend-mode` would then composite against the empty layer instead of the windows and dither beneath; the tier goes on its children (`.drag-surface > * { z-index: 2_000_001 }`), which places the outline in `.screen`'s own context, above the menu bar and under the corner mask. An icon reaches it through the `vf-drag-surface-request` handshake (§5 vf-icon): the desktop listens on its host, and the nearest desktop fills a still-null `detail.surface`. Not the top layer: stable Safari cannot blend an element there against the page (`XOR_UNAVAILABLE`, src/cursor.ts), and a second kit entry in the top layer would need the re-promotion handshake KNOWN-BUGS.md #1 describes. `npm run verify:icon-drag` (OUTLINE).
|
|
205
|
+
- **Menu tier:** a slotted `vf-menu-bar` — or a free-standing `vf-menu` placed on the desktop — is restated at `z-index: 2_000_000` by the desktop's own `::slotted(vf-menu-bar), ::slotted(vf-menu)` rule (an outer-tree `::slotted` declaration beats the bar's inner `:host { z-index: 1000 }`), a band above the floating tier, so a dropped menu paints over palettes and document windows alike — the Menu Manager drew menus over every window. Only the drag outline, a window's opening rects and the screen-corner mask are in front. The full stack inside the screen's isolated context: consumer tile grid (−1) < document windows (counter) < utility windows (counter + 1_000_000) < menu tier (2_000_000) < drag outline and window rects (2_000_001) < corner mask (max). A popup *inside* a window (`vf-select`'s fixed panel) is a different case: it stacks within its window's context, so a palette can still cover it — KNOWN-BUGS.md. `npm run verify:archetypes` (MENU TIER).
|
|
206
|
+
- **The drag surface:** the Finder drew a dragged icon's outline on the *screen*, clipped at its edge and phase-locked to its raster, so the screen renders the layer that outline draws on — `.drag-surface`, a full-bleed child of `.screen` after the slot, `pointer-events: none` (never a hit for the page's `elementsFromPoint`), inside the raster's clip. It is deliberately no stacking context (no z-index of its own): a stacking context is an isolated group, and the outline's `mix-blend-mode` would then composite against the empty layer instead of the windows and dither beneath; the tier goes on its children (`.drag-surface > * { z-index: 2_000_001 }`), which places the outline in `.screen`'s own context, above the menu bar and under the corner mask. An icon reaches it through the `vf-drag-surface-request` handshake (§5 vf-icon): the desktop listens on its host, and the nearest desktop fills a still-null `detail.surface`. A window asks the same way and draws there with the same pen: its zoom rects when it opens from or closes to a rect (§5 vf-window, `show` and `hide`), its outline under `outline-drag`. Not the top layer: stable Safari cannot blend an element there against the page (`XOR_UNAVAILABLE`, src/cursor.ts), and a second kit entry in the top layer would need the re-promotion handshake KNOWN-BUGS.md #1 describes. `npm run verify:icon-drag` (OUTLINE).
|
|
207
207
|
- **Deactivation:** on a real System 7 machine clicking the desktop clicked the *Finder* — the frontmost application's windows lost their stripes. `clearActive()` is that gesture's handler: it clears `active` from the whole document tier, and **zero active windows is a legal state**, held until a press or `focusin` re-enters a document window or a new one is slotted (opening a window brings its application forward — a newly slotted window ends a deliberate deactivation, where a mere survivor never does: removing a *background* window while deactivated promotes nothing, while removing the holder outside a deactivation promotes the topmost survivor). The desktop never takes the decision itself — a press on its own bare dither changes nothing; desktop furniture is slotted light DOM, so only the page knows which of its children mean "the Finder". Utility windows keep their `active` through a deactivation (their dots stay drawn). Every change of holder — window to window, window to none, none to window — fires `vf-activate`, once per change; re-asserting the current holder is silent. `npm run verify:desktop-activate`.
|
|
208
208
|
- **Events:** `vf-activate` (detail `{ window: HTMLElement | null }` — the new holder, `null` on deactivation).
|
|
209
209
|
- **Parts:** `desktop`.
|
|
210
210
|
|
|
211
211
|
#### `vf-window` (`VfWindow`, vf-window.ts)
|
|
212
212
|
The desktop-window shell: the classic document window (see DragThing screenshot), parameterized down to the windoid (see the Group A recipe table).
|
|
213
|
-
- **Attributes/props:** `heading: string` (title text), `width: number` / `height: number` (**declare them both** — whole system px, so the window keeps its proportion to the chrome inside it at every density. A window is a fixed box in both axes, the way a WIND resource was: one that grows with its body is one the user can neither predict nor control via the grow box. Each missing dimension falls back to something different — width to block layout, height to the content — and the window names whichever are missing, once, in the console), `active: boolean` (default **true**; reflect), `closable: boolean` (default true), `zoomable: boolean` (default false), `movable: boolean` (default false), `resizable: boolean` (default false), `min-width` / `min-height: number` (default 80 / 54) and `max-width` / `max-height: number` (unbounded) — the grow box's sizeRect, whole system px (see Behavior), `variant?: 'utility'` (the slim windoid chrome), `scrollbars?: 'vertical' | 'horizontal' | 'both'` (edge scroll rails), `header-height: number` (`headerHeight` — the header's height in whole system px, rule included; unset, the header is as tall as its content plus the rule).
|
|
213
|
+
- **Attributes/props:** `heading: string` (title text), `width: number` / `height: number` (**declare them both** — whole system px, so the window keeps its proportion to the chrome inside it at every density. A window is a fixed box in both axes, the way a WIND resource was: one that grows with its body is one the user can neither predict nor control via the grow box. Each missing dimension falls back to something different — width to block layout, height to the content — and the window names whichever are missing, once, in the console), `active: boolean` (default **true**; reflect), `closable: boolean` (default true), `zoomable: boolean` (default false), `movable: boolean` (default false), `outline-drag: boolean` (`outlineDrag`, default false — the title-bar drag moves an outline and the release moves the window; see Behavior), `resizable: boolean` (default false), `min-width` / `min-height: number` (default 80 / 54) and `max-width` / `max-height: number` (unbounded) — the grow box's sizeRect, whole system px (see Behavior), `variant?: 'utility'` (the slim windoid chrome), `scrollbars?: 'vertical' | 'horizontal' | 'both'` (edge scroll rails), `header-height: number` (`headerHeight` — the header's height in whole system px, rule included; unset, the header is as tall as its content plus the rule).
|
|
214
214
|
- **Visual:** `vfChromeFrame` + `vfTitleBar` (§4), plus a full-size flex-column layout on the frame — title bar, header, body, status strip. `display: block`. Sets `--vf-surface: var(--vf-white, #fff)` on itself.
|
|
215
215
|
- Title bar: from `vfTitleBar` — height `var(--vf-titlebar-height, 18px)`, white bg, bottom `1px solid black`, contains `.vf-stripes` layer (only when `active`). `touch-action: none` only when `[movable]`.
|
|
216
216
|
- Title: centered, bold, on a white patch (`padding: 0 6px`; cap band on interior rows 4..12, 7px of white between ink and stripes — see §4 `vfTitleBar` for the traced geometry and the lattice hold) above the stripes, with `--vf-title-inset: 60px` of clearance so it ellipsizes before reaching the widgets. Inactive: no stripes, widgets undrawn (transparent ink — they keep their tab stops; see A11y below), the grow box's nested squares hidden, and every managed scroll rail inside the window blanked (see "always-a-rail" §5 vf-scroll-area) — but the title text stays black (System 7 never grayed the window title).
|
|
@@ -223,8 +223,8 @@ The desktop-window shell: the classic document window (see DragThing screenshot)
|
|
|
223
223
|
- Edge scroll rails (if `scrollbars`): the body slot renders inside a shadow `vf-scroll-area` (its `axis` = the attribute's value, `label` = the heading, `viewport` part re-exported) carrying the TeachText composition internally — `calc(100% + 2px·scale)` with `margin: -1px·scale`, one system px under the frame on every side, so the area's own frame border repaints the window's border lines and a `resizable` window's grow box (z-index 1) lands exactly over the rail-corner cell. the area's viewport carries no inset, so content and the (0,0) of placed children sit at the content region's corner; a `resizable` window sets the area's `corner`, so a single-axis rail stops at the corner cell too — unless the status strip is populated, when the grow box sits in the strip and the rail runs edge to edge onto the strip's rule. Same caveats as the slotted composition (see vf-scroll-area §5).
|
|
224
224
|
- Utility variant (`variant="utility"`): the slim windoid bar traced from `Windows/utility-window.png` — `--vf-titlebar-height-utility` (12px = 11px interior + 1px rule), the `vfDots` dither instead of stripes (flush to the side borders — see §4 vfDots), 7×7 widgets (`top: 2px`; close `left: 7px`, zoom `right: 8px` — the art is asymmetric by that pixel) with a 2px patch ring where the striped bar's is 1px (`--vf-widget-ring`, internal geometry: the windoid sheet clears two px of dither beside its widgets), and the nested zoom square shrunk so its edges land at sprite col/row 3. No title patch: the display face's 16px line box can't sit in an 11px interior, so `.vf-title` is `display: none` under the variant (a retheming consumer can re-show it via `::part(title)`) and the heading names the widgets. A pressed windoid widget inverts whole — black interior under a white (invisible) borderline — rather than flashing the big bar's 9×9 sunburst, which can't land on a 5×5 interior.
|
|
225
225
|
- **A11y:** the close/zoom `aria-label`s are qualified by the title when there is one (`Close ${heading}` / `Zoom ${heading}`, falling back to bare `Close` / `Zoom`) — several windows are open at once by design, so a bare repeated "Close" gives an AT user no way to tell which window a widget belongs to. The frame is `role="group"`, named by the title patch via `aria-labelledby` when there is a heading (the utility variant's hidden patch still names it — AccName resolves hidden labelledby targets) — `group` rather than `region` deliberately, so a desktop of windows doesn't pollute landmark navigation; the title bar is a `<div>`, never a `<header>`, which would map to an unnamed `banner` landmark even inside the shadow root. An inactive window's widgets stay in the tree and the tab order but paint no ink (transparent border/background/patch ring — the bare System 7 bar): a background window whose body holds nothing focusable is still reachable, activated by `vf-desktop`'s `focusin` raise the moment Tab lands on a widget, and never drops focus to `<body>` when it deactivates. `npm run verify:window-a11y`.
|
|
226
|
-
- **Behavior:** close box click → `vf-close` (does NOT remove itself; consumer decides). Zoom box click → `vf-zoom`. If `movable`: dragging the title bar moves the window — the drag seeds its origin from the current offset position (once, converting to system px) and then states `left`/`top` via pointer capture, in whole system px like any authored placement. If `resizable`: dragging the grow box states `width`/`height` the same way, each axis clamped into the sizeRect (`min-width`/`max-width`, `min-height`/`max-height` — GrowWindow's: the app stated the rectangle of sizes and the Window Manager clamped the drag to it; the mins default to the 80×54 floor a window can still be worked at, the maxes are unbounded, and a min equal to its max locks the axis — the strip that scrolls sideways and never grows taller). The clamp runs after the lattice snap, so a bound lands exactly the way an authored size does even off the lattice (an odd height at 2×); where a min and max cross the max wins, so a window authored under the floor and held there never jumps to it. The rect bounds the gesture only: a size authored outside it renders as declared, and the first grow-box move brings it inside. The drag fires `vf-resize` (detail `{ width, height, commit }`, sizes in whole system px) — one event per size the drag actually writes (`commit: false`), fired *after* the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles (release or cancel), only when it changed the size. Window content can follow the grow box from this event alone, no ResizeObserver. Fired by the gesture only: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.
|
|
227
|
-
- **Methods:** `placementAt(clientX, clientY, child?)` → a viewport point as a placement in the content region — `{ left, top }` in whole system px on the placement lattice: the body's corner, or under `scrollbars` the scrolled plane with the scroll offset already in it (the built-in area's own `placementAt`). The pair a child dropped into the window is written with; with `child`, the pair to write to that element, its `origin` offset folded in. `measure()` → the built-in scroll area's `measure()`, a no-op without `scrollbars`; the area re-measures by itself on its content's box, its slot and every placement write (`vf-placement-change`, §2), so this is the escape hatch for a range that changes with none of those.
|
|
226
|
+
- **Behavior:** close box click → `vf-close` (does NOT remove itself; consumer decides). Zoom box click → `vf-zoom`. If `movable`: dragging the title bar moves the window — the drag seeds its origin from the current offset position (once, converting to system px) and then states `left`/`top` via pointer capture, in whole system px like any authored placement. With `outline-drag` the drag moves an outline instead: the press draws a one-pixel ring around the window's border box (the frame, shadow excluded) on the desktop's drag surface, with the drag outline's pen (`xorPenCanvas`, dotted at the screen phase — black over the dither), and each step moves it to the landing the live drag would write (`PlacementController.resolve()`: the grabbable-strip clamp, snapped), so the outline stops where the window can go; the release writes the last landing once (one `vf-placement-change`) and a release with no step writes nothing, and Escape — a gesture-scoped document capture listener armed at the press, the key stopped there — or a `pointercancel` takes the outline down and writes nothing. With no `vf-desktop` on its path the drag moves the window live. `npm run verify:window` (DRAG OUTLINE). If `resizable`: dragging the grow box states `width`/`height` the same way, each axis clamped into the sizeRect (`min-width`/`max-width`, `min-height`/`max-height` — GrowWindow's: the app stated the rectangle of sizes and the Window Manager clamped the drag to it; the mins default to the 80×54 floor a window can still be worked at, the maxes are unbounded, and a min equal to its max locks the axis — the strip that scrolls sideways and never grows taller). The clamp runs after the lattice snap, so a bound lands exactly the way an authored size does even off the lattice (an odd height at 2×); where a min and max cross the max wins, so a window authored under the floor and held there never jumps to it. The rect bounds the gesture only: a size authored outside it renders as declared, and the first grow-box move brings it inside. The drag fires `vf-resize` (detail `{ width, height, commit }`, sizes in whole system px) — one event per size the drag actually writes (`commit: false`), fired *after* the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles (release or cancel), only when it changed the size. Window content can follow the grow box from this event alone, no ResizeObserver. Fired by the gesture only: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.
|
|
227
|
+
- **Methods:** `placementAt(clientX, clientY, child?)` → a viewport point as a placement in the content region — `{ left, top }` in whole system px on the placement lattice: the body's corner, or under `scrollbars` the scrolled plane with the scroll offset already in it (the built-in area's own `placementAt`). The pair a child dropped into the window is written with; with `child`, the pair to write to that element, its `origin` offset folded in. `measure()` → the built-in scroll area's `measure()`, a no-op without `scrollbars`; the area re-measures by itself on its content's box, its slot and every placement write (`vf-placement-change`, §2), so this is the escape hatch for a range that changes with none of those. `show(options?)` → `Promise<boolean>`: clears `hidden`, and with `options.from` — a box in viewport CSS px (`{ left, top, right, bottom }`; a `DOMRect` fits, a zero-size box is a point), such as `vf-icon.cellRect()` — opens the window from it with the zoom rects. The window knows nothing about icons: `from` is any box. 14 rects, rect k at the fraction 0.7^(14−k) of the way from the box to the window's border box — each edge `from + trunc((frame − from) · t)`, so they bunch at the box and the last stops at 0.7 — four on screen at a time, 17 ms per step, then four steps draining; the window draws after the eighteenth (`WINDOW_RECT_*`, `windowRectFraction`, src/motion.ts). Each rect is a canvas of its own on the desktop's drag surface (the `vf-drag-surface-request` handshake), framed once with the drag outline's pen (`xorPenCanvas`, `paintSelectionRect`, src/open-art.ts) and dotted at the screen phase (`penPhase` — black lines over the dither), and a Web Animation (`element.animate()`) shows it for its four steps, every one padded with `endDelay` to the run's length so all of them finish when the window draws. Where two rects share a pixel the later one inverts it back: each canvas blends over those beneath it. Same-task writes to `left`/`top`/`width`/`height`, before or after the call, land first, so the rects end on the frame the page set. Until the rects drain the window matches `:state(opening)` and has `opacity: 0` — laid out, focusable and a hit target, so a page can focus into it at once and a press lands on it — never `visibility`, which would drop focus and pass the press through. A press anywhere or Escape (consumed in the capture phase) finishes the rects' animations (`finish()`), as they finish the walk; a second `show()` finishes the first, as it finishes a `hide()`'s; a removal, or a `hide()` before the window draws, cancels them (`cancel()`) and resolves `false`, while a move — `vf-desktop` re-inserting a raised window — is not a removal (the disconnect checks `isConnected` a microtask later, and the reconnect re-attaches the finishers). Resolves once the window draws: `true` when the rects ran, `false` when it showed at once — no `from`, `prefers-reduced-motion`, no `vf-desktop` on its path, nothing of the rects on the screen, or not connected. Raises and activates nothing; a window already on screen goes blank for the run, so a page brings an open window forward with `bringToFront` instead. `npm run verify:window` (OPEN). `hide(options?)` → `Promise<boolean>`: sets `hidden`, and with `options.to` — a box as for `show` — closes the window to it: the window's border box is read at the call, the window hidden, and the same 14 rects run the other way, rect k shown from step 13 − k, so the trail starts at the rect nearest the frame, closes in on the box and drains into it, eighteen steps in all. A press anywhere or Escape finishes them, a `show()` finishes them and shows the window, and a second `hide()` finishes the first. Removing the window leaves them running — they are the desktop's, and the window is already gone — so a page can remove a closed window straight after the call. A `show()` still opening is taken down (cancelled, so it resolves `false`) and the window hides at once. Resolves once the rects are done: `true` when they ran, `false` when the window hid at once — no `to`, `prefers-reduced-motion`, no `vf-desktop` on its path, nothing of the rects on the screen, not connected, already hidden, or still opening. `npm run verify:window` (CLOSE).
|
|
228
228
|
- **Slots:** default (body content), `header` (the header strip between the title bar and the body), `status` (the bottom status strip — see Visual).
|
|
229
229
|
- **Parts:** `frame`, `title-bar`, `title`, `close-box`, `zoom-box`, `header`, `body`, `status-bar`, `grow-box`, plus `viewport` re-exported from the built-in scroll area when `scrollbars` is set.
|
|
230
230
|
- **Events:** `vf-close`, `vf-zoom` (detail `{}`), `vf-resize` (detail `{ width, height, commit }`).
|
|
@@ -490,7 +490,7 @@ The Finder icon — pixel art in a reserved cell with its name on a plate below,
|
|
|
490
490
|
- **Every centered offset is whole, by parity.** The frame centers two things over one axis, and a centered child sits at `(box − child) / 2` — whole exactly when box and child share a parity. Half a system pixel is what fringes 1-bit art: glyph stems smear across two device columns and go gray while the plate behind them stays sharp, since backgrounds are pixel-snapped by the compositor and glyphs are not (hence a crisp plate under a gray name). So the component makes the parities agree rather than correcting afterwards. The cell is 32 or 16; `#measurePlate()` measures the text with a `Range` over the plate's own contents — the plate already carries the width this last computed, so reading the element back would only return it — and sizes the plate (an `inline-block`) to that plus its 1px padding, rounded UP to a whole **even** number of system px, so it never clips the run it was measured from. The measurement is in system px and therefore scale-invariant, which is why nothing re-runs on a density change; it re-runs on `label`, `_draft`, `_editing`, and once on `document.fonts.ready`, since a name measured against the fallback face is a different width. The text is NOT centered inside its own plate (`text-align: left` against the block's `center`) — that would re-introduce the half pixel one level down; it starts at the 1px padding edge, so its x is the plate's plus one whole pixel, and the rounding slack lands after it as a hairline of extra plate. The rename field puts its run exactly where the plate's is, so the name does not shift when editing starts; its 1px border and 1px white well are a wrapper around the input rather than the input's own border and padding, because the selection behind a highlighted name paints at the face's full 16px em and is clipped only at the input's own edge — split, the input stands the plate's 12px line box (clipping the selection to it) and the well stays white, for an edit box 16 system px tall in all, not the em plus trim. Staying on one line is part of the same argument: one run, one measured width, one parity. Nothing is snapped and nothing leans on the rasterizer — the kit's normal antialiasing stays on, and `verify:icon` asserts both the geometry and zero gray pixels across a field of names at dpr 1/2/3, declared width and auto, with a control that forces an odd plate and requires the fringe to return. The one thing the component cannot round is a `width` the consumer chose, so that must be even.
|
|
491
491
|
- **The 31-char cap** is `maxlength` on the rename field, and exceeding it fires `vf-name-too-long` rather than dropping characters silently. Detected in `beforeinput`, not `input`: with `maxlength` set, a blocked keystroke fires `beforeinput` and then *no* `input` at all, while an overshooting paste reaches `input` already trimmed — only the "about to change" event still knows what was attempted. `input` carries a trimming backstop for any route that skips it. The cap bounds typing only; a `label` set from consumer code is displayed as given and fires nothing (the name is the consumer's data).
|
|
492
492
|
- **An empty name is refused, not applied.** A file has to be called something, and System 7 would not commit a nameless one: a rename committed empty — or as nothing but spaces, which would read as blank either way — drops the edit and restores the previous name, firing `vf-name-rejected` and deliberately no `vf-change`. An empty `label` remains a valid *starting* state (a freshly made icon has no name yet): it draws no plate but stays selectable, focusable and renameable. The caption element is therefore rendered whenever there is a name **or** an edit is open — without that an unnamed icon has nowhere to put the field and could never be named at all. The plate hugs its text in BOTH states, which is what keeps a commit from moving the name: it is the same box either side, so the glyphs do not shift. The single exception is an edit with no text in it at all, which reserves the cell width so the field is usable rather than a 2px sliver, and goes back to hugging on the first character. That floor is the cell, which is even, so the parity rule holds.
|
|
493
|
-
- **Methods:** `startEditing()`, `commitEditing()`, `cancelEditing()` — the rename field's three routes. `setSelected(next)` → select or deselect as a press would, `vf-select` included (a `selected` write from code is silent, the way a value set fires no `vf-change`); the route a container's gesture takes — `vf-icon-field`'s rubber band. `touches(box)` → whether a viewport box (CSS px `left/top/right/bottom`) reaches the icon's art cell or its name plate — the Finder's selection-rectangle test, in which the empty cell around narrower art and the frame's margin beside the plate count for nothing; the rubber band reads it. `moveTo(left, top)` → place as a drop would: clamped whole in the container, snapped, one `vf-placement-change`. `dragTo(left, top)` → the outline travels and the icon lands, a promise — the walk, above.
|
|
493
|
+
- **Methods:** `startEditing()`, `commitEditing()`, `cancelEditing()` — the rename field's three routes. `setSelected(next)` → select or deselect as a press would, `vf-select` included (a `selected` write from code is silent, the way a value set fires no `vf-change`); the route a container's gesture takes — `vf-icon-field`'s rubber band. `touches(box)` → whether a viewport box (CSS px `left/top/right/bottom`) reaches the icon's art cell or its name plate — the Finder's selection-rectangle test, in which the empty cell around narrower art and the frame's margin beside the plate count for nothing; the rubber band reads it. `cellRect()` → the reserved art cell's box in viewport CSS px — 32×32 system px, 16×16 under `size="small"`, at the top of the frame — the same box whatever the art, the name, `width`, `open` or `selected` (the slotted art is no stand-in: `open` hides the slot, so it measures nothing); `null` while the icon isn't rendered — out of the document, or under `display: none` (`checkVisibility()`); the box a page hands a window opening from the icon or closing to it (`vf-window.show({ from })`, `hide({ to })`). `moveTo(left, top)` → place as a drop would: clamped whole in the container, snapped, one `vf-placement-change`. `dragTo(left, top)` → the outline travels and the icon lands, a promise — the walk, above.
|
|
494
494
|
- **Slots:** `large` (32×32 art, normally a `vf-img`), `small` (16×16). **Parts:** `frame`, `icon`, `label`, `plate`, `input`.
|
|
495
495
|
- **Events:** `vf-select` `{ selected }`, `vf-change` `{ label, previous }`, `vf-open` `{}`, `vf-name-too-long` `{ attempted, accepted, limit }`, `vf-name-rejected` `{ attempted, kept, reason }`, `vf-drag-start` `{ left, top, icons }`, `vf-drag` and `vf-drop` `{ clientX, clientY, left, top, x, y, icons }` (`vf-drop` cancelable), `vf-drag-cancel` `{}`.
|
|
496
496
|
|
|
@@ -231,7 +231,7 @@
|
|
|
231
231
|
},
|
|
232
232
|
{
|
|
233
233
|
"name": "vf-desktop",
|
|
234
|
-
"description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the screen-corner mask is in front of it.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, one whole-surface raster at one image px per system px, 1-bit at\nevery density and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. Not the
|
|
234
|
+
"description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the screen-corner mask is in front of it.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, one whole-surface raster at one image px per system px, 1-bit at\nevery density and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. A window asks the same way and draws\nthere with the same pen: its zoom rects when it opens from a rect\n(`vf-window.show({ from })`), its outline under an `outline-drag`. Not the\ntop layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).\n---\n\n\n### **Events:**\n - **vf-activate** - The active document-tier window changed. Detail `{ window: HTMLElement | null }` — the new holder, or `null` when the document tier deactivated (a {@link clearActive} call, or the active window leaving the DOM with none behind it). Fired once per change of holder, never for a re-assertion of the same one.\n\n### **Slots:**\n - _default_ - Default slot: menu bar, windows, anything.\n\n### **CSS Properties:**\n - **--vf-desktop** - base color under the desktop pattern — occluded by the pattern's opaque paper, so it only shows through a custom `--vf-desktop-pattern` _(default: #808080)_\n- **--vf-desktop-pattern** - a consumer's own desktop tile, in place of `pattern` — the kit's default is the 50% checker drawn as opaque black-on-white rects on a 30-system-px tile. Override the whole tile; consumer art renders as a placed tile grid at that same geometry (raster art magnifies nearest-neighbor, the `vf-img` idiom) _(default: undefined)_\n\n### **CSS Parts:**\n - **desktop** - The patterned screen surface — the whole-system-px raster (inset by `bezel` when one is set).",
|
|
235
235
|
"attributes": [
|
|
236
236
|
{
|
|
237
237
|
"name": "width",
|
|
@@ -1679,7 +1679,7 @@
|
|
|
1679
1679
|
},
|
|
1680
1680
|
{
|
|
1681
1681
|
"name": "vf-window",
|
|
1682
|
-
"description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n---\n\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close' }` (shape- compatible with vf-dialog's `vf-close`). The window does NOT remove itself; the consumer decides what closing means.\n- **vf-zoom** - Zoom box clicked. Detail `{}`.\n- **vf-resize** - The grow box resized the window. Detail `{ width, height, commit }`, sizes in whole system px: one event per size the drag writes (`commit: false`), fired after the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles — only when it changed the size. Fired by the gesture alone: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.\n\n### **Slots:**\n - _default_ - Default slot: window body content.\n- **header** - Optional header content — a band between the title bar and the body, the full width of the window (the Finder window's header line): a white interior over a 1px rule, no inset of its own, a positioning anchor for placed children. As tall as its content unless `header-height` states it. Under `scrollbars` the vertical rail begins below it, so the header spans the rail's column. Takes no space until populated.\n- **status** - Optional status-bar content — the classic bottom readout strip (\"40px x 40px\"): a 1px rule over a 15px white band under the body, body-face text on its native line. Takes no space until populated; a `resizable` window's grow box sits flush in its right end.\n\n### **CSS Properties:**\n - **--vf-dots-pattern** - the windoid bar's dot-grid dither — a 2×2 motif, one black pixel at its origin, on a 30-system-px tile (`vfDots`; override the whole tile like `--vf-desktop-pattern` — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n- **--vf-titlebar-height** - window/dialog title bars _(default: undefined)_\n- **--vf-titlebar-height-utility** - the slim `vf-window[variant=\"utility\"]` (windoid) bar — 11px interior + 1px bottom rule, traced from `Windows/utility-window.png` _(default: 12px)_\n- **--vf-status-bar-height** - the status strip: 1px rule + 14px interior — the grow box's own height, so the two compose flush _(default: 15px)_\n- **--vf-line-height** - the body face's native line, which the status strip's text rides (whole-pixel centered in the 14px interior) _(default: 12px)_\n\n### **CSS Parts:**\n - **frame** - The outer chrome frame.\n- **title-bar** - The striped (or dithered) title bar.\n- **title** - The centered title patch (hidden on the utility bar).\n- **close-box** - The close widget (left).\n- **zoom-box** - The zoom widget (right).\n- **body** - The content area.\n- **header** - The header strip between the title bar and the body (when the `header` slot is populated).\n- **status-bar** - The bottom status strip (when the `status` slot is populated).\n- **grow-box** - The resize widget (bottom-right, when `resizable`).\n- **viewport** - The built-in scroll area's viewport (when `scrollbars`; re-exported from vf-scroll-area).",
|
|
1682
|
+
"description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n\n**Opening from a rect.** show clears `hidden`, and given `from` — a\nbox in viewport CSS px, an icon's `cellRect()` or a button's rect — opens\nthe window from it: dotted rectangles grow from that box toward the\nwindow's frame, four on screen at a time, bunched at the box and opening\nout toward the window, drawn with the icon drag outline's XOR pen on the\ndesktop's drag surface; the window draws once they have drained, eighteen\nsteps later. Until then it matches `:state(opening)` and paints nothing —\nstill laid out, focusable and a hit target, so a page can place it, raise\nit and focus into it at once, and a press finishes the rects and lands on\nthe window. Under `prefers-reduced-motion`, with no `vf-desktop` on its\npath, or without `from`, the window shows at once.\n\n**Closing to a rect.** hide sets `hidden`, and given `to` closes\nthe window to it: the same rectangles run the other way, from where the\nframe was in toward the box, the window gone from the first step. A press\nfinishes them, and removing the window leaves them running. Under\n`prefers-reduced-motion`, with no `vf-desktop` on its path, or without\n`to`, the window hides at once. The window knows nothing about icons:\n`from` and `to` are any rect.\n\n**Dragging as an outline.** A `movable` window moves live under the\npointer. With `outline-drag` a press on the title bar draws a dotted\noutline around the window on the desktop's drag surface instead, with the\nsame pen; the drag moves the outline, held where the window can land, and\nthe window moves there on the release. Escape cancels.\n---\n\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close' }` (shape- compatible with vf-dialog's `vf-close`). The window does NOT remove itself; the consumer decides what closing means.\n- **vf-zoom** - Zoom box clicked. Detail `{}`.\n- **vf-resize** - The grow box resized the window. Detail `{ width, height, commit }`, sizes in whole system px: one event per size the drag writes (`commit: false`), fired after the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles — only when it changed the size. Fired by the gesture alone: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.\n\n### **Slots:**\n - _default_ - Default slot: window body content.\n- **header** - Optional header content — a band between the title bar and the body, the full width of the window (the Finder window's header line): a white interior over a 1px rule, no inset of its own, a positioning anchor for placed children. As tall as its content unless `header-height` states it. Under `scrollbars` the vertical rail begins below it, so the header spans the rail's column. Takes no space until populated.\n- **status** - Optional status-bar content — the classic bottom readout strip (\"40px x 40px\"): a 1px rule over a 15px white band under the body, body-face text on its native line. Takes no space until populated; a `resizable` window's grow box sits flush in its right end.\n\n### **CSS Properties:**\n - **--vf-dots-pattern** - the windoid bar's dot-grid dither — a 2×2 motif, one black pixel at its origin, on a 30-system-px tile (`vfDots`; override the whole tile like `--vf-desktop-pattern` — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n- **--vf-titlebar-height** - window/dialog title bars _(default: undefined)_\n- **--vf-titlebar-height-utility** - the slim `vf-window[variant=\"utility\"]` (windoid) bar — 11px interior + 1px bottom rule, traced from `Windows/utility-window.png` _(default: 12px)_\n- **--vf-status-bar-height** - the status strip: 1px rule + 14px interior — the grow box's own height, so the two compose flush _(default: 15px)_\n- **--vf-line-height** - the body face's native line, which the status strip's text rides (whole-pixel centered in the 14px interior) _(default: 12px)_\n\n### **CSS Parts:**\n - **frame** - The outer chrome frame.\n- **title-bar** - The striped (or dithered) title bar.\n- **title** - The centered title patch (hidden on the utility bar).\n- **close-box** - The close widget (left).\n- **zoom-box** - The zoom widget (right).\n- **body** - The content area.\n- **header** - The header strip between the title bar and the body (when the `header` slot is populated).\n- **status-bar** - The bottom status strip (when the `status` slot is populated).\n- **grow-box** - The resize widget (bottom-right, when `resizable`).\n- **viewport** - The built-in scroll area's viewport (when `scrollbars`; re-exported from vf-scroll-area).",
|
|
1683
1683
|
"attributes": [
|
|
1684
1684
|
{
|
|
1685
1685
|
"name": "variant",
|
|
@@ -1711,6 +1711,11 @@
|
|
|
1711
1711
|
"description": "Allow dragging the window by its title bar.",
|
|
1712
1712
|
"values": []
|
|
1713
1713
|
},
|
|
1714
|
+
{
|
|
1715
|
+
"name": "outline-drag",
|
|
1716
|
+
"description": "Drag by the title bar as an outline: a press draws a dotted one-pixel\nrectangle around the window on the desktop's drag surface, with the\nicon drag outline's pen; the drag moves it, held where the window can\nland, and the window moves there on the release, in one write. Escape\ncancels and writes nothing, as does a release that never moved it.\nUnset, the window moves live under the pointer; with no `vf-desktop` on\nits path it moves live either way. Only matters with `movable`.",
|
|
1717
|
+
"values": []
|
|
1718
|
+
},
|
|
1714
1719
|
{
|
|
1715
1720
|
"name": "resizable",
|
|
1716
1721
|
"description": "Show a grow box at the bottom-right corner for resizing. The drag is\nbounded per axis by the sizeRect below.",
|
package/editor/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
|
|
3
3
|
"name": "vintage-frames",
|
|
4
|
-
"version": "0.12.
|
|
4
|
+
"version": "0.12.3",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
@@ -537,7 +537,7 @@
|
|
|
537
537
|
},
|
|
538
538
|
{
|
|
539
539
|
"name": "vf-desktop",
|
|
540
|
-
"description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the screen-corner mask is in front of it.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, one whole-surface raster at one image px per system px, 1-bit at\nevery density and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. Not the
|
|
540
|
+
"description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the screen-corner mask is in front of it.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its four corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, one whole-surface raster at one image px per system px, 1-bit at\nevery density and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. A window asks the same way and draws\nthere with the same pen: its zoom rects when it opens from a rect\n(`vf-window.show({ from })`), its outline under an `outline-drag`. Not the\ntop layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).\n---\n\n\n### **Events:**\n - **vf-activate** - The active document-tier window changed. Detail `{ window: HTMLElement | null }` — the new holder, or `null` when the document tier deactivated (a {@link clearActive} call, or the active window leaving the DOM with none behind it). Fired once per change of holder, never for a re-assertion of the same one.\n\n### **Slots:**\n - _default_ - Default slot: menu bar, windows, anything.\n\n### **CSS Properties:**\n - **--vf-desktop** - base color under the desktop pattern — occluded by the pattern's opaque paper, so it only shows through a custom `--vf-desktop-pattern` _(default: #808080)_\n- **--vf-desktop-pattern** - a consumer's own desktop tile, in place of `pattern` — the kit's default is the 50% checker drawn as opaque black-on-white rects on a 30-system-px tile. Override the whole tile; consumer art renders as a placed tile grid at that same geometry (raster art magnifies nearest-neighbor, the `vf-img` idiom) _(default: undefined)_\n\n### **CSS Parts:**\n - **desktop** - The patterned screen surface — the whole-system-px raster (inset by `bezel` when one is set).",
|
|
541
541
|
"doc-url": "",
|
|
542
542
|
"attributes": [
|
|
543
543
|
{
|
|
@@ -3989,7 +3989,7 @@
|
|
|
3989
3989
|
},
|
|
3990
3990
|
{
|
|
3991
3991
|
"name": "vf-window",
|
|
3992
|
-
"description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n---\n\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close' }` (shape- compatible with vf-dialog's `vf-close`). The window does NOT remove itself; the consumer decides what closing means.\n- **vf-zoom** - Zoom box clicked. Detail `{}`.\n- **vf-resize** - The grow box resized the window. Detail `{ width, height, commit }`, sizes in whole system px: one event per size the drag writes (`commit: false`), fired after the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles — only when it changed the size. Fired by the gesture alone: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.\n\n### **Slots:**\n - _default_ - Default slot: window body content.\n- **header** - Optional header content — a band between the title bar and the body, the full width of the window (the Finder window's header line): a white interior over a 1px rule, no inset of its own, a positioning anchor for placed children. As tall as its content unless `header-height` states it. Under `scrollbars` the vertical rail begins below it, so the header spans the rail's column. Takes no space until populated.\n- **status** - Optional status-bar content — the classic bottom readout strip (\"40px x 40px\"): a 1px rule over a 15px white band under the body, body-face text on its native line. Takes no space until populated; a `resizable` window's grow box sits flush in its right end.\n\n### **CSS Properties:**\n - **--vf-dots-pattern** - the windoid bar's dot-grid dither — a 2×2 motif, one black pixel at its origin, on a 30-system-px tile (`vfDots`; override the whole tile like `--vf-desktop-pattern` — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n- **--vf-titlebar-height** - window/dialog title bars _(default: undefined)_\n- **--vf-titlebar-height-utility** - the slim `vf-window[variant=\"utility\"]` (windoid) bar — 11px interior + 1px bottom rule, traced from `Windows/utility-window.png` _(default: 12px)_\n- **--vf-status-bar-height** - the status strip: 1px rule + 14px interior — the grow box's own height, so the two compose flush _(default: 15px)_\n- **--vf-line-height** - the body face's native line, which the status strip's text rides (whole-pixel centered in the 14px interior) _(default: 12px)_\n\n### **CSS Parts:**\n - **frame** - The outer chrome frame.\n- **title-bar** - The striped (or dithered) title bar.\n- **title** - The centered title patch (hidden on the utility bar).\n- **close-box** - The close widget (left).\n- **zoom-box** - The zoom widget (right).\n- **body** - The content area.\n- **header** - The header strip between the title bar and the body (when the `header` slot is populated).\n- **status-bar** - The bottom status strip (when the `status` slot is populated).\n- **grow-box** - The resize widget (bottom-right, when `resizable`).\n- **viewport** - The built-in scroll area's viewport (when `scrollbars`; re-exported from vf-scroll-area).",
|
|
3992
|
+
"description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n\n**Opening from a rect.** show clears `hidden`, and given `from` — a\nbox in viewport CSS px, an icon's `cellRect()` or a button's rect — opens\nthe window from it: dotted rectangles grow from that box toward the\nwindow's frame, four on screen at a time, bunched at the box and opening\nout toward the window, drawn with the icon drag outline's XOR pen on the\ndesktop's drag surface; the window draws once they have drained, eighteen\nsteps later. Until then it matches `:state(opening)` and paints nothing —\nstill laid out, focusable and a hit target, so a page can place it, raise\nit and focus into it at once, and a press finishes the rects and lands on\nthe window. Under `prefers-reduced-motion`, with no `vf-desktop` on its\npath, or without `from`, the window shows at once.\n\n**Closing to a rect.** hide sets `hidden`, and given `to` closes\nthe window to it: the same rectangles run the other way, from where the\nframe was in toward the box, the window gone from the first step. A press\nfinishes them, and removing the window leaves them running. Under\n`prefers-reduced-motion`, with no `vf-desktop` on its path, or without\n`to`, the window hides at once. The window knows nothing about icons:\n`from` and `to` are any rect.\n\n**Dragging as an outline.** A `movable` window moves live under the\npointer. With `outline-drag` a press on the title bar draws a dotted\noutline around the window on the desktop's drag surface instead, with the\nsame pen; the drag moves the outline, held where the window can land, and\nthe window moves there on the release. Escape cancels.\n---\n\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close' }` (shape- compatible with vf-dialog's `vf-close`). The window does NOT remove itself; the consumer decides what closing means.\n- **vf-zoom** - Zoom box clicked. Detail `{}`.\n- **vf-resize** - The grow box resized the window. Detail `{ width, height, commit }`, sizes in whole system px: one event per size the drag writes (`commit: false`), fired after the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles — only when it changed the size. Fired by the gesture alone: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.\n\n### **Slots:**\n - _default_ - Default slot: window body content.\n- **header** - Optional header content — a band between the title bar and the body, the full width of the window (the Finder window's header line): a white interior over a 1px rule, no inset of its own, a positioning anchor for placed children. As tall as its content unless `header-height` states it. Under `scrollbars` the vertical rail begins below it, so the header spans the rail's column. Takes no space until populated.\n- **status** - Optional status-bar content — the classic bottom readout strip (\"40px x 40px\"): a 1px rule over a 15px white band under the body, body-face text on its native line. Takes no space until populated; a `resizable` window's grow box sits flush in its right end.\n\n### **CSS Properties:**\n - **--vf-dots-pattern** - the windoid bar's dot-grid dither — a 2×2 motif, one black pixel at its origin, on a 30-system-px tile (`vfDots`; override the whole tile like `--vf-desktop-pattern` — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n- **--vf-titlebar-height** - window/dialog title bars _(default: undefined)_\n- **--vf-titlebar-height-utility** - the slim `vf-window[variant=\"utility\"]` (windoid) bar — 11px interior + 1px bottom rule, traced from `Windows/utility-window.png` _(default: 12px)_\n- **--vf-status-bar-height** - the status strip: 1px rule + 14px interior — the grow box's own height, so the two compose flush _(default: 15px)_\n- **--vf-line-height** - the body face's native line, which the status strip's text rides (whole-pixel centered in the 14px interior) _(default: 12px)_\n\n### **CSS Parts:**\n - **frame** - The outer chrome frame.\n- **title-bar** - The striped (or dithered) title bar.\n- **title** - The centered title patch (hidden on the utility bar).\n- **close-box** - The close widget (left).\n- **zoom-box** - The zoom widget (right).\n- **body** - The content area.\n- **header** - The header strip between the title bar and the body (when the `header` slot is populated).\n- **status-bar** - The bottom status strip (when the `status` slot is populated).\n- **grow-box** - The resize widget (bottom-right, when `resizable`).\n- **viewport** - The built-in scroll area's viewport (when `scrollbars`; re-exported from vf-scroll-area).",
|
|
3993
3993
|
"doc-url": "",
|
|
3994
3994
|
"attributes": [
|
|
3995
3995
|
{
|
|
@@ -4022,6 +4022,11 @@
|
|
|
4022
4022
|
"description": "Allow dragging the window by its title bar.",
|
|
4023
4023
|
"value": { "type": "boolean", "default": "false" }
|
|
4024
4024
|
},
|
|
4025
|
+
{
|
|
4026
|
+
"name": "outline-drag",
|
|
4027
|
+
"description": "Drag by the title bar as an outline: a press draws a dotted one-pixel\nrectangle around the window on the desktop's drag surface, with the\nicon drag outline's pen; the drag moves it, held where the window can\nland, and the window moves there on the release, in one write. Escape\ncancels and writes nothing, as does a release that never moved it.\nUnset, the window moves live under the pointer; with no `vf-desktop` on\nits path it moves live either way. Only matters with `movable`.",
|
|
4028
|
+
"value": { "type": "boolean", "default": "false" }
|
|
4029
|
+
},
|
|
4025
4030
|
{
|
|
4026
4031
|
"name": "resizable",
|
|
4027
4032
|
"description": "Show a grow box at the bottom-right corner for resizing. The drag is\nbounded per axis by the sizeRect below.",
|
|
@@ -4147,6 +4152,11 @@
|
|
|
4147
4152
|
"description": "Allow dragging the window by its title bar.",
|
|
4148
4153
|
"type": "boolean"
|
|
4149
4154
|
},
|
|
4155
|
+
{
|
|
4156
|
+
"name": "outlineDrag",
|
|
4157
|
+
"description": "Drag by the title bar as an outline: a press draws a dotted one-pixel\nrectangle around the window on the desktop's drag surface, with the\nicon drag outline's pen; the drag moves it, held where the window can\nland, and the window moves there on the release, in one write. Escape\ncancels and writes nothing, as does a release that never moved it.\nUnset, the window moves live under the pointer; with no `vf-desktop` on\nits path it moves live either way. Only matters with `movable`.",
|
|
4158
|
+
"type": "boolean"
|
|
4159
|
+
},
|
|
4150
4160
|
{
|
|
4151
4161
|
"name": "resizable",
|
|
4152
4162
|
"description": "Show a grow box at the bottom-right corner for resizing. The drag is\nbounded per axis by the sizeRect below.",
|