vintage-frames 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1974,7 +1974,7 @@
1974
1974
  "declarations": [
1975
1975
  {
1976
1976
  "kind": "class",
1977
- "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.** No border, background, role,\nkeyboard behavior or selection what it holds decides what it is.\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 shadow box below owns the `position: relative` anchor\nand the `vf-snap` class together, so the correction\nmoves the whole coordinate system — everything placed against it rides\nalong instead of being 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.",
1977
+ "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 the box's own border\n(vfRule), inside the declared size, so a 24px `rule=\"bottom\"`\nstrip is 23 rows of box over the line; content, `fill-width` children and\nplaced children begin inside it, as a rectangle's interior begins inside\nFrameRect'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 whole-surface\nraster mechanism as the desktop dither, so it is 1-bit at every density\nand zoom (src/pattern-fill.ts). A declared `width`/`height` sizes the\nraster exactly; an undeclared axis (`fill-width`, a shrink-wrapped\nheight) is measured. Under 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 shadow box below owns the `position: relative` anchor\nand the `vf-snap` class together, so the correction\nmoves the whole coordinate system — everything placed against it rides\nalong instead of being 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.",
1978
1978
  "name": "VfContainer",
1979
1979
  "slots": [
1980
1980
  {
@@ -1983,6 +1983,74 @@
1983
1983
  }
1984
1984
  ],
1985
1985
  "members": [
1986
+ {
1987
+ "kind": "field",
1988
+ "name": "pattern",
1989
+ "type": {
1990
+ "text": "string | null | undefined"
1991
+ },
1992
+ "description": "A 1-bit fill for the box: a library pattern by name (`bricks`,\n`gray-50`, … — the 38 standard MacPaint patterns, docs/PATTERNS.md) or\nsixteen hex digits stating a custom 8×8 pattern row by row, bit 7 the\nleftmost pixel, 1 = ink (`\"DD 77 DD 77 DD 77 DD 77\"`). Painted in black\non a `--vf-white` ground under the content, anchored at the box's\ntop-left. Unset, the container paints nothing; an unrecognized value\npaints nothing and warns once.",
1993
+ "attribute": "pattern"
1994
+ },
1995
+ {
1996
+ "kind": "field",
1997
+ "name": "_pattern",
1998
+ "type": {
1999
+ "text": "Pattern | null"
2000
+ },
2001
+ "privacy": "private",
2002
+ "default": "null",
2003
+ "description": "`pattern`, resolved — what the fill paints; null paints nothing."
2004
+ },
2005
+ {
2006
+ "kind": "field",
2007
+ "name": "#warnedPattern",
2008
+ "privacy": "private",
2009
+ "type": {
2010
+ "text": "boolean"
2011
+ },
2012
+ "default": "false",
2013
+ "description": "One warning per element for an unrecognized `pattern`, not per render."
2014
+ },
2015
+ {
2016
+ "kind": "field",
2017
+ "name": "rule",
2018
+ "type": {
2019
+ "text": "string | null | undefined"
2020
+ },
2021
+ "description": "The 1px rule on the box's edges: edge names separated by spaces —\n`\"bottom\"`, `\"top bottom\"`, up to all four in any order. Drawn as the\nbox's own border inside the declared size, in `--vf-black`, scaled with\nthe display like every kit frame's border; content and placed children\nbegin inside it.\nUnset, no rule; a value naming anything but an edge draws none and\nwarns once.",
2022
+ "attribute": "rule"
2023
+ },
2024
+ {
2025
+ "kind": "field",
2026
+ "name": "_rule",
2027
+ "type": {
2028
+ "text": "RuleEdge[]"
2029
+ },
2030
+ "privacy": "private",
2031
+ "default": "[]",
2032
+ "description": "`rule`, resolved — the edges drawn; empty draws nothing."
2033
+ },
2034
+ {
2035
+ "kind": "field",
2036
+ "name": "#warnedRule",
2037
+ "privacy": "private",
2038
+ "type": {
2039
+ "text": "boolean"
2040
+ },
2041
+ "default": "false",
2042
+ "description": "One warning per element for an unrecognized `rule`, not per render."
2043
+ },
2044
+ {
2045
+ "kind": "field",
2046
+ "name": "box",
2047
+ "type": {
2048
+ "text": "HTMLDivElement"
2049
+ },
2050
+ "privacy": "private",
2051
+ "readonly": true,
2052
+ "description": "The shadow box the fill paints on; exists from the first render."
2053
+ },
1986
2054
  {
1987
2055
  "kind": "field",
1988
2056
  "name": "scale",
@@ -1999,6 +2067,14 @@
1999
2067
  "default": "new GridSnapController(this)",
2000
2068
  "description": "Hold the box on the device-pixel grid — see the\nclass doc. The host is what gets measured; `.box` (vf-snap) is where the\ncorrection lands."
2001
2069
  },
2070
+ {
2071
+ "kind": "field",
2072
+ "name": "patternFill",
2073
+ "privacy": "private",
2074
+ "readonly": true,
2075
+ "default": "new PatternFillController(this, { getBox: () => this.box, getPattern: () => this._pattern, getSize: () => ({ width: this.width, height: this.height }), })",
2076
+ "description": "The pattern fill, painted on `.box` so it rides the snap correction with\nthe coordinate system. A declared axis sizes the raster exactly; an\nundeclared one is measured (src/pattern-fill.ts)."
2077
+ },
2002
2078
  {
2003
2079
  "kind": "field",
2004
2080
  "name": "width",
@@ -2052,23 +2128,23 @@
2052
2128
  }
2053
2129
  }
2054
2130
  ],
2055
- "mixins": [
2131
+ "attributes": [
2056
2132
  {
2057
- "name": "VfSized",
2058
- "module": "/src/size.js"
2133
+ "name": "pattern",
2134
+ "type": {
2135
+ "text": "string | null | undefined"
2136
+ },
2137
+ "description": "A 1-bit fill for the box: a library pattern by name (`bricks`,\n`gray-50`, … — the 38 standard MacPaint patterns, docs/PATTERNS.md) or\nsixteen hex digits stating a custom 8×8 pattern row by row, bit 7 the\nleftmost pixel, 1 = ink (`\"DD 77 DD 77 DD 77 DD 77\"`). Painted in black\non a `--vf-white` ground under the content, anchored at the box's\ntop-left. Unset, the container paints nothing; an unrecognized value\npaints nothing and warns once.",
2138
+ "fieldName": "pattern"
2059
2139
  },
2060
2140
  {
2061
- "name": "VfPositioned",
2062
- "module": "/src/position.js"
2063
- }
2064
- ],
2065
- "superclass": {
2066
- "name": "LitElement",
2067
- "package": "lit"
2068
- },
2069
- "tagName": "vf-container",
2070
- "customElement": true,
2071
- "attributes": [
2141
+ "name": "rule",
2142
+ "type": {
2143
+ "text": "string | null | undefined"
2144
+ },
2145
+ "description": "The 1px rule on the box's edges: edge names separated by spaces —\n`\"bottom\"`, `\"top bottom\"`, up to all four in any order. Drawn as the\nbox's own border inside the declared size, in `--vf-black`, scaled with\nthe display like every kit frame's border; content and placed children\nbegin inside it.\nUnset, no rule; a value naming anything but an edge draws none and\nwarns once.",
2146
+ "fieldName": "rule"
2147
+ },
2072
2148
  {
2073
2149
  "name": "width",
2074
2150
  "type": {
@@ -2117,7 +2193,23 @@
2117
2193
  "module": "src/position.ts"
2118
2194
  }
2119
2195
  }
2120
- ]
2196
+ ],
2197
+ "mixins": [
2198
+ {
2199
+ "name": "VfSized",
2200
+ "module": "/src/size.js"
2201
+ },
2202
+ {
2203
+ "name": "VfPositioned",
2204
+ "module": "/src/position.js"
2205
+ }
2206
+ ],
2207
+ "superclass": {
2208
+ "name": "LitElement",
2209
+ "package": "lit"
2210
+ },
2211
+ "tagName": "vf-container",
2212
+ "customElement": true
2121
2213
  }
