vintage-frames 0.14.0 → 0.14.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.
Files changed (46) hide show
  1. package/README.md +16 -0
  2. package/custom-elements.json +177 -13
  3. package/dist/components/vf-container.d.ts +3 -1
  4. package/dist/components/vf-container.js +34 -16
  5. package/dist/components/vf-desktop.d.ts +114 -12
  6. package/dist/components/vf-desktop.js +119 -58
  7. package/dist/components/vf-icon-field.d.ts +1 -1
  8. package/dist/components/vf-icon.js +68 -61
  9. package/dist/components/vf-menu-bar.d.ts +5 -0
  10. package/dist/components/vf-menu-bar.js +67 -43
  11. package/dist/components/vf-window.d.ts +13 -0
  12. package/dist/components/vf-window.js +10 -1
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.js +44 -43
  15. package/dist/pattern-fill.d.ts +11 -8
  16. package/dist/pattern-fill.js +2 -4
  17. package/dist/position.d.ts +25 -7
  18. package/dist/position.js +6 -3
  19. package/dist/shell/alert.d.ts +32 -0
  20. package/dist/shell/alert.js +44 -0
  21. package/dist/shell/catalog.d.ts +168 -0
  22. package/dist/shell/catalog.js +278 -0
  23. package/dist/shell/clock.d.ts +16 -0
  24. package/dist/shell/clock.js +36 -0
  25. package/dist/shell/filing.d.ts +39 -0
  26. package/dist/shell/filing.js +68 -0
  27. package/dist/shell/finder.d.ts +116 -0
  28. package/dist/shell/finder.js +564 -0
  29. package/dist/shell/geometry.d.ts +196 -0
  30. package/dist/shell/geometry.js +241 -0
  31. package/dist/shell/index.d.ts +29 -0
  32. package/dist/shell/index.js +11 -0
  33. package/dist/shell/shell.d.ts +150 -0
  34. package/dist/shell/shell.js +213 -0
  35. package/dist/shell/state.d.ts +88 -0
  36. package/dist/shell/state.js +103 -0
  37. package/dist/shell/storage.d.ts +14 -0
  38. package/dist/shell/storage.js +41 -0
  39. package/dist/shell/windows.d.ts +215 -0
  40. package/dist/shell/windows.js +343 -0
  41. package/dist/window-chrome.d.ts +43 -0
  42. package/dist/window-chrome.js +13 -0
  43. package/docs/SPEC.md +12 -11
  44. package/editor/vscode.html-custom-data.json +9 -4
  45. package/editor/web-types.json +45 -9
  46. package/package.json +10 -2
