vintage-frames 0.19.1 → 0.20.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.
- package/README.md +1 -18
- package/custom-elements.json +8 -8
- package/dist/text-control.d.ts +3 -3
- package/docs/SPEC.md +3 -3
- package/editor/vscode.html-custom-data.json +3 -3
- package/editor/web-types.json +7 -7
- package/package.json +4 -34
- package/dist/build/app-file.d.ts +0 -21
- package/dist/build/app-file.js +0 -42
- package/dist/build/box.d.ts +0 -57
- package/dist/build/box.js +0 -93
- package/dist/build/client.d.ts +0 -12
- package/dist/build/frame-picture.d.ts +0 -6
- package/dist/build/frame-picture.js +0 -4
- package/dist/build/frame.d.ts +0 -11
- package/dist/build/frame.js +0 -44
- package/dist/build/index.d.ts +0 -14
- package/dist/build/index.js +0 -4
- package/dist/build/plugins.d.ts +0 -41
- package/dist/build/plugins.js +0 -134
- package/dist/build/png.d.ts +0 -24
- package/dist/build/png.js +0 -116
- package/dist/shell/alert.d.ts +0 -40
- package/dist/shell/alert.js +0 -41
- package/dist/shell/app-file.d.ts +0 -115
- package/dist/shell/app-file.js +0 -189
- package/dist/shell/catalog.d.ts +0 -250
- package/dist/shell/catalog.js +0 -349
- package/dist/shell/clock.d.ts +0 -16
- package/dist/shell/clock.js +0 -36
- package/dist/shell/filing.d.ts +0 -39
- package/dist/shell/filing.js +0 -68
- package/dist/shell/finder.d.ts +0 -180
- package/dist/shell/finder.js +0 -676
- package/dist/shell/geometry.d.ts +0 -203
- package/dist/shell/geometry.js +0 -257
- package/dist/shell/index.d.ts +0 -30
- package/dist/shell/index.js +0 -12
- package/dist/shell/pure.d.ts +0 -13
- package/dist/shell/pure.js +0 -6
- package/dist/shell/shell.d.ts +0 -177
- package/dist/shell/shell.js +0 -277
- package/dist/shell/state.d.ts +0 -88
- package/dist/shell/state.js +0 -103
- package/dist/shell/storage.d.ts +0 -14
- package/dist/shell/storage.js +0 -41
- package/dist/shell/version.d.ts +0 -2
- package/dist/shell/version.js +0 -4
- package/dist/shell/windows.d.ts +0 -278
- package/dist/shell/windows.js +0 -447
- package/dist/styles/body-strike.d.ts +0 -12
- package/dist/styles/body-strike.js +0 -3961
- package/dist/styles/display-strike.d.ts +0 -12
- package/dist/styles/display-strike.js +0 -4343
- package/dist/styles/strike.d.ts +0 -30
package/dist/shell/shell.d.ts
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The shell: applications over one desktop.
|
|
3
|
-
*
|
|
4
|
-
* - The menu bar holds the system menu — the page's own first menu, the
|
|
5
|
-
* Apple menu — then the front application's menus, then the clock in the
|
|
6
|
-
* bar's `end` slot. Each application's menus are parsed once and the same
|
|
7
|
-
* nodes come back each time it is front, so items keep their state. A menu
|
|
8
|
-
* off the bar has no key equivalents, so only the front application's fire.
|
|
9
|
-
* The bar's label is the front application's name.
|
|
10
|
-
* - The system menu is the page's own items, then what Apple Menu Items
|
|
11
|
-
* holds, by name, following the folder as it changes. Choosing an entry
|
|
12
|
-
* opens its item as a double-click would. No application adds to it.
|
|
13
|
-
* - While an application's held dialog is open, the bar shows that
|
|
14
|
-
* application's name and menus instead; nothing else changes.
|
|
15
|
-
* - Each application's `init(ctx)` gets the desktop, the window manager, its
|
|
16
|
-
* own menus, dialogs and window markup, the other applications' actions
|
|
17
|
-
* (read at the call), the catalog when a Finder runs, the site's services,
|
|
18
|
-
* `ask()`, `gate()`, `onFront()` and `onDispose()`. Everything it sets up
|
|
19
|
-
* comes down through `onDispose()`, and its dialogs with it, so a hot
|
|
20
|
-
* reload starts clean. The shell composes no UI of its own.
|
|
21
|
-
* - A window showing a catalog item follows it: its title follows a rename,
|
|
22
|
-
* and it closes when the item is removed.
|
|
23
|
-
* - The boot waits for the desktop and its menu bar to render, reads the
|
|
24
|
-
* catalog, seeds it once, reopens the last session's windows deepest
|
|
25
|
-
* first and the active one last, opens what Startup Items holds, then
|
|
26
|
-
* starts saving.
|
|
27
|
-
*/
|
|
28
|
-
import { VfWindow } from '../index.js';
|
|
29
|
-
import type { VfDesktop, VfDialog, VfMenu, VfMenuItem, VfModalDialog, VfViewportBox } from '../index.js';
|
|
30
|
-
import type { Catalog, CatalogStorage, Item, SystemFolders, Volumes } from './catalog.js';
|
|
31
|
-
import type { WindowManager } from './windows.js';
|
|
32
|
-
import type { ShellState } from './state.js';
|
|
33
|
-
/** A kind of catalog item an application registers: how it looks and opens. */
|
|
34
|
-
export interface KindDefinition {
|
|
35
|
-
/** Its 32×32 art: one URL, or one per item. */
|
|
36
|
-
art: string | ((item: Item) => string);
|
|
37
|
-
/** Open the item, its window out of `from` (its icon's box). */
|
|
38
|
-
open(item: Item, from: VfViewportBox | null): void;
|
|
39
|
-
/** Renameable in place. Default true. */
|
|
40
|
-
editable?: boolean;
|
|
41
|
-
/** Bytes, for Empty Trash's alert. */
|
|
42
|
-
size?(item: Item): number;
|
|
43
|
-
/** Copy `from`'s payload to `to` when the Finder copies the item. */
|
|
44
|
-
copy?(from: Item, to: Item): void | Promise<void>;
|
|
45
|
-
/** Empty Trash removed the item: drop its payload. */
|
|
46
|
-
onRemove?(item: Item): void | Promise<void>;
|
|
47
|
-
/** Make an item of a pasted or dropped file in `parent`; resolves whether it did. */
|
|
48
|
-
claim?(file: Blob, parent: string | null): Promise<boolean>;
|
|
49
|
-
/** The item on the system clipboard, as a file. */
|
|
50
|
-
export?(item: Item): Promise<Blob | null>;
|
|
51
|
-
}
|
|
52
|
-
/** The catalog a Finder brings: where it is stored, its volumes, its System Folder and its seed. */
|
|
53
|
-
export interface CatalogSetup {
|
|
54
|
-
storage: CatalogStorage | null;
|
|
55
|
-
volumes?: Volumes;
|
|
56
|
-
/** The System Folder, named; its folders drive the Apple menu and what opens at startup. */
|
|
57
|
-
system?: SystemFolders;
|
|
58
|
-
/** Run once per storage, to store the site's defaults. */
|
|
59
|
-
seed?: (catalog: Catalog) => void | Promise<void>;
|
|
60
|
-
}
|
|
61
|
-
/** One application. */
|
|
62
|
-
export interface AppDefinition<Actions = unknown> {
|
|
63
|
-
id: string;
|
|
64
|
-
/** The bar's accessible name while it is front. */
|
|
65
|
-
name: string;
|
|
66
|
-
/** Its 32×32 art, for the Finder's icon of it. */
|
|
67
|
-
icon?: string;
|
|
68
|
-
/** A fragment of `vf-menu` elements, each with a `data-menu` name. */
|
|
69
|
-
menus?: string;
|
|
70
|
-
/**
|
|
71
|
-
* A fragment of dialogs, each with a `data-dialog` name: a `vf-dialog`, or
|
|
72
|
-
* an element that renders one inside itself. Held under the application.
|
|
73
|
-
*/
|
|
74
|
-
dialogs?: string;
|
|
75
|
-
/**
|
|
76
|
-
* A fragment of windows, each with a `data-window` name, kept inert:
|
|
77
|
-
* `ctx.window(name)` makes a fresh copy of one on each call.
|
|
78
|
-
*/
|
|
79
|
-
windows?: string;
|
|
80
|
-
/** The catalog kinds it opens. */
|
|
81
|
-
kinds?: Record<string, KindDefinition>;
|
|
82
|
-
/** The Finder's: the catalog every application shares. */
|
|
83
|
-
catalog?: CatalogSetup;
|
|
84
|
-
/**
|
|
85
|
-
* Set everything up and return what other applications may call. An
|
|
86
|
-
* application that reopens windows across a reload — or opens from an
|
|
87
|
-
* icon — provides `open({ item, from })`.
|
|
88
|
-
*/
|
|
89
|
-
init(ctx: AppContext): Actions | void;
|
|
90
|
-
}
|
|
91
|
-
/** What an application's `init` gets. */
|
|
92
|
-
export interface AppContext {
|
|
93
|
-
readonly desktop: VfDesktop;
|
|
94
|
-
readonly windows: WindowManager;
|
|
95
|
-
/** Its own menus, parsed from `menus`. */
|
|
96
|
-
readonly menus: VfMenu[];
|
|
97
|
-
/** Every application's actions by id, read at the call. */
|
|
98
|
-
readonly apps: Record<string, any>;
|
|
99
|
-
/** The catalog, when a Finder runs; read before the boot has refreshed it, it lists the kept containers alone. */
|
|
100
|
-
readonly catalog: Catalog | null;
|
|
101
|
-
/** The site's own services. */
|
|
102
|
-
readonly services: Record<string, any>;
|
|
103
|
-
/** The saved session, or null. */
|
|
104
|
-
readonly state: ShellState | null;
|
|
105
|
-
/** Every kind the applications register, the shell's `app` and `alias` kinds included, in registration order. */
|
|
106
|
-
readonly kinds: ReadonlyMap<string, KindDefinition>;
|
|
107
|
-
/** A kind's definition, or undefined. */
|
|
108
|
-
kind(kind: string): KindDefinition | undefined;
|
|
109
|
-
/** The application definition with `id`. */
|
|
110
|
-
app(id: string): AppDefinition | undefined;
|
|
111
|
-
/** One of its menus by `data-menu`; throws when the markup drifts. */
|
|
112
|
-
menu(name: string): VfMenu;
|
|
113
|
-
/** One of its menu items by `value`; throws when the markup drifts. */
|
|
114
|
-
item(value: string): VfMenuItem;
|
|
115
|
-
/** Every pick from its menus — never while a modal is open. */
|
|
116
|
-
onMenu(fn: (value: string, item: VfMenuItem) => void): void;
|
|
117
|
-
/** One of its dialogs by `data-dialog`; throws when the markup drifts. */
|
|
118
|
-
dialog<T extends HTMLElement = VfDialog>(name: string): T;
|
|
119
|
-
/**
|
|
120
|
-
* A fresh copy of one of its windows by `data-window`, upgraded and not yet
|
|
121
|
-
* appended: `open({ create: () => ctx.window('viewer') })`. Throws when the
|
|
122
|
-
* markup drifts.
|
|
123
|
-
*/
|
|
124
|
-
window<T extends HTMLElement = VfWindow>(name: string): T;
|
|
125
|
-
/** Hold a dialog it made in code: appended to the desktop, held under it, removed with it. Returns it. */
|
|
126
|
-
hold<T extends Element>(dialog: T): T;
|
|
127
|
-
/** Let a dialog it holds go, and remove it. */
|
|
128
|
-
release(dialog: Element): void;
|
|
129
|
-
/**
|
|
130
|
-
* Show a dialog it holds, and resolve its `returnValue`: the value of the
|
|
131
|
-
* button that submitted it, or null when it closed without one. Throws for
|
|
132
|
-
* a dialog it doesn't hold.
|
|
133
|
-
*/
|
|
134
|
-
ask(dialog: VfModalDialog): Promise<string | null>;
|
|
135
|
-
/** Keep `item` disabled while `test` is false: re-run on every press, key, selection change and activation. */
|
|
136
|
-
gate(item: VfMenuItem, test: () => boolean): void;
|
|
137
|
-
/** Whether a text field has keyboard focus, read through shadow roots at the call. */
|
|
138
|
-
typing(): boolean;
|
|
139
|
-
/** Whether a modal dialog is open. */
|
|
140
|
-
modalOpen(): boolean;
|
|
141
|
-
/** Follow whether this application is front; called at once with the current state. */
|
|
142
|
-
onFront(fn: (front: boolean) => void): void;
|
|
143
|
-
/** A listener torn down with the application. */
|
|
144
|
-
on(target: EventTarget, type: string, fn: (e: Event) => void, options?: boolean | AddEventListenerOptions): void;
|
|
145
|
-
/** Run `fn` when the shell is disposed. */
|
|
146
|
-
onDispose(fn: () => void): void;
|
|
147
|
-
}
|
|
148
|
-
/** Declare an application, with its actions' type. */
|
|
149
|
-
export declare function defineApp<Actions>(definition: AppDefinition<Actions>): AppDefinition<Actions>;
|
|
150
|
-
export interface ShellOptions {
|
|
151
|
-
/** The applications; the first with a `catalog` is the Finder. */
|
|
152
|
-
apps: AppDefinition<any>[];
|
|
153
|
-
/** The application front while no window is active. Default the Finder, else the first. */
|
|
154
|
-
defaultApp?: string;
|
|
155
|
-
/** `'viewport'` sizes the desktop to the viewport on every resize and scale change. */
|
|
156
|
-
fit?: 'viewport' | false;
|
|
157
|
-
/** Where the session is saved; null keeps nothing, so a reload resets. */
|
|
158
|
-
state?: ShellState | null;
|
|
159
|
-
/** The site's own services, handed to every application. */
|
|
160
|
-
services?: Record<string, unknown>;
|
|
161
|
-
/** The clock in the menu bar's `end` slot. Default true. */
|
|
162
|
-
clock?: boolean;
|
|
163
|
-
/** Open what Startup Items holds once the session is back. Default true; false skips it for this boot. */
|
|
164
|
-
startup?: boolean;
|
|
165
|
-
}
|
|
166
|
-
/** A running shell. */
|
|
167
|
-
export interface Shell {
|
|
168
|
-
readonly desktop: VfDesktop;
|
|
169
|
-
readonly windows: WindowManager;
|
|
170
|
-
readonly catalog: Catalog | null;
|
|
171
|
-
readonly apps: Record<string, any>;
|
|
172
|
-
/** Settles once the boot has read the catalog and reopened the session. */
|
|
173
|
-
readonly ready: Promise<void>;
|
|
174
|
-
dispose(): void;
|
|
175
|
-
}
|
|
176
|
-
/** Start the shell over `desktop`. The page has imported `vintage-frames` first. */
|
|
177
|
-
export declare function createShell(desktop: VfDesktop, options: ShellOptions): Shell;
|
package/dist/shell/shell.js
DELETED
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
import { onScaleChange as e } from "../scale.js";
|
|
2
|
-
import { VfWindow as t } from "../components/vf-window.js";
|
|
3
|
-
import "../index.js";
|
|
4
|
-
import { ALIAS_KIND as n, appIdOf as r, appleMenuOf as i, createCatalog as a, resolve as o, startupItemsOf as s } from "./catalog.js";
|
|
5
|
-
import { createWindowManager as c, deepActiveElement as l, desktopRendered as u } from "./windows.js";
|
|
6
|
-
import { openWindowsOf as d } from "./state.js";
|
|
7
|
-
import { startClock as f } from "./clock.js";
|
|
8
|
-
//#region src/shell/shell.ts
|
|
9
|
-
function p(e) {
|
|
10
|
-
return e;
|
|
11
|
-
}
|
|
12
|
-
var m = (e) => e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement || e?.isContentEditable === !0;
|
|
13
|
-
function h(p, h) {
|
|
14
|
-
let g = h.apps, _ = g.find((e) => e.catalog), v = h.defaultApp ?? _?.id ?? g[0]?.id ?? "", y = h.state ?? null, b = h.services ?? {}, x = [], S = p.querySelector(":scope > vf-menu-bar");
|
|
15
|
-
if (!S) throw Error("vintage-frames/shell: the desktop has no vf-menu-bar");
|
|
16
|
-
let C = S.querySelector(":scope > vf-menu");
|
|
17
|
-
if (!C) throw Error("vintage-frames/shell: the menu bar has no system menu");
|
|
18
|
-
S.shortcuts = !0;
|
|
19
|
-
let w = y?.saved?.pattern;
|
|
20
|
-
w && (p.pattern = w);
|
|
21
|
-
let T = null;
|
|
22
|
-
if (h.fit === "viewport") {
|
|
23
|
-
let t = () => {
|
|
24
|
-
let e = {
|
|
25
|
-
width: p.width,
|
|
26
|
-
height: p.height
|
|
27
|
-
};
|
|
28
|
-
p.fitWithin(document.documentElement.clientWidth, document.documentElement.clientHeight), T?.resized(e);
|
|
29
|
-
};
|
|
30
|
-
t(), window.addEventListener("resize", t);
|
|
31
|
-
let n = e(t);
|
|
32
|
-
x.push(() => {
|
|
33
|
-
window.removeEventListener("resize", t), n();
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
let E = c(p, {
|
|
37
|
-
defaultApp: v,
|
|
38
|
-
savedPin: (e) => y?.pin(e) ?? null
|
|
39
|
-
});
|
|
40
|
-
T = E;
|
|
41
|
-
let D = (e) => {
|
|
42
|
-
e.target === p && p.clearActive();
|
|
43
|
-
};
|
|
44
|
-
p.addEventListener("pointerdown", D), x.push(() => p.removeEventListener("pointerdown", D));
|
|
45
|
-
let O = {}, k = (e, t = null) => O[_?.id ?? ""]?.open?.({
|
|
46
|
-
item: e,
|
|
47
|
-
from: t
|
|
48
|
-
}), A = /* @__PURE__ */ new Map();
|
|
49
|
-
A.set("app", {
|
|
50
|
-
art: (e) => g.find((t) => t.id === r(e))?.icon ?? "",
|
|
51
|
-
open: (e, t) => void O[r(e) ?? ""]?.open?.({ from: t }),
|
|
52
|
-
editable: !1
|
|
53
|
-
}), A.set(n, {
|
|
54
|
-
art: (e) => {
|
|
55
|
-
let t = j && o(j.get(), e.id), n = t && A.get(t.kind);
|
|
56
|
-
return !t || !n ? "" : typeof n.art == "function" ? n.art(t) : n.art;
|
|
57
|
-
},
|
|
58
|
-
open: (e, t) => void k(e.id, t)
|
|
59
|
-
});
|
|
60
|
-
for (let e of g) for (let [t, n] of Object.entries(e.kinds ?? {})) A.set(t, n);
|
|
61
|
-
let j = _?.catalog ? a({
|
|
62
|
-
storage: _.catalog.storage,
|
|
63
|
-
volumes: _.catalog.volumes,
|
|
64
|
-
system: _.catalog.system,
|
|
65
|
-
listed: (e) => A.has(e),
|
|
66
|
-
hooks: (e) => A.get(e)
|
|
67
|
-
}) : null, M = () => !!document.querySelector("vf-dialog[open]"), N = () => m(l()), P = /* @__PURE__ */ new Map(), F = () => {
|
|
68
|
-
for (let [e, t] of P) {
|
|
69
|
-
let n = !t();
|
|
70
|
-
e.disabled !== n && (e.disabled = n);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
document.addEventListener("pointerdown", F, !0), document.addEventListener("keydown", F, !0), p.addEventListener("vf-select", F), p.addEventListener("vf-activate", F), x.push(() => document.removeEventListener("pointerdown", F, !0), () => document.removeEventListener("keydown", F, !0), () => p.removeEventListener("vf-select", F), () => p.removeEventListener("vf-activate", F), E.onWindows(F), E.onLayout(F)), j && x.push(j.subscribe(F));
|
|
74
|
-
let I = (e) => {
|
|
75
|
-
if (!e) return [];
|
|
76
|
-
let t = document.createElement("div");
|
|
77
|
-
t.innerHTML = e, customElements.upgrade(t);
|
|
78
|
-
let n = [...t.children].filter((e) => e instanceof HTMLElement);
|
|
79
|
-
for (let e of n) e.remove();
|
|
80
|
-
return n;
|
|
81
|
-
}, L = g.map((e) => ({
|
|
82
|
-
def: e,
|
|
83
|
-
menus: I(e.menus).filter((e) => e.localName === "vf-menu"),
|
|
84
|
-
dialogs: I(e.dialogs).filter((e) => e.dataset.dialog != null),
|
|
85
|
-
windows: (() => {
|
|
86
|
-
let t = document.createElement("template");
|
|
87
|
-
return t.innerHTML = e.windows ?? "", t.content;
|
|
88
|
-
})()
|
|
89
|
-
})), R = [];
|
|
90
|
-
for (let { def: e, menus: t, dialogs: n, windows: r } of L) {
|
|
91
|
-
let i = [], a = `vintage-frames/shell (${e.id})`, o = /* @__PURE__ */ new Set();
|
|
92
|
-
for (let t of n) E.holdDialog(t, { app: e.id }), o.add(t);
|
|
93
|
-
i.push(() => {
|
|
94
|
-
for (let e of o) E.releaseDialog(e), e.remove();
|
|
95
|
-
o.clear();
|
|
96
|
-
});
|
|
97
|
-
let s = {
|
|
98
|
-
desktop: p,
|
|
99
|
-
windows: E,
|
|
100
|
-
menus: t,
|
|
101
|
-
apps: O,
|
|
102
|
-
catalog: j,
|
|
103
|
-
services: b,
|
|
104
|
-
state: y,
|
|
105
|
-
kinds: A,
|
|
106
|
-
kind: (e) => A.get(e),
|
|
107
|
-
app: (e) => g.find((t) => t.id === e),
|
|
108
|
-
menu(e) {
|
|
109
|
-
let n = t.find((t) => t.dataset.menu === e);
|
|
110
|
-
if (!n) throw Error(`${a}: missing menu ${e}`);
|
|
111
|
-
return n;
|
|
112
|
-
},
|
|
113
|
-
item(e) {
|
|
114
|
-
for (let n of t) {
|
|
115
|
-
let t = n.querySelector(`vf-menu-item[value="${CSS.escape(e)}"]`);
|
|
116
|
-
if (t) return t;
|
|
117
|
-
}
|
|
118
|
-
throw Error(`${a}: missing menu item ${e}`);
|
|
119
|
-
},
|
|
120
|
-
onMenu(e) {
|
|
121
|
-
for (let n of t) s.on(n, "vf-menu-select", (t) => {
|
|
122
|
-
if (M()) return;
|
|
123
|
-
let { value: n, item: r } = t.detail;
|
|
124
|
-
e(n, r);
|
|
125
|
-
});
|
|
126
|
-
},
|
|
127
|
-
dialog(e) {
|
|
128
|
-
let t = n.find((t) => t.dataset.dialog === e);
|
|
129
|
-
if (!t) throw Error(`${a}: missing dialog ${e}`);
|
|
130
|
-
return t;
|
|
131
|
-
},
|
|
132
|
-
window(e) {
|
|
133
|
-
let t = [...r.children].find((t) => t.dataset.window === e);
|
|
134
|
-
if (!t) throw Error(`${a}: missing window ${e}`);
|
|
135
|
-
let n = document.importNode(t, !0);
|
|
136
|
-
return customElements.upgrade(n), n;
|
|
137
|
-
},
|
|
138
|
-
hold(t) {
|
|
139
|
-
return E.holdDialog(t, { app: e.id }), o.add(t), t;
|
|
140
|
-
},
|
|
141
|
-
release(e) {
|
|
142
|
-
o.delete(e) && (E.releaseDialog(e), e.remove());
|
|
143
|
-
},
|
|
144
|
-
ask(t) {
|
|
145
|
-
if (E.appOf(t) !== e.id) throw Error(`${a}: ask() takes a dialog the application holds`);
|
|
146
|
-
return new Promise((e) => {
|
|
147
|
-
let n = (r) => {
|
|
148
|
-
r.target === t && (t.removeEventListener("vf-close", n), e(r.detail.returnValue));
|
|
149
|
-
};
|
|
150
|
-
t.show(), t.addEventListener("vf-close", n);
|
|
151
|
-
});
|
|
152
|
-
},
|
|
153
|
-
gate(e, t) {
|
|
154
|
-
P.set(e, t), i.push(() => P.delete(e));
|
|
155
|
-
let n = !t();
|
|
156
|
-
e.disabled !== n && (e.disabled = n);
|
|
157
|
-
},
|
|
158
|
-
typing: N,
|
|
159
|
-
modalOpen: M,
|
|
160
|
-
onFront(t) {
|
|
161
|
-
t(E.front === e.id), i.push(E.onFront((n) => t(n === e.id)));
|
|
162
|
-
},
|
|
163
|
-
on(e, t, n, r) {
|
|
164
|
-
e.addEventListener(t, n, r), i.push(() => e.removeEventListener(t, n, r));
|
|
165
|
-
},
|
|
166
|
-
onDispose(e) {
|
|
167
|
-
i.push(e);
|
|
168
|
-
}
|
|
169
|
-
}, c = e.init(s);
|
|
170
|
-
c && (O[e.id] = c), R.push(() => {
|
|
171
|
-
for (let e of i.splice(0).reverse()) e();
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
let z = null, B = () => {
|
|
175
|
-
let e = E.asking ?? E.front, t = L.find((t) => t.def.id === e) ?? L.find((e) => e.def.id === v) ?? null;
|
|
176
|
-
if (!(!t || t === z)) {
|
|
177
|
-
if (z) for (let e of z.menus) e.remove();
|
|
178
|
-
C.after(...t.menus), S.label = t.def.name, z = t, F();
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
x.push(E.onFront(B), E.onDialogs(B)), B();
|
|
182
|
-
let V = /* @__PURE__ */ new Map(), H = [], U = "", W = (e) => e.map((e) => e.entries ? `${e.item.id}(${W(e.entries)})` : e.item.id).join(","), G = (e) => e.flatMap((e) => [e.item, ...e.entries ? G(e.entries) : []]), K = (e, t) => {
|
|
183
|
-
for (let { item: n, entries: r } of e) {
|
|
184
|
-
let e = document.createElement("vf-menu-item");
|
|
185
|
-
if (e.setAttribute("value", n.id), e.append(n.name), t.append(e), V.set(e, n.id), H.push(e), r) {
|
|
186
|
-
let t = document.createElement("vf-menu");
|
|
187
|
-
t.slot = "submenu", e.append(t), K(r, t);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}, q = () => {
|
|
191
|
-
let e = j ? i(j.get()) : [], t = W(e);
|
|
192
|
-
if (t === U) {
|
|
193
|
-
G(e).forEach((e, t) => {
|
|
194
|
-
let n = H[t];
|
|
195
|
-
n.labelText !== e.name && n.firstChild.replaceWith(e.name);
|
|
196
|
-
});
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
for (let [e] of V) e.remove();
|
|
200
|
-
V.clear(), H = [], U = t, K(e, C);
|
|
201
|
-
}, J = (e) => {
|
|
202
|
-
let t = V.get(e.detail.item);
|
|
203
|
-
t != null && !M() && k(t);
|
|
204
|
-
};
|
|
205
|
-
if (C.addEventListener("vf-menu-select", J), x.push(() => {
|
|
206
|
-
C.removeEventListener("vf-menu-select", J);
|
|
207
|
-
for (let [e] of V) e.remove();
|
|
208
|
-
V.clear();
|
|
209
|
-
}), j && x.push(j.subscribe(q)), h.clock !== !1) {
|
|
210
|
-
let e = S.querySelector(":scope > [slot=\"end\"]"), t = !e;
|
|
211
|
-
e || (e = document.createElement("vf-label"), e.slot = "end", S.append(e));
|
|
212
|
-
let n = f(e);
|
|
213
|
-
x.push(() => {
|
|
214
|
-
n(), t && e.remove();
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
let Y = /* @__PURE__ */ new WeakSet(), X = () => {
|
|
218
|
-
if (j) for (let e of p.stackingOrder) {
|
|
219
|
-
if (!(e instanceof t)) continue;
|
|
220
|
-
let n = E.itemOf(e);
|
|
221
|
-
if (n == null) continue;
|
|
222
|
-
let r = j.item(n);
|
|
223
|
-
r ? (Y.add(e), e.heading !== r.name && (e.heading = r.name)) : Y.has(e) && j.get().available && E.close(e, { to: null });
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
return j && x.push(j.subscribe(X), E.onWindows(X)), y?.hold(), {
|
|
227
|
-
desktop: p,
|
|
228
|
-
windows: E,
|
|
229
|
-
catalog: j,
|
|
230
|
-
apps: O,
|
|
231
|
-
ready: (async () => {
|
|
232
|
-
try {
|
|
233
|
-
await u(p), j && (await j.refresh(), _?.catalog?.seed && await j.seed(_.catalog.seed));
|
|
234
|
-
for (let { item: e, app: t } of d(y?.saved ?? null)) await O[t]?.open?.({ item: e });
|
|
235
|
-
let e = y?.saved?.active;
|
|
236
|
-
if (e) {
|
|
237
|
-
let t = E.windowFor(e);
|
|
238
|
-
t && p.bringToFront(t);
|
|
239
|
-
}
|
|
240
|
-
if (j && h.startup !== !1) for (let e of s(j.get())) await k(e.id);
|
|
241
|
-
} finally {
|
|
242
|
-
if (y) {
|
|
243
|
-
let e = y.start(() => ({
|
|
244
|
-
windows: E.snapshot(),
|
|
245
|
-
active: E.itemOf(p.activeWindow),
|
|
246
|
-
pattern: p.pattern ?? null
|
|
247
|
-
}), (e) => {
|
|
248
|
-
let t = [
|
|
249
|
-
E.onLayout(e),
|
|
250
|
-
E.onWindows(e),
|
|
251
|
-
...j ? [j.subscribe(e)] : []
|
|
252
|
-
];
|
|
253
|
-
p.addEventListener("vf-activate", e);
|
|
254
|
-
let n = new MutationObserver(e);
|
|
255
|
-
return n.observe(p, {
|
|
256
|
-
attributes: !0,
|
|
257
|
-
attributeFilter: ["pattern"]
|
|
258
|
-
}), () => {
|
|
259
|
-
for (let e of t) e();
|
|
260
|
-
p.removeEventListener("vf-activate", e), n.disconnect();
|
|
261
|
-
};
|
|
262
|
-
});
|
|
263
|
-
x.push(e), y.release();
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
})(),
|
|
267
|
-
dispose() {
|
|
268
|
-
for (let e of R.splice(0).reverse()) e();
|
|
269
|
-
if (z) for (let e of z.menus) e.remove();
|
|
270
|
-
z = null;
|
|
271
|
-
for (let e of x.splice(0)) e();
|
|
272
|
-
E.dispose(), j?.dispose();
|
|
273
|
-
}
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
//#endregion
|
|
277
|
-
export { h as createShell, p as defineApp };
|
package/dist/shell/state.d.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The saved session: one versioned localStorage key holding each window's
|
|
3
|
-
* box as a nine-slice pin and its depth, the active window's item, the
|
|
4
|
-
* desktop pattern, and the site's own keys.
|
|
5
|
-
*
|
|
6
|
-
* - A window entry with a depth (`z`) was open at the write. One without is a
|
|
7
|
-
* box alone — a palette, or a window closed this session — which a reload
|
|
8
|
-
* never reopens and an open() still lands in.
|
|
9
|
-
* - Writes wait a moment and are held until the boot has reopened the
|
|
10
|
-
* session: a write while windows are still reopening would drop the depth
|
|
11
|
-
* of the ones not reached yet. Hiding or leaving the page writes at once.
|
|
12
|
-
* - `?fresh=1` neither reads nor writes.
|
|
13
|
-
*
|
|
14
|
-
* The parsing is pure, so it is tested under Node; the storage is the page's
|
|
15
|
-
* localStorage, reached only when a state is made.
|
|
16
|
-
*/
|
|
17
|
-
import type { Pin } from './geometry.js';
|
|
18
|
-
/** One saved window. */
|
|
19
|
-
export interface SavedWindow {
|
|
20
|
-
/** The application that owns it, which reopens it. */
|
|
21
|
-
app: string;
|
|
22
|
-
pin: Pin;
|
|
23
|
-
/** Its depth when saved, 0 the bottom; unset for a box alone. */
|
|
24
|
-
z?: number;
|
|
25
|
-
}
|
|
26
|
-
/** What the session holds. */
|
|
27
|
-
export interface SavedSession {
|
|
28
|
-
v: number;
|
|
29
|
-
/** Windows by the item they show. */
|
|
30
|
-
windows: Record<string, SavedWindow>;
|
|
31
|
-
/** The active window's item, or null. */
|
|
32
|
-
active: string | null;
|
|
33
|
-
/** The desktop pattern, or null for the desktop's own. */
|
|
34
|
-
pattern: string | null;
|
|
35
|
-
/** The site's own keys. */
|
|
36
|
-
extra: Record<string, unknown>;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* A parsed blob as a session, its bad entries dropped, or null for anything
|
|
40
|
-
* that isn't one of this version.
|
|
41
|
-
*/
|
|
42
|
-
export declare function readSession(parsed: unknown): SavedSession | null;
|
|
43
|
-
/** The windows that were open, deepest first: the order a boot reopens them in. */
|
|
44
|
-
export declare function openWindowsOf(session: SavedSession | null): {
|
|
45
|
-
item: string;
|
|
46
|
-
app: string;
|
|
47
|
-
pin: Pin;
|
|
48
|
-
}[];
|
|
49
|
-
/** What the shell reports at a write: the windows it knows and the active one. */
|
|
50
|
-
export interface SessionSnapshot {
|
|
51
|
-
windows: Record<string, SavedWindow>;
|
|
52
|
-
active: string | null;
|
|
53
|
-
pattern: string | null;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* The session to write: every window known before keeps its box and loses
|
|
57
|
-
* its depth, and the snapshot's windows — the open ones with theirs — go
|
|
58
|
-
* over them.
|
|
59
|
-
*/
|
|
60
|
-
export declare function mergeSession(known: Record<string, SavedWindow>, snapshot: SessionSnapshot, extra: Record<string, unknown>): SavedSession;
|
|
61
|
-
/** A saved session the shell reads at boot and writes as the desktop changes. */
|
|
62
|
-
export interface ShellState {
|
|
63
|
-
/** The session read at boot, or null. */
|
|
64
|
-
readonly saved: SavedSession | null;
|
|
65
|
-
/** A saved window's pin, by item, or null. */
|
|
66
|
-
pin(item: string): Pin | null;
|
|
67
|
-
/** A site key's saved value, else its default. */
|
|
68
|
-
get<T = unknown>(key: string): T;
|
|
69
|
-
/** Set a site key and write at once, so an immediate reload still finds it. */
|
|
70
|
-
set(key: string, value: unknown): void;
|
|
71
|
-
/** Hold every write until {@link release}. */
|
|
72
|
-
hold(): void;
|
|
73
|
-
/** Release the hold and write what is on screen. */
|
|
74
|
-
release(): void;
|
|
75
|
-
/**
|
|
76
|
-
* Start writing: `snapshot` is read at each write, after `subscribe`'s
|
|
77
|
-
* signal settles. Returns the stop function.
|
|
78
|
-
*/
|
|
79
|
-
start(snapshot: () => SessionSnapshot, subscribe: (fn: () => void) => () => void): () => void;
|
|
80
|
-
}
|
|
81
|
-
export interface LocalStorageStateOptions {
|
|
82
|
-
/** The site's own keys and their defaults: `{ greet: true, seeded: false }`. */
|
|
83
|
-
extra?: Record<string, unknown>;
|
|
84
|
-
/** Neither read nor write. Default: `?fresh=1` in the address. */
|
|
85
|
-
fresh?: boolean;
|
|
86
|
-
}
|
|
87
|
-
/** A session kept under one localStorage `key`. */
|
|
88
|
-
export declare function localStorageState(key: string, options?: LocalStorageStateOptions): ShellState;
|
package/dist/shell/state.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { isPin as e } from "./geometry.js";
|
|
2
|
-
//#region src/shell/state.ts
|
|
3
|
-
var t = 1, n = 400;
|
|
4
|
-
function r(t) {
|
|
5
|
-
let n = t;
|
|
6
|
-
return !n || typeof n != "object" || typeof n.app != "string" || !n.app || !e(n.pin) ? null : Number.isInteger(n.z) ? {
|
|
7
|
-
app: n.app,
|
|
8
|
-
pin: n.pin,
|
|
9
|
-
z: n.z
|
|
10
|
-
} : {
|
|
11
|
-
app: n.app,
|
|
12
|
-
pin: n.pin
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
function i(e) {
|
|
16
|
-
let n = e;
|
|
17
|
-
if (!n || typeof n != "object" || n.v !== t) return null;
|
|
18
|
-
let i = {};
|
|
19
|
-
if (n.windows && typeof n.windows == "object") for (let [e, t] of Object.entries(n.windows)) {
|
|
20
|
-
let n = r(t);
|
|
21
|
-
n && (i[e] = n);
|
|
22
|
-
}
|
|
23
|
-
return {
|
|
24
|
-
v: t,
|
|
25
|
-
windows: i,
|
|
26
|
-
active: typeof n.active == "string" && n.active ? n.active : null,
|
|
27
|
-
pattern: typeof n.pattern == "string" && n.pattern.trim() ? n.pattern : null,
|
|
28
|
-
extra: n.extra && typeof n.extra == "object" && !Array.isArray(n.extra) ? { ...n.extra } : {}
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
function a(e) {
|
|
32
|
-
return Object.entries(e?.windows ?? {}).filter(([, e]) => e.z != null).sort(([, e], [, t]) => e.z - t.z).map(([e, t]) => ({
|
|
33
|
-
item: e,
|
|
34
|
-
app: t.app,
|
|
35
|
-
pin: t.pin
|
|
36
|
-
}));
|
|
37
|
-
}
|
|
38
|
-
function o(e, n, i) {
|
|
39
|
-
let a = {};
|
|
40
|
-
for (let [t, n] of Object.entries(e)) a[t] = {
|
|
41
|
-
app: n.app,
|
|
42
|
-
pin: n.pin
|
|
43
|
-
};
|
|
44
|
-
for (let [e, t] of Object.entries(n.windows)) {
|
|
45
|
-
let n = r(t);
|
|
46
|
-
n && (a[e] = n);
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
v: t,
|
|
50
|
-
windows: a,
|
|
51
|
-
active: n.active,
|
|
52
|
-
pattern: n.pattern,
|
|
53
|
-
extra: { ...i }
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function s(e, t = {}) {
|
|
57
|
-
let r = t.fresh ?? new URLSearchParams(location.search).get("fresh") === "1", i = t.extra ?? {}, a = r ? null : c(e), s = {
|
|
58
|
-
...i,
|
|
59
|
-
...a?.extra ?? {}
|
|
60
|
-
}, l = { ...a?.windows ?? {} }, u = !1, d = null, f = () => {
|
|
61
|
-
if (r || u || !d) return;
|
|
62
|
-
let t = o(l, d(), s);
|
|
63
|
-
l = t.windows;
|
|
64
|
-
try {
|
|
65
|
-
localStorage.setItem(e, JSON.stringify(t));
|
|
66
|
-
} catch {}
|
|
67
|
-
};
|
|
68
|
-
return {
|
|
69
|
-
saved: a,
|
|
70
|
-
pin: (e) => a?.windows[e]?.pin ?? null,
|
|
71
|
-
get: (e) => s[e],
|
|
72
|
-
set(e, t) {
|
|
73
|
-
s[e] = t, f();
|
|
74
|
-
},
|
|
75
|
-
hold() {
|
|
76
|
-
u = !0;
|
|
77
|
-
},
|
|
78
|
-
release() {
|
|
79
|
-
u = !1, f();
|
|
80
|
-
},
|
|
81
|
-
start(e, t) {
|
|
82
|
-
if (r) return () => {};
|
|
83
|
-
d = e;
|
|
84
|
-
let i = 0, a = () => {
|
|
85
|
-
clearTimeout(i), i = window.setTimeout(f, n);
|
|
86
|
-
}, o = () => {
|
|
87
|
-
document.visibilityState === "hidden" && f();
|
|
88
|
-
}, s = t(a);
|
|
89
|
-
return document.addEventListener("visibilitychange", o), window.addEventListener("pagehide", f), () => {
|
|
90
|
-
clearTimeout(i), s(), document.removeEventListener("visibilitychange", o), window.removeEventListener("pagehide", f), d = null;
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function c(e) {
|
|
96
|
-
try {
|
|
97
|
-
return i(JSON.parse(localStorage.getItem(e) ?? "null"));
|
|
98
|
-
} catch {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
//#endregion
|
|
103
|
-
export { s as localStorageState, o as mergeSession, a as openWindowsOf, i as readSession };
|
package/dist/shell/storage.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Catalog storage that keeps: IndexedDB, one object store of item records
|
|
3
|
-
* keyed by `id`.
|
|
4
|
-
*
|
|
5
|
-
* The database opens at the profile's own version, and one version up when
|
|
6
|
-
* the store is missing — opening at a fixed version throws `VersionError` on
|
|
7
|
-
* a profile already past it. It opens on first use; a failed open is retried
|
|
8
|
-
* on the next call. Every call rejects on failure (a private window), and the
|
|
9
|
-
* catalog then reports itself unavailable. A connection closes itself when
|
|
10
|
-
* another tab upgrades the database, and the next call reopens.
|
|
11
|
-
*/
|
|
12
|
-
import type { CatalogStorage } from './catalog.js';
|
|
13
|
-
/** A catalog storage in the IndexedDB database `name`, which keeps it across reloads. */
|
|
14
|
-
export declare function indexedDbStorage(name: string): CatalogStorage;
|