2122
2214
  ],
2123
2215
  "exports": [
@@ -2145,22 +2237,22 @@
2145
2237
  "declarations": [
2146
2238
  {
2147
2239
  "kind": "class",
2148
- "description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the 50%-dither gray desktop pattern and manages the stacking order\nand `active` state of slotted `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, at pointer-gesture\nends), so tabbing walks the stack the way the eye does and Shift+Tab is\nits 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.\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 top corners\nwith the classic corner mask.\n\nCustom properties:\n- `--vf-desktop-pattern` — the dither's tile art (default a 1-bit 50%\n checker, opaque black-on-white on a 30-system-px tile). Overriding it\n renders the token 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 layer (default\n `#808080`). The default tile is opaque, so this only becomes visible when\n `--vf-desktop-pattern` is overridden with a tile that has transparent\n cells (or with `none`).",
2240
+ "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, at pointer-gesture\nends), so tabbing walks the stack the way the eye does and Shift+Tab is\nits 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.\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 top 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\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`).",
2149
2241
  "name": "VfDesktop",
2150
2242
  "cssProperties": [
2151
2243
  {
2152
- "description": "base color under the desktop dither — occluded by the default (opaque) tile, so it only shows through a custom `--vf-desktop-pattern`",
2244
+ "description": "base color under the desktop pattern — occluded by the pattern's opaque paper, so it only shows through a custom `--vf-desktop-pattern`",
2153
2245
  "name": "--vf-desktop",
2154
2246
  "default": "#808080"
2155
2247
  },
2156
2248
  {
2157
- "description": "the desktop dither's art a 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)",
2249
+ "description": "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)",
2158
2250
  "name": "--vf-desktop-pattern"
2159
2251
  }
2160
2252
  ],
2161
2253
  "cssParts": [
2162
2254
  {
2163
- "description": "The dithered screen surface — the whole-system-px raster (inset by `bezel` when one is set).",
2255
+ "description": "The patterned screen surface — the whole-system-px raster (inset by `bezel` when one is set).",
2164
2256
  "name": "desktop"
2165
2257
  }
2166
2258
  ],
@@ -2201,6 +2293,16 @@
2201
2293
  "description": "Width of the black screen bezel, in system px (`0` = none), added onto\nthe declared screen on every side — a `width=\"502\" bezel=\"5\"` desktop\nrenders a 512-system-px host box. The compact Mac's CRT showed an\nunlit black margin between the desktop's raster and the case; `bezel`\ndraws it around the screen and puts the classic screen-corner mask on\nthe screen's two *top* corners — only the top pair was rounded in the\nframebuffer. Flow, window coordinates and the drag clip all belong to\nthe screen, so windows crop at its edge. Inside a bezeled desktop a\nmenu bar needs no `rounded` of its own — the desktop's mask lands on\nthe same pixels.",
2202
2294
  "attribute": "bezel"
2203
2295
  },
2296
+ {
2297
+ "kind": "field",
2298
+ "name": "pattern",
2299
+ "type": {
2300
+ "text": "string | null | undefined"
2301
+ },
2302
+ "default": "'gray-50'",
2303
+ "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.",
2304
+ "attribute": "pattern"
2305
+ },
2204
2306
  {
2205
2307
  "kind": "method",
2206
2308
  "name": "fitWithin",
@@ -2252,21 +2354,51 @@
2252
2354
  },
2253
2355
  {
2254
2356
  "kind": "field",
2255
- "name": "_pattern",
2357
+ "name": "_token",
2256
2358
  "type": {
2257
2359
  "text": "string"
2258
2360
  },
2259
2361
  "privacy": "private",
2260
2362
  "default": "''",
2261
- "description": "The consumer's `--vf-desktop-pattern` override, or `''` for the kit\ndither — which of the two exact-fill paths render() takes (see\nsrc/tile-grid.ts). Re-read every update; a token swapped at runtime\nwithout touching the component wants a `requestUpdate()`."
2363
+ "description": "The consumer's `--vf-desktop-pattern` override, or `''` for the kit\npattern — which of the two exact-fill paths render() takes (see\nsrc/tile-grid.ts). Re-read every update; a token swapped at runtime\nwithout touching the component wants a `requestUpdate()`."
2262
2364
  },
2263
2365
  {
2264
2366
  "kind": "field",
2265
- "name": "#raster",
2367
+ "name": "_desktopPattern",
2368
+ "type": {
2369
+ "text": "Pattern"
2370
+ },
2371
+ "privacy": "private",
2372
+ "default": "PATTERNS[DEFAULT_PATTERN]",
2373
+ "description": "`pattern`, resolved — the kit path's art; the dither when unset."
2374
+ },
2375
+ {
2376
+ "kind": "field",
2377
+ "name": "#warnedPattern",
2378
+ "privacy": "private",
2379
+ "type": {
2380
+ "text": "boolean"
2381
+ },
2382
+ "default": "false",
2383
+ "description": "One warning per element for an unrecognized `pattern`, not per render."
2384
+ },
2385
+ {
2386
+ "kind": "field",
2387
+ "name": "screen",
2388
+ "type": {
2389
+ "text": "HTMLDivElement"
2390
+ },
2266
2391
  "privacy": "private",
2267
2392
  "readonly": true,
2268
- "default": "new TileRasterCache()",
2269
- "description": "The whole-surface dither raster, cached against its ceiled size."
2393
+ "description": "The screen surface the pattern paints on; exists from the first render."
2394
+ },
2395
+ {
2396
+ "kind": "field",
2397
+ "name": "patternFill",
2398
+ "privacy": "private",
2399
+ "readonly": true,
2400
+ "default": "new PatternFillController(this, { getBox: () => this.screen, getPattern: () => (this._token ? null : this._desktopPattern), getSize: () => ({ width: this.width ?? DEFAULT_SCREEN_WIDTH, height: this.height ?? DEFAULT_SCREEN_HEIGHT, }), })",
2401
+ "description": "The desktop pattern, painted as the screen's own background\n(src/pattern-fill.ts) from the declared raster — scale-independent, so\ndensity and zoom re-encode nothing — and silent while a consumer token\nowns the fill."
2270
2402
  },
2271
2403
  {
2272
2404
  "kind": "field",
@@ -2640,6 +2772,15 @@
2640
2772
  "description": "Width of the black screen bezel, in system px (`0` = none), added onto\nthe declared screen on every side — a `width=\"502\" bezel=\"5\"` desktop\nrenders a 512-system-px host box. The compact Mac's CRT showed an\nunlit black margin between the desktop's raster and the case; `bezel`\ndraws it around the screen and puts the classic screen-corner mask on\nthe screen's two *top* corners — only the top pair was rounded in the\nframebuffer. Flow, window coordinates and the drag clip all belong to\nthe screen, so windows crop at its edge. Inside a bezeled desktop a\nmenu bar needs no `rounded` of its own — the desktop's mask lands on\nthe same pixels.",
2641
2773
  "fieldName": "bezel"
2642
2774
  },
2775
+ {
2776
+ "name": "pattern",
2777
+ "type": {
2778
+ "text": "string | null | undefined"
2779
+ },
2780
+ "default": "DEFAULT_PATTERN",
2781
+ "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.",
2782
+ "fieldName": "pattern"
2783
+ },
2643
2784
  {
2644
2785
  "name": "top",
2645
2786
  "type": {
@@ -2704,7 +2845,7 @@
2704
2845
  "declarations": [
2705
2846
  {
2706
2847
  "kind": "class",
2707
- "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):\n\n- **Default:** a striped title bar with a centered title over a white body —\n the movable-modal look. Drag the title bar to move it. `closable` adds the\n standard close box (left of the bar) — the HIG's own figures disagree on\n whether a movable modal carries one (Figure 5-1 says yes, Figure 6-1 and\n the Chapter 6 text say no), so the component enables either reading rather\n than enforcing one.\n- **`frame=\"plain\"`:** the classic dBoxProc modal-dialog frame1px outer\n border, 2px gap, 2px inner band, no shadow, no title bar and immovable,\n like the original. A `heading` renders as a centered display-face heading\n at the top of the body (the reference art's \"Dialog title\"); `closable` is\n ignored, there being no bar to carry the widget.\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' }`.",
2848
+ "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 boxthe bare frame, no title bar —\n and immovable, like the original. A `heading` renders as a centered\n display-face heading at the top of the body (the reference art's \"Dialog\n title\"); `closable` is ignored, there being no bar to carry the widget.\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' }`.",
2708
2849
  "name": "VfDialog",
2709
2850
  "cssProperties": [
2710
2851
  {
@@ -2726,7 +2867,7 @@
2726
2867
  ],
2727
2868
  "cssParts": [
2728
2869
  {
2729
- "description": "The outer chrome frame (striped-bar or plain).",
2870
+ "description": "The outer frame (the double frame's 1px rule; the bar and the inner band sit inside it).",
2730
2871
  "name": "frame"
2731
2872
  },
2732
2873
  {
@@ -2822,7 +2963,7 @@
2822
2963
  "type": {
2823
2964
  "text": "'plain' | undefined"
2824
2965
  },
2825
- "description": "Frame chrome. Omit for the striped title bar (movable modal); `'plain'`\nfor the immovable dBoxProc double frame with no bar (modal dialog box).",
2966
+ "description": "Frame chrome. Omit for the movable modal (the double frame with the\nstriped title bar set into it); `'plain'` for the immovable modal dialog\nbox (the bare double frame, no bar).",
2826
2967
  "attribute": "frame",
2827
2968
  "reflects": true
2828
2969
  },
@@ -3377,7 +3518,7 @@
3377
3518
  "type": {
3378
3519
  "text": "'plain' | undefined"
3379
3520
  },
3380
- "description": "Frame chrome. Omit for the striped title bar (movable modal); `'plain'`\nfor the immovable dBoxProc double frame with no bar (modal dialog box).",
3521
+ "description": "Frame chrome. Omit for the movable modal (the double frame with the\nstriped title bar set into it); `'plain'` for the immovable modal dialog\nbox (the bare double frame, no bar).",
3381
3522
  "fieldName": "frame"
3382
3523
  },
3383
3524
  {
@@ -1,4 +1,4 @@
1
- import { LitElement } from 'lit';
1
+ import { LitElement, type PropertyValues } from 'lit';
2
2
  declare const VfContainer_base: (new (...args: any[]) => import("../size.js").VfSizedInterface) & (new (...args: any[]) => import("../position.js").VfPositionedInterface) & typeof LitElement;
3
3
  /**
4
4
  * `<vf-container>` — a box that is nothing but its declared size.
@@ -41,8 +41,28 @@ declare const VfContainer_base: (new (...args: any[]) => import("../size.js").Vf
41
41
  * width, because a layout box that silently claimed a size nobody declared
42
42
  * would be inventing one (the `vf-stack` rule, held here too).
43
43
  *
44
- * **It paints nothing and means nothing.** No border, background, role,
45
- * keyboard behavior or selection what it holds decides what it is.
44
+ * **It paints nothing and means nothing** unless `pattern` or `rule` say
45
+ * what to paint. No role, keyboard behavior or selection; what it holds
46
+ * decides what it is.
47
+ *
48
+ * **`rule` draws the 1px rule on the edges it names** — `rule="bottom"` is
49
+ * the menu bar's anatomy (the box's rows over one row of ink), `rule="top"`
50
+ * a status strip's, all four a framed box. The rule is the box's own border
51
+ * ({@link vfRule}), inside the declared size, so a 24px `rule="bottom"`
52
+ * strip is 23 rows of box over the line; content, `fill-width` children and
53
+ * placed children begin inside it, as a rectangle's interior begins inside
54
+ * FrameRect's line.
55
+ *
56
+ * **`pattern` fills the box with a 1-bit pattern**: one of the 38 standard
57
+ * MacPaint patterns by name (`pattern="bricks"`, `pattern="gray-50"` —
58
+ * docs/PATTERNS.md has the table), or sixteen hex digits stating a custom
59
+ * 8×8 pattern row by row, the way a PAT resource did. It is painted as the
60
+ * box's own background — black ink on a `--vf-white` ground, anchored at
61
+ * the box's top-left corner, under the content — by the same whole-surface
62
+ * raster mechanism as the desktop dither, so it is 1-bit at every density
63
+ * and zoom (src/pattern-fill.ts). A declared `width`/`height` sizes the
64
+ * raster exactly; an undeclared axis (`fill-width`, a shrink-wrapped
65
+ * height) is measured. Under forced colors the pattern goes flat Canvas.
46
66
  *
47
67
  * **It holds its box on the device-pixel grid** — with a `GridSnapController`.
48
68
  * A container's box is itself the consumer's coordinate system, including for
@@ -71,7 +91,34 @@ declare const VfContainer_base: (new (...args: any[]) => import("../size.js").Vf
71
91
  * fills it to the declared box.
72
92
  */
73
93
  export declare class VfContainer extends VfContainer_base {
94
+ #private;
74
95
  static styles: import("lit").CSSResult[];
96
+ /**
97
+ * A 1-bit fill for the box: a library pattern by name (`bricks`,
98
+ * `gray-50`, … — the 38 standard MacPaint patterns, docs/PATTERNS.md) or
99
+ * sixteen hex digits stating a custom 8×8 pattern row by row, bit 7 the
100
+ * leftmost pixel, 1 = ink (`"DD 77 DD 77 DD 77 DD 77"`). Painted in black
101
+ * on a `--vf-white` ground under the content, anchored at the box's
102
+ * top-left. Unset, the container paints nothing; an unrecognized value
103
+ * paints nothing and warns once.
104
+ */
105
+ pattern?: string | null;
106
+ /** `pattern`, resolved — what the fill paints; null paints nothing. */
107
+ private _pattern;
108
+ /**
109
+ * The 1px rule on the box's edges: edge names separated by spaces —
110
+ * `"bottom"`, `"top bottom"`, up to all four in any order. Drawn as the
111
+ * box's own border inside the declared size, in `--vf-black`, scaled with
112
+ * the display like every kit frame's border; content and placed children
113
+ * begin inside it.
114
+ * Unset, no rule; a value naming anything but an edge draws none and
115
+ * warns once.
116
+ */
117
+ rule?: string | null;
118
+ /** `rule`, resolved — the edges drawn; empty draws nothing. */
119
+ private _rule;
120
+ /** The shadow box the fill paints on; exists from the first render. */
121
+ private readonly box;
75
122
  /**
76
123
  * Default-on display scaling (true 72dpi size); see src/scale.ts. Without
77
124
  * one, a lone container on a plain page would resolve its declared size
@@ -85,6 +132,13 @@ export declare class VfContainer extends VfContainer_base {
85
132
  * correction lands.
86
133
  */
87
134
  private readonly gridSnap;
135
+ /**
136
+ * The pattern fill, painted on `.box` so it rides the snap correction with
137
+ * the coordinate system. A declared axis sizes the raster exactly; an
138
+ * undeclared one is measured (src/pattern-fill.ts).
139
+ */
140
+ private readonly patternFill;
141
+ protected willUpdate(changed: PropertyValues<this>): void;
88
142
  protected render(): import("lit-html").TemplateResult<1>;
89
143
  }
90
144
  declare global {
@@ -3,16 +3,31 @@ import { ScaleController as t } from "../scale.js";
3
3
  import n from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
4
4
  import { VfPositioned as r } from "../position.js";
5
5
  import { vfBase as i } from "../styles/recipes/host.js";
6
- import { GridSnapController as a } from "../grid-snap.js";
7
- import { VfSized as o } from "../size.js";
8
- import { LitElement as s, css as c, html as l } from "lit";
6
+ import { parsePattern as a } from "../patterns.js";
7
+ import { parseRule as o, ruleClasses as s, vfRule as c } from "../styles/recipes/rule.js";
8
+ import { PatternFillController as l, vfPatternFill as u } from "../pattern-fill.js";
9
+ import { GridSnapController as d } from "../grid-snap.js";
10
+ import { VfSized as f } from "../size.js";
11
+ import { LitElement as p, css as m, html as h } from "lit";
12
+ import { property as g, query as _ } from "lit/decorators.js";
9
13
  //#region src/components/vf-container.ts
10
- var u = class extends o(r(s)) {
14
+ var v = class extends f(r(p)) {
11
15
  constructor(...e) {
12
- super(...e), this.scale = new t(this), this.gridSnap = new a(this);
16
+ super(...e), this._pattern = null, this.#e = !1, this._rule = [], this.#t = !1, this.scale = new t(this), this.gridSnap = new d(this), this.patternFill = new l(this, {
17
+ getBox: () => this.box,
18
+ getPattern: () => this._pattern,
19
+ getSize: () => ({
20
+ width: this.width,
21
+ height: this.height
22
+ })
23
+ });
13
24
  }
14
25
  static {
15
- this.styles = [i, c`
26
+ this.styles = [
27
+ i,
28
+ u,
29
+ c,
30
+ m`
16
31
  :host {
17
32
  display: block;
18
33
  /* Shrink-wrap an undeclared axis rather than claim the parent's
@@ -75,12 +90,26 @@ var u = class extends o(r(s)) {
75
90
  ::slotted([fill-height]) {
76
91
  height: 100%;
77
92
  }
78
- `];
93
+ `
94
+ ];
95
+ }
96
+ #e;
97
+ #t;
98
+ willUpdate(e) {
99
+ if (super.willUpdate(e), e.has("pattern") && (this._pattern = a(this.pattern), this._pattern === null && this.pattern?.trim() && !this.#e && (this.#e = !0, console.warn(`vf-container: unknown pattern "${this.pattern}" — a library name (docs/PATTERNS.md) or sixteen hex digits. Painting nothing.`))), e.has("rule")) {
100
+ let e = o(this.rule);
101
+ this._rule = e ?? [], e === null && !this.#t && (this.#t = !0, console.warn(`vf-container: unknown rule "${this.rule}" — edge names (top, right, bottom, left) separated by spaces. Drawing none.`));
102
+ }
79
103
  }
80
104
  render() {
81
- return l`<div class="vf-snap box"><slot></slot></div>`;
105
+ let e = s(this._rule);
106
+ return h`<div
107
+ class="vf-snap box vf-pattern-fill${this._pattern ? " vf-patterned" : ""}${e ? ` ${e}` : ""}"
108
+ >
109
+ <slot></slot>
110
+ </div>`;
82
111
  }
83
112
  };
84
- u = n([e("vf-container")], u);
113
+ n([g()], v.prototype, "pattern", void 0), n([g()], v.prototype, "rule", void 0), n([_(".box")], v.prototype, "box", void 0), v = n([e("vf-container")], v);
85
114
  //#endregion
86
- export { u as VfContainer };
115
+ export { v as VfContainer };
@@ -3,8 +3,10 @@ declare const VfDesktop_base: (new (...args: any[]) => import("../position.js").
3
3
  /**
4
4
  * `<vf-desktop>` — the full-bleed classic desktop container.
5
5
  *
6
- * Renders the 50%-dither gray desktop pattern and manages the stacking order
7
- * and `active` state of slotted `vf-window` children: a `pointerdown` or
6
+ * Renders the desktop pattern the classic 50% dither by default, or any of
7
+ * the standard patterns by name (`pattern`, System 7's General Controls
8
+ * setting) — and manages the stacking order and `active` state of slotted
9
+ * `vf-window` children: a `pointerdown` or
8
10
  * `focusin` (keyboard focus) anywhere inside a window brings it to the front
9
11
  * and makes it the single active window. The windows' light-DOM order is kept
10
12
  * in step with the stacking order (bottom-most first, at pointer-gesture
@@ -43,16 +45,24 @@ declare const VfDesktop_base: (new (...args: any[]) => import("../position.js").
43
45
  * the CRT's unlit margin — around the screen, rounding its top corners
44
46
  * with the classic corner mask.
45
47
  *
48
+ * **`pattern`** names the desktop pattern — `gray-50` (the dither) by
49
+ * default, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen
50
+ * hex digits stating a custom 8×8 pattern, as on `vf-container`. It is
51
+ * painted as the screen's own background: black ink on an opaque white
52
+ * paper, one whole-surface raster at one image px per system px, 1-bit at
53
+ * every density and zoom (src/pattern-fill.ts).
54
+ *
46
55
  * Custom properties:
47
- * - `--vf-desktop-pattern` — the dither's tile art (default a 1-bit 50%
48
- * checker, opaque black-on-white on a 30-system-px tile). Overriding it
49
- * renders the token as a placed tile grid at that same 30-px geometry
56
+ * - `--vf-desktop-pattern` — a consumer's own tile art in place of the
57
+ * pattern (the kit's default is a 1-bit 50% checker, opaque
58
+ * black-on-white on a 30-system-px tile). Set, it wins over `pattern` and
59
+ * renders as a placed tile grid at that same 30-px geometry
50
60
  * (src/tile-grid.ts); a token swapped at runtime without touching the
51
61
  * component wants a `requestUpdate()`.
52
- * - `--vf-desktop` — base color painted *under* the pattern layer (default
53
- * `#808080`). The default tile is opaque, so this only becomes visible when
54
- * `--vf-desktop-pattern` is overridden with a tile that has transparent
55
- * cells (or with `none`).
62
+ * - `--vf-desktop` — base color painted *under* the pattern (default
63
+ * `#808080`). The pattern's paper is opaque, so this only becomes visible
64
+ * when `--vf-desktop-pattern` is overridden with a tile that has
65
+ * transparent cells (or with `none`).
56
66
  *
57
67
  * @slot - Default slot: menu bar, windows, anything.
58
68
  * @fires vf-activate - The active document-tier window changed. Detail
@@ -60,15 +70,16 @@ declare const VfDesktop_base: (new (...args: any[]) => import("../position.js").
60
70
  * document tier deactivated (a {@link clearActive} call, or the active
61
71
  * window leaving the DOM with none behind it). Fired once per change of
62
72
  * holder, never for a re-assertion of the same one.
63
- * @csspart desktop - The dithered screen surface — the whole-system-px
73
+ * @csspart desktop - The patterned screen surface — the whole-system-px
64
74
  * raster (inset by `bezel` when one is set).
65
- * @cssprop [--vf-desktop=#808080] - base color under the desktop dither
66
- * occluded by the default (opaque) tile, so it only shows through a custom
67
- * `--vf-desktop-pattern`
68
- * @cssprop --vf-desktop-pattern - the desktop dither's art a 50% checker
69
- * drawn as opaque black-on-white rects, on a 30-system-px tile. Override the
70
- * whole tile; consumer art renders as a placed tile grid at that same
71
- * geometry (raster art magnifies nearest-neighbor, the `vf-img` idiom)
75
+ * @cssprop [--vf-desktop=#808080] - base color under the desktop pattern
76
+ * occluded by the pattern's opaque paper, so it only shows through a
77
+ * custom `--vf-desktop-pattern`
78
+ * @cssprop --vf-desktop-pattern - a consumer's own desktop tile, in place of
79
+ * `pattern` the kit's default is the 50% checker drawn as opaque
80
+ * black-on-white rects on a 30-system-px tile. Override the whole tile;
81
+ * consumer art renders as a placed tile grid at that same geometry (raster
82
+ * art magnifies nearest-neighbor, the `vf-img` idiom)
72
83
  */
73
84
  export declare class VfDesktop extends VfDesktop_base {
74
85
  #private;
@@ -98,6 +109,17 @@ export declare class VfDesktop extends VfDesktop_base {
98
109
  * the same pixels.
99
110
  */
100
111
  bezel: number;
112
+ /**
113
+ * The desktop pattern — System 7's General Controls setting. A library
114
+ * pattern by name (`gray-50`, the classic dither, by default; `gray-75`,
115
+ * `bricks`, … — docs/PATTERNS.md) or sixteen hex digits stating a custom
116
+ * 8×8 pattern, as on `vf-container`. Painted black on opaque white over
117
+ * the whole screen, 1-bit at every density and zoom. A
118
+ * `--vf-desktop-pattern` token override still wins and renders the
119
+ * consumer's tile as a placed grid; an unrecognized value warns once and
120
+ * keeps the dither.
121
+ */
122
+ pattern: string | null | undefined;
101
123
  /**
102
124
  * Size the screen to the largest whole-system-px raster whose host box —
103
125
  * bezel included — fits a CSS-px bound, and return what was set. The
@@ -120,11 +142,22 @@ export declare class VfDesktop extends VfDesktop_base {
120
142
  private readonly gridSnap;
121
143
  /**
122
144
  * The consumer's `--vf-desktop-pattern` override, or `''` for the kit
123
- * dither — which of the two exact-fill paths render() takes (see
145
+ * pattern — which of the two exact-fill paths render() takes (see
124
146
  * src/tile-grid.ts). Re-read every update; a token swapped at runtime
125
147
  * without touching the component wants a `requestUpdate()`.
126
148
  */
127
- private _pattern;
149
+ private _token;
150
+ /** `pattern`, resolved — the kit path's art; the dither when unset. */
151
+ private _desktopPattern;
152
+ /** The screen surface the pattern paints on; exists from the first render. */
153
+ private readonly screen;
154
+ /**
155
+ * The desktop pattern, painted as the screen's own background
156
+ * (src/pattern-fill.ts) from the declared raster — scale-independent, so
157
+ * density and zoom re-encode nothing — and silent while a consumer token
158
+ * owns the fill.
159
+ */
160
+ private readonly patternFill;
128
161
  /** Monotonic z-index counter for window stacking. */
129
162
  private _zCounter;
130
163
  /**