@@ -36,6 +36,25 @@ declare const VfDesktop_base: (new (...args: any[]) => import("../position.js").
36
36
  * reads the current holder, and every change of holder — including to and
37
37
  * from none — fires `vf-activate`.
38
38
  *
39
+ * **Hidden windows.** A window with `hidden` — closed by a page that hides
40
+ * its windows rather than removing them, through `vf-window.hide()` or the
41
+ * attribute itself — never holds the active state. One slotted hidden waits
42
+ * to be shown; `bringToFront()` on one gives it its place in the stack and
43
+ * activates nothing; the holder hiding hands the state to the topmost shown
44
+ * document window, or to none. A window shown into a tier where none is
45
+ * active, unless the tier was deliberately deactivated, takes it.
46
+ * {@link stackingOrder} lists the windows as they stand, bottom-most first.
47
+ *
48
+ * **The work area.** A slotted `vf-menu-bar` along the top takes its band
49
+ * off the screen: {@link workArea} is what is left, and the gestures on the
50
+ * screen stay in it — a window dragged up stops with its title bar under the
51
+ * bar, where it can still be grabbed, and an icon dropped, nudged or walked
52
+ * on the desktop lands below the bar. `window-top` deepens the area for
53
+ * windows alone ({@link windowArea}), for a page with a strip of its own
54
+ * under the bar. A gesture asks for the box it is clamped into with
55
+ * `vf-placement-bounds-request` (bubbles, non-composed), the drag surface's
56
+ * handshake, and the desktop answers for the hosts placed on its screen.
57
+ *
39
58
  * The desktop is a raster with an explicit size, always: **`width` and
40
59
  * `height`**, in system px, the way a WIND resource declared a window's —
41
60
  * the host box renders at the declared screen plus `2 × bezel` per axis, a
@@ -88,8 +107,8 @@ declare const VfDesktop_base: (new (...args: any[]) => import("../position.js").
88
107
  * @fires vf-activate - The active document-tier window changed. Detail
89
108
  * `{ window: HTMLElement | null }` — the new holder, or `null` when the
90
109
  * document tier deactivated (a {@link clearActive} call, or the active
91
- * window leaving the DOM with none behind it). Fired once per change of
92
- * holder, never for a re-assertion of the same one.
110
+ * window leaving the DOM or hiding with no shown window behind it). Fired
111
+ * once per change of holder, never for a re-assertion of the same one.
93
112
  * @csspart desktop - The patterned screen surface — the whole-system-px
94
113
  * raster (inset by `bezel` when one is set).
95
114
  * @cssprop [--vf-desktop=#808080] - base color under the desktop pattern —
@@ -139,6 +158,38 @@ export declare class VfDesktop extends VfDesktop_base {
139
158
  * keeps the dither.
140
159
  */
141
160
  pattern: string | null | undefined;
161
+ /**
162
+ * The least `top` a window may take, in system px from the screen's top —
163
+ * for a page that keeps a strip of its own under the menu bar, over the
164
+ * windows. A window dragged on this desktop stops there, and
165
+ * {@link windowArea} starts there; icons still stop at the menu bar. Below
166
+ * the bar's bottom it changes nothing. Unset, windows stop at the bar.
167
+ */
168
+ windowTop?: number | null;
169
+ /**
170
+ * The screen less a slotted `vf-menu-bar` along its top edge, in system
171
+ * px from the screen's corner (the bezel excluded, as for
172
+ * {@link placementAt}): `{ left, top, width, height }`. The desktop clamps
173
+ * the gestures on its screen into it — an icon dropped, nudged or walked
174
+ * on the desktop lands below the menu bar, where it can be grabbed again.
175
+ * Without a bar it is the whole screen. Measured at the call.
176
+ */
177
+ get workArea(): {
178
+ left: number;
179
+ top: number;
180
+ width: number;
181
+ height: number;
182
+ };
183
+ /**
184
+ * {@link workArea}, deepened to {@link windowTop} where that is lower: the
185
+ * area windows are dragged in. A window's title bar never goes above it.
186
+ */
187
+ get windowArea(): {
188
+ left: number;
189
+ top: number;
190
+ width: number;
191
+ height: number;
192
+ };
142
193
  /**
143
194
  * Size the screen to the largest whole-system-px raster whose host box —
144
195
  * bezel included — fits a CSS-px bound, and return what was set. The
@@ -223,10 +274,13 @@ export declare class VfDesktop extends VfDesktop_base {
223
274
  private readonly _gestureEnd;
224
275
  connectedCallback(): void;
225
276
  disconnectedCallback(): void;
226
- /** Whether a slotted window belongs to the floating (utility) tier. The
227
- * attribute is the source of truth here so a not-yet-upgraded element
228
- * still lands in the right tier (variant reflects, so an upgraded
229
- * property-set window agrees). */
277
+ /** Whether a slotted window belongs to the floating (utility) tier: the
278
+ * `variant` property where the window has one, else the attribute, so a
279
+ * not-yet-upgraded element still lands in the right tier. Not the
280
+ * attribute alone — it reflects in the window's next update, after a
281
+ * window made by script and appended in the same task has been slotted,
282
+ * and that palette would land in the document tier and lose its
283
+ * `active`. */
230
284
  private _isUtility;
231
285
  /**
232
286
  * Bring a slotted window to the front of its tier. A document-tier window
@@ -247,8 +301,17 @@ export declare class VfDesktop extends VfDesktop_base {
247
301
  */
248
302
  bringToFront(win: HTMLElement): void;
249
303
  /** The active document-tier window, or null while the tier is deactivated
250
- * (or has no windows). Utility windows are never the holder. */
304
+ * (or has no shown windows). Utility windows and hidden windows are never
305
+ * the holder. */
251
306
  get activeWindow(): HTMLElement | null;
307
+ /**
308
+ * The slotted windows in stacking order, bottom-most first: the document
309
+ * tier, then the floating tier above it, each in the order its windows
310
+ * were last raised. Hidden windows are included where they stand. This is
311
+ * the order the eye sees; the windows' light-DOM order catches up with it
312
+ * only once a gesture ends, and a raise by keyboard focus moves no node.
313
+ */
314
+ get stackingOrder(): HTMLElement[];
252
315
  /**
253
316
  * Deactivate the whole document tier — clear `active` from every document
254
317
  * window, leaving none the holder. Pages route "the user clicked the
@@ -258,7 +321,11 @@ export declare class VfDesktop extends VfDesktop_base {
258
321
  * slotted.
259
322
  */
260
323
  clearActive(): void;
261
- /** The z/active half of a raise, shared by every path. */
324
+ /**
325
+ * The z/active half of a raise, shared by every path. A hidden window
326
+ * takes its place in the stack and nothing else: it activates when it is
327
+ * shown and raised.
328
+ */
262
329
  private _restack;
263
330
  /**
264
331
  * Schedule the DOM-order sync: one task from now, coalesced, and never
@@ -316,6 +383,19 @@ export declare class VfDesktop extends VfDesktop_base {
316
383
  * detail alone.
317
384
  */
318
385
  private _onDragSurfaceRequest;
386
+ /**
387
+ * A gesture measuring the box it is clamped into (src/position.ts): for a
388
+ * host placed on this screen — a slotted window, an icon in an unplaced
389
+ * field — the screen, bezel excluded, and the work area's top as the
390
+ * floor: {@link windowArea}'s for a window, {@link workArea}'s for
391
+ * anything else. The floor is rounded up to the host's placement lattice,
392
+ * so a snapped landing never rounds above it. A host placed in a box of its
393
+ * own (a window body, a placed field) is that box's; an outer desktop
394
+ * leaves a filled detail alone.
395
+ */
396
+ private _onBoundsRequest;
397
+ /** The bottom of a slotted, shown menu bar along the screen's top edge, system px; else 0. */
398
+ private _menuBarFloor;
319
399
  /**
320
400
  * Delegated focusin: raise the window keyboard focus entered, so tabbing
321
401
  * into a background window brings it to front (and reveals its close/zoom
@@ -329,6 +409,19 @@ export declare class VfDesktop extends VfDesktop_base {
329
409
  private _windowFromEvent;
330
410
  /** Wire up newly slotted windows: seed z-indices, normalize `active`. */
331
411
  private _onSlotChange;
412
+ /**
413
+ * Resolve who should hold `active` after the window set or a window's
414
+ * visibility changed: a holder still slotted and shown keeps it, a
415
+ * deliberate deactivation is preserved (never promote a survivor over the
416
+ * user's "clicked the Finder"), and only then does the topmost shown
417
+ * window inherit — the active window left the DOM or was hidden, or a
418
+ * window was shown into a tier with none active. Applied even when null,
419
+ * so a stray `active` attribute on slotted markup, or on a hidden window,
420
+ * is normalized in the deactivated state too.
421
+ */
422
+ private _resolveActive;
423
+ /** Watch the `hidden` attribute of every slotted window, and no other. */
424
+ private _observeWindows;
332
425
  /**
333
426
  * Re-assert the single-active-window state after slotted `<vf-window>`
334
427
  * elements upgrade. One-shot: the flag collapses repeat slotchanges into a
@@ -365,15 +458,24 @@ export declare class VfDesktop extends VfDesktop_base {
365
458
  * Minimal-move: windows already in relative order are never touched (the
366
459
  * common case — after one raise, one window moves). Non-window siblings
367
460
  * (a menu bar, page content) keep their positions; only a window that must
368
- * cross the stack moves past them. Moving a node containing the focused
369
- * element drops focus to `<body>`, so it is restored afterwards — behind
370
- * `_restoringFocus`, because the restore re-fires focusin (see
371
- * {@link _onFocusIn}) on an element that may sit in a background window.
461
+ * cross the stack moves past them. `moveBefore()` keeps the focus inside a
462
+ * moved window where the browser has it ({@link _move}); re-inserting a
463
+ * node containing the focused element drops focus to `<body>`, so it is
464
+ * restored afterwards — behind `_restoringFocus`, because the restore
465
+ * re-fires focusin (see {@link _onFocusIn}) on an element that may sit in a
466
+ * background window.
372
467
  * Reached only through {@link _requestDomSync}'s task — after a pointer
373
468
  * gesture or a programmatic raise, never from a focus-driven one (see
374
469
  * {@link _raise} for why).
375
470
  */
376
471
  private _syncDomOrder;
472
+ /**
473
+ * Move a slotted window before `before`: with `Element.moveBefore()` where
474
+ * the browser has it, which keeps the focus inside the window — a rename
475
+ * field, a text field's caret — through the move; else by re-inserting it,
476
+ * and {@link _syncDomOrder} puts the focus back.
477
+ */
478
+ private _move;
377
479
  protected willUpdate(changed: PropertyValues<this>): void;
378
480
  protected updated(changed: Map<PropertyKey, unknown>): void;
379
481
  protected render(): unknown;
@@ -1,25 +1,25 @@
1
1
  import { emit as e } from "../events.js";
2
2
  import { vfElement as t } from "../define.js";
3
- import { ScaleController as n, effectiveScale as r, sysLength as i } from "../scale.js";
4
- import a from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
5
- import { VfPositioned as o, placementIn as s } from "../position.js";
6
- import { vfBase as c } from "../styles/recipes/host.js";
7
- import { tileImage as l, tileRects as u, tileSpan as d } from "../styles/recipes/tile.js";
8
- import { patternOverride as f, tileGrid as p, vfTileGrid as m } from "../tile-grid.js";
9
- import { PATTERNS as h, parsePattern as g, patternMotif as _ } from "../patterns.js";
10
- import { PatternFillController as v, vfPatternFill as y } from "../pattern-fill.js";
11
- import { GridSnapController as b } from "../grid-snap.js";
12
- import { DocumentListenersController as x } from "../document-listeners.js";
13
- import { SCREEN_CORNER as S, steppedCornerClip as C } from "../pixel-frame.js";
14
- import { LitElement as w, css as T, html as E, unsafeCSS as D } from "lit";
15
- import { property as O, query as k, queryAssignedElements as A } from "lit/decorators.js";
3
+ import { ScaleController as n, effectiveScale as r, sysLength as i, systemPxQuantum as a } from "../scale.js";
4
+ import o from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
5
+ import { VfPositioned as s, placementIn as c } from "../position.js";
6
+ import { vfBase as l } from "../styles/recipes/host.js";
7
+ import { tileImage as u, tileRects as d, tileSpan as f } from "../styles/recipes/tile.js";
8
+ import { patternOverride as p, tileGrid as m, vfTileGrid as h } from "../tile-grid.js";
9
+ import { PATTERNS as g, parsePattern as _, patternMotif as v } from "../patterns.js";
10
+ import { PatternFillController as y, vfPatternFill as b } from "../pattern-fill.js";
11
+ import { GridSnapController as x } from "../grid-snap.js";
12
+ import { DocumentListenersController as S } from "../document-listeners.js";
13
+ import { SCREEN_CORNER as C, steppedCornerClip as w } from "../pixel-frame.js";
14
+ import { LitElement as T, css as E, html as D, unsafeCSS as O } from "lit";
15
+ import { property as k, query as A, queryAssignedElements as j } from "lit/decorators.js";
16
16
  //#region src/components/vf-desktop.ts
17
- var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I], "#000000", "#ffffff"), R = l(L.width, L.height, u(L.rects)), z = d(L.width), B = class extends o(w) {
17
+ var M = 1e6, N = 2 * M, P = 2000001, F = 512, I = 342, L = "gray-50", R = v(g[L], "#000000", "#ffffff"), z = u(R.width, R.height, d(R.rects)), B = f(R.width), V = (e) => Number(e.style.zIndex) || 0, H = class extends s(T) {
18
18
  constructor(...e) {
19
- super(...e), this.width = P, this.height = F, this.bezel = 0, this.pattern = I, this.scale = new n(this), this.gridSnap = new b(this), this._token = "", this._desktopPattern = h[I], this.#e = !1, this.patternFill = new v(this, {
19
+ super(...e), this.width = F, this.height = I, this.bezel = 0, this.pattern = L, this.scale = new n(this), this.gridSnap = new x(this), this._token = "", this._desktopPattern = g[L], this.#e = !1, this.patternFill = new y(this, {
20
20
  getBox: () => this.screen,
21
21
  getPattern: () => this._token ? null : this._desktopPattern
22
- }), this._zCounter = 0, this._activeWindow = null, this._deactivated = !1, this._awaitingUpgrade = !1, this._pointerGesture = !1, this._restoringFocus = !1, this._domSyncTimer = 0, this._gestureEnd = new x(this, () => [[
22
+ }), this._zCounter = 0, this._activeWindow = null, this._deactivated = !1, this._awaitingUpgrade = !1, this.#t = new MutationObserver(() => this._resolveActive()), this._pointerGesture = !1, this._restoringFocus = !1, this._domSyncTimer = 0, this._gestureEnd = new S(this, () => [[
23
23
  document,
24
24
  "pointerup",
25
25
  this._onGestureEnd,
@@ -38,6 +38,15 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
38
38
  }, this._onDragSurfaceRequest = (e) => {
39
39
  let t = e.detail;
40
40
  !t || t.surface || (t.surface = this.dragSurface);
41
+ }, this._onBoundsRequest = (e) => {
42
+ let t = e.detail, n = e.target;
43
+ if (!t || t.bounds || !(n instanceof HTMLElement) || n.offsetParent !== this) return;
44
+ let r = n.localName === "vf-window" ? this.windowArea : this.workArea, i = a(n);
45
+ t.bounds = {
46
+ width: r.left + r.width,
47
+ height: r.top + r.height,
48
+ top: Math.ceil(r.top / i) * i
49
+ };
41
50
  }, this._onFocusIn = (e) => {
42
51
  if (this._restoringFocus) return;
43
52
  let t = this._windowFromEvent(e);
@@ -46,10 +55,10 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
46
55
  }
47
56
  static {
48
57
  this.styles = [
49
- c,
50
- m,
51
- y,
52
- T`
58
+ l,
59
+ h,
60
+ b,
61
+ E`
53
62
  :host {
54
63
  display: block;
55
64
  position: relative;
@@ -110,7 +119,7 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
110
119
  position: absolute;
111
120
  inset: 0;
112
121
  z-index: -1;
113
- --_vf-tile-image: var(--vf-desktop-pattern, ${D(R)});
122
+ --_vf-tile-image: var(--vf-desktop-pattern, ${O(z)});
114
123
  /* Forced colors: hidden with the pattern, same posture. */
115
124
  @media (forced-colors: active) {
116
125
  display: none;
@@ -142,7 +151,7 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
142
151
  ::slotted(vf-menu-bar),
143
152
  ::slotted(vf-menu) {
144
153
  position: relative;
145
- z-index: ${M};
154
+ z-index: ${N};
146
155
  }
147
156
  /* The drag surface (see the class doc): the box an icon's outline is
148
157
  positioned in — the screen's own, bezel excluded — clipped with
@@ -158,7 +167,7 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
158
167
  pointer-events: none;
159
168
  }
160
169
  .drag-surface > * {
161
- z-index: ${N};
170
+ z-index: ${P};
162
171
  }
163
172
  /* With a bezel, the screen's four corners wear the SCREEN_CORNER mask,
164
173
  rounding into the surrounding black; the bottom pair is the same
@@ -169,8 +178,8 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
169
178
  maximal z-index. */
170
179
  .corner {
171
180
  position: absolute;
172
- width: calc(var(--vf-scale, 1) * ${S[0]}px);
173
- height: calc(var(--vf-scale, 1) * ${S.length}px);
181
+ width: calc(var(--vf-scale, 1) * ${C[0]}px);
182
+ height: calc(var(--vf-scale, 1) * ${C.length}px);
174
183
  background: var(--vf-black, #000);
175
184
  pointer-events: none;
176
185
  z-index: 2147483647;
@@ -178,26 +187,43 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
178
187
  .corner.tl {
179
188
  top: 0;
180
189
  left: 0;
181
- clip-path: ${D(C(S, "left"))};
190
+ clip-path: ${O(w(C, "left"))};
182
191
  }
183
192
  .corner.tr {
184
193
  top: 0;
185
194
  right: 0;
186
- clip-path: ${D(C(S, "right"))};
195
+ clip-path: ${O(w(C, "right"))};
187
196
  }
188
197
  .corner.bl {
189
198
  bottom: 0;
190
199
  left: 0;
191
- clip-path: ${D(C(S, "left", "bottom"))};
200
+ clip-path: ${O(w(C, "left", "bottom"))};
192
201
  }
193
202
  .corner.br {
194
203
  bottom: 0;
195
204
  right: 0;
196
- clip-path: ${D(C(S, "right", "bottom"))};
205
+ clip-path: ${O(w(C, "right", "bottom"))};
197
206
  }
198
207
  `
199
208
  ];
200
209
  }
210
+ get workArea() {
211
+ let e = this.width ?? F, t = this.height ?? I, n = Math.min(t, this._menuBarFloor());
212
+ return {
213
+ left: 0,
214
+ top: n,
215
+ width: e,
216
+ height: t - n
217
+ };
218
+ }
219
+ get windowArea() {
220
+ let e = this.workArea, t = Math.min(e.top + e.height, Math.max(e.top, this.windowTop ?? 0));
221
+ return {
222
+ ...e,
223
+ top: t,
224
+ height: e.top + e.height - t
225
+ };
226
+ }
201
227
  fitWithin(e, t) {
202
228
  let n = r(this), i = (e) => Math.max(0, Math.floor(e / n + 1e-6) - 2 * this.bezel);
203
229
  return this.width = i(e), this.height = i(t), {
@@ -206,17 +232,18 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
206
232
  };
207
233
  }
208
234
  placementAt(e, t, n) {
209
- return s(this.screen ?? this, e, t, this, n);
235
+ return c(this.screen ?? this, e, t, this, n);
210
236
  }
211
237
  #e;
238
+ #t;
212
239
  connectedCallback() {
213
- super.connectedCallback(), this.addEventListener("pointerdown", this._onPointerDown), this.addEventListener("focusin", this._onFocusIn), this.addEventListener("vf-drag-surface-request", this._onDragSurfaceRequest);
240
+ super.connectedCallback(), this.addEventListener("pointerdown", this._onPointerDown), this.addEventListener("focusin", this._onFocusIn), this.addEventListener("vf-drag-surface-request", this._onDragSurfaceRequest), this.addEventListener("vf-placement-bounds-request", this._onBoundsRequest), this.hasUpdated && this._observeWindows();
214
241
  }
215
242
  disconnectedCallback() {
216
- this.removeEventListener("pointerdown", this._onPointerDown), this.removeEventListener("focusin", this._onFocusIn), this.removeEventListener("vf-drag-surface-request", this._onDragSurfaceRequest), this._pointerGesture = !1, this._domSyncTimer &&= (clearTimeout(this._domSyncTimer), 0), super.disconnectedCallback();
243
+ this.removeEventListener("pointerdown", this._onPointerDown), this.removeEventListener("focusin", this._onFocusIn), this.removeEventListener("vf-drag-surface-request", this._onDragSurfaceRequest), this.removeEventListener("vf-placement-bounds-request", this._onBoundsRequest), this.#t.disconnect(), this._pointerGesture = !1, this._domSyncTimer &&= (clearTimeout(this._domSyncTimer), 0), super.disconnectedCallback();
217
244
  }
218
245
  _isUtility(e) {
219
- return e.getAttribute("variant") === "utility";
246
+ return ("variant" in e ? e.variant : e.getAttribute("variant")) === "utility";
220
247
  }
221
248
  bringToFront(e) {
222
249
  this._restack(e), this._requestDomSync();
@@ -224,12 +251,15 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
224
251
  get activeWindow() {
225
252
  return this._activeWindow;
226
253
  }
254
+ get stackingOrder() {
255
+ return [...this._windows].sort((e, t) => V(e) - V(t));
256
+ }
227
257
  clearActive() {
228
258
  this._deactivated = !0, this._setActive(null);
229
259
  }
230
260
  _restack(e) {
231
261
  let t = this._isUtility(e);
232
- e.style.zIndex = String(++this._zCounter + (t ? j : 0)), t || this._setActive(e);
262
+ e.style.zIndex = String(++this._zCounter + (t ? M : 0)), !t && !e.hidden && this._setActive(e);
233
263
  }
234
264
  _requestDomSync() {
235
265
  this._pointerGesture || this._domSyncTimer || (this._domSyncTimer = window.setTimeout(() => {
@@ -237,27 +267,47 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
237
267
  }, 0));
238
268
  }
239
269
  _raise(e) {
240
- let t = this._isUtility(e), n = this._windows.filter((e) => this._isUtility(e) === t);
270
+ let t = this._isUtility(e), n = this._windows.filter((e) => this._isUtility(e) === t && !e.hidden);
241
271
  this._topmost(n) === e && (t || e.hasAttribute("active")) || this._restack(e);
242
272
  }
273
+ _menuBarFloor() {
274
+ let e = this.screen;
275
+ if (!e) return 0;
276
+ let t = [...this.children].find((e) => e.localName === "vf-menu-bar" && !e.hidden);
277
+ if (!t) return 0;
278
+ let n = t.getBoundingClientRect();
279
+ if (n.height <= 0) return 0;
280
+ let i = e.getBoundingClientRect(), a = r(this);
281
+ return Math.round((n.top - i.top) / a) > 0 ? 0 : Math.max(0, Math.round((n.bottom - i.top) / a));
282
+ }
243
283
  _windowFromEvent(e) {
244
284
  let t = this._windows;
245
285
  return e.composedPath().find((e) => e instanceof HTMLElement && t.includes(e));
246
286
  }
247
287
  _onSlotChange() {
248
- let e = this._windows, t = null;
288
+ let e = this._windows;
289
+ this._observeWindows();
290
+ let t = null;
249
291
  for (let n of e) if (!n.style.zIndex) {
250
292
  let e = this._isUtility(n);
251
- n.style.zIndex = String(++this._zCounter + (e ? j : 0)), e || (t = n);
293
+ n.style.zIndex = String(++this._zCounter + (e ? M : 0)), !e && !n.hidden && (t = n);
252
294
  }
253
- let n = e.filter((e) => !this._isUtility(e)), r = this._activeWindow && n.includes(this._activeWindow) ? this._activeWindow : null;
254
- this._setActive(t ?? r ?? (this._deactivated ? null : this._topmost(n))), customElements.get("vf-window") || this._normalizeAfterUpgrade();
295
+ t ? this._setActive(t) : this._resolveActive(), customElements.get("vf-window") || this._normalizeAfterUpgrade();
296
+ }
297
+ _resolveActive() {
298
+ let e = this._windows.filter((e) => !this._isUtility(e) && !e.hidden), t = this._activeWindow && e.includes(this._activeWindow) ? this._activeWindow : null;
299
+ this._setActive(t ?? (this._deactivated ? null : this._topmost(e)));
300
+ }
301
+ _observeWindows() {
302
+ this.#t.takeRecords(), this.#t.disconnect();
303
+ for (let e of this._windows) this.#t.observe(e, {
304
+ attributes: !0,
305
+ attributeFilter: ["hidden"]
306
+ });
255
307
  }
256
308
  _normalizeAfterUpgrade() {
257
309
  this._awaitingUpgrade || (this._awaitingUpgrade = !0, customElements.whenDefined("vf-window").then(() => {
258
- if (this._awaitingUpgrade = !1, !this.isConnected) return;
259
- let e = this._windows.filter((e) => !this._isUtility(e)), t = this._activeWindow && e.includes(this._activeWindow) ? this._activeWindow : null;
260
- this._setActive(t ?? (this._deactivated ? null : this._topmost(e)));
310
+ this._awaitingUpgrade = !1, this.isConnected && this._resolveActive();
261
311
  }));
262
312
  }
263
313
  _setActive(t) {
@@ -274,7 +324,7 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
274
324
  _topmost(e) {
275
325
  let t = null, n = -Infinity;
276
326
  for (let r of e) {
277
- let e = Number(r.style.zIndex) || 0;
327
+ let e = V(r);
278
328
  e >= n && (n = e, t = r);
279
329
  }
280
330
  return t;
@@ -288,31 +338,39 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
288
338
  if (!this.isConnected) return;
289
339
  let e = this._windows;
290
340
  if (e.length < 2) return;
291
- let t = [...e].sort((e, t) => (Number(e.style.zIndex) || 0) - (Number(t.style.zIndex) || 0)), n = this._deepActiveElement(), r = !1, i = null;
292
- for (let e of t) i && i.compareDocumentPosition(e) & Node.DOCUMENT_POSITION_PRECEDING && (this.insertBefore(e, i.nextSibling), r = !0), i = e;
341
+ let t = [...e].sort((e, t) => V(e) - V(t)), n = this._deepActiveElement(), r = !1, i = null;
342
+ for (let e of t) i && i.compareDocumentPosition(e) & Node.DOCUMENT_POSITION_PRECEDING && (this._move(e, i.nextSibling), r = !0), i = e;
293
343
  r && n instanceof HTMLElement && n.isConnected && this._deepActiveElement() !== n && (this._restoringFocus = !0, n.focus({ preventScroll: !0 }), this._restoringFocus = !1);
294
344
  }
345
+ _move(e, t) {
346
+ let n = this;
347
+ if (typeof n.moveBefore == "function") try {
348
+ n.moveBefore(e, t);
349
+ return;
350
+ } catch {}
351
+ this.insertBefore(e, t);
352
+ }
295
353
  willUpdate(e) {
296
- if (super.willUpdate(e), this._token = f(this, "--vf-desktop-pattern"), e.has("pattern")) {
297
- let e = g(this.pattern);
298
- e === null && this.pattern?.trim() && !this.#e && (this.#e = !0, console.warn(`vf-desktop: unknown pattern "${this.pattern}" — a library name (docs/PATTERNS.md) or sixteen hex digits. Keeping ${I}.`)), this._desktopPattern = e ?? h[I];
354
+ if (super.willUpdate(e), this._token = p(this, "--vf-desktop-pattern"), e.has("pattern")) {
355
+ let e = _(this.pattern);
356
+ e === null && this.pattern?.trim() && !this.#e && (this.#e = !0, console.warn(`vf-desktop: unknown pattern "${this.pattern}" — a library name (docs/PATTERNS.md) or sixteen hex digits. Keeping ${L}.`)), this._desktopPattern = e ?? g[L];
299
357
  }
300
358
  }
301
359
  updated(e) {
302
360
  if (e.has("bezel") && (this.bezel > 0 ? this.style.setProperty("--vf-desktop-bezel", i(this.bezel)) : this.style.removeProperty("--vf-desktop-bezel")), e.has("width") || e.has("height") || e.has("bezel")) {
303
- let e = this.width ?? P, t = this.height ?? F;
361
+ let e = this.width ?? F, t = this.height ?? I;
304
362
  this.style.width = i(e + 2 * this.bezel), this.style.height = i(t + 2 * this.bezel);
305
363
  }
306
364
  }
307
365
  render() {
308
- let e = this.width ?? P, t = this.height ?? F, n = this._token ? E`<div class="vf-tile-grid">
309
- ${p({
310
- cols: Math.ceil(e / z),
311
- rows: Math.ceil(t / z),
312
- tile: z
366
+ let e = this.width ?? F, t = this.height ?? I, n = this._token ? D`<div class="vf-tile-grid">
367
+ ${m({
368
+ cols: Math.ceil(e / B),
369
+ rows: Math.ceil(t / B),
370
+ tile: B
313
371
  })}
314
372
  </div>` : null;
315
- return E`
373
+ return D`
316
374
  <div class=${this.bezel > 0 ? "desktop vf-snap bezeled" : "desktop vf-snap"}>
317
375
  <div
318
376
  class="screen vf-pattern-fill${this._token ? "" : " vf-patterned"}"
@@ -321,7 +379,7 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
321
379
  ${n}
322
380
  <slot @slotchange=${this._onSlotChange}></slot>
323
381
  <div class="drag-surface"></div>
324
- ${this.bezel > 0 ? E`<div class="corner tl"></div>
382
+ ${this.bezel > 0 ? D`<div class="corner tl"></div>
325
383
  <div class="corner tr"></div>
326
384
  <div class="corner bl"></div>
327
385
  <div class="corner br"></div>` : null}
@@ -330,6 +388,9 @@ var j = 1e6, M = 2 * j, N = 2000001, P = 512, F = 342, I = "gray-50", L = _(h[I]
330
388
  `;
331
389
  }
332
390
  };
333
- a([O({ type: Number })], B.prototype, "width", void 0), a([O({ type: Number })], B.prototype, "height", void 0), a([O({ type: Number })], B.prototype, "bezel", void 0), a([O()], B.prototype, "pattern", void 0), a([A({ selector: "vf-window" })], B.prototype, "_windows", void 0), a([k(".screen")], B.prototype, "screen", void 0), a([k(".drag-surface")], B.prototype, "dragSurface", void 0), B = a([t("vf-desktop")], B);
391
+ o([k({ type: Number })], H.prototype, "width", void 0), o([k({ type: Number })], H.prototype, "height", void 0), o([k({ type: Number })], H.prototype, "bezel", void 0), o([k()], H.prototype, "pattern", void 0), o([k({
392
+ type: Number,
393
+ attribute: "window-top"
394
+ })], H.prototype, "windowTop", void 0), o([j({ selector: "vf-window" })], H.prototype, "_windows", void 0), o([A(".screen")], H.prototype, "screen", void 0), o([A(".drag-surface")], H.prototype, "dragSurface", void 0), H = o([t("vf-desktop")], H);
334
395
  //#endregion
335
- export { B as VfDesktop };
396
+ export { H as VfDesktop };
@@ -67,7 +67,7 @@ declare const VfIconField_base: (new (...args: any[]) => import("../size.js").Vf
67
67
  * the pointer, and the selection follows it live by the Finder's one rule:
68
68
  * **an icon is selected exactly when it was selected at the press XOR the
69
69
  * rectangle touches it.** The press fixes that anchor: a plain press has
70
- * already cleared everything (the way any press outside an icon does), so
70
+ * already cleared everything (a press on a field's background does), so
71
71
  * the rectangle simply selects what it touches and releases what it leaves;
72
72
  * with Shift or ⌘ held the selection survives the press and the rectangle
73
73
  * *toggles* against it — an icon already selected deselects while the