vintage-frames 0.15.0 → 0.16.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 (47) hide show
  1. package/custom-elements.json +681 -65
  2. package/dist/chrome.d.ts +10 -1
  3. package/dist/chrome.js +8 -3
  4. package/dist/components/vf-desktop.d.ts +5 -2
  5. package/dist/components/vf-desktop.js +5 -5
  6. package/dist/components/vf-dialog.d.ts +7 -2
  7. package/dist/components/vf-dialog.js +27 -27
  8. package/dist/components/vf-menu-bar.js +9 -10
  9. package/dist/components/vf-menu-item.js +18 -13
  10. package/dist/components/vf-menu.d.ts +8 -3
  11. package/dist/components/vf-menu.js +85 -61
  12. package/dist/components/vf-number-field.d.ts +4 -0
  13. package/dist/components/vf-number-field.js +28 -25
  14. package/dist/components/vf-paragraph.d.ts +3 -1
  15. package/dist/components/vf-paragraph.js +7 -0
  16. package/dist/components/vf-scroll-area.d.ts +16 -1
  17. package/dist/components/vf-scroll-area.js +39 -29
  18. package/dist/components/vf-select.d.ts +5 -4
  19. package/dist/components/vf-select.js +26 -17
  20. package/dist/components/vf-text-field.d.ts +5 -5
  21. package/dist/components/vf-text-field.js +1 -1
  22. package/dist/components/vf-window.d.ts +22 -5
  23. package/dist/components/vf-window.js +113 -98
  24. package/dist/cursor.d.ts +8 -0
  25. package/dist/cursor.js +70 -73
  26. package/dist/index.d.ts +2 -1
  27. package/dist/modal-dialog.d.ts +20 -2
  28. package/dist/modal-dialog.js +63 -51
  29. package/dist/shell/catalog.d.ts +1 -1
  30. package/dist/shell/catalog.js +32 -18
  31. package/dist/shell/finder.d.ts +6 -1
  32. package/dist/shell/finder.js +182 -176
  33. package/dist/shell/index.d.ts +1 -1
  34. package/dist/shell/pure.d.ts +10 -0
  35. package/dist/shell/pure.js +4 -0
  36. package/dist/shell/shell.d.ts +16 -3
  37. package/dist/shell/shell.js +141 -126
  38. package/dist/shell/windows.d.ts +48 -10
  39. package/dist/shell/windows.js +142 -85
  40. package/dist/text-control.d.ts +24 -1
  41. package/dist/text-control.js +25 -4
  42. package/dist/top-layer.d.ts +13 -0
  43. package/dist/top-layer.js +16 -0
  44. package/docs/SPEC.md +23 -22
  45. package/editor/vscode.html-custom-data.json +21 -11
  46. package/editor/web-types.json +51 -23
  47. package/package.json +5 -1
@@ -18,17 +18,18 @@ import { FocusRuleController as C } from "../focus-modality.js";
18
18
  import { VfOption as w } from "./vf-option.js";
19
19
  import { clampScroll as T, ensureVisibleScroll as E, firstPickableRow as D, lastPickableRow as O, layoutClampedPopup as k } from "../popup-overflow.js";
20
20
  import { TypeAheadBuffer as A } from "../type-ahead.js";
21
- import { css as j, html as M, nothing as N } from "lit";
22
- import { property as P, query as F, queryAssignedElements as I, state as L } from "lit/decorators.js";
23
- import { classMap as R } from "lit/directives/class-map.js";
21
+ import { hideFromTopLayer as j, showInTopLayer as M } from "../top-layer.js";
22
+ import { css as N, html as P, nothing as F } from "lit";
23
+ import { property as I, query as L, queryAssignedElements as R, state as z } from "lit/decorators.js";
24
+ import { classMap as B } from "lit/directives/class-map.js";
24
25
  //#region src/components/vf-select.ts
