vintage-frames 0.15.0 → 0.16.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.
- package/custom-elements.json +907 -99
- package/dist/chrome.d.ts +10 -1
- package/dist/chrome.js +8 -3
- package/dist/components/vf-desktop.d.ts +5 -2
- package/dist/components/vf-desktop.js +5 -5
- package/dist/components/vf-dialog.d.ts +14 -8
- package/dist/components/vf-dialog.js +27 -27
- package/dist/components/vf-menu-bar.js +9 -10
- package/dist/components/vf-menu-item.js +18 -13
- package/dist/components/vf-menu.d.ts +8 -3
- package/dist/components/vf-menu.js +85 -61
- package/dist/components/vf-number-field.d.ts +4 -0
- package/dist/components/vf-number-field.js +28 -25
- package/dist/components/vf-paragraph.d.ts +3 -1
- package/dist/components/vf-paragraph.js +7 -0
- package/dist/components/vf-scroll-area.d.ts +16 -1
- package/dist/components/vf-scroll-area.js +39 -29
- package/dist/components/vf-select.d.ts +5 -4
- package/dist/components/vf-select.js +26 -17
- package/dist/components/vf-text-field.d.ts +5 -5
- package/dist/components/vf-text-field.js +1 -1
- package/dist/components/vf-window.d.ts +22 -5
- package/dist/components/vf-window.js +113 -98
- package/dist/cursor.d.ts +8 -0
- package/dist/cursor.js +70 -73
- package/dist/index.d.ts +2 -1
- package/dist/modal-dialog.d.ts +27 -5
- package/dist/modal-dialog.js +63 -51
- package/dist/shell/catalog.d.ts +1 -1
- package/dist/shell/catalog.js +32 -18
- package/dist/shell/finder.d.ts +6 -1
- package/dist/shell/finder.js +182 -176
- package/dist/shell/index.d.ts +1 -1
- package/dist/shell/pure.d.ts +10 -0
- package/dist/shell/pure.js +4 -0
- package/dist/shell/shell.d.ts +16 -3
- package/dist/shell/shell.js +141 -126
- package/dist/shell/windows.d.ts +57 -13
- package/dist/shell/windows.js +214 -154
- package/dist/text-control.d.ts +46 -3
- package/dist/text-control.js +33 -5
- package/dist/top-layer.d.ts +13 -0
- package/dist/top-layer.js +16 -0
- package/docs/SPEC.md +25 -24
- package/editor/vscode.html-custom-data.json +36 -11
- package/editor/web-types.json +81 -23
- package/package.json +5 -1
package/dist/shell/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export { createShell, defineApp, appIdOf, APP_KIND } from './shell.js';
|
|
13
13
|
export type { AppContext, AppDefinition, CatalogSetup, KindDefinition, Shell, ShellOptions } from './shell.js';
|
|
14
14
|
export { createWindowManager, focusInto } from './windows.js';
|
|
15
|
-
export type { AdoptOptions, ArrangeGroup, BoxInput, FocusHome, HomeBox, OpenOptions, PlacePolicy, Placed, WindowManager, WindowManagerOptions, } from './windows.js';
|
|
15
|
+
export type { AdoptOptions, ArrangeGroup, BoxInput, CloseHandler, FocusHome, HomeBox, OpenOptions, PlacePolicy, Placed, WindowManager, WindowManagerOptions, } from './windows.js';
|
|
16
16
|
export { finder, FINDER } from './finder.js';
|
|
17
17
|
export type { FinderAlert, FinderAlertHandler, FinderAlerts, FinderApi, FinderArt, FinderCommand, FinderCommandSpec, FinderFailedAction, FinderMenu, FinderOpen, FinderOptions, } from './finder.js';
|
|
18
18
|
export { fileByDrag } from './filing.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vintage-frames/shell/pure` — the shell's pure modules on their own: the
|
|
3
|
+
* catalog, the geometry and the saved session. They import nothing from the
|
|
4
|
+
* kit and touch no DOM at import, so a site's own pure modules and their
|
|
5
|
+
* tests import them under Node. `vintage-frames/shell` exports the same
|
|
6
|
+
* names.
|
|
7
|
+
*/
|
|
8
|
+
export * from './catalog.js';
|
|
9
|
+
export * from './geometry.js';
|
|
10
|
+
export * from './state.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { DISK as e, FOLDER as t, TRASH as n, UNTITLED_FOLDER as r, childrenOf as i, containerOf as a, copyName as o, createCatalog as s, descendantsOf as c, enclosingFolders as l, isContainerKind as u, isInside as d, isTrashed as f, isVolume as p, itemCount as m, itemOf as h, memoryStorage as g, nextFolderName as _ } from "./catalog.js";
|
|
2
|
+
import { BAND as v, CASCADE_SLOTS as y, CASCADE_STEP as b, ICON_CELL as x, ICON_COLUMN as S, ICON_INSET as C, ICON_ROW as w, NEAR as T, WINDOW_ORIGIN as E, cascadeFrom as D, cascadeSlot as O, cascadedBox as k, centeredBox as A, cleanUp as j, collisions as M, desktopLattice as N, fieldExtent as P, fillOrder as F, folderLattice as I, frameOf as L, isPin as R, latticeCell as z, latticeSlot as B, nearBox as V, nextFreeCell as H, pinOf as U, pinTo as W, trashCell as G, windowOrigin as K } from "./geometry.js";
|
|
3
|
+
import { localStorageState as q, mergeSession as J, openWindowsOf as Y, readSession as X } from "./state.js";
|
|
4
|
+
export { v as BAND, y as CASCADE_SLOTS, b as CASCADE_STEP, e as DISK, 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, 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, c as descendantsOf, N as desktopLattice, l as enclosingFolders, P as fieldExtent, F as fillOrder, I as folderLattice, L as frameOf, 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, q as localStorageState, g as memoryStorage, J as mergeSession, V as nearBox, _ as nextFolderName, H as nextFreeCell, Y as openWindowsOf, U as pinOf, W as pinTo, X as readSession, G as trashCell, K as windowOrigin };
|
package/dist/shell/shell.d.ts
CHANGED
|
@@ -10,16 +10,18 @@
|
|
|
10
10
|
* - While an application's held dialog is open, the bar shows that
|
|
11
11
|
* application's name and menus instead; nothing else changes.
|
|
12
12
|
* - Each application's `init(ctx)` gets the desktop, the window manager, its
|
|
13
|
-
* own menus and
|
|
13
|
+
* own menus, dialogs and window markup, the system menu, the other applications' actions
|
|
14
14
|
* (read at the call), the catalog when a Finder runs, the site's services,
|
|
15
15
|
* `ask()`, `gate()`, `onFront()` and `onDispose()`. Everything it sets up
|
|
16
16
|
* comes down through `onDispose()`, and its dialogs with it, so a hot
|
|
17
17
|
* reload starts clean. The shell composes no UI of its own.
|
|
18
18
|
* - A window showing a catalog item follows it: its title follows a rename,
|
|
19
19
|
* and it closes when the item is removed.
|
|
20
|
-
* - The boot
|
|
21
|
-
*
|
|
20
|
+
* - The boot waits for the desktop and its menu bar to render, reads the
|
|
21
|
+
* catalog, seeds it once, reopens the last session's windows deepest
|
|
22
|
+
* first and the active one last, then starts saving.
|
|
22
23
|
*/
|
|
24
|
+
import { VfWindow } from '../index.js';
|
|
23
25
|
import type { VfDesktop, VfDialog, VfMenu, VfMenuItem, VfModalDialog, VfViewportBox } from '../index.js';
|
|
24
26
|
import type { Catalog, CatalogStorage, Item, Volumes } from './catalog.js';
|
|
25
27
|
import type { WindowManager } from './windows.js';
|
|
@@ -64,6 +66,11 @@ export interface AppDefinition<Actions = unknown> {
|
|
|
64
66
|
* an element that renders one inside itself. Held under the application.
|
|
65
67
|
*/
|
|
66
68
|
dialogs?: string;
|
|
69
|
+
/**
|
|
70
|
+
* A fragment of windows, each with a `data-window` name, kept inert:
|
|
71
|
+
* `ctx.window(name)` makes a fresh copy of one on each call.
|
|
72
|
+
*/
|
|
73
|
+
windows?: string;
|
|
67
74
|
/** The catalog kinds it opens. */
|
|
68
75
|
kinds?: Record<string, KindDefinition>;
|
|
69
76
|
/** The Finder's: the catalog every application shares. */
|
|
@@ -107,6 +114,12 @@ export interface AppContext {
|
|
|
107
114
|
systemItem(value: string, label: string, fn: () => void): VfMenuItem;
|
|
108
115
|
/** One of its dialogs by `data-dialog`; throws when the markup drifts. */
|
|
109
116
|
dialog<T extends HTMLElement = VfDialog>(name: string): T;
|
|
117
|
+
/**
|
|
118
|
+
* A fresh copy of one of its windows by `data-window`, upgraded and not yet
|
|
119
|
+
* appended: `open({ create: () => ctx.window('viewer') })`. Throws when the
|
|
120
|
+
* markup drifts.
|
|
121
|
+
*/
|
|
122
|
+
window<T extends HTMLElement = VfWindow>(name: string): T;
|
|
110
123
|
/** Hold a dialog it made in code: appended to the desktop, held under it, removed with it. Returns it. */
|
|
111
124
|
hold<T extends Element>(dialog: T): T;
|
|
112
125
|
/** Let a dialog it holds go, and remove it. */
|
package/dist/shell/shell.js
CHANGED
|
@@ -2,99 +2,103 @@ import { onScaleChange as e } from "../scale.js";
|
|
|
2
2
|
import { VfWindow as t } from "../components/vf-window.js";
|
|
3
3
|
import "../index.js";
|
|
4
4
|
import { createCatalog as n } from "./catalog.js";
|
|
5
|
-
import { createWindowManager as r, deepActiveElement as i } from "./windows.js";
|
|
6
|
-
import { openWindowsOf as
|
|
7
|
-
import { startClock as
|
|
5
|
+
import { createWindowManager as r, deepActiveElement as i, desktopRendered as a } from "./windows.js";
|
|
6
|
+
import { openWindowsOf as o } from "./state.js";
|
|
7
|
+
import { startClock as s } from "./clock.js";
|
|
8
8
|
//#region src/shell/shell.ts
|
|
9
|
-
function
|
|
9
|
+
function c(e) {
|
|
10
10
|
return e;
|
|
11
11
|
}
|
|
12
|
-
var
|
|
13
|
-
function
|
|
14
|
-
let
|
|
15
|
-
if (!
|
|
16
|
-
let
|
|
17
|
-
if (!
|
|
18
|
-
|
|
19
|
-
let
|
|
20
|
-
|
|
21
|
-
let
|
|
22
|
-
if (
|
|
12
|
+
var l = "app", u = (e) => e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement || e?.isContentEditable === !0;
|
|
13
|
+
function d(c, l) {
|
|
14
|
+
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");
|
|
15
|
+
if (!v) throw Error("vintage-frames/shell: the desktop has no vf-menu-bar");
|
|
16
|
+
let y = v.querySelector(":scope > vf-menu");
|
|
17
|
+
if (!y) throw Error("vintage-frames/shell: the menu bar has no system menu");
|
|
18
|
+
v.shortcuts = !0;
|
|
19
|
+
let b = h?.saved?.pattern;
|
|
20
|
+
b && (c.pattern = b);
|
|
21
|
+
let x = null;
|
|
22
|
+
if (l.fit === "viewport") {
|
|
23
23
|
let t = () => {
|
|
24
24
|
let e = {
|
|
25
|
-
width:
|
|
26
|
-
height:
|
|
25
|
+
width: c.width,
|
|
26
|
+
height: c.height
|
|
27
27
|
};
|
|
28
|
-
|
|
28
|
+
c.fitWithin(document.documentElement.clientWidth, document.documentElement.clientHeight), x?.resized(e);
|
|
29
29
|
};
|
|
30
30
|
t(), window.addEventListener("resize", t);
|
|
31
31
|
let n = e(t);
|
|
32
|
-
|
|
32
|
+
_.push(() => {
|
|
33
33
|
window.removeEventListener("resize", t), n();
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
-
let
|
|
37
|
-
defaultApp:
|
|
38
|
-
savedPin: (e) =>
|
|
36
|
+
let S = r(c, {
|
|
37
|
+
defaultApp: m,
|
|
38
|
+
savedPin: (e) => h?.pin(e) ?? null
|
|
39
39
|
});
|
|
40
|
-
|
|
41
|
-
let
|
|
42
|
-
e.target ===
|
|
40
|
+
x = S;
|
|
41
|
+
let C = (e) => {
|
|
42
|
+
e.target === c && c.clearActive();
|
|
43
43
|
};
|
|
44
|
-
|
|
45
|
-
let
|
|
46
|
-
|
|
47
|
-
art: (e) =>
|
|
48
|
-
open: (e, t) => void
|
|
44
|
+
c.addEventListener("pointerdown", C), _.push(() => c.removeEventListener("pointerdown", C));
|
|
45
|
+
let w = {}, T = /* @__PURE__ */ new Map();
|
|
46
|
+
T.set("app", {
|
|
47
|
+
art: (e) => d.find((t) => t.id === f(e))?.icon ?? "",
|
|
48
|
+
open: (e, t) => void w[f(e) ?? ""]?.open?.({ from: t }),
|
|
49
49
|
editable: !1
|
|
50
50
|
});
|
|
51
|
-
for (let e of
|
|
52
|
-
let
|
|
53
|
-
storage:
|
|
54
|
-
volumes:
|
|
55
|
-
listed: (e) =>
|
|
56
|
-
hooks: (e) =>
|
|
57
|
-
}) : null,
|
|
58
|
-
for (let [e, t] of
|
|
51
|
+
for (let e of d) for (let [t, n] of Object.entries(e.kinds ?? {})) T.set(t, n);
|
|
52
|
+
let E = p?.catalog ? n({
|
|
53
|
+
storage: p.catalog.storage,
|
|
54
|
+
volumes: p.catalog.volumes,
|
|
55
|
+
listed: (e) => T.has(e),
|
|
56
|
+
hooks: (e) => T.get(e)
|
|
57
|
+
}) : null, D = () => !!document.querySelector("vf-dialog[open]"), O = () => u(i()), k = /* @__PURE__ */ new Map(), A = () => {
|
|
58
|
+
for (let [e, t] of k) {
|
|
59
59
|
let n = !t();
|
|
60
60
|
e.disabled !== n && (e.disabled = n);
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
-
document.addEventListener("pointerdown",
|
|
64
|
-
let
|
|
63
|
+
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));
|
|
64
|
+
let j = (e) => {
|
|
65
65
|
if (!e) return [];
|
|
66
66
|
let t = document.createElement("div");
|
|
67
67
|
t.innerHTML = e, customElements.upgrade(t);
|
|
68
68
|
let n = [...t.children].filter((e) => e instanceof HTMLElement);
|
|
69
69
|
for (let e of n) e.remove();
|
|
70
70
|
return n;
|
|
71
|
-
},
|
|
71
|
+
}, M = d.map((e) => ({
|
|
72
72
|
def: e,
|
|
73
|
-
menus:
|
|
74
|
-
dialogs:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
menus: j(e.menus).filter((e) => e.localName === "vf-menu"),
|
|
74
|
+
dialogs: j(e.dialogs).filter((e) => e.dataset.dialog != null),
|
|
75
|
+
windows: (() => {
|
|
76
|
+
let t = document.createElement("template");
|
|
77
|
+
return t.innerHTML = e.windows ?? "", t.content;
|
|
78
|
+
})()
|
|
79
|
+
})), N = [];
|
|
80
|
+
for (let { def: e, menus: t, dialogs: n, windows: r } of M) {
|
|
81
|
+
let i = [], a = `vintage-frames/shell (${e.id})`, o = /* @__PURE__ */ new Set();
|
|
82
|
+
for (let t of n) S.holdDialog(t, { app: e.id }), o.add(t);
|
|
83
|
+
i.push(() => {
|
|
84
|
+
for (let e of o) S.releaseDialog(e), e.remove();
|
|
85
|
+
o.clear();
|
|
82
86
|
});
|
|
83
|
-
let
|
|
84
|
-
desktop:
|
|
85
|
-
windows:
|
|
87
|
+
let s = {
|
|
88
|
+
desktop: c,
|
|
89
|
+
windows: S,
|
|
86
90
|
menus: t,
|
|
87
|
-
systemMenu:
|
|
88
|
-
apps:
|
|
89
|
-
catalog:
|
|
90
|
-
services:
|
|
91
|
-
state:
|
|
92
|
-
kinds:
|
|
93
|
-
kind: (e) =>
|
|
94
|
-
app: (e) =>
|
|
91
|
+
systemMenu: y,
|
|
92
|
+
apps: w,
|
|
93
|
+
catalog: E,
|
|
94
|
+
services: g,
|
|
95
|
+
state: h,
|
|
96
|
+
kinds: T,
|
|
97
|
+
kind: (e) => T.get(e),
|
|
98
|
+
app: (e) => d.find((t) => t.id === e),
|
|
95
99
|
menu(e) {
|
|
96
100
|
let n = t.find((t) => t.dataset.menu === e);
|
|
97
|
-
if (!n) throw Error(`${
|
|
101
|
+
if (!n) throw Error(`${a}: missing menu ${e}`);
|
|
98
102
|
return n;
|
|
99
103
|
},
|
|
100
104
|
item(e) {
|
|
@@ -102,34 +106,40 @@ function u(s, c) {
|
|
|
102
106
|
let t = n.querySelector(`vf-menu-item[value="${CSS.escape(e)}"]`);
|
|
103
107
|
if (t) return t;
|
|
104
108
|
}
|
|
105
|
-
throw Error(`${
|
|
109
|
+
throw Error(`${a}: missing menu item ${e}`);
|
|
106
110
|
},
|
|
107
111
|
onMenu(e) {
|
|
108
|
-
for (let n of t)
|
|
109
|
-
if (
|
|
112
|
+
for (let n of t) s.on(n, "vf-menu-select", (t) => {
|
|
113
|
+
if (D()) return;
|
|
110
114
|
let { value: n, item: r } = t.detail;
|
|
111
115
|
e(n, r);
|
|
112
116
|
});
|
|
113
117
|
},
|
|
114
118
|
systemItem(e, t, n) {
|
|
115
|
-
let
|
|
116
|
-
return
|
|
117
|
-
!
|
|
118
|
-
}),
|
|
119
|
+
let r = document.createElement("vf-menu-item");
|
|
120
|
+
return r.setAttribute("value", e), r.textContent = t, y.append(r), s.on(y, "vf-menu-select", (t) => {
|
|
121
|
+
!D() && t.detail.value === e && n();
|
|
122
|
+
}), i.push(() => r.remove()), r;
|
|
119
123
|
},
|
|
120
124
|
dialog(e) {
|
|
121
125
|
let t = n.find((t) => t.dataset.dialog === e);
|
|
122
|
-
if (!t) throw Error(`${
|
|
126
|
+
if (!t) throw Error(`${a}: missing dialog ${e}`);
|
|
123
127
|
return t;
|
|
124
128
|
},
|
|
129
|
+
window(e) {
|
|
130
|
+
let t = [...r.children].find((t) => t.dataset.window === e);
|
|
131
|
+
if (!t) throw Error(`${a}: missing window ${e}`);
|
|
132
|
+
let n = document.importNode(t, !0);
|
|
133
|
+
return customElements.upgrade(n), n;
|
|
134
|
+
},
|
|
125
135
|
hold(t) {
|
|
126
|
-
return
|
|
136
|
+
return S.holdDialog(t, { app: e.id }), o.add(t), t;
|
|
127
137
|
},
|
|
128
138
|
release(e) {
|
|
129
|
-
|
|
139
|
+
o.delete(e) && (S.releaseDialog(e), e.remove());
|
|
130
140
|
},
|
|
131
141
|
ask(t) {
|
|
132
|
-
if (
|
|
142
|
+
if (S.appOf(t) !== e.id) throw Error(`${a}: ask() takes a dialog the application holds`);
|
|
133
143
|
return new Promise((e) => {
|
|
134
144
|
let n = (r) => {
|
|
135
145
|
r.target === t && (t.removeEventListener("vf-close", n), e(r.detail.returnValue));
|
|
@@ -138,97 +148,102 @@ function u(s, c) {
|
|
|
138
148
|
});
|
|
139
149
|
},
|
|
140
150
|
gate(e, t) {
|
|
141
|
-
|
|
151
|
+
k.set(e, t), i.push(() => k.delete(e));
|
|
142
152
|
let n = !t();
|
|
143
153
|
e.disabled !== n && (e.disabled = n);
|
|
144
154
|
},
|
|
145
|
-
typing:
|
|
146
|
-
modalOpen:
|
|
155
|
+
typing: O,
|
|
156
|
+
modalOpen: D,
|
|
147
157
|
onFront(t) {
|
|
148
|
-
t(
|
|
158
|
+
t(S.front === e.id), i.push(S.onFront((n) => t(n === e.id)));
|
|
149
159
|
},
|
|
150
|
-
on(e, t, n,
|
|
151
|
-
e.addEventListener(t, n,
|
|
160
|
+
on(e, t, n, r) {
|
|
161
|
+
e.addEventListener(t, n, r), i.push(() => e.removeEventListener(t, n, r));
|
|
152
162
|
},
|
|
153
163
|
onDispose(e) {
|
|
154
|
-
|
|
164
|
+
i.push(e);
|
|
155
165
|
}
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
for (let e of
|
|
166
|
+
}, l = e.init(s);
|
|
167
|
+
l && (w[e.id] = l), N.push(() => {
|
|
168
|
+
for (let e of i.splice(0).reverse()) e();
|
|
159
169
|
});
|
|
160
170
|
}
|
|
161
|
-
let
|
|
162
|
-
let e =
|
|
163
|
-
if (!(!t || t ===
|
|
164
|
-
if (
|
|
165
|
-
|
|
171
|
+
let P = null, F = () => {
|
|
172
|
+
let e = S.asking ?? S.front, t = M.find((t) => t.def.id === e) ?? M.find((e) => e.def.id === m) ?? null;
|
|
173
|
+
if (!(!t || t === P)) {
|
|
174
|
+
if (P) for (let e of P.menus) e.remove();
|
|
175
|
+
y.after(...t.menus), v.label = t.def.name, P = t, A();
|
|
166
176
|
}
|
|
167
177
|
};
|
|
168
|
-
if (
|
|
169
|
-
let e =
|
|
170
|
-
e || (e = document.createElement("vf-label"), e.slot = "end",
|
|
171
|
-
let n =
|
|
172
|
-
|
|
178
|
+
if (_.push(S.onFront(F), S.onDialogs(F)), F(), l.clock !== !1) {
|
|
179
|
+
let e = v.querySelector(":scope > [slot=\"end\"]"), t = !e;
|
|
180
|
+
e || (e = document.createElement("vf-label"), e.slot = "end", v.append(e));
|
|
181
|
+
let n = s(e);
|
|
182
|
+
_.push(() => {
|
|
173
183
|
n(), t && e.remove();
|
|
174
184
|
});
|
|
175
185
|
}
|
|
176
|
-
let
|
|
177
|
-
if (
|
|
186
|
+
let I = /* @__PURE__ */ new WeakSet(), L = () => {
|
|
187
|
+
if (E) for (let e of c.stackingOrder) {
|
|
178
188
|
if (!(e instanceof t)) continue;
|
|
179
|
-
let n =
|
|
189
|
+
let n = S.itemOf(e);
|
|
180
190
|
if (n == null) continue;
|
|
181
|
-
let r =
|
|
182
|
-
r ? (
|
|
191
|
+
let r = E.item(n);
|
|
192
|
+
r ? (I.add(e), e.heading !== r.name && (e.heading = r.name)) : I.has(e) && E.get().available && S.close(e, { to: null });
|
|
183
193
|
}
|
|
184
194
|
};
|
|
185
|
-
return
|
|
186
|
-
desktop:
|
|
187
|
-
windows:
|
|
188
|
-
catalog:
|
|
189
|
-
apps:
|
|
195
|
+
return E && _.push(E.subscribe(L), S.onWindows(L)), h?.hold(), {
|
|
196
|
+
desktop: c,
|
|
197
|
+
windows: S,
|
|
198
|
+
catalog: E,
|
|
199
|
+
apps: w,
|
|
190
200
|
ready: (async () => {
|
|
191
201
|
try {
|
|
192
|
-
|
|
193
|
-
for (let { item: e, app: t } of
|
|
194
|
-
let e =
|
|
202
|
+
await a(c), E && (await E.refresh(), p?.catalog?.seed && await E.seed(p.catalog.seed));
|
|
203
|
+
for (let { item: e, app: t } of o(h?.saved ?? null)) await w[t]?.open?.({ item: e });
|
|
204
|
+
let e = h?.saved?.active;
|
|
195
205
|
if (e) {
|
|
196
|
-
let t =
|
|
197
|
-
t &&
|
|
206
|
+
let t = S.windowFor(e);
|
|
207
|
+
t && c.bringToFront(t);
|
|
198
208
|
}
|
|
199
209
|
} finally {
|
|
200
|
-
if (
|
|
201
|
-
let e =
|
|
202
|
-
windows:
|
|
203
|
-
active:
|
|
204
|
-
pattern:
|
|
210
|
+
if (h) {
|
|
211
|
+
let e = h.start(() => ({
|
|
212
|
+
windows: S.snapshot(),
|
|
213
|
+
active: S.itemOf(c.activeWindow),
|
|
214
|
+
pattern: c.pattern ?? null
|
|
205
215
|
}), (e) => {
|
|
206
216
|
let t = [
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
...
|
|
217
|
+
S.onLayout(e),
|
|
218
|
+
S.onWindows(e),
|
|
219
|
+
...E ? [E.subscribe(e)] : []
|
|
210
220
|
];
|
|
211
|
-
|
|
221
|
+
c.addEventListener("vf-activate", e);
|
|
222
|
+
let n = new MutationObserver(e);
|
|
223
|
+
return n.observe(c, {
|
|
224
|
+
attributes: !0,
|
|
225
|
+
attributeFilter: ["pattern"]
|
|
226
|
+
}), () => {
|
|
212
227
|
for (let e of t) e();
|
|
213
|
-
|
|
228
|
+
c.removeEventListener("vf-activate", e), n.disconnect();
|
|
214
229
|
};
|
|
215
230
|
});
|
|
216
|
-
|
|
231
|
+
_.push(e), h.release();
|
|
217
232
|
}
|
|
218
233
|
}
|
|
219
234
|
})(),
|
|
220
235
|
dispose() {
|
|
221
|
-
for (let e of
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
for (let e of
|
|
225
|
-
|
|
236
|
+
for (let e of N.splice(0).reverse()) e();
|
|
237
|
+
if (P) for (let e of P.menus) e.remove();
|
|
238
|
+
P = null;
|
|
239
|
+
for (let e of _.splice(0)) e();
|
|
240
|
+
S.dispose(), E?.dispose();
|
|
226
241
|
}
|
|
227
242
|
};
|
|
228
243
|
}
|
|
229
|
-
function
|
|
244
|
+
function f(e) {
|
|
230
245
|
let t = e.data;
|
|
231
246
|
return typeof t?.app == "string" ? t.app : null;
|
|
232
247
|
}
|
|
233
248
|
//#endregion
|
|
234
|
-
export {
|
|
249
|
+
export { l as APP_KIND, f as appIdOf, d as createShell, c as defineApp };
|
package/dist/shell/windows.d.ts
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* none is active. `beforeFront` runs before a change, `onFront` reports it.
|
|
18
18
|
* - Palettes are an application's utility windows: shown while it is front
|
|
19
19
|
* and hidden otherwise, never closed or removed.
|
|
20
|
-
* - Opening a window moves keyboard focus into it
|
|
20
|
+
* - Opening a window moves keyboard focus into it, and a new window's field
|
|
21
|
+
* marked `autoselect` opens with its text selected; closing the active one
|
|
21
22
|
* moves focus to the window that becomes active, else to the closed
|
|
22
23
|
* window's icon, else to the desktop's icons. Focus left behind in a
|
|
23
24
|
* window that is no longer active — a title-bar press moves none — goes
|
|
@@ -29,12 +30,15 @@
|
|
|
29
30
|
* resizable window's pin when its size changed, floors it at the window's
|
|
30
31
|
* own size limits and caps it to the window area, so growing back restores
|
|
31
32
|
* every place.
|
|
33
|
+
* - The zoom box, for a window adopted with `zoom`: the window goes to its
|
|
34
|
+
* zoomed box and back to the box it had, kept as a pin; a zoomed window
|
|
35
|
+
* stays zoomed across a resize.
|
|
32
36
|
* - Dialogs: `holdDialog()` holds an application's dialog under it, in the
|
|
33
37
|
* desktop. The manager follows each held dialog's `open` attribute, and
|
|
34
38
|
* `asking` is the application of the one opened last and still open.
|
|
35
39
|
*/
|
|
36
40
|
import { VfWindow } from '../index.js';
|
|
37
|
-
import type { VfDesktop, VfViewportBox } from '../index.js';
|
|
41
|
+
import type { VfCloseKeys, VfDesktop, VfViewportBox } from '../index.js';
|
|
38
42
|
import type { Bands, Box, Pin, Policy, Size } from './geometry.js';
|
|
39
43
|
/** A box to write, any edge optional. */
|
|
40
44
|
export interface BoxInput {
|
|
@@ -68,6 +72,13 @@ export interface AdoptOptions {
|
|
|
68
72
|
policy?: ((cur: Box) => Policy) | null;
|
|
69
73
|
/** A box held across a raster resize while the window is near it. */
|
|
70
74
|
keep?: ((area: Box) => Box) | null;
|
|
75
|
+
/**
|
|
76
|
+
* Its zoom box: the window's zoomed box on the window area, given its
|
|
77
|
+
* current box (to keep its top-left, say). Set, the manager runs the zoom
|
|
78
|
+
* box: it goes to this box and back to the box the user had, and a window
|
|
79
|
+
* at it stays zoomed across a raster resize.
|
|
80
|
+
*/
|
|
81
|
+
zoom?: ((area: Box, box: Box) => BoxInput) | null;
|
|
71
82
|
/**
|
|
72
83
|
* A palette: shown while its application is front — and while the
|
|
73
84
|
* predicate holds, for one the application hides for its own reasons —
|
|
@@ -77,10 +88,14 @@ export interface AdoptOptions {
|
|
|
77
88
|
/**
|
|
78
89
|
* What the close box means. Unset, it closes the window. Set, the close box
|
|
79
90
|
* asks the application, which calls `close()` once it decides — after a
|
|
80
|
-
* Save prompt, say — or never.
|
|
91
|
+
* Save prompt, say — or never. It gets the modifier keys held on the
|
|
92
|
+
* close box's click; a close from a menu or code holds none. A handler
|
|
93
|
+
* that decides later returns its promise, so `closeAll()` waits for it.
|
|
81
94
|
*/
|
|
82
|
-
close?:
|
|
95
|
+
close?: CloseHandler | null;
|
|
83
96
|
}
|
|
97
|
+
/** A window's close handler: decides whether it closes, and calls `close()` if so. */
|
|
98
|
+
export type CloseHandler = (win: VfWindow, keys: Partial<VfCloseKeys>) => void | Promise<void>;
|
|
84
99
|
/** What opening a window for an item declares. */
|
|
85
100
|
export interface OpenOptions extends Omit<AdoptOptions, 'pin' | 'palette'> {
|
|
86
101
|
/** Makes the window, not yet appended. */
|
|
@@ -108,16 +123,26 @@ export interface WindowManagerOptions {
|
|
|
108
123
|
/** A saved pin by item — the session the boot read. */
|
|
109
124
|
savedPin?: (item: string) => Pin | null;
|
|
110
125
|
}
|
|
126
|
+
/** Settles once the desktop and its menu bar have rendered, so `workArea` measures the bar. */
|
|
127
|
+
export declare function desktopRendered(desktop: VfDesktop): Promise<void>;
|
|
111
128
|
/** The innermost focused element, through open shadow roots. */
|
|
112
129
|
export declare function deepActiveElement(): Element | null;
|
|
113
130
|
/**
|
|
114
|
-
* Move keyboard focus
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
131
|
+
* Move keyboard focus into `win`'s content once that content has rendered —
|
|
132
|
+
* and then only if `when`, read at that point, still holds: to the first
|
|
133
|
+
* descendant marked `autofocus` or `autoselect`, else to the first thing that
|
|
134
|
+
* takes the focus. With `select`, a control marked `autoselect` has its text
|
|
135
|
+
* selected too; the window manager passes it on a window's first open only,
|
|
136
|
+
* so a caret placed in the window survives a return to it. An element whose
|
|
137
|
+
* `tabindex` is negative is passed over: a roving cell parked off the tab
|
|
138
|
+
* order, a disabled control. A kit host with no `tabindex` goes through,
|
|
139
|
+
* since it hands the focus on inside itself. Resolves whether the focus
|
|
140
|
+
* moved: a window with nothing focusable in its content keeps the focus where
|
|
141
|
+
* it was.
|
|
118
142
|
*/
|
|
119
|
-
export declare function focusInto(win: Element, { when }?: {
|
|
143
|
+
export declare function focusInto(win: Element, { when, select }?: {
|
|
120
144
|
when?: () => boolean;
|
|
145
|
+
select?: boolean;
|
|
121
146
|
}): Promise<boolean>;
|
|
122
147
|
/** The window manager over a desktop. */
|
|
123
148
|
export declare function createWindowManager(desktop: VfDesktop, options: WindowManagerOptions): {
|
|
@@ -135,7 +160,8 @@ export declare function createWindowManager(desktop: VfDesktop, options: WindowM
|
|
|
135
160
|
* Open the window for `item`: raise it if one is open, else make it,
|
|
136
161
|
* append it, adopt it and place it — this session's pin, then the saved
|
|
137
162
|
* one, then `place`, by default the first free cascade slot — and show it
|
|
138
|
-
* out of `from`. Keyboard focus moves into it
|
|
163
|
+
* out of `from`. Keyboard focus moves into it, and a new window's field
|
|
164
|
+
* marked `autoselect` has its text selected. Returns the window.
|
|
139
165
|
*/
|
|
140
166
|
open(opts: OpenOptions): VfWindow;
|
|
141
167
|
/**
|
|
@@ -146,8 +172,18 @@ export declare function createWindowManager(desktop: VfDesktop, options: WindowM
|
|
|
146
172
|
close(win: VfWindow, { to }?: {
|
|
147
173
|
to?: VfViewportBox | null;
|
|
148
174
|
}): Promise<boolean>;
|
|
149
|
-
/**
|
|
150
|
-
|
|
175
|
+
/**
|
|
176
|
+
* The close box's route: the window's own close, given the modifier keys
|
|
177
|
+
* held, or a plain close. Resolves whether the window closed, once its
|
|
178
|
+
* close handler has settled.
|
|
179
|
+
*/
|
|
180
|
+
requestClose(win: VfWindow, keys?: Partial<VfCloseKeys>): Promise<boolean>;
|
|
181
|
+
/**
|
|
182
|
+
* Ask each of an application's windows to close, front to back, palettes
|
|
183
|
+
* aside: a Quit. Resolves false at the first window still open once its
|
|
184
|
+
* handler has settled — a Cancel — and true when all have closed.
|
|
185
|
+
*/
|
|
186
|
+
closeAll(app: string, keys?: Partial<VfCloseKeys>): Promise<boolean>;
|
|
151
187
|
/** The window showing `item`, or null. */
|
|
152
188
|
windowFor(item: string): VfWindow | null;
|
|
153
189
|
/** Every window of an application, back to front. */
|
|
@@ -180,8 +216,16 @@ export declare function createWindowManager(desktop: VfDesktop, options: WindowM
|
|
|
180
216
|
hasWindows(app: string): boolean;
|
|
181
217
|
/** Re-read every palette's predicate: an application hid or showed one. */
|
|
182
218
|
palettesChanged(): void;
|
|
183
|
-
/** Write a box for a gesture the owner runs itself —
|
|
219
|
+
/** Write a box for a gesture the owner runs itself — its group's arrange, a zoom of its own. */
|
|
184
220
|
write(win: VfWindow, box: Placed): void;
|
|
221
|
+
/**
|
|
222
|
+
* The zoom box, for a window adopted with a `zoom`: away from its zoomed
|
|
223
|
+
* box, the window goes there, its box remembered; at it, the window goes
|
|
224
|
+
* back to that box, carried by its pin through any raster resize since,
|
|
225
|
+
* or to its `place` with none remembered. The zoom box's press runs it;
|
|
226
|
+
* so can a menu command.
|
|
227
|
+
*/
|
|
228
|
+
zoom(win: VfWindow): void;
|
|
185
229
|
/** The box a placement would write, not written. */
|
|
186
230
|
clamped(win: VfWindow, box: BoxInput): Placed;
|
|
187
231
|
/** A window's `place` on the live area, clamped, or null. */
|