vintage-frames 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/custom-elements.json +181 -32
  2. package/dist/components/vf-button.js +25 -25
  3. package/dist/components/vf-checkbox.js +15 -15
  4. package/dist/components/vf-container.d.ts +37 -3
  5. package/dist/components/vf-container.js +32 -10
  6. package/dist/components/vf-desktop.d.ts +103 -22
  7. package/dist/components/vf-desktop.js +100 -121
  8. package/dist/components/vf-dialog.d.ts +19 -15
  9. package/dist/components/vf-dialog.js +45 -53
  10. package/dist/components/vf-icon.js +56 -56
  11. package/dist/components/vf-list-item.js +16 -16
  12. package/dist/components/vf-list.js +26 -26
  13. package/dist/components/vf-menu-item.js +24 -24
  14. package/dist/components/vf-menu.js +27 -27
  15. package/dist/components/vf-number-field.js +17 -17
  16. package/dist/components/vf-progress-bar.js +3 -3
  17. package/dist/components/vf-radio-group.js +13 -13
  18. package/dist/components/vf-radio.js +16 -16
  19. package/dist/components/vf-select.js +27 -27
  20. package/dist/components/vf-slider.js +32 -32
  21. package/dist/components/vf-window.js +69 -69
  22. package/dist/index.d.ts +14 -0
  23. package/dist/index.js +73 -71
  24. package/dist/modal-dialog.js +21 -21
  25. package/dist/pattern-fill.d.ts +91 -0
  26. package/dist/pattern-fill.js +74 -0
  27. package/dist/patterns.d.ts +80 -0
  28. package/dist/patterns.js +477 -0
  29. package/dist/scroll-rail.js +37 -37
  30. package/dist/styles/recipes/pattern.d.ts +2 -12
  31. package/dist/styles/recipes/pattern.js +11 -15
  32. package/dist/styles/recipes/scroll-rail.d.ts +3 -12
  33. package/dist/styles/recipes/scroll-rail.js +7 -15
  34. package/dist/styles/recipes/surface.d.ts +18 -7
  35. package/dist/styles/recipes/surface.js +15 -0
  36. package/dist/text-control.js +11 -11
  37. package/dist/tile-grid.d.ts +4 -2
  38. package/dist/tile-grid.js +3 -3
  39. package/dist/toggle-control.js +5 -5
  40. package/dist/track-width.d.ts +7 -0
  41. package/dist/track-width.js +6 -3
  42. package/docs/SPEC.md +14 -10
  43. package/editor/vscode.html-custom-data.json +14 -4
  44. package/editor/web-types.json +46 -8
  45. package/package.json +3 -1
@@ -1,6 +1,6 @@
1
- import { ScaleController as e, onScaleChange as t, snapSys as n, sysLength as r, toSysExact as i } from "./scale.js";
2
- import a from "./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
3
- import { emit as o } from "./events.js";
1
+ import { emit as e } from "./events.js";
2
+ import { ScaleController as t, onScaleChange as n, snapSys as r, sysLength as i, toSysExact as a } from "./scale.js";
3
+ import o from "./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
4
4
  import { LitElement as s, css as c } from "lit";
5
5
  import { property as l, query as u } from "lit/decorators.js";
6
6
  var d = 24, f = c`
@@ -43,13 +43,13 @@ var d = 24, f = c`
43
43
  cursor: var(--vf-cursor, default);
44
44
  }
45
45
  `, p = class extends s {
46
- constructor(...t) {
47
- super(...t), this.scale = new e(this), this.open = !1, this.#e = !1, this.#n = null, this.#r = null, this.#l = () => this.settle();
46
+ constructor(...e) {
47
+ super(...e), this.scale = new t(this), this.open = !1, this.#e = !1, this.#n = null, this.#r = null, this.#l = () => this.settle();
48
48
  }
49
49
  get dialogSize() {
50
50
  return {
51
- width: r(this.width ?? 260),
52
- height: this.height === void 0 ? null : r(this.height)
51
+ width: i(this.width ?? 260),
52
+ height: this.height === void 0 ? null : i(this.height)
53
53
  };
54
54
  }
55
55
  #e;
@@ -75,12 +75,12 @@ var d = 24, f = c`
75
75
  #i() {
76
76
  let e = document.documentElement;
77
77
  return {
78
- width: i(e.clientWidth || window.innerWidth, this),
79
- height: i(e.clientHeight || window.innerHeight, this)
78
+ width: a(e.clientWidth || window.innerWidth, this),
79
+ height: a(e.clientHeight || window.innerHeight, this)
80
80
  };
81
81
  }
82
82
  #a(e, t) {