25
- var z = class extends o(S) {
26
+ var V = class extends o(S) {
26
27
  static {
27
28
  this.styles = [
28
29
  l,
29
30
  c,
30
31
  f,
31
- j`
32
+ N`
32
33
  :host {
33
34
  /* A popup menu is sized to its widest option — never stretched to fill
34
35
  its container. fit-content holds that intrinsic width even inside a
@@ -201,7 +202,14 @@ var z = class extends o(S) {
201
202
  display: none;
202
203
  position: fixed;
203
204
  z-index: 10000;
205
+ /* A manual popover, shown in the top layer while open: the UA's
206
+ popover box (inset 0, auto margins, overflow, system colors) is
207
+ reset to the fixed panel positionPanel places. The recipe states
208
+ the border, padding and background. */
209
+ inset: auto;
204
210
  margin: 0;
211
+ overflow: visible;
212
+ color: inherit;
205
213
  /* Match the closed pill's 1px hard shadow — the shared .vf-panel recipe
206
214
  defaults to the 2px menu shadow, which would overhang the pill's shadow
207
215
  by 1px on the right and bottom. */
@@ -492,7 +500,7 @@ var z = class extends o(S) {
492
500
  }
493
501
  async openPanel() {
494
502
  if (this.open || this.isDisabled) return;
495
- this.open = !0, this.panelListeners.attach(), await this.updateComplete;
503
+ this.open = !0, this.panelListeners.attach(), await this.updateComplete, this.open && this.panelEl && M(this.panelEl);
496
504
  let e = this.optionItems.findIndex((e) => e.selected && !e.disabled);
497
505
  e === -1 && (e = this.firstEnabledIndex()), this.positionPanel(Math.max(e, 0)), e !== -1 && this.setActive(e);
498
506
  }
@@ -510,7 +518,7 @@ var z = class extends o(S) {
510
518
  n.style.minWidth = `${r.width}px`, n.style.top = `${l.panelTop}px`, n.style.left = `${u}px`, n.style.height = `${l.panelHeight}px`, this.rowHeight = s, this.visibleSlots = l.visibleSlots, this.rowScroll = l.initialScroll, this.applyScroll();
511
519
  }
512
520
  closePanel(e) {
513
- this.open && (this.open = !1, this.cancelBlink(), this.stopArrowScroll(), this.endPress(), this.clearActive(), this.typeAhead.reset(), this.panelListeners.detach(), e && this.controlEl?.focus());
521
+ this.open && (this.open = !1, this.panelEl && j(this.panelEl), this.cancelBlink(), this.stopArrowScroll(), this.endPress(), this.clearActive(), this.typeAhead.reset(), this.panelListeners.detach(), e && this.controlEl?.focus());
514
522
  }
515
523
  applyScroll() {
516
524
  let e = this.rowScroll * this.rowHeight;
@@ -647,9 +655,9 @@ var z = class extends o(S) {
647
655
  }
648
656
  render() {
649
657
  let e = this.optionItems.find((e) => this.optionValue(e) === this.value), t = e ? (e.textContent ?? "").trim() : "", n = this.isDisabled, r = this.size === "small", i = r ? v : _, a = r ? b : y;
650
- return M`
658
+ return P`
651
659
  <div
652
- class=${R({
660
+ class=${B({
653
661
  control: !0,
654
662
  "vf-snap": !0,
655
663
  "vf-focus-rule": this.focusRule.marked && !this.open,
@@ -661,16 +669,16 @@ var z = class extends o(S) {
661
669
  aria-expanded=${this.open ? "true" : "false"}
662
670
  aria-disabled=${n ? "true" : "false"}
663
671
  aria-controls="listbox"
664
- aria-label=${this.label || this.hostLabel || N}
672
+ aria-label=${this.label || this.hostLabel || F}
665
673
  aria-describedby=${this.describedBy}
666
- aria-required=${this.required ? "true" : N}
667
- aria-invalid=${this.validity.valid ? N : "true"}
674
+ aria-required=${this.required ? "true" : F}
675
+ aria-invalid=${this.validity.valid ? F : "true"}
668
676
  tabindex=${n ? "-1" : "0"}
669
677
  >
670
678
  <span class="label" part="label">
671
679
  <span class="value">${t}</span>
672
680
  <span class="sizer" aria-hidden="true">
673
- ${this.optionItems.map((e) => M`<span>${(e.textContent ?? "").trim()}</span>`)}
681
+ ${this.optionItems.map((e) => P`<span>${(e.textContent ?? "").trim()}</span>`)}
674
682
  </span>
675
683
  </span>
676
684
  <span class="arrow" part="arrow" aria-hidden="true"
@@ -681,8 +689,9 @@ var z = class extends o(S) {
681
689
  id="listbox"
682
690
  class="panel vf-panel ${this.open ? "open" : ""}"
683
691
  part="panel"
692
+ popover="manual"
684
693
  role="listbox"
685
- aria-label=${this.label || this.hostLabel || N}
694
+ aria-label=${this.label || this.hostLabel || F}
686
695
  aria-hidden=${this.open ? "false" : "true"}
687
696
  >
688
697
  <!-- role="presentation" on the clip and the strip that carries the
@@ -723,10 +732,10 @@ var z = class extends o(S) {
723
732
  `;
724
733
  }
725
734
  };
726
- a([P()], z.prototype, "value", void 0), a([P({ reflect: !0 })], z.prototype, "name", void 0), a([P({ reflect: !0 })], z.prototype, "size", void 0), a([P({
735
+ a([I()], V.prototype, "value", void 0), a([I({ reflect: !0 })], V.prototype, "name", void 0), a([I({ reflect: !0 })], V.prototype, "size", void 0), a([I({
727
736
  type: Boolean,
728
737
  reflect: !0,
729
738
  attribute: "no-shadow"
730
- })], z.prototype, "noShadow", void 0), a([P()], z.prototype, "label", void 0), a([L()], z.prototype, "open", void 0), a([F(".control")], z.prototype, "controlEl", void 0), a([F(".panel")], z.prototype, "panelEl", void 0), a([F(".rows")], z.prototype, "rowsEl", void 0), a([F(".arrow-slot.up")], z.prototype, "upArrowEl", void 0), a([F(".arrow-slot.down")], z.prototype, "downArrowEl", void 0), a([I({ selector: "vf-option" })], z.prototype, "assignedOptions", void 0), a([L()], z.prototype, "cachedOptions", void 0), z = a([n("vf-select")], z);
739
+ })], V.prototype, "noShadow", void 0), a([I()], V.prototype, "label", void 0), a([z()], V.prototype, "open", void 0), a([L(".control")], V.prototype, "controlEl", void 0), a([L(".panel")], V.prototype, "panelEl", void 0), a([L(".rows")], V.prototype, "rowsEl", void 0), a([L(".arrow-slot.up")], V.prototype, "upArrowEl", void 0), a([L(".arrow-slot.down")], V.prototype, "downArrowEl", void 0), a([R({ selector: "vf-option" })], V.prototype, "assignedOptions", void 0), a([z()], V.prototype, "cachedOptions", void 0), V = a([n("vf-select")], V);
731
740
  //#endregion
732
- export { z as VfSelect };
741
+ export { V as VfSelect };
@@ -11,7 +11,8 @@ declare const VfTextField_base: (new (...args: any[]) => import("../position.js"
11
11
  * {@link VfTextControlBase}.
12
12
  *
13
13
  * @fires vf-input - On every keystroke. `detail: { value: string }`.
14
- * @fires vf-change - On commit (native `change`). `detail: { value: string }`.
14
+ * @fires vf-change - On commit: the native `change`, leaving the field, or
15
+ * Return in a form, before its submission. `detail: { value: string }`.
15
16
  * @fires input - The native keystroke event: the inner input's own, composed,
16
17
  * so it crosses the shadow boundary and retargets to the host by itself.
17
18
  * @fires change - The native commit event, re-dispatched from the host (the
@@ -38,10 +39,9 @@ export declare class VfTextField extends VfTextField_base {
38
39
  /** The shared forwarded set plus `pattern`, which only an `<input>` takes. */
39
40
  protected static readonly forwardedAttributes: readonly string[];
40
41
  /**
41
- * Enter in a single-line field triggers the associated form's implicit
42
- * submission. The native `<input>` is shadow-encapsulated, so its form owner
43
- * is null and the browser won't do this itself. A press the form took is
44
- * cancelled, so an enclosing modal does not route it again.
42
+ * Enter in a single-line field commits, then triggers the associated
43
+ * form's implicit submission. The native `<input>` is shadow-encapsulated,
44
+ * so its form owner is null and the browser won't do this itself.
45
45
  */
46
46
  private handleKeydown;
47
47
  protected render(): import("lit-html").TemplateResult<1>;
@@ -43,7 +43,7 @@ var f = class extends n(o) {
43
43
  this.forwardedAttributes = [...o.forwardedAttributes, "pattern"];
44
44
  }
45
45
  handleKeydown(e) {
46
- this.isSubmitEnter(e) && this.requestImplicitSubmit() && e.preventDefault();
46
+ this.submitOnEnter(e);
47
47
  }
48
48
  render() {
49
49
  return c`
@@ -37,7 +37,7 @@ declare const VfWindow_base: (new (...args: any[]) => import("../size.js").VfSiz
37
37
  * HIG compliance, it doesn't enforce it (see docs/LAYOUT.md "Window archetypes"):
38
38
  * the full document window is `closable zoomable movable resizable
39
39
  * scrollbars="both"`, a modeless dialog box is `closable movable`, a utility
40
- * window is `variant="utility" movable`. Place inside `<vf-desktop>` to get
40
+ * window is `variant="utility" closable movable`. Place inside `<vf-desktop>` to get
41
41
  * click-to-front stacking and automatic `active` management (utility windows
42
42
  * float above the document tier).
43
43
  *
@@ -105,9 +105,11 @@ declare const VfWindow_base: (new (...args: any[]) => import("../size.js").VfSiz
105
105
  * @csspart grow-box - The resize widget (bottom-right, when `resizable`).
106
106
  * @csspart viewport - The built-in scroll area's viewport (when `scrollbars`;
107
107
  * re-exported from vf-scroll-area).
108
- * @fires vf-close - Close box clicked. Detail `{ reason: 'close' }` (shape-
109
- * compatible with vf-dialog's `vf-close`). The window does NOT
110
- * remove itself; the consumer decides what closing means.
108
+ * @fires vf-close - Close box clicked. Detail `{ reason: 'close', altKey,
109
+ * shiftKey, metaKey, ctrlKey }`, the modifier keys held on the click
110
+ * (shape-compatible with vf-dialog's `vf-close`), so an Option-click can
111
+ * mean more than a click. The window does NOT remove itself; the consumer
112
+ * decides what closing means.
111
113
  * @fires vf-zoom - Zoom box clicked. Detail `{}`.
112
114
  * @fires vf-resize - The grow box resized the window. Detail `{ width,
113
115
  * height, commit }`, sizes in whole system px: one event per size the drag
@@ -146,7 +148,11 @@ export declare class VfWindow extends VfWindow_base {
146
148
  * Managed automatically by an enclosing `vf-desktop`.
147
149
  */
148
150
  active: boolean;
149
- /** Show the close box (left side of the title bar). */
151
+ /**
152
+ * Show the close box (left side of the title bar). Off by default, like
153
+ * every other boolean attribute, so markup can leave it out; the document
154
+ * window and the modeless dialog box recipes say `closable`.
155
+ */
150
156
  closable: boolean;
151
157
  /** Show the zoom box (right side of the title bar). */
152
158
  zoomable: boolean;
@@ -224,6 +230,17 @@ export declare class VfWindow extends VfWindow_base {
224
230
  * the `header` slot is populated.
225
231
  */
226
232
  headerHeight?: number | null;
233
+ /**
234
+ * A 1-bit fill for the body, as on `vf-container`: a library pattern by
235
+ * name (docs/PATTERNS.md) or sixteen hex digits, under the content and
236
+ * anchored at the content region's corner. Under `scrollbars` it fills the
237
+ * scrolled plane and scrolls with it (`vf-scroll-area`'s `pattern`). Unset,
238
+ * the body stays white; an unrecognized value paints nothing and warns
239
+ * once.
240
+ */
241
+ pattern?: string | null;
242
+ /** `pattern`, resolved — what the body's fill paints; null paints nothing. */
243
+ private _pattern;
227
244
  /** Whether the `header` slot has assigned content (drives the header). */
228
245
  private _hasHeader;
229
246
  /** Whether the `status` slot has assigned content (drives the strip). */
@@ -5,17 +5,19 @@ import s from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js
5
5
  import { PlacementController as c, VfPositioned as l, placementIn as u, warnMovableContract as d } from "../position.js";
6
6
  import { vfBase as f } from "../styles/recipes/host.js";
7
7
  import { RepeatTileController as p, patternOverride as m, tileGrid as h, vfTileGrid as g } from "../tile-grid.js";
8
- import { DOTS as _, DOT_SPAN as v, vfDots as y, vfStripes as b } from "../styles/recipes/pattern.js";
9
- import { vfStrokeDecls as x, vfStrokeInset as S } from "../styles/recipes/stroke.js";
10
- import { vfChromeFrame as C } from "../styles/recipes/surface.js";
11
- import { vfRule as w } from "../styles/recipes/rule.js";
12
- import { vfTitleBar as T, vfWindowWidgets as E } from "../styles/recipes/title-bar.js";
13
- import { vfFocus as D } from "../styles/recipes/focus.js";
14
- import { GridSnapController as O } from "../grid-snap.js";
15
- import { DocumentListenersController as k } from "../document-listeners.js";
16
- import { VfSized as A } from "../size.js";
17
- import { DragController as j } from "../drag.js";
18
- import { TitleCenterController as M, chromeTitleBar as N, closeBox as P, widgetLabel as F, zoomBox as I } from "../chrome.js";
8
+ import { parsePattern as ee } from "../patterns.js";
9
+ import { DOTS as _, DOT_SPAN as v, vfDots as te, vfStripes as y } from "../styles/recipes/pattern.js";
10
+ import { vfStrokeDecls as b, vfStrokeInset as x } from "../styles/recipes/stroke.js";
11
+ import { vfChromeFrame as S } from "../styles/recipes/surface.js";
12
+ import { vfRule as C } from "../styles/recipes/rule.js";
13
+ import { vfTitleBar as w, vfWindowWidgets as T } from "../styles/recipes/title-bar.js";
14
+ import { vfFocus as E } from "../styles/recipes/focus.js";
15
+ import { PatternFillController as D, vfPatternFill as O } from "../pattern-fill.js";
16
+ import { GridSnapController as k } from "../grid-snap.js";
17
+ import { DocumentListenersController as A } from "../document-listeners.js";
18
+ import { VfSized as j } from "../size.js";
19
+ import { DragController as ne } from "../drag.js";
20
+ import { TitleCenterController as re, chromeTitleBar as M, closeBox as N, closeKeys as P, widgetLabel as F, zoomBox as I } from "../chrome.js";
19
21
  import { prefersReducedMotion as L, windowRectFraction as R } from "../motion.js";
20
22
  import { paintSelectionRect as z, penPhase as B, xorPenCanvas as V } from "../open-art.js";
21
23
  import "../window-chrome.js";
@@ -28,65 +30,69 @@ function Z(e, t, n) {
28
30
  let r = Math.max(t, e);
29
31
  return n == null ? r : Math.min(n, r);
30
32
  }
31
- var Q = 24, $ = class extends A(l(H)) {
33
+ var Q = 24, $ = class extends j(l(H)) {
32
34
  constructor(...e) {
33
- super(...e), this.heading = "", this.active = !0, this.closable = !0, this.zoomable = !1, this.movable = !1, this.outlineDrag = !1, this.resizable = !1, this._hasHeader = !1, this._hasStatus = !1, this.scale = new n(this), this.gridSnap = new O(this), this.titleCenter = new M(this), this._dotsPattern = "", this.#e = new p(this, {
35
+ super(...e), this.heading = "", this.active = !0, this.closable = !1, this.zoomable = !1, this.movable = !1, this.outlineDrag = !1, this.resizable = !1, this._pattern = null, this.#e = !1, this._hasHeader = !1, this._hasStatus = !1, this.scale = new n(this), this.gridSnap = new k(this), this.titleCenter = new re(this), this._dotsPattern = "", this.#t = new p(this, {
34
36
  getBox: () => this.renderRoot?.querySelector(".vf-dots"),
35
37
  getArt: () => this.variant === "utility" && !this._dotsPattern ? _ : null,
36
38
  property: "--_vf-dots-tile"
37
- }), this.#t = (e, t, n) => {
39
+ }), this.#n = new D(this, {
40
+ getBox: () => this.body,
41
+ getPattern: () => this.scrollbars ? null : this._pattern
42
+ }), this.#r = (e, t, n) => {
38
43
  let r = o(this.offsetWidth, this);
39
44
  return {
40
45
  x: Math.min(Math.max(e, Q - r), n.width - Q),
41
46
  y: Math.min(Math.max(t, n.top), Math.max(n.top, n.height - Q))
42
47
  };
43
- }, this._placement = new c(this, (e, t, n) => this.#t(e, t, n)), this._drag = new j(this, {
48
+ }, this._placement = new c(this, (e, t, n) => this.#r(e, t, n)), this._drag = new ne(this, {
44
49
  onDragStart: (e) => {
45
50
  if (!this.movable || e.button !== 0 || e.composedPath().some((e) => e instanceof HTMLElement && e.classList.contains("box"))) return null;
46
- this.#S();
51
+ this.#w();
47
52
  let t = this._placement.seed();
48
- return this.#h(t), t;
53
+ return this.#_(t), t;
49
54
  },
50
55
  onDrag: (e, t) => {
51
- this.#f ? this.#g(e, t) : this._placement.moveTo(e, t);
56
+ this.#m ? this.#v(e, t) : this._placement.moveTo(e, t);
52
57
  },
53
58
  onDragEnd: (e, t) => {
54
- this.#y(t);
59
+ this.#x(t);
55
60
  }
56
- }), this._resizeState = null, this.#n = this.attachInternals(), this.#r = null, this.#i = new k(this, () => [[
61
+ }), this._resizeState = null, this.#i = this.attachInternals(), this.#a = null, this.#o = new A(this, () => [[
57
62
  document,
58
63
  "pointerdown",
59
- this.#a,
64
+ this.#s,
60
65
  !0
61
66
  ], [
62
67
  document,
63
68
  "keydown",
64
- this.#o,
69
+ this.#c,
65
70
  !0
66
- ]]), this.#a = () => {
67
- this.#s("finish");
68
- }, this.#o = (e) => {
69
- e.key !== "Escape" || !this.#r || (e.preventDefault(), e.stopPropagation(), this.#s("finish"));
70
- }, this.#f = null, this.#p = new k(this, () => [[
71
+ ]]), this.#s = () => {
72
+ this.#l("finish");
73
+ }, this.#c = (e) => {
74
+ e.key !== "Escape" || !this.#a || (e.preventDefault(), e.stopPropagation(), this.#l("finish"));
75
+ }, this.#m = null, this.#h = new A(this, () => [[
71
76
  document,
72
77
  "keydown",
73
- this.#m,
78
+ this.#g,
74
79
  !0
75
- ]]), this.#m = (e) => {
76
- e.key !== "Escape" || !this.#f || (e.preventDefault(), e.stopPropagation(), this._drag.cancel());
77
- }, this.#b = !1, this.#x = !1;
80
+ ]]), this.#g = (e) => {
81
+ e.key !== "Escape" || !this.#m || (e.preventDefault(), e.stopPropagation(), this._drag.cancel());
82
+ }, this.#S = !1, this.#C = !1;
78
83
  }
