vintage-frames 0.14.1 → 0.15.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.
@@ -14,7 +14,7 @@ export type { AppContext, AppDefinition, CatalogSetup, KindDefinition, Shell, Sh
14
14
  export { createWindowManager, focusInto } from './windows.js';
15
15
  export type { AdoptOptions, ArrangeGroup, BoxInput, FocusHome, HomeBox, OpenOptions, PlacePolicy, Placed, WindowManager, WindowManagerOptions, } from './windows.js';
16
16
  export { finder, FINDER } from './finder.js';
17
- export type { FinderApi, FinderArt, FinderCommand, FinderCommandSpec, FinderMenu, FinderOpen, FinderOptions, } from './finder.js';
17
+ export type { FinderAlert, FinderAlertHandler, FinderAlerts, FinderApi, FinderArt, FinderCommand, FinderCommandSpec, FinderFailedAction, FinderMenu, FinderOpen, FinderOptions, } from './finder.js';
18
18
  export { fileByDrag } from './filing.js';
19
19
  export type { FilingOptions } from './filing.js';
20
20
  export { createCatalog, memoryStorage, childrenOf, containerOf, copyName, descendantsOf, enclosingFolders, isContainerKind, isInside, isTrashed, isVolume, itemCount, itemOf, nextFolderName, DISK, FOLDER, TRASH, UNTITLED_FOLDER, } from './catalog.js';
@@ -25,5 +25,3 @@ export type { LocalStorageStateOptions, SavedSession, SavedWindow, SessionSnapsh
25
25
  export { BAND, CASCADE_SLOTS, CASCADE_STEP, ICON_CELL, ICON_COLUMN, ICON_INSET, ICON_ROW, NEAR, WINDOW_ORIGIN, cascadedBox, cascadeFrom, cascadeSlot, centeredBox, cleanUp, collisions, desktopLattice, fieldExtent, fillOrder, folderLattice, frameOf, isPin, latticeCell, latticeSlot, nearBox, nextFreeCell, pinOf, pinTo, trashCell, windowOrigin, } from './geometry.js';
26
26
  export type { Bands, Box, CascadeOptions, EdgePin, Frame, Lattice, LatticeOptions, Pin, Point, Policy, Size, } from './geometry.js';
27
27
  export { startClock, formatDate, formatTime, DATE_HOLD_MS } from './clock.js';
28
- export { showAlert } from './alert.js';
29
- export type { AlertButton, AlertOptions } from './alert.js';
@@ -3,9 +3,8 @@ import { BAND as v, CASCADE_SLOTS as y, CASCADE_STEP as b, ICON_CELL as x, ICON_
3
3
  import { createWindowManager as q, focusInto as J } from "./windows.js";
4
4
  import { localStorageState as Y, mergeSession as X, openWindowsOf as Z, readSession as Q } from "./state.js";
5
5
  import { DATE_HOLD_MS as $, formatDate as ee, formatTime as te, startClock as ne } from "./clock.js";
6
- import { showAlert as re } from "./alert.js";
7
- import { APP_KIND as ie, appIdOf as ae, createShell as oe, defineApp as se } from "./shell.js";
8
- import { fileByDrag as ce } from "./filing.js";
9
- import { FINDER as le, finder as ue } from "./finder.js";
10
- import { indexedDbStorage as de } from "./storage.js";
11
- export { ie as APP_KIND, v as BAND, y as CASCADE_SLOTS, b as CASCADE_STEP, $ as DATE_HOLD_MS, e as DISK, le as FINDER, t as FOLDER, x as ICON_CELL, S as ICON_COLUMN, C as ICON_INSET, w as ICON_ROW, T as NEAR, n as TRASH, r as UNTITLED_FOLDER, E as WINDOW_ORIGIN, ae as appIdOf, D as cascadeFrom, O as cascadeSlot, k as cascadedBox, A as centeredBox, i as childrenOf, j as cleanUp, M as collisions, a as containerOf, o as copyName, s as createCatalog, oe as createShell, q as createWindowManager, se as defineApp, c as descendantsOf, N as desktopLattice, l as enclosingFolders, P as fieldExtent, ce as fileByDrag, F as fillOrder, ue as finder, J as focusInto, I as folderLattice, ee as formatDate, te as formatTime, L as frameOf, de as indexedDbStorage, u as isContainerKind, d as isInside, R as isPin, f as isTrashed, p as isVolume, m as itemCount, h as itemOf, z as latticeCell, B as latticeSlot, Y as localStorageState, g as memoryStorage, X as mergeSession, V as nearBox, _ as nextFolderName, H as nextFreeCell, Z as openWindowsOf, U as pinOf, W as pinTo, Q as readSession, re as showAlert, ne as startClock, G as trashCell, K as windowOrigin };
6
+ import { APP_KIND as re, appIdOf as ie, createShell as ae, defineApp as oe } from "./shell.js";
7
+ import { fileByDrag as se } from "./filing.js";
8
+ import { FINDER as ce, finder as le } from "./finder.js";
9
+ import { indexedDbStorage as ue } from "./storage.js";
10
+ export { re as APP_KIND, v as BAND, y as CASCADE_SLOTS, b as CASCADE_STEP, $ as DATE_HOLD_MS, e as DISK, ce as FINDER, t as FOLDER, x as ICON_CELL, S as ICON_COLUMN, C as ICON_INSET, w as ICON_ROW, T as NEAR, n as TRASH, r as UNTITLED_FOLDER, E as WINDOW_ORIGIN, ie as appIdOf, D as cascadeFrom, O as cascadeSlot, k as cascadedBox, A as centeredBox, i as childrenOf, j as cleanUp, M as collisions, a as containerOf, o as copyName, s as createCatalog, ae as createShell, q as createWindowManager, oe as defineApp, c as descendantsOf, N as desktopLattice, l as enclosingFolders, P as fieldExtent, se as fileByDrag, F as fillOrder, le as finder, J as focusInto, I as folderLattice, ee as formatDate, te as formatTime, L as frameOf, ue as indexedDbStorage, u as isContainerKind, d as isInside, R as isPin, f as isTrashed, p as isVolume, m as itemCount, h as itemOf, z as latticeCell, B as latticeSlot, Y as localStorageState, g as memoryStorage, X as mergeSession, V as nearBox, _ as nextFolderName, H as nextFreeCell, Z as openWindowsOf, U as pinOf, W as pinTo, Q as readSession, ne as startClock, G as trashCell, K as windowOrigin };
@@ -7,21 +7,23 @@
7
7
  * nodes come back each time it is front, so items keep their state. A menu
8
8
  * off the bar has no key equivalents, so only the front application's fire.
9
9
  * The bar's label is the front application's name.
10
+ * - While an application's held dialog is open, the bar shows that
11
+ * application's name and menus instead; nothing else changes.
10
12
  * - Each application's `init(ctx)` gets the desktop, the window manager, its
11
- * own menus, the system menu, the other applications' actions (read at the
12
- * call), the catalog when a Finder runs, the site's services, `alert()`,
13
- * `gate()`, `onFront()` and `onDispose()`. Everything it sets up comes down
14
- * through `onDispose()`, so a hot reload starts clean.
13
+ * own menus and dialogs, the system menu, the other applications' actions
14
+ * (read at the call), the catalog when a Finder runs, the site's services,
15
+ * `ask()`, `gate()`, `onFront()` and `onDispose()`. Everything it sets up
16
+ * comes down through `onDispose()`, and its dialogs with it, so a hot
17
+ * reload starts clean. The shell composes no UI of its own.
15
18
  * - A window showing a catalog item follows it: its title follows a rename,
16
19
  * and it closes when the item is removed.
17
20
  * - The boot reads the catalog, seeds it once, reopens the last session's
18
21
  * windows deepest first and the active one last, then starts saving.
19
22
  */
20
- import type { VfDesktop, VfMenu, VfMenuItem, VfViewportBox } from '../index.js';
23
+ import type { VfDesktop, VfDialog, VfMenu, VfMenuItem, VfModalDialog, VfViewportBox } from '../index.js';
21
24
  import type { Catalog, CatalogStorage, Item, Volumes } from './catalog.js';
22
25
  import type { WindowManager } from './windows.js';
23
26
  import type { ShellState } from './state.js';
24
- import type { AlertOptions } from './alert.js';
25
27
  /** A kind of catalog item an application registers: how it looks and opens. */
26
28
  export interface KindDefinition {
27
29
  /** Its 32×32 art: one URL, or one per item. */
@@ -57,6 +59,11 @@ export interface AppDefinition<Actions = unknown> {
57
59
  icon?: string;
58
60
  /** A fragment of `vf-menu` elements, each with a `data-menu` name. */
59
61
  menus?: string;
62
+ /**
63
+ * A fragment of dialogs, each with a `data-dialog` name: a `vf-dialog`, or
64
+ * an element that renders one inside itself. Held under the application.
65
+ */
66
+ dialogs?: string;
60
67
  /** The catalog kinds it opens. */
61
68
  kinds?: Record<string, KindDefinition>;
62
69
  /** The Finder's: the catalog every application shares. */
@@ -98,8 +105,18 @@ export interface AppContext {
98
105
  onMenu(fn: (value: string, item: VfMenuItem) => void): void;
99
106
  /** Put an item in the system menu, which runs `fn` when picked. */
100
107
  systemItem(value: string, label: string, fn: () => void): VfMenuItem;
101
- /** Show an alert with the site's caution art; resolves the button's value. */
102
- alert(message: string, options?: AlertOptions): Promise<string | null>;
108
+ /** One of its dialogs by `data-dialog`; throws when the markup drifts. */
109
+ dialog<T extends HTMLElement = VfDialog>(name: string): T;
110
+ /** Hold a dialog it made in code: appended to the desktop, held under it, removed with it. Returns it. */
111
+ hold<T extends Element>(dialog: T): T;
112
+ /** Let a dialog it holds go, and remove it. */
113
+ release(dialog: Element): void;
114
+ /**
115
+ * Show a dialog it holds, and resolve its `returnValue`: the value of the
116
+ * button that submitted it, or null when it closed without one. Throws for
117
+ * a dialog it doesn't hold.
118
+ */
119
+ ask(dialog: VfModalDialog): Promise<string | null>;
103
120
  /** Keep `item` disabled while `test` is false: re-run on every press, key, selection change and activation. */
104
121
  gate(item: VfMenuItem, test: () => boolean): void;
105
122
  /** Whether a text field has keyboard focus, read through shadow roots at the call. */
@@ -128,8 +145,6 @@ export interface ShellOptions {
128
145
  services?: Record<string, unknown>;
129
146
  /** The clock in the menu bar's `end` slot. Default true. */
130
147
  clock?: boolean;
131
- /** The 32×32 caution art the alerts carry. */
132
- caution?: string | null;
133
148
  }
134
149
  /** A running shell. */
135
150
  export interface Shell {
@@ -139,7 +154,6 @@ export interface Shell {
139
154
  readonly apps: Record<string, any>;
140
155
  /** Settles once the boot has read the catalog and reopened the session. */
141
156
  readonly ready: Promise<void>;
142
- alert(message: string, options?: AlertOptions): Promise<string | null>;
143
157
  dispose(): void;
144
158
  }
145
159
  /** The kind the shell registers: an application's icon, which opens it. */
@@ -5,93 +5,96 @@ import { createCatalog as n } from "./catalog.js";
5
5
  import { createWindowManager as r, deepActiveElement as i } from "./windows.js";
6
6
  import { openWindowsOf as a } from "./state.js";
7
7
  import { startClock as o } from "./clock.js";
8
- import { showAlert as s } from "./alert.js";
9
8
  //#region src/shell/shell.ts
10
- function c(e) {
9
+ function s(e) {
11
10
  return e;
12
11
  }
13
- var l = "app", u = (e) => e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement || e?.isContentEditable === !0;
14
- function d(c, l) {
15
- let d = l.apps, p = d.find((e) => e.catalog), m = l.defaultApp ?? p?.id ?? d[0]?.id ?? "", h = l.state ?? null, g = l.services ?? {}, _ = [], v = c.querySelector(":scope > vf-menu-bar");
16
- if (!v) throw Error("vintage-frames/shell: the desktop has no vf-menu-bar");
17
- let y = v.querySelector(":scope > vf-menu");
18
- if (!y) throw Error("vintage-frames/shell: the menu bar has no system menu");
19
- v.shortcuts = !0;
20
- let b = h?.saved?.pattern;
21
- b && (c.pattern = b);
22
- let x = null;
23
- if (l.fit === "viewport") {
12
+ var c = "app", l = (e) => e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement || e?.isContentEditable === !0;
13
+ function u(s, c) {
14
+ let u = c.apps, f = u.find((e) => e.catalog), p = c.defaultApp ?? f?.id ?? u[0]?.id ?? "", m = c.state ?? null, h = c.services ?? {}, g = [], _ = s.querySelector(":scope > vf-menu-bar");
15
+ if (!_) throw Error("vintage-frames/shell: the desktop has no vf-menu-bar");
16
+ let v = _.querySelector(":scope > vf-menu");
17
+ if (!v) throw Error("vintage-frames/shell: the menu bar has no system menu");
18
+ _.shortcuts = !0;
19
+ let y = m?.saved?.pattern;
20
+ y && (s.pattern = y);
21
+ let b = null;
22
+ if (c.fit === "viewport") {
24
23
  let t = () => {
25
24
  let e = {
26
- width: c.width,
27
- height: c.height
25
+ width: s.width,
26
+ height: s.height
28
27
  };
29
- c.fitWithin(document.documentElement.clientWidth, document.documentElement.clientHeight), x?.resized(e);
28
+ s.fitWithin(document.documentElement.clientWidth, document.documentElement.clientHeight), b?.resized(e);
30
29
  };
31
30
  t(), window.addEventListener("resize", t);
32
31
  let n = e(t);
33
- _.push(() => {
32
+ g.push(() => {
34
33
  window.removeEventListener("resize", t), n();
35
34
  });
36
35
  }
37
- let S = r(c, {
38
- defaultApp: m,
39
- savedPin: (e) => h?.pin(e) ?? null
36
+ let x = r(s, {
37
+ defaultApp: p,
38
+ savedPin: (e) => m?.pin(e) ?? null
40
39
  });
41
- x = S;
42
- let C = (e) => {
43
- e.target === c && c.clearActive();
40
+ b = x;
41
+ let S = (e) => {
42
+ e.target === s && s.clearActive();
44
43
  };
45
- c.addEventListener("pointerdown", C), _.push(() => c.removeEventListener("pointerdown", C));
46
- let w = {}, T = /* @__PURE__ */ new Map();
47
- T.set("app", {
48
- art: (e) => d.find((t) => t.id === f(e))?.icon ?? "",
49
- open: (e, t) => void w[f(e) ?? ""]?.open?.({ from: t }),
44
+ s.addEventListener("pointerdown", S), g.push(() => s.removeEventListener("pointerdown", S));
45
+ let C = {}, w = /* @__PURE__ */ new Map();
46
+ w.set("app", {
47
+ art: (e) => u.find((t) => t.id === d(e))?.icon ?? "",
48
+ open: (e, t) => void C[d(e) ?? ""]?.open?.({ from: t }),
50
49
  editable: !1
51
50
  });
52
- for (let e of d) for (let [t, n] of Object.entries(e.kinds ?? {})) T.set(t, n);
53
- let E = p?.catalog ? n({
54
- storage: p.catalog.storage,
55
- volumes: p.catalog.volumes,
56
- listed: (e) => T.has(e),
57
- hooks: (e) => T.get(e)
58
- }) : null, D = () => !!document.querySelector("vf-dialog[open]"), O = () => u(i()), k = /* @__PURE__ */ new Map(), A = () => {
59
- for (let [e, t] of k) {
51
+ for (let e of u) for (let [t, n] of Object.entries(e.kinds ?? {})) w.set(t, n);
52
+ let T = f?.catalog ? n({
53
+ storage: f.catalog.storage,
54
+ volumes: f.catalog.volumes,
55
+ listed: (e) => w.has(e),
56
+ hooks: (e) => w.get(e)
57
+ }) : null, E = () => !!document.querySelector("vf-dialog[open]"), D = () => l(i()), O = /* @__PURE__ */ new Map(), k = () => {
58
+ for (let [e, t] of O) {
60
59
  let n = !t();
61
60
  e.disabled !== n && (e.disabled = n);
62
61
  }
63
62
  };
64
- document.addEventListener("pointerdown", A, !0), document.addEventListener("keydown", A, !0), c.addEventListener("vf-select", A), c.addEventListener("vf-activate", A), _.push(() => document.removeEventListener("pointerdown", A, !0), () => document.removeEventListener("keydown", A, !0), () => c.removeEventListener("vf-select", A), () => c.removeEventListener("vf-activate", A), S.onWindows(A), S.onLayout(A)), E && _.push(E.subscribe(A));
65
- let j = (e, t = {}) => s(c, e, {
66
- art: l.caution ?? null,
67
- ...t
68
- }), M = (e) => {
63
+ document.addEventListener("pointerdown", k, !0), document.addEventListener("keydown", k, !0), s.addEventListener("vf-select", k), s.addEventListener("vf-activate", k), g.push(() => document.removeEventListener("pointerdown", k, !0), () => document.removeEventListener("keydown", k, !0), () => s.removeEventListener("vf-select", k), () => s.removeEventListener("vf-activate", k), x.onWindows(k), x.onLayout(k)), T && g.push(T.subscribe(k));
64
+ let A = (e) => {
69
65
  if (!e) return [];
70
66
  let t = document.createElement("div");
71
67
  t.innerHTML = e, customElements.upgrade(t);
72
- let n = [...t.children].filter((e) => e.localName === "vf-menu");
68
+ let n = [...t.children].filter((e) => e instanceof HTMLElement);
73
69
  for (let e of n) e.remove();
74
70
  return n;
75
- }, N = d.map((e) => ({
71
+ }, j = u.map((e) => ({
76
72
  def: e,
77
- menus: M(e.menus)
78
- })), P = [];
79
- for (let { def: e, menus: t } of N) {
80
- let n = [], r = `vintage-frames/shell (${e.id})`, i = {
81
- desktop: c,
82
- windows: S,
73
+ menus: A(e.menus).filter((e) => e.localName === "vf-menu"),
74
+ dialogs: A(e.dialogs).filter((e) => e.dataset.dialog != null)
75
+ })), M = [];
76
+ for (let { def: e, menus: t, dialogs: n } of j) {
77
+ let r = [], i = `vintage-frames/shell (${e.id})`, a = /* @__PURE__ */ new Set();
78
+ for (let t of n) x.holdDialog(t, { app: e.id }), a.add(t);
79
+ r.push(() => {
80
+ for (let e of a) x.releaseDialog(e), e.remove();
81
+ a.clear();
82
+ });
83
+ let o = {
84
+ desktop: s,
85
+ windows: x,
83
86
  menus: t,
84
- systemMenu: y,
85
- apps: w,
86
- catalog: E,
87
- services: g,
88
- state: h,
89
- kinds: T,
90
- kind: (e) => T.get(e),
91
- app: (e) => d.find((t) => t.id === e),
87
+ systemMenu: v,
88
+ apps: C,
89
+ catalog: T,
90
+ services: h,
91
+ state: m,
92
+ kinds: w,
93
+ kind: (e) => w.get(e),
94
+ app: (e) => u.find((t) => t.id === e),
92
95
  menu(e) {
93
96
  let n = t.find((t) => t.dataset.menu === e);
94
- if (!n) throw Error(`${r}: missing menu ${e}`);
97
+ if (!n) throw Error(`${i}: missing menu ${e}`);
95
98
  return n;
96
99
  },
97
100
  item(e) {
@@ -99,115 +102,133 @@ function d(c, l) {
99
102
  let t = n.querySelector(`vf-menu-item[value="${CSS.escape(e)}"]`);
100
103
  if (t) return t;
101
104
  }
102
- throw Error(`${r}: missing menu item ${e}`);
105
+ throw Error(`${i}: missing menu item ${e}`);
103
106
  },
104
107
  onMenu(e) {
105
- for (let n of t) i.on(n, "vf-menu-select", (t) => {
106
- if (D()) return;
108
+ for (let n of t) o.on(n, "vf-menu-select", (t) => {
109
+ if (E()) return;
107
110
  let { value: n, item: r } = t.detail;
108
111
  e(n, r);
109
112
  });
110
113
  },
111
- systemItem(e, t, r) {
112
- let a = document.createElement("vf-menu-item");
113
- return a.setAttribute("value", e), a.textContent = t, y.append(a), i.on(y, "vf-menu-select", (t) => {
114
- !D() && t.detail.value === e && r();
115
- }), n.push(() => a.remove()), a;
114
+ systemItem(e, t, n) {
115
+ let i = document.createElement("vf-menu-item");
116
+ return i.setAttribute("value", e), i.textContent = t, v.append(i), o.on(v, "vf-menu-select", (t) => {
117
+ !E() && t.detail.value === e && n();
118
+ }), r.push(() => i.remove()), i;
119
+ },
120
+ dialog(e) {
121
+ let t = n.find((t) => t.dataset.dialog === e);
122
+ if (!t) throw Error(`${i}: missing dialog ${e}`);
123
+ return t;
124
+ },
125
+ hold(t) {
126
+ return x.holdDialog(t, { app: e.id }), a.add(t), t;
127
+ },
128
+ release(e) {
129
+ a.delete(e) && (x.releaseDialog(e), e.remove());
130
+ },
131
+ ask(t) {
132
+ if (x.appOf(t) !== e.id) throw Error(`${i}: ask() takes a dialog the application holds`);
133
+ return new Promise((e) => {
134
+ let n = (r) => {
135
+ r.target === t && (t.removeEventListener("vf-close", n), e(r.detail.returnValue));
136
+ };
137
+ t.addEventListener("vf-close", n), t.show();
138
+ });
116
139
  },
117
- alert: j,
118
140
  gate(e, t) {
119
- k.set(e, t), n.push(() => k.delete(e));
120
- let r = !t();
121
- e.disabled !== r && (e.disabled = r);
141
+ O.set(e, t), r.push(() => O.delete(e));
142
+ let n = !t();
143
+ e.disabled !== n && (e.disabled = n);
122
144
  },
123
- typing: O,
124
- modalOpen: D,
145
+ typing: D,
146
+ modalOpen: E,
125
147
  onFront(t) {
126
- t(S.front === e.id), n.push(S.onFront((n) => t(n === e.id)));
148
+ t(x.front === e.id), r.push(x.onFront((n) => t(n === e.id)));
127
149
  },
128
- on(e, t, r, i) {
129
- e.addEventListener(t, r, i), n.push(() => e.removeEventListener(t, r, i));
150
+ on(e, t, n, i) {
151
+ e.addEventListener(t, n, i), r.push(() => e.removeEventListener(t, n, i));
130
152
  },
131
153
  onDispose(e) {
132
- n.push(e);
154
+ r.push(e);
133
155
  }
134
- }, a = e.init(i);
135
- a && (w[e.id] = a), P.push(() => {
136
- for (let e of n.splice(0).reverse()) e();
156
+ }, c = e.init(o);
157
+ c && (C[e.id] = c), M.push(() => {
158
+ for (let e of r.splice(0).reverse()) e();
137
159
  });
138
160
  }
139
- let F = null, I = () => {
140
- let e = N.find((e) => e.def.id === S.front) ?? N.find((e) => e.def.id === m) ?? null;
141
- if (!(!e || e === F)) {
142
- if (F) for (let e of F.menus) e.remove();
143
- y.after(...e.menus), v.label = e.def.name, F = e, A();
161
+ let N = null, P = () => {
162
+ let e = x.asking ?? x.front, t = j.find((t) => t.def.id === e) ?? j.find((e) => e.def.id === p) ?? null;
163
+ if (!(!t || t === N)) {
164
+ if (N) for (let e of N.menus) e.remove();
165
+ v.after(...t.menus), _.label = t.def.name, N = t, k();
144
166
  }
145
167
  };
146
- if (_.push(S.onFront(I)), I(), l.clock !== !1) {
147
- let e = v.querySelector(":scope > [slot=\"end\"]"), t = !e;
148
- e || (e = document.createElement("vf-label"), e.slot = "end", v.append(e));
168
+ if (g.push(x.onFront(P), x.onDialogs(P)), P(), c.clock !== !1) {
169
+ let e = _.querySelector(":scope > [slot=\"end\"]"), t = !e;
170
+ e || (e = document.createElement("vf-label"), e.slot = "end", _.append(e));
149
171
  let n = o(e);
150
- _.push(() => {
172
+ g.push(() => {
151
173
  n(), t && e.remove();
152
174
  });
153
175
  }
154
- let L = /* @__PURE__ */ new WeakSet(), R = () => {
155
- if (E) for (let e of c.stackingOrder) {
176
+ let F = /* @__PURE__ */ new WeakSet(), I = () => {
177
+ if (T) for (let e of s.stackingOrder) {
156
178
  if (!(e instanceof t)) continue;
157
- let n = S.itemOf(e);
179
+ let n = x.itemOf(e);
158
180
  if (n == null) continue;
159
- let r = E.item(n);
160
- r ? (L.add(e), e.heading !== r.name && (e.heading = r.name)) : L.has(e) && E.get().available && S.close(e, { to: null });
181
+ let r = T.item(n);
182
+ r ? (F.add(e), e.heading !== r.name && (e.heading = r.name)) : F.has(e) && T.get().available && x.close(e, { to: null });
161
183
  }
162
184
  };
163
- return E && _.push(E.subscribe(R), S.onWindows(R)), h?.hold(), {
164
- desktop: c,
165
- windows: S,
166
- catalog: E,
167
- apps: w,
185
+ return T && g.push(T.subscribe(I), x.onWindows(I)), m?.hold(), {
186
+ desktop: s,
187
+ windows: x,
188
+ catalog: T,
189
+ apps: C,
168
190
  ready: (async () => {
169
191
  try {
170
- E && (await E.refresh(), p?.catalog?.seed && await E.seed(p.catalog.seed));
171
- for (let { item: e, app: t } of a(h?.saved ?? null)) await w[t]?.open?.({ item: e });
172
- let e = h?.saved?.active;
192
+ T && (await T.refresh(), f?.catalog?.seed && await T.seed(f.catalog.seed));
193
+ for (let { item: e, app: t } of a(m?.saved ?? null)) await C[t]?.open?.({ item: e });
194
+ let e = m?.saved?.active;
173
195
  if (e) {
174
- let t = S.windowFor(e);
175
- t && c.bringToFront(t);
196
+ let t = x.windowFor(e);
197
+ t && s.bringToFront(t);
176
198
  }
177
199
  } finally {
178
- if (h) {
179
- let e = h.start(() => ({
180
- windows: S.snapshot(),
181
- active: S.itemOf(c.activeWindow),
182
- pattern: c.pattern ?? null
200
+ if (m) {
201
+ let e = m.start(() => ({
202
+ windows: x.snapshot(),
203
+ active: x.itemOf(s.activeWindow),
204
+ pattern: s.pattern ?? null
183
205
  }), (e) => {
184
206
  let t = [
185
- S.onLayout(e),
186
- S.onWindows(e),
187
- ...E ? [E.subscribe(e)] : []
207
+ x.onLayout(e),
208
+ x.onWindows(e),
209
+ ...T ? [T.subscribe(e)] : []
188
210
  ];
189
- return c.addEventListener("vf-activate", e), () => {
211
+ return s.addEventListener("vf-activate", e), () => {
190
212
  for (let e of t) e();
191
- c.removeEventListener("vf-activate", e);
213
+ s.removeEventListener("vf-activate", e);
192
214
  };
193
215
  });
194
- _.push(e), h.release();
216
+ g.push(e), m.release();
195
217
  }
196
218
  }
197
219
  })(),
198
- alert: j,
199
220
  dispose() {
200
- for (let e of P.splice(0).reverse()) e();
201
- if (F) for (let e of F.menus) e.remove();
202
- F = null;
203
- for (let e of _.splice(0)) e();
204
- S.dispose(), E?.dispose();
221
+ for (let e of M.splice(0).reverse()) e();
222
+ if (N) for (let e of N.menus) e.remove();
223
+ N = null;
224
+ for (let e of g.splice(0)) e();
225
+ x.dispose(), T?.dispose();
205
226
  }
206
227
  };
207
228
  }
208
- function f(e) {
229
+ function d(e) {
209
230
  let t = e.data;
210
231
  return typeof t?.app == "string" ? t.app : null;
211
232
  }
212
233
  //#endregion
213
- export { l as APP_KIND, f as appIdOf, d as createShell, c as defineApp };
234
+ export { c as APP_KIND, d as appIdOf, u as createShell, s as defineApp };
@@ -29,6 +29,9 @@
29
29
  * resizable window's pin when its size changed, floors it at the window's
30
30
  * own size limits and caps it to the window area, so growing back restores
31
31
  * every place.
32
+ * - Dialogs: `holdDialog()` holds an application's dialog under it, in the
33
+ * desktop. The manager follows each held dialog's `open` attribute, and
34
+ * `asking` is the application of the one opened last and still open.
32
35
  */
33
36
  import { VfWindow } from '../index.js';
34
37
  import type { VfDesktop, VfViewportBox } from '../index.js';
@@ -151,8 +154,22 @@ export declare function createWindowManager(desktop: VfDesktop, options: WindowM
151
154
  windowsOf(app: string): VfWindow[];
152
155
  /** A window's item, or null. */
153
156
  itemOf(win: Element | null): string | null;
154
- /** A window's application, or null when the manager doesn't hold it. */
155
- appOf(win: Element | null): string | null;
157
+ /** A window's or a held dialog's application, or null when the manager doesn't hold it. */
158
+ appOf(el: Element | null): string | null;
159
+ /**
160
+ * Hold an application's dialog: a `vf-dialog`, or an element that renders
161
+ * one inside itself. It is appended to the desktop unless it is already
162
+ * in it, and its `open` is followed from then on.
163
+ */
164
+ holdDialog(dialog: Element, { app }: {
165
+ app: string;
166
+ }): void;
167
+ /** Let a held dialog go. It stays where it is; the caller removes it. */
168
+ releaseDialog(dialog: Element): void;
169
+ /** An application's held dialogs. */
170
+ dialogsOf(app: string): Element[];
171
+ /** The application of the held dialog opened last and still open, or null. */
172
+ readonly asking: string | null;
156
173
  /** Whether `win` is a palette. */
157
174
  isPalette(win: Element | null): boolean;
158
175
  /** Change the item a window shows: an untitled document's first save, say. */
@@ -209,6 +226,8 @@ export declare function createWindowManager(desktop: VfDesktop, options: WindowM
209
226
  beforeFront(fn: (win: HTMLElement | null) => void): () => void;
210
227
  /** The front application changed. */
211
228
  onFront(fn: (app: string) => void): () => void;
229
+ /** A held dialog opened or closed, so `asking` may have changed. */
230
+ onDialogs(fn: () => void): () => void;
212
231
  dispose(): void;
213
232
  };
214
233
  export type WindowManager = ReturnType<typeof createWindowManager>;