83
- let n = this.#i(), r = i(this._dialog?.offsetWidth ?? 0, this);
83
+ let n = this.#i(), r = a(this._dialog?.offsetWidth ?? 0, this);
84
84
  return {
85
85
  x: Math.min(Math.max(e, d - r), n.width - d),
86
86
  y: Math.min(Math.max(t, 0), Math.max(0, n.height - d))
@@ -89,13 +89,13 @@ var d = 24, f = c`
89
89
  #o() {
90
90
  let e = this.#i(), t = this._dialog?.getBoundingClientRect();
91
91
  return {
92
- x: (e.width - i(t?.width ?? 0, this)) / 2,
93
- y: (e.height - i(t?.height ?? 0, this)) / 2
92
+ x: (e.width - a(t?.width ?? 0, this)) / 2,
93
+ y: (e.height - a(t?.height ?? 0, this)) / 2
94
94
  };
95
95
  }
96
96
  placeAt(e, t) {
97
- let r = this.#a(e, t);
98
- this.left = n(r.x, this), this.top = n(r.y, this);
97
+ let n = this.#a(e, t);
98
+ this.left = r(n.x, this), this.top = r(n.y, this);
99
99
  }
100
100
  settle() {
101
101
  let e = this._dialog;
@@ -103,8 +103,8 @@ var d = 24, f = c`
103
103
  let t = this.left != null || this.top != null ? {
104
104
  x: this.left ?? 0,
105
105
  y: this.top ?? 0
106
- } : this.#o(), i = this.#a(t.x, t.y);
107
- e.style.left = r(n(i.x, this)), e.style.top = r(n(i.y, this)), e.style.right = "auto", e.style.bottom = "auto", e.style.margin = "0";
106
+ } : this.#o(), n = this.#a(t.x, t.y);
107
+ e.style.left = i(r(n.x, this)), e.style.top = i(r(n.y, this)), e.style.right = "auto", e.style.bottom = "auto", e.style.margin = "0";
108
108
  }
109
109
  #s() {
110
110
  let e = this._dialog?.style;
@@ -124,7 +124,7 @@ var d = 24, f = c`
124
124
  #u;
125
125
  #d;
126
126
  #f(e) {
127
- typeof ResizeObserver < "u" && !this.#u && (this.#u = new ResizeObserver(this.#l), this.#u.observe(e)), window.addEventListener("resize", this.#l), this.#d ??= t(this.#l);
127
+ typeof ResizeObserver < "u" && !this.#u && (this.#u = new ResizeObserver(this.#l), this.#u.observe(e)), window.addEventListener("resize", this.#l), this.#d ??= n(this.#l);
128
128
  }
129
129
  #p() {
130
130
  this.#u?.disconnect(), this.#u = void 0, window.removeEventListener("resize", this.#l), this.#d?.(), this.#d = void 0;
@@ -143,13 +143,13 @@ var d = 24, f = c`
143
143
  this.#n = "escape";
144
144
  }
145
145
  _onNativeClose() {
146
- let e = this.#n ?? "close";
147
- this.#n = null, this.#r = null, this.#p(), this.open = !1, this.#s(), o(this, "vf-close", { reason: e });
146
+ let t = this.#n ?? "close";
147
+ this.#n = null, this.#r = null, this.#p(), this.open = !1, this.#s(), e(this, "vf-close", { reason: t });
148
148
  }
149
149
  };
150
- a([l({
150
+ o([l({
151
151
  type: Boolean,
152
152
  reflect: !0
153
- })], p.prototype, "open", void 0), a([l({ type: Number })], p.prototype, "width", void 0), a([l({ type: Number })], p.prototype, "height", void 0), a([l({ type: Number })], p.prototype, "top", void 0), a([l({ type: Number })], p.prototype, "left", void 0), a([u("dialog")], p.prototype, "_dialog", void 0);
153
+ })], p.prototype, "open", void 0), o([l({ type: Number })], p.prototype, "width", void 0), o([l({ type: Number })], p.prototype, "height", void 0), o([l({ type: Number })], p.prototype, "top", void 0), o([l({ type: Number })], p.prototype, "left", void 0), o([u("dialog")], p.prototype, "_dialog", void 0);
154
154
  //#endregion
155
155
  export { p as VfModalDialog, f as modalDialogStyles };