79
84
  static {
80
85
  this.styles = [
81
86
  f,
82
- b,
83
87
  y,
88
+ te,
84
89
  g,
85
- D,
90
+ E,
91
+ S,
86
92
  C,
87
93
  w,
88
94
  T,
89
- E,
95
+ O,
90
96
  U`
91
97
  :host {
92
98
  display: block;
@@ -338,15 +344,15 @@ var Q = 24, $ = class extends A(l(H)) {
338
344
  lower-right one clear of its top/left stroke. */
339
345
  .grow {
340
346
  position: absolute;
341
- right: ${S(0)};
342
- bottom: ${S(0)};
347
+ right: ${x(0)};
348
+ bottom: ${x(0)};
343
349
  z-index: 1;
344
350
  width: calc(var(--vf-scale, 1) * 15px);
345
351
  height: calc(var(--vf-scale, 1) * 15px);
346
352
  background: var(--vf-white, #ffffff);
347
353
  touch-action: none;
348
354
  cursor: var(--vf-cursor, default);
349
- ${x({ edges: ["top", "left"] })}
355
+ ${b({ edges: ["top", "left"] })}
350
356
  }
351
357
  .grow::before {
352
358
  content: '';
@@ -355,17 +361,17 @@ var Q = 24, $ = class extends A(l(H)) {
355
361
  bottom: calc(var(--vf-scale, 1) * 1px);
356
362
  width: calc(var(--vf-scale, 1) * 9px);
357
363
  height: calc(var(--vf-scale, 1) * 9px);
358
- ${x()}
364
+ ${b()}
359
365
  }
360
366
  .grow::after {
361
367
  content: '';
362
368
  position: absolute;
363
- top: ${S(2)};
364
- left: ${S(2)};
369
+ top: ${x(2)};
370
+ left: ${x(2)};
365
371
  width: calc(var(--vf-scale, 1) * 7px);
366
372
  height: calc(var(--vf-scale, 1) * 7px);
367
373
  background: var(--vf-white, #ffffff);
368
- ${x()}
374
+ ${b()}
369
375
  }
370
376
  /* Inactive window: the size box empties with the scroll rails (the same
371
377
  HIG no-interactive-UX treatment ScrollStateController drives for the
@@ -386,8 +392,10 @@ var Q = 24, $ = class extends A(l(H)) {
386
392
  };
387
393
  }
388
394
  #e;
395
+ #t;
396
+ #n;
389
397
  willUpdate(e) {
390
- super.willUpdate(e), this.variant === "utility" && (this._dotsPattern = m(this, "--vf-dots-pattern"));
398
+ super.willUpdate(e), this.variant === "utility" && (this._dotsPattern = m(this, "--vf-dots-pattern")), e.has("pattern") && (this._pattern = ee(this.pattern), this._pattern === null && this.pattern?.trim() && !this.#e && (this.#e = !0, console.warn(`vf-window: unknown pattern "${this.pattern}" — a library name (docs/PATTERNS.md) or sixteen hex digits. Painting nothing.`)));
391
399
  }
392
400
  _dotsTexture() {
393
401
  if (!this._dotsPattern || this.width == null) return;
@@ -398,7 +406,7 @@ var Q = 24, $ = class extends A(l(H)) {
398
406
  tile: v
399
407
  });
400
408
  }
401
- #t;
409
+ #r;
402
410
  placementAt(e, t, n) {
403
411
  let r = this.scrollArea;
404
412
  return r ? r.placementAt(e, t, n) : u(this.body ?? this, e, t, this, n);
@@ -407,54 +415,54 @@ var Q = 24, $ = class extends A(l(H)) {
407
415
  this.scrollArea?.measure();
408
416
  }
409
417
  async show(e = {}) {
410
- this.#s("finish"), this.hidden = !1;
418
+ this.#l("finish"), this.hidden = !1;
411
419
  let t = e.from;
412
420
  if (!t || L()) return !1;
413
421
  let n = {
414
422
  closing: !1,
415
423
  animations: []
416
424
  };
417
- this.#r = n, this.#n.states?.add("opening"), this.#i.attach();
425
+ this.#a = n, this.#i.states?.add("opening"), this.#o.attach();
418
426
  do
419
427
  await this.updateComplete;
420
- while (this.isUpdatePending && this.#r === n);
421
- if (this.#r !== n) return !1;
422
- let r = this.isConnected ? this.#u(t, this.getBoundingClientRect(), !1) : null;
423
- return r ? (n.animations = r.animations, this.#l(n, r)) : (this.#c(), !1);
428
+ while (this.isUpdatePending && this.#a === n);
429
+ if (this.#a !== n) return !1;
430
+ let r = this.isConnected ? this.#f(t, this.getBoundingClientRect(), !1) : null;
431
+ return r ? (n.animations = r.animations, this.#d(n, r)) : (this.#u(), !1);
424
432
  }
425
433
  async hide(e = {}) {
426
- let t = this.#r?.closing === !1, n = !this.hidden && !t;
427
- this.#s(t ? "cancel" : "finish");
428
- let r = e.to, i = r && n && this.isConnected && !L() ? this.#u(r, this.getBoundingClientRect(), !0) : null;
434
+ let t = this.#a?.closing === !1, n = !this.hidden && !t;
435
+ this.#l(t ? "cancel" : "finish");
436
+ let r = e.to, i = r && n && this.isConnected && !L() ? this.#f(r, this.getBoundingClientRect(), !0) : null;
429
437
  if (this.hidden = !0, !i) return !1;
430
438
  let a = {
431
439
  closing: !0,
432
440
  animations: i.animations
433
441
  };
434
- return this.#r = a, this.#i.attach(), this.#l(a, i);
442
+ return this.#a = a, this.#o.attach(), this.#d(a, i);
435
443
  }
436
- #n;
437
- #r;
438
444
  #i;
439
445
  #a;
440
446
  #o;
441
- #s(e) {
442
- let t = this.#r;
447
+ #s;
448
+ #c;
449
+ #l(e) {
450
+ let t = this.#a;
443
451
  if (t) {
444
- this.#c();
452
+ this.#u();
445
453
  for (let n of t.animations) n[e]();
446
454
  }
447
455
  }
448
- #c() {
449
- this.#r = null, this.#n.states?.delete("opening"), this.#i.detach();
456
+ #u() {
457
+ this.#a = null, this.#i.states?.delete("opening"), this.#o.detach();
450
458
  }
451
- async #l(e, t) {
459
+ async #d(e, t) {
452
460
  let n = await Promise.all(t.animations.map((e) => e.finished)).then(() => !0, () => !1);
453
461
  for (let e of t.canvases) e.remove();
454
- return this.#r === e && this.#c(), n;
462
+ return this.#a === e && this.#u(), n;
455
463
  }
456
- #u(e, t, n) {
457
- let i = this.#d();
464
+ #f(e, t, n) {
465
+ let i = this.#p();
458
466
  if (!i) return null;
459
467
  let o = r(this), s = i.getBoundingClientRect(), c = (e) => ({
460
468
  left: Math.round((e.left - s.left) / o),
@@ -479,36 +487,36 @@ var Q = 24, $ = class extends A(l(H)) {
479
487
  let f = new Animation(new KeyframeEffect(null, null, 306));
480
488
  return f.play(), d.animations.push(f), d;
481
489
  }
482
- #d() {
490
+ #p() {
483
491
  let t = { surface: null };
484
492
  return e(this, "vf-drag-surface-request", t, { composed: !1 }), t.surface;
485
493
  }
486
- #f;
487
- #p;
488
494
  #m;
489
- #h(e) {
490
- if (this.#y(!0), !this.outlineDrag) return;
491
- let t = this.#v(this.getBoundingClientRect());
492
- t && (this.#f = {
495
+ #h;
496
+ #g;
497
+ #_(e) {
498
+ if (this.#x(!0), !this.outlineDrag) return;
499
+ let t = this.#b(this.getBoundingClientRect());
500
+ t && (this.#m = {
493
501
  origin: e,
494
502
  outline: t,
495
503
  landing: null
496
- }, this.#_(t, 0, 0), this.#p.attach());
504
+ }, this.#y(t, 0, 0), this.#h.attach());
497
505
  }
498
- #g(e, t) {
499
- let n = this.#f;
506
+ #v(e, t) {
507
+ let n = this.#m;
500
508
  if (!n) return;
501
509
  let r = this._placement.resolve(e, t);
502
- n.landing = r, this.#_(n.outline, r.x - n.origin.x, r.y - n.origin.y);
510
+ n.landing = r, this.#y(n.outline, r.x - n.origin.x, r.y - n.origin.y);
503
511
  }
504
- #_(e, t, n) {
512
+ #y(e, t, n) {
505
513
  let r = e.base.x + t, i = e.base.y + n;
506
514
  e.canvas.style.left = a(r), e.canvas.style.top = a(i);
507
515
  let o = B(r, i);
508
516
  o !== e.phase && (e.phase = o, z(e.canvas, e.width, e.height, o));
509
517
  }
510
- #v(e) {
511
- let t = this.#d();
518
+ #b(e) {
519
+ let t = this.#p();
512
520
  if (!t) return null;
513
521
  let n = r(this), i = t.getBoundingClientRect(), o = Math.max(1, Math.round(e.width / n)), s = Math.max(1, Math.round(e.height / n)), c = V("drag-outline"), l = c.style;
514
522
  return l.position = "absolute", l.width = a(o), l.height = a(s), t.append(c), {
@@ -522,12 +530,12 @@ var Q = 24, $ = class extends A(l(H)) {
522
530
  phase: -1
523
531
  };
524
532
  }
525
- #y(e) {
526
- let t = this.#f;
527
- this.#f = null, t && (this.#p.detach(), t.outline.canvas.remove(), !e && t.landing && this._placement.moveTo(t.landing.x, t.landing.y));
533
+ #x(e) {
534
+ let t = this.#m;
535
+ this.#m = null, t && (this.#h.detach(), t.outline.canvas.remove(), !e && t.landing && this._placement.moveTo(t.landing.x, t.landing.y));
528
536
  }
529
537
  updated() {
530
- this.#C();
538
+ this.#T();
531
539
  let t = this._resizeState;
532
540
  t?.emitPending && (t.emitPending = !1, e(this, "vf-resize", {
533
541
  width: this.width,
@@ -535,22 +543,22 @@ var Q = 24, $ = class extends A(l(H)) {
535
543
  commit: !1
536
544
  }));
537
545
  }
538
- #b;
539
- #x;
540
- #S() {
541
- this.#x ||= d(this, `vf-window${this.heading ? ` ("${this.heading}")` : ""}`, "<vf-window movable top=\"30\" left=\"40\" width=\"240\" height=\"176\">");
546
+ #S;
547
+ #C;
548
+ #w() {
549
+ this.#C ||= d(this, `vf-window${this.heading ? ` ("${this.heading}")` : ""}`, "<vf-window movable top=\"30\" left=\"40\" width=\"240\" height=\"176\">");
542
550
  }
543
- #C() {
544
- if (this.#b) return;
551
+ #T() {
552
+ if (this.#S) return;
545
553
  let e = [];
546
- this.width == null && this.style.width === "" && e.push("width (falling back to block layout)"), this.height == null && this.style.height === "" && e.push("height (falling back to the content)"), e.length !== 0 && (this.#b = !0, console.warn(`vf-window${this.heading ? ` ("${this.heading}")` : ""}: no ${e.join(", no ")}. A window owns its whole box — declare both in system px: <vf-window width="240" height="176">.`));
554
+ this.width == null && this.style.width === "" && e.push("width (falling back to block layout)"), this.height == null && this.style.height === "" && e.push("height (falling back to the content)"), e.length !== 0 && (this.#S = !0, console.warn(`vf-window${this.heading ? ` ("${this.heading}")` : ""}: no ${e.join(", no ")}. A window owns its whole box — declare both in system px: <vf-window width="240" height="176">.`));
547
555
  }
548
556
  connectedCallback() {
549
- super.connectedCallback(), this.#r && this.#i.attach();
557
+ super.connectedCallback(), this.#a && this.#o.attach();
550
558
  }
551
559
  disconnectedCallback() {
552
- super.disconnectedCallback(), this._resizeState = null, this.#f?.outline.canvas.remove(), this.#f = null, this.#r && !this.#r.closing && queueMicrotask(() => {
553
- !this.isConnected && this.#r?.closing === !1 && this.#s("cancel");
560
+ super.disconnectedCallback(), this._resizeState = null, this.#m?.outline.canvas.remove(), this.#m = null, this.#a && !this.#a.closing && queueMicrotask(() => {
561
+ !this.isConnected && this.#a?.closing === !1 && this.#l("cancel");
554
562
  });
555
563
  }
556
564
  _onStatusSlotChange(e) {
@@ -561,8 +569,11 @@ var Q = 24, $ = class extends A(l(H)) {
561
569
  let t = e.target;
562
570
  this._hasHeader = t.assignedElements().length > 0;
563
571
  }
564
- _onCloseClick() {
565
- e(this, "vf-close", { reason: "close" });
572
+ _onCloseClick(t) {
573
+ e(this, "vf-close", {
574
+ reason: "close",
575
+ ...P(t)
576
+ });
566
577
  }
567
578
  _onZoomClick() {
568
579
  e(this, "vf-zoom", {});
@@ -605,8 +616,8 @@ var Q = 24, $ = class extends A(l(H)) {
605
616
  role="group"
606
617
  aria-labelledby=${this.heading ? "title" : G}
607
618
  >
608
- ${N(this._drag, W`
609
- ${this.closable ? P(F("Close", this.heading), this._onCloseClick) : G}
619
+ ${M(this._drag, W`
620
+ ${this.closable ? N(F("Close", this.heading), this._onCloseClick) : G}
610
621
  <span class="vf-title" part="title" id="title">${this.heading}</span>
611
622
  ${this.zoomable ? I(F("Zoom", this.heading), this._onZoomClick) : G}
612
623
  `, this.variant === "utility" ? this._dotsPattern ? "vf-dots vf-dots-token" : "vf-dots" : "vf-stripes", this.variant === "utility" ? this._dotsTexture() : void 0)}
@@ -617,13 +628,17 @@ var Q = 24, $ = class extends A(l(H)) {
617
628
  >
618
629
  <slot name="header" @slotchange=${this._onHeaderSlotChange}></slot>
619
630
  </div>
620
- <div class="body" part="body">
631
+ <div
632
+ class="body vf-pattern-fill${this._pattern && !this.scrollbars ? " vf-patterned" : ""}"
633
+ part="body"
634
+ >
621
635
  ${this.scrollbars ? W`
622
636
  <vf-scroll-area
623
637
  class="edge-scroll"
624
638
  axis=${this.scrollbars}
625
639
  ?corner=${this.resizable && !this._hasStatus}
626
640
  label=${this.heading || G}
641
+ pattern=${this._pattern ? this.pattern : G}
627
642
  exportparts="viewport"
628
643
  >
629
644
  <slot></slot>
@@ -682,6 +697,6 @@ s([K({ reflect: !0 })], $.prototype, "variant", void 0), s([K()], $.prototype, "
682
697
  })], $.prototype, "maxHeight", void 0), s([K({ reflect: !0 })], $.prototype, "scrollbars", void 0), s([K({
683
698
  type: Number,
684
699
  attribute: "header-height"
685
- })], $.prototype, "headerHeight", void 0), s([J()], $.prototype, "_hasHeader", void 0), s([J()], $.prototype, "_hasStatus", void 0), s([q(".body")], $.prototype, "body", void 0), s([q("vf-scroll-area")], $.prototype, "scrollArea", void 0), $ = s([t("vf-window")], $);
700
+ })], $.prototype, "headerHeight", void 0), s([K()], $.prototype, "pattern", void 0), s([J()], $.prototype, "_hasHeader", void 0), s([J()], $.prototype, "_hasStatus", void 0), s([q(".body")], $.prototype, "body", void 0), s([q("vf-scroll-area")], $.prototype, "scrollArea", void 0), $ = s([t("vf-window")], $);
686
701
  //#endregion
687
702
  export { $ as VfWindow };
package/dist/cursor.d.ts CHANGED
@@ -18,6 +18,7 @@ export interface VfCursorArt {
18
18
  */
19
19
  staticSrc?: string | string[];
20
20
  }
21
+ /** The four built-in kinds. A page adds its own by name (`kinds`). */
21
22
  export type VfCursorKind = 'arrow' | 'text' | 'crosshair' | 'wait';
22
23
  export interface VfCursorOptions {
23
24
  /** The default pointer. Defaults to the embedded System 7 arrow. */
@@ -30,6 +31,13 @@ export interface VfCursorOptions {
30
31
  /** While `aria-busy`. Defaults to the embedded wristwatch; `null`
31
32
  * disables. */
32
33
  wait?: VfCursorArt | null;
34
+ /**
35
+ * The page's own cursors by name — an eyedropper, a paint bucket —
36
+ * claimed with `data-vf-cursor="name"` like the built-ins. The arrow
37
+ * shows while one's art loads, or if it fails to. A built-in's name is
38
+ * ignored: its own option sets its art.
39
+ */
40
+ kinds?: Record<string, VfCursorArt>;
33
41
  /**
34
42
  * Element whose top-left corner anchors the system-pixel lattice the
35
43
  * cursor snaps to. Defaults to the page's `vf-desktop` (else the document