@@ -0,0 +1,91 @@
1
+ import { type CSSResult, type ReactiveController, type ReactiveControllerHost } from 'lit';
2
+ import { type Pattern } from './patterns.js';
3
+ /** What a component hands the controller. */
4
+ export interface PatternFillOptions {
5
+ /** The element whose background the pattern paints — a component's own
6
+ * snapped box, so the fill rides the grid-snap correction with it. */
7
+ getBox: () => HTMLElement | null | undefined;
8
+ /** The resolved pattern, or null to paint nothing ({@link parsePattern}
9
+ * turns an attribute value into one). */
10
+ getPattern: () => Pattern | null | undefined;
11
+ /**
12
+ * The box's declared size in system px, per axis. A declared axis is used
13
+ * as it is — exact, and free of any measurement; an axis left `null` or
14
+ * `undefined` is measured off the box instead (a `ResizeObserver`,
15
+ * `toSys`-rounded). Omit the whole option to measure both.
16
+ */
17
+ getSize?: () => {
18
+ width?: number | null;
19
+ height?: number | null;
20
+ } | null | undefined;
21
+ }
22
+ /**
23
+ * Paints a {@link Pattern} as a box's own background — the fill behind
24
+ * `vf-container pattern="…"` and `vf-desktop pattern="…"`.
25
+ *
26
+ * It is the exact-fill idiom (src/tile-grid.ts) in its background form: the
27
+ * motif is encoded as one raster at one image px per system px
28
+ * (`tileRaster`, cached by pattern and size), sized in system px to the box
29
+ * ceiled up to whole pattern cells plus one cell of overdraw, and stretched
30
+ * onto that stated size under `image-rendering: pixelated`. Nearest-neighbor
31
+ * sampling can only emit source colors and one image has no interior seams,
32
+ * so the fill is 1-bit at every density and zoom — the same argument the
33
+ * desktop dither rests on. A `background-size` stated in system px is one
34
+ * stored length quantized once over the whole image (≤ 1/128 CSS px), not a
35
+ * repeat, and the box's background painting area crops the overdraw.
36
+ *
37
+ * A background rather than a child element, on purpose: it paints below all
38
+ * content by definition — no `isolation: isolate` + `z-index: -1`, so a
39
+ * patterned box never becomes a stacking context that traps a slotted
40
+ * control's drop-open panel under a later sibling — and it clips itself, so
41
+ * the box keeps `overflow: visible` (a container's outgrowing content
42
+ * overflows by contract). The one thing a background costs is that
43
+ * `image-rendering` is an inherited property: {@link vfPatternFill} sets it
44
+ * on the box only while patterned and hands `auto` back to the slot, so
45
+ * slotted content renders as it did.
46
+ *
47
+ * What it writes, all on the box's inline style and removed together the
48
+ * moment there is nothing to paint: `--_vf-pattern-image` (the raster) and
49
+ * `--_vf-pattern-size` (two live `sysLength`s). Custom-property channels,
50
+ * not `background-image` itself, so the recipe's forced-colors rule can
51
+ * out-cascade them without `!important` — an inline `background-image`
52
+ * could not be overridden from a stylesheet. The paper is the recipe's, a
53
+ * token: `var(--vf-white)` behind ink that is literal black, like every kit
54
+ * raster. Density and zoom re-encode nothing: the raster is keyed on system
55
+ * px, and the stated lengths are live against `--vf-scale`.
56
+ *
57
+ * Phase is anchored at the box's top-left — where the desktop, trough and
58
+ * swatch anchor theirs. Two same-pattern boxes meeting at an offset that is
59
+ * not a multiple of 8 show a phase seam; a port-anchored phase would be a
60
+ * contained follow-up, not a change here.
61
+ */
62
+ export declare class PatternFillController implements ReactiveController {
63
+ #private;
64
+ constructor(host: ReactiveControllerHost & HTMLElement, opts: PatternFillOptions);
65
+ /** The raster's current box in system px (overdraw included), or null
66
+ * while nothing is painted. */
67
+ get size(): {
68
+ width: number;
69
+ height: number;
70
+ } | null;
71
+ hostUpdated(): void;
72
+ }
73
+ /**
74
+ * The rules behind {@link PatternFillController}, for the box it paints:
75
+ * give the box the `vf-pattern-fill` class always, and `vf-patterned` while
76
+ * a pattern is resolved (the component's template knows).
77
+ *
78
+ * Painting: the raster the controller wrote, at the size it stated, once
79
+ * (`no-repeat` — the overdraw is the repeat), nearest-neighbor. The paper
80
+ * and the pixelation ride the `vf-patterned` state so an unpatterned box
81
+ * paints exactly nothing and inherits nothing new. `image-rendering`
82
+ * inherits, so the slot hands `auto` back to slotted content: a consumer's
83
+ * own `<img>` renders as it would anywhere else, and a kit element that
84
+ * wants nearest-neighbor (`vf-img`, a `vf-icon`) says so itself. A page
85
+ * that set `pixelated` on an ancestor gets `auto` inside a patterned box —
86
+ * its own declaration on the child still wins, as everywhere.
87
+ *
88
+ * Forced colors: the image goes, the paper is the remapped Canvas — the
89
+ * desktop's posture, a backdrop being decoration.
90
+ */
91
+ export declare const vfPatternFill: CSSResult;
@@ -0,0 +1,74 @@
1
+ import { sysLength as e, toSys as t } from "./scale.js";
2
+ import { patternHex as n, patternMotif as r } from "./patterns.js";
3
+ import { TileRasterCache as i } from "./tile-grid.js";
4
+ import { TrackWidthController as a } from "./track-width.js";
5
+ import { css as o } from "lit";
6
+ //#region src/pattern-fill.ts
7
+ var s = class {
8
+ #e;
9
+ #t;
10
+ #n = new i();
11
+ #r;
12
+ #i = null;
13
+ #a = "";
14
+ #o = "";
15
+ #s = null;
16
+ constructor(e, t) {
17
+ this.#e = e, this.#t = t, this.#r = new a(e, () => {
18
+ if (!t.getPattern()) return null;
19
+ let e = t.getSize?.();
20
+ return e == null || e.width == null || e.height == null ? t.getBox() : null;
21
+ }), e.addController(this);
22
+ }
23
+ get size() {
24
+ return this.#s;
25
+ }
26
+ hostUpdated() {
27
+ this.#c();
28
+ }
29
+ #c() {
30
+ let t = this.#t.getBox() ?? null;
31
+ t !== this.#i && (this.#u(), this.#i = t);
32
+ let i = this.#t.getPattern() ?? null;
33
+ if (!t || !i) {
34
+ this.#u();
35
+ return;
36
+ }
37
+ let a = this.#t.getSize?.() ?? null, o = c(a?.width) ?? this.#l(this.#r.width), s = c(a?.height) ?? this.#l(this.#r.height);
38
+ if (!o || !s) {
39
+ this.#u();
40
+ return;
41
+ }
42
+ let l = (e) => Math.ceil((e + 8) / 8) * 8, u = l(o), d = l(s), f = r(i), p = this.#n.for(f.width, f.height, f.rects, u, d, n(i)), m = `${e(u)} ${e(d)}`;
43
+ this.#s = {
44
+ width: u,
45
+ height: d
46
+ }, (p !== this.#a || m !== this.#o) && (this.#a = p, this.#o = m, t.style.setProperty("--_vf-pattern-image", p), t.style.setProperty("--_vf-pattern-size", m));
47
+ }
48
+ #l(e) {
49
+ return e > 0 ? t(e, this.#e) : 0;
50
+ }
51
+ #u() {
52
+ this.#s = null, !(!this.#a && !this.#o) && (this.#a = "", this.#o = "", this.#i?.style.removeProperty("--_vf-pattern-image"), this.#i?.style.removeProperty("--_vf-pattern-size"));
53
+ }
54
+ }, c = (e) => typeof e == "number" && Number.isFinite(e) && e > 0 ? Math.trunc(e) : null, l = o`
55
+ .vf-pattern-fill {
56
+ background-image: var(--_vf-pattern-image, none);
57
+ background-size: var(--_vf-pattern-size, auto);
58
+ background-repeat: no-repeat;
59
+ }
60
+ .vf-pattern-fill.vf-patterned {
61
+ background-color: var(--vf-white, #fff);
62
+ image-rendering: pixelated;
63
+ }
64
+ .vf-pattern-fill.vf-patterned > slot {
65
+ image-rendering: auto;
66
+ }
67
+ @media (forced-colors: active) {
68
+ .vf-pattern-fill {
69
+ background-image: none;
70
+ }
71
+ }
72
+ `;
73
+ //#endregion
74
+ export { s as PatternFillController, l as vfPatternFill };
@@ -0,0 +1,80 @@
1
+ import type { TileRect } from './styles/recipes/tile.js';
2
+ /**
3
+ * The standard Macintosh pattern set — the 38 fills of MacPaint's pattern
4
+ * bar and the System file's `PAT#` 0 — as 8-byte bitmaps, with the helpers
5
+ * that turn one into the art the kit's exact-fill machinery draws
6
+ * (src/pattern-fill.ts paints one as a box's background; src/tile-grid.ts
7
+ * is the raster mechanism under it).
8
+ *
9
+ * A {@link Pattern} is the QuickDraw type: eight rows, top row first, each a
10
+ * byte whose bit 7 is the leftmost pixel and whose set bits are ink. That is
11
+ * also what a `pattern` attribute spells as a custom value — sixteen hex
12
+ * digits, the way a PAT resource read in ResEdit — so the library, the
13
+ * attribute and the data model are one representation.
14
+ *
15
+ * Transcribed 2026-08-22 from a 2× capture of the tear-off pattern palette
16
+ * (a windoid over a 5×8 grid of 19×16 cells on a 20×17 pitch, the first two
17
+ * merged into the current-pattern well; native pixel (x, y) at image
18
+ * (2x+1, 2y+1), cell (c, r) at (5 + 20c, 15 + 17r)) and normalized to the
19
+ * palette window's port origin (5, 4) — the phase QuickDraw drew them at.
20
+ * Under it the three toolbox constants come out byte-for-byte (`gray` AA55,
21
+ * `ltGray` 8822, `dkGray` DD77), and so do the kit's own desktop dither,
22
+ * scroll trough and windoid dots, which are those three patterns. Designed
23
+ * at Apple for MacPaint and the System; stated here as data, the way
24
+ * `fonts/VF-*.glyphs.txt` states the strikes. The capture is not in the
25
+ * repo. docs/PATTERNS.md is the consumer table.
26
+ */
27
+ export type Pattern = readonly [
28
+ number,
29
+ number,
30
+ number,
31
+ number,
32
+ number,
33
+ number,
34
+ number,
35
+ number
36
+ ];
37
+ /** The library's names, in palette order. */
38
+ export type PatternName = 'black' | 'gray-50' | 'white' | 'gray-88' | 'gray-75' | 'gray-75-striped' | 'gray-75-diagonal' | 'dots' | 'pinpoint' | 'dots-sparse' | 'speckle' | 'gray-12' | 'gray-25' | 'stripes-vertical' | 'stripes-horizontal' | 'diagonal' | 'grid' | 'weave' | 'lines-vertical' | 'lines-horizontal' | 'diagonal-wide' | 'grid-wide' | 'zigzag' | 'arrows' | 'chevrons' | 'shingles' | 'dotted-grid' | 'birds' | 'waves' | 'bricks' | 'scales' | 'basket-weave' | 'lattice' | 'twigs' | 'hexagons' | 'pyramids' | 'mesh' | 'stones';
39
+ /**
40
+ * The 38 standard patterns, in palette order (row-major from the cell after
41
+ * the well — insertion order is the order). The ordered-dither ramp is named
42
+ * `gray-N` by ink percentage; everything else for what it reads as.
43
+ */
44
+ export declare const PATTERNS: Readonly<Record<PatternName, Pattern>>;
45
+ /** The names in palette order — a chooser's iteration order. */
46
+ export declare const PATTERN_NAMES: readonly PatternName[];
47
+ /** A pattern's side in system px — what a custom literal states. */
48
+ export declare const PATTERN_SIZE = 8;
49
+ /**
50
+ * The value a `pattern` attribute takes, resolved: a library name, or
51
+ * sixteen hex digits (two per row, top row first; whitespace between bytes
52
+ * and either case allowed — `"DD 77 DD 77 DD 77 DD 77"`). Anything else,
53
+ * including an empty string, is `null`: the caller paints nothing.
54
+ */
55
+ export declare function parsePattern(value: string | null | undefined): Pattern | null;
56
+ /** The attribute form of a pattern: sixteen upper-case hex digits, no spaces. */
57
+ export declare function patternHex(pattern: Pattern): string;
58
+ /**
59
+ * A pattern's art as the rect data the tile machinery takes
60
+ * (`tileRaster`, `tileImage` via `tileRects`): `width × height` is the cell
61
+ * the rects cover and tile at.
62
+ */
63
+ export interface PatternMotif {
64
+ readonly width: number;
65
+ readonly height: number;
66
+ readonly rects: readonly TileRect[];
67
+ }
68
+ /**
69
+ * A pattern's art on its **minimal** cell — the smallest period along each
70
+ * axis — as ink runs over a transparent ground, or over an opaque `paper`
71
+ * rect when one is given. This is how the kit's own surfaces state their
72
+ * dithers from the library: `gray-50` comes back as the 2×2 cell with two
73
+ * black pixels the desktop always had.
74
+ */
75
+ export declare function patternMotif(pattern: Pattern, ink?: string, paper?: string): PatternMotif;
76
+ /**
77
+ * A pattern's art on the full 8×8 cell, as {@link patternMotif}'s rects —
78
+ * for a caller that wants the literal QuickDraw cell regardless of period.
79
+ */
80
+ export declare function patternRects(pattern: Pattern, ink?: string, paper?: string): TileRect[];