vintage-frames 0.19.0 → 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.
Files changed (68) hide show
  1. package/README.md +1 -18
  2. package/custom-elements.json +247 -59
  3. package/dist/components/vf-menu-bar.d.ts +6 -4
  4. package/dist/components/vf-menu-bar.js +26 -56
  5. package/dist/components/vf-menu-item.d.ts +33 -5
  6. package/dist/components/vf-menu-item.js +128 -61
  7. package/dist/components/vf-menu.d.ts +35 -0
  8. package/dist/components/vf-menu.js +171 -111
  9. package/dist/glyphs.d.ts +6 -0
  10. package/dist/glyphs.js +18 -14
  11. package/dist/menu-navigation.d.ts +34 -0
  12. package/dist/menu-navigation.js +53 -0
  13. package/dist/menu-press.d.ts +25 -7
  14. package/dist/menu-press.js +78 -44
  15. package/dist/motion.d.ts +10 -0
  16. package/dist/text-control.d.ts +3 -3
  17. package/docs/SPEC.md +13 -9
  18. package/editor/vscode.html-custom-data.json +13 -8
  19. package/editor/web-types.json +51 -15
  20. package/package.json +4 -34
  21. package/dist/build/app-file.d.ts +0 -21
  22. package/dist/build/app-file.js +0 -42
  23. package/dist/build/box.d.ts +0 -57
  24. package/dist/build/box.js +0 -93
  25. package/dist/build/client.d.ts +0 -12
  26. package/dist/build/frame-picture.d.ts +0 -6
  27. package/dist/build/frame-picture.js +0 -4
  28. package/dist/build/frame.d.ts +0 -11
  29. package/dist/build/frame.js +0 -44
  30. package/dist/build/index.d.ts +0 -14
  31. package/dist/build/index.js +0 -4
  32. package/dist/build/plugins.d.ts +0 -41
  33. package/dist/build/plugins.js +0 -134
  34. package/dist/build/png.d.ts +0 -24
  35. package/dist/build/png.js +0 -116
  36. package/dist/shell/alert.d.ts +0 -40
  37. package/dist/shell/alert.js +0 -41
  38. package/dist/shell/app-file.d.ts +0 -115
  39. package/dist/shell/app-file.js +0 -189
  40. package/dist/shell/catalog.d.ts +0 -212
  41. package/dist/shell/catalog.js +0 -317
  42. package/dist/shell/clock.d.ts +0 -16
  43. package/dist/shell/clock.js +0 -36
  44. package/dist/shell/filing.d.ts +0 -39
  45. package/dist/shell/filing.js +0 -68
  46. package/dist/shell/finder.d.ts +0 -179
  47. package/dist/shell/finder.js +0 -662
  48. package/dist/shell/geometry.d.ts +0 -203
  49. package/dist/shell/geometry.js +0 -257
  50. package/dist/shell/index.d.ts +0 -30
  51. package/dist/shell/index.js +0 -12
  52. package/dist/shell/pure.d.ts +0 -13
  53. package/dist/shell/pure.js +0 -6
  54. package/dist/shell/shell.d.ts +0 -177
  55. package/dist/shell/shell.js +0 -272
  56. package/dist/shell/state.d.ts +0 -88
  57. package/dist/shell/state.js +0 -103
  58. package/dist/shell/storage.d.ts +0 -14
  59. package/dist/shell/storage.js +0 -41
  60. package/dist/shell/version.d.ts +0 -2
  61. package/dist/shell/version.js +0 -4
  62. package/dist/shell/windows.d.ts +0 -278
  63. package/dist/shell/windows.js +0 -447
  64. package/dist/styles/body-strike.d.ts +0 -12
  65. package/dist/styles/body-strike.js +0 -3961
  66. package/dist/styles/display-strike.d.ts +0 -12
  67. package/dist/styles/display-strike.js +0 -4343
  68. package/dist/styles/strike.d.ts +0 -30
@@ -1,317 +0,0 @@
1
- //#region src/shell/catalog.ts
2
- var e = "trash", t = "disk", n = "system-folder", r = "apple-menu-items", i = "startup-items", a = "folder", o = "app", s = "alias", c = "untitled folder", l = "#seeded", u = (e) => e === "folder" || e === "trash" || e === "disk", d = (e) => e === "trash" || e === "disk", f = (e) => d(e) || e === "system-folder" || e === "apple-menu-items" || e === "startup-items";
3
- function p(e) {
4
- let t = e.data;
5
- return typeof t?.app == "string" ? t.app : null;
6
- }
7
- function m(e) {
8
- if (e.kind !== "alias") return null;
9
- let t = e.data;
10
- return typeof t?.original == "string" ? t.original : null;
11
- }
12
- function h(e, t) {
13
- return t == null ? null : e.items.find((e) => e.id === t) ?? null;
14
- }
15
- function g(e, t) {
16
- let n = /* @__PURE__ */ new Set(), r = h(e, t);
17
- for (; r?.kind === s;) {
18
- if (n.has(r.id)) return null;
19
- n.add(r.id), r = h(e, m(r));
20
- }
21
- return r;
22
- }
23
- function _(e, t) {
24
- let n = h(e, t);
25
- return n && u(n.kind) ? n.id : null;
26
- }
27
- function v(e, t) {
28
- let n = _(e, t);
29
- return e.items.filter((t) => t.id !== n && _(e, t.parent) === n);
30
- }
31
- function y(e, t) {
32
- return v(e, t).length;
33
- }
34
- var b = (e, t) => _(e, h(e, t)?.parent ?? null);
35
- function x(e, t, n) {
36
- let r = /* @__PURE__ */ new Set(), i = b(e, t);
37
- for (; i != null && !r.has(i);) {
38
- if (i === n) return !0;
39
- r.add(i), i = b(e, i);
40
- }
41
- return !1;
42
- }
43
- function S(e, t) {
44
- let n = [], r = /* @__PURE__ */ new Set(), i = _(e, t ?? null);
45
- for (; i != null && !r.has(i);) n.push(i), r.add(i), i = b(e, i);
46
- return n;
47
- }
48
- function C(e, t) {
49
- return t == null ? !1 : t === "trash" || x(e, t, "trash");
50
- }
51
- function w(e, t) {
52
- let n = [], r = /* @__PURE__ */ new Set(), i = (t) => {
53
- for (let a of v(e, t)) r.has(a.id) || (r.add(a.id), n.push(a), u(a.kind) && i(a.id));
54
- };
55
- return i(_(e, t)), n;
56
- }
57
- function T(e, t) {
58
- let n = new Set(v(e, t).filter((e) => e.kind === a).map((e) => e.name));
59
- if (!n.has("untitled folder")) return c;
60
- for (let e = 2;; e++) {
61
- let t = `${c} ${e}`;
62
- if (!n.has(t)) return t;
63
- }
64
- }
65
- var E = (e) => e.replace(/ copy( \d+)?$/, "");
66
- function D(e, t, n, r) {
67
- let i = new Set(v(e, t).filter((e) => e.kind === r).map((e) => e.name));
68
- if (!i.has(n)) return n;
69
- let a = E(n);
70
- if (!i.has(`${a} copy`)) return `${a} copy`;
71
- for (let e = 2;; e++) {
72
- let t = `${a} copy ${e}`;
73
- if (!i.has(t)) return t;
74
- }
75
- }
76
- function O(e, t, n) {
77
- let r = new Set(v(e, t).filter((e) => e.kind === s).map((e) => e.name)), i = `${n} alias`;
78
- if (!r.has(i)) return i;
79
- for (let e = 2;; e++) {
80
- let t = `${i} ${e}`;
81
- if (!r.has(t)) return t;
82
- }
83
- }
84
- var k = new Intl.Collator("en");
85
- function A(e, t) {
86
- return h(e, t) ? v(e, t).sort((e, t) => k.compare(e.name, t.name) || N(e, t)) : [];
87
- }
88
- var j = (e) => A(e, r), M = (e) => A(e, i), N = (e, t) => e.createdAt - t.createdAt || (e.id < t.id ? -1 : 1);
89
- function P(e, t) {
90
- let n = { ...e };
91
- return t ? (n.left = t.left, n.top = t.top) : (delete n.left, delete n.top), n;
92
- }
93
- function F(o) {
94
- let { storage: s, volumes: c = {}, system: d, listed: p = () => !0, hooks: g = () => void 0, placeDelay: v = 400 } = o, y = () => (o.now ?? Date.now)(), b = () => o.newId ? o.newId() : crypto.randomUUID(), S = [], E = (e, t, n, r) => S.push({
95
- id: e,
96
- name: t,
97
- kind: n,
98
- parent: r,
99
- createdAt: 0,
100
- modifiedAt: 0
101
- });
102
- c.disk && E(t, c.disk, t, null), c.trash !== !1 && E(e, c.trash ?? "Trash", e, null), d && (E(n, d.folder, a, c.disk ? t : null), E(r, d.appleMenu, a, n), E(i, d.startup, a, n));
103
- let O = /* @__PURE__ */ new Map(), k = !1, A = !1, j = F(), M = /* @__PURE__ */ new Set();
104
- function F() {
105
- let e = S.map((e) => {
106
- let t = O.get(e.id);
107
- return t?.left != null && t.top != null ? {
108
- ...e,
109
- left: t.left,
110
- top: t.top
111
- } : e;
112
- }), t = [...O.values()].filter((e) => !f(e.id) && (e.kind === "folder" || p(e.kind))).sort(N);
113
- return {
114
- available: k,
115
- items: [...e, ...t]
116
- };
117
- }
118
- function I() {
119
- j = F();
120
- for (let e of [...M]) e(j);
121
- }
122
- let L = () => {
123
- if (!s) throw Error("storage is unavailable");
124
- return s;
125
- };
126
- async function R(e) {
127
- await L().put(e), O.set(e.id, e);
128
- }
129
- let z = /* @__PURE__ */ new Set(), B = null;
130
- async function V() {
131
- B && clearTimeout(B), B = null;
132
- let e = [...z];
133
- if (z.clear(), !(!s || !k)) for (let t of e) {
134
- let e = O.get(t);
135
- e && await s.put(e).catch(() => {});
136
- }
137
- }
138
- let H = (e) => {
139
- let t = S.find((t) => t.id === e);
140
- return O.get(e) ?? {
141
- ...t,
142
- name: ""
143
- };
144
- };
145
- async function U() {
146
- z.clear();
147
- for (let e of await L().list()) e.id !== l && (await L().remove(e.id), O.delete(e.id));
148
- O = /* @__PURE__ */ new Map();
149
- }
150
- let W = {
151
- get: () => j,
152
- subscribe(e) {
153
- return M.add(e), () => {
154
- M.delete(e);
155
- };
156
- },
157
- item: (e) => h(j, e),
158
- async refresh() {
159
- if (!s) {
160
- O = /* @__PURE__ */ new Map(), k = !1, I();
161
- return;
162
- }
163
- try {
164
- let e = await s.list();
165
- O = /* @__PURE__ */ new Map(), A = !1;
166
- for (let t of e) t.id === l ? A = !0 : O.set(t.id, t);
167
- k = !0;
168
- } catch (e) {
169
- console.warn("vintage-frames/shell: the catalog could not be read —", e), O = /* @__PURE__ */ new Map(), k = !1;
170
- }
171
- I();
172
- },
173
- async seed(e) {
174
- return !s || !k || A ? !1 : (await e(W), await s.put({
175
- id: l,
176
- name: "",
177
- kind: l,
178
- parent: null,
179
- createdAt: y(),
180
- modifiedAt: y()
181
- }), A = !0, !0);
182
- },
183
- async create(e = {}) {
184
- let t = _(j, e.parent ?? null);
185
- if (C(j, t)) return null;
186
- let n = e.kind ?? "folder", r = e.at ?? y(), i = {
187
- id: b(),
188
- name: e.name ?? (n === "folder" ? T(j, t) : "untitled"),
189
- kind: n,
190
- parent: t,
191
- createdAt: r,
192
- modifiedAt: r
193
- };
194
- return e.data !== void 0 && (i.data = e.data), e.left != null && e.top != null && (i = P(i, {
195
- left: e.left,
196
- top: e.top
197
- })), await R(i), I(), i;
198
- },
199
- async rename(e, t) {
200
- let n = O.get(e);
201
- return !n || f(e) || n.name === t ? !1 : (await R({
202
- ...n,
203
- name: t,
204
- modifiedAt: y()
205
- }), I(), !0);
206
- },
207
- async update(e, t) {
208
- let n = O.get(e);
209
- return !n || f(e) ? !1 : (await R({
210
- ...n,
211
- data: t,
212
- modifiedAt: y()
213
- }), I(), !0);
214
- },
215
- async move(e, t, n) {
216
- let r = _(j, t), i = [];
217
- for (let t of e) {
218
- let e = O.get(t);
219
- !e || f(t) || r === t || r != null && u(e.kind) && x(j, r, t) || _(j, e.parent) !== r && (z.delete(t), await R(P({
220
- ...e,
221
- parent: r
222
- }, n?.get(t) ?? null)), i.push(t));
223
- }
224
- return i.length && I(), i;
225
- },
226
- place(e) {
227
- let t = !1;
228
- for (let [n, r] of e) {
229
- let e = f(n) ? S.some((e) => e.id === n) ? H(n) : null : O.get(n);
230
- !e || e.left === r.left && e.top === r.top || (O.set(n, P(e, r)), z.add(n), t = !0);
231
- }
232
- t && (I(), B && clearTimeout(B), B = setTimeout(() => void V(), v));
233
- },
234
- flush: V,
235
- async copy(e, t) {
236
- let n = _(j, t);
237
- if (C(j, n)) return [];
238
- let r = [];
239
- for (let t of e) {
240
- let e = h(j, t);
241
- if (!e || f(t)) continue;
242
- let i = u(e.kind) ? w(j, t) : [], a = /* @__PURE__ */ new Map(), o = async (e, t, n) => {
243
- let r = y(), i = {
244
- ...e,
245
- id: b(),
246
- name: n ? D(j, t, e.name, e.kind) : e.name,
247
- parent: t,
248
- createdAt: r,
249
- modifiedAt: r
250
- };
251
- return n && (i = P(i, null)), a.set(e.id, i.id), await R(i), await g(e.kind)?.copy?.(e, i), i;
252
- };
253
- r.push(await o(e, n, !0));
254
- for (let e of i) await o(e, a.get(_(j, e.parent) ?? "") ?? null, !1);
255
- I();
256
- }
257
- return r;
258
- },
259
- async emptyTrash() {
260
- let t = w(j, e);
261
- for (let e of t) await L().remove(e.id), O.delete(e.id), z.delete(e.id), await g(e.kind)?.onRemove?.(e);
262
- return t.length && I(), t;
263
- },
264
- trashSize() {
265
- return w(j, e).reduce((e, t) => e + (g(t.kind)?.size?.(t) ?? 0), 0);
266
- },
267
- dump() {
268
- return { items: [...O.values()] };
269
- },
270
- async clear() {
271
- if (s) try {
272
- await U();
273
- } finally {
274
- I();
275
- }
276
- },
277
- async import(e, { mode: t = "merge" } = {}) {
278
- let n = /* @__PURE__ */ new Map(), r = e.items.filter((e) => e.id !== l && !f(e.id));
279
- if (!r.length || !s) return n;
280
- let i = t === "replace", a = /* @__PURE__ */ new Map();
281
- for (let e of r) a.set(e.id, i ? e.id : b());
282
- let o = (e) => e == null || f(e) ? e : a.get(e) ?? null;
283
- try {
284
- i && await U();
285
- for (let e of r) {
286
- let t = {
287
- ...e,
288
- id: a.get(e.id),
289
- parent: o(e.parent)
290
- }, r = m(e);
291
- r != null && a.has(r) && (t.data = {
292
- ...e.data,
293
- original: a.get(r)
294
- }), await R(t), n.set(e.id, t);
295
- }
296
- } finally {
297
- I();
298
- }
299
- return n;
300
- },
301
- async dispose() {
302
- await V(), M.clear();
303
- }
304
- };
305
- return W;
306
- }
307
- function I(e = []) {
308
- let t = new Map(e.map((e) => [e.id, e]));
309
- return {
310
- records: t,
311
- list: async () => [...t.values()],
312
- put: async (e) => t.set(e.id, e),
313
- remove: async (e) => t.delete(e)
314
- };
315
- }
316
- //#endregion
317
- export { s as ALIAS_KIND, r as APPLE_MENU_ITEMS, o as APP_KIND, t as DISK, a as FOLDER, i as STARTUP_ITEMS, n as SYSTEM_FOLDER, e as TRASH, c as UNTITLED_FOLDER, O as aliasName, p as appIdOf, j as appleMenuItemsOf, v as childrenOf, _ as containerOf, D as copyName, F as createCatalog, w as descendantsOf, S as enclosingFolders, u as isContainerKind, x as isInside, f as isKept, C as isTrashed, d as isVolume, y as itemCount, h as itemOf, I as memoryStorage, T as nextFolderName, m as originalOf, g as resolve, M as startupItemsOf };
@@ -1,16 +0,0 @@
1
- /**
2
- * The menu bar's clock: a label in the bar's `end` slot showing the time,
3
- * updated on the minute. A press shows the date for a few seconds; a second
4
- * press returns to the time early.
5
- */
6
- /** How long a press shows the date, ms. */
7
- export declare const DATE_HOLD_MS = 3000;
8
- /** "7:27 PM": 12-hour local time. */
9
- export declare function formatTime(d: Date): string;
10
- /** "9/27/26": M/D/YY local date. */
11
- export declare function formatDate(d: Date): string;
12
- /** Render the clock into `label` now and on every minute boundary. Returns the stop function. */
13
- export declare function startClock(label: HTMLElement, { now, holdMs }?: {
14
- now?: () => number;
15
- holdMs?: number;
16
- }): () => void;
@@ -1,36 +0,0 @@
1
- //#region src/shell/clock.ts
2
- var e = 3e3, t = (e) => String(e).padStart(2, "0");
3
- function n(e) {
4
- let n = e.getHours();
5
- return `${n % 12 || 12}:${t(e.getMinutes())} ${n < 12 ? "AM" : "PM"}`;
6
- }
7
- function r(e) {
8
- return `${e.getMonth() + 1}/${e.getDate()}/${t(e.getFullYear() % 100)}`;
9
- }
10
- function i(t, { now: i = Date.now, holdMs: a = e } = {}) {
11
- let o = !1, s = 0, c = 0, l = () => {
12
- let e = new Date(i());
13
- t.textContent = o ? r(e) : n(e);
14
- }, u = () => {
15
- c = window.setTimeout(() => {
16
- l(), u();
17
- }, 6e4 - i() % 6e4 + 20);
18
- }, d = () => {
19
- clearTimeout(s), s = 0, o = !1, l();
20
- }, f = (e) => {
21
- if (e.button === 0) {
22
- if (e.preventDefault(), o) {
23
- d();
24
- return;
25
- }
26
- o = !0, l(), s = window.setTimeout(d, a);
27
- }
28
- }, p = () => {
29
- document.hidden || l();
30
- };
31
- return l(), u(), t.addEventListener("pointerdown", f), document.addEventListener("visibilitychange", p), () => {
32
- clearTimeout(c), clearTimeout(s), t.removeEventListener("pointerdown", f), document.removeEventListener("visibilitychange", p);
33
- };
34
- }
35
- //#endregion
36
- export { e as DATE_HOLD_MS, r as formatDate, n as formatTime, i as startClock };
@@ -1,39 +0,0 @@
1
- /**
2
- * Filing by drag: what a drop of icons means. A drop onto a folder icon
3
- * files the icons into that folder at its next free cells; a drop into a
4
- * folder window lands each icon where its outline was let go; a drop out of
5
- * a window onto the desktop the same. The folder under the pointer wears
6
- * `target`. A folder never goes into itself or a folder inside it, and a
7
- * drop over another application's window moves nothing. A drop inside the
8
- * icons' own field is the kit's: its default action moves them there.
9
- *
10
- * The Finder runs it over its catalog; a page of its own can run it over the
11
- * DOM, with callbacks that say what a container is and what filing does.
12
- */
13
- import type { VfDesktop, VfIcon, VfWindow } from '../index.js';
14
- import type { Point } from './geometry.js';
15
- /** What filing needs to know, and what it does. */
16
- export interface FilingOptions {
17
- /**
18
- * The container a window shows — a folder's id — or undefined for a
19
- * window that holds no icons of the page's: a drop over it moves nothing.
20
- */
21
- folderOfWindow(win: VfWindow): string | undefined;
22
- /** The container a folder icon opens — its folder's id — or undefined for any other icon. */
23
- folderOfIcon(icon: VfIcon): string | undefined;
24
- /**
25
- * The container an icon sits in: a folder's id, or null for the desktop.
26
- * Undefined for an icon filing leaves alone, another application's: its
27
- * drags go by.
28
- */
29
- containerOf(icon: VfIcon): string | null | undefined;
30
- /** Whether `icons` may go into `folder` (null: the desktop). */
31
- canFile(icons: VfIcon[], folder: string | null): boolean;
32
- /**
33
- * File the icons into `folder` (null: the desktop), each at its landing
34
- * there in whole system px, or null for the folder's next free cell.
35
- */
36
- file(icons: VfIcon[], folder: string | null, landings: Map<VfIcon, Point | null>): void;
37
- }
38
- /** Wire filing by drag on a desktop. Returns the teardown. */
39
- export declare function fileByDrag(desktop: VfDesktop, options: FilingOptions): () => void;
@@ -1,68 +0,0 @@
1
- //#region src/shell/filing.ts
2
- function e(e, t) {
3
- let { folderOfWindow: n, folderOfIcon: r, containerOf: i, canFile: a, file: o } = t, s = (t, i, a) => {
4
- let o = document.elementsFromPoint(t, i).filter((e) => !a.includes(e)), s = o.findIndex((e) => e.localName === "vf-window"), c = (s < 0 ? o : o.slice(0, s)).find((e) => e.localName === "vf-icon" && r(e) !== void 0) ?? null, l = s < 0 ? null : o[s];
5
- return {
6
- folderIcon: c,
7
- folder: c ? r(c) : void 0,
8
- win: l,
9
- window: l ? n(l) : void 0,
10
- desktop: !l && o.includes(e)
11
- };
12
- }, c = null, l = (e) => {
13
- c !== e && (c && (c.target = !1), c = e, c && (c.target = !0));
14
- }, u = (e) => {
15
- if (i(e.target) === void 0) return;
16
- let { clientX: t, clientY: n, icons: r } = e.detail, o = s(t, n, r);
17
- l(o.folderIcon && a(r, o.folder ?? null) ? o.folderIcon : null);
18
- }, d = () => l(null), f = (t) => {
19
- let n = t.target, r = i(n);
20
- if (r === void 0) return;
21
- let { clientX: c, clientY: u, x: d, y: f, icons: p } = t.detail, m = s(c, u, p);
22
- l(null);
23
- let h = n.getBoundingClientRect(), g = p.map((e) => {
24
- let t = e.getBoundingClientRect();
25
- return {
26
- icon: e,
27
- x: t.left + (d - h.left),
28
- y: t.top + (f - h.top)
29
- };
30
- });
31
- if (m.folder !== void 0) {
32
- t.preventDefault(), a(p, m.folder) && o(p, m.folder, new Map(p.map((e) => [e, null])));
33
- return;
34
- }
35
- if (m.win && m.window === void 0) {
36
- t.preventDefault();
37
- return;
38
- }
39
- if (m.win && m.window !== void 0 && m.window !== r) {
40
- if (t.preventDefault(), !a(p, m.window)) return;
41
- let e = m.win;
42
- o(p, m.window, new Map(g.map(({ icon: t, x: n, y: r }) => {
43
- let i = e.placementAt(n, r);
44
- return [t, {
45
- left: Math.max(0, i.left),
46
- top: Math.max(0, i.top)
47
- }];
48
- })));
49
- return;
50
- }
51
- if (m.desktop && r !== null) {
52
- if (t.preventDefault(), !a(p, null)) return;
53
- let n = e.workArea.top;
54
- o(p, null, new Map(g.map(({ icon: t, x: r, y: i }) => {
55
- let a = e.placementAt(r, i);
56
- return [t, {
57
- left: Math.max(0, a.left),
58
- top: Math.max(n, a.top)
59
- }];
60
- })));
61
- }
62
- };
63
- return e.addEventListener("vf-drag", u), e.addEventListener("vf-drag-cancel", d), e.addEventListener("vf-drop", f), () => {
64
- l(null), e.removeEventListener("vf-drag", u), e.removeEventListener("vf-drag-cancel", d), e.removeEventListener("vf-drop", f);
65
- };
66
- }
67
- //#endregion
68
- export { e as fileByDrag };
@@ -1,179 +0,0 @@
1
- /**
2
- * The Finder: the stock application that shows the catalog as icons on the
3
- * desktop and in folder windows.
4
- *
5
- * - Icons are reconciled from the catalog into each container's field: the
6
- * desktop's, and that of every open folder window. An icon that changes
7
- * container moves with its item and keeps its selection.
8
- * - Positions are the items'. An icon is placed where its item says, and an
9
- * item with none takes its container's next free cell — the Trash the
10
- * desktop's bottom-right corner. Every move (a drag, a nudge, a Clean Up
11
- * landing, the resize rule) goes back onto the item. The desktop's lattice
12
- * runs down its right edge below the menu bar; a folder's runs in rows. On
13
- * a raster resize the desktop's icons re-pin by the nine-slice rule.
14
- * - A press anywhere in the desktop's field, on an icon or the bare desktop,
15
- * brings the Finder forward. The selection survives an application switch.
16
- * - Folder windows are made on open and removed on close, each with its item
17
- * count in the header and a field sized to the scroll range.
18
- * - A window closes into its item's icon, else the nearest open folder
19
- * around it. An application's icon stands for the application: it is drawn
20
- * open while the application has a window open, and the application's own
21
- * windows close into it.
22
- * - An alias wears its original's art with its name in italics, and opens
23
- * its original; with the original gone, an alert says so. Make Alias makes
24
- * one beside each selected application or document.
25
- * - Menus: File (Open, New Folder, Close, Make Alias), Edit (Copy, Paste,
26
- * Select All), View (Arrange Windows), Special (Clean Up, Empty Trash…),
27
- * each disabled while it has nothing to act on. A site switches commands
28
- * off and adds its own.
29
- * - Alerts are the Finder's own, composed with the site's caution art and
30
- * held by it while they are up. A site answers any of them with its own
31
- * dialog through `alertWith`.
32
- */
33
- import type { VfIcon, VfMenuItem, VfViewportBox } from '../index.js';
34
- import type { Catalog, CatalogStorage, Item, SystemFolders, Volumes } from './catalog.js';
35
- import type { LatticeOptions, Size } from './geometry.js';
36
- import type { AppContext, AppDefinition } from './shell.js';
37
- /** The Finder's application id. */
38
- export declare const FINDER = "finder";
39
- /** The Finder's art: the kit ships none. */
40
- export interface FinderArt {
41
- /** 32×32, as each of these. */
42
- folder: string;
43
- /** The Trash, empty and full. */
44
- trash: string;
45
- trashFull: string;
46
- /** An item whose kind has no art of its own. */
47
- document: string;
48
- /** The startup disk; the folder's art without it. */
49
- disk?: string;
50
- /** The System Folder and its two folders; the folder's art without them. */
51
- systemFolder?: string;
52
- appleMenuItems?: string;
53
- startupItems?: string;
54
- /** 12×12: the mark before the item count in the Trash's window and trashed folders. */
55
- trashMark?: string;
56
- /** 32×32: beside the message in the Finder's alerts. Without it they have no art. */
57
- caution?: string;
58
- }
59
- /** What a failed command was, in the `failed` alert. */
60
- export type FinderFailedAction = 'new-folder' | 'empty-trash' | 'paste' | 'add-file' | 'make-alias';
61
- /** The Finder's alerts, by id, with the details each one's handler gets. */
62
- export interface FinderAlerts {
63
- /** Special → Empty Trash…: an answer of `'ok'` empties the Trash. */
64
- 'empty-trash': {
65
- count: number;
66
- bytes: number;
67
- };
68
- /** New Folder, Paste, a dropped file or `storageReady()`, without storage. */
69
- 'storage-unavailable': Record<string, never>;
70
- /** A rename past the limit. */
71
- 'name-too-long': {
72
- limit: number;
73
- };
74
- /** A rename to nothing. */
75
- 'name-rejected': Record<string, never>;
76
- /** A filing storage refused. */
77
- 'move-failed': {
78
- error: Error;
79
- };
80
- /** Opening an alias whose original is gone. */
81
- 'original-missing': {
82
- alias: Item;
83
- };
84
- /** New Folder, Empty Trash, Paste, Make Alias or a dropped file failed. */
85
- failed: {
86
- action: FinderFailedAction;
87
- error: Error;
88
- };
89
- }
90
- /** One of the Finder's alerts. */
91
- export type FinderAlert = keyof FinderAlerts;
92
- /** Answers one of the Finder's alerts: resolves the answer, or null. */
93
- export type FinderAlertHandler<K extends FinderAlert> = (details: FinderAlerts[K]) => string | null | Promise<string | null>;
94
- /** The Finder's own commands, each of which a site can switch off. */
95
- export type FinderCommand = 'open' | 'new-folder' | 'close' | 'make-alias' | 'copy' | 'paste' | 'select-all' | 'arrange' | 'clean-up' | 'empty-trash';
96
- /** The Finder's menus, by `data-menu`. */
97
- export type FinderMenu = 'file' | 'edit' | 'view' | 'special';
98
- /** A command a site adds to the Finder's menus. */
99
- export interface FinderCommandSpec {
100
- menu: FinderMenu;
101
- /** The item's `value`, unique among the Finder's. */
102
- value: string;
103
- label: string;
104
- shortcut?: string;
105
- /** Put a separator before it. */
106
- separator?: boolean;
107
- run(): void;
108
- /** Whether it has anything to act on; disabled while false. */
109
- enabled?(): boolean;
110
- }
111
- /** What the Finder's `open` action takes: the item to open, and the box it opens out of. */
112
- export interface FinderOpen {
113
- item?: string | null;
114
- from?: VfViewportBox | null;
115
- }
116
- /** The Finder's actions, for a site's extension and other applications. */
117
- export interface FinderApi {
118
- readonly catalog: Catalog;
119
- /** The selected items, volumes included, in listing order. */
120
- selection(): Item[];
121
- /** Make `ids` the selection. */
122
- select(ids: readonly string[]): void;
123
- /** The folder whose window is active, or null for the desktop. */
124
- activeFolder(): string | null;
125
- /** Open an item as a double-click would: a folder's window, or the item's kind. The boot reopens folder windows through it. */
126
- open(target?: FinderOpen): void;
127
- /** Select an item's icon and open its rename box. */
128
- rename(id: string): Promise<void>;
129
- /** Clean Up a container (null: the desktop). */
130
- cleanUp(folder: string | null): Promise<void>;
131
- /** An item's icon, where one is rendered. */
132
- iconFor(id: string): VfIcon | null;
133
- /** Add a command to the Finder's menus. Call it from `extend`, before the menus first reach the bar. */
134
- addCommand(spec: FinderCommandSpec): VfMenuItem;
135
- /** Answer one of the Finder's alerts with the site's own dialog. Call it from `extend`. */
136
- alertWith<K extends FinderAlert>(id: K, handler: FinderAlertHandler<K>): void;
137
- /**
138
- * Whether storage answers. When it doesn't, raises Storage Unavailable —
139
- * the site's answer to it, where `alertWith` gave one — and returns false.
140
- */
141
- storageReady(): boolean;
142
- }
143
- export interface FinderOptions {
144
- /** Where the catalog is kept: `memoryStorage()`, `indexedDbStorage(name)`, the site's own, or null for nothing. */
145
- storage: CatalogStorage | null;
146
- art: FinderArt;
147
- /** The volumes: the Trash by default, and a startup disk when named. */
148
- volumes?: Volumes;
149
- /**
150
- * The System Folder and its folders, named: what Apple Menu Items holds is
151
- * the Apple menu, and what Startup Items holds opens at startup. Unset, none.
152
- */
153
- system?: SystemFolders;
154
- /**
155
- * What the catalog starts with, stored once per storage: `'markup'` reads
156
- * the `<vf-icon data-app>` and `<template data-folder="Name">` the page
157
- * put in the desktop's field, and a `<template data-system="apple-menu-items">`
158
- * files what it holds in that folder of the System Folder; a function
159
- * stores the site's defaults.
160
- */
161
- seed?: 'markup' | ((catalog: Catalog) => void | Promise<void>);
162
- /** The lattices' cells, pitches and insets (default: 64px cells, 80px columns, 72px rows, 16px insets). */
163
- lattice?: {
164
- desktop?: LatticeOptions;
165
- folder?: LatticeOptions;
166
- };
167
- /** Clean Up the desktop once a resize settles with icons overlapping. Default false. */
168
- cleanUpAfterResize?: boolean;
169
- /** Switch commands off: `{ 'empty-trash': false }`. */
170
- commands?: Partial<Record<FinderCommand, boolean>>;
171
- /** The site's dialogs for the Finder, each with a `data-dialog` name, held by it: `ctx.dialog(name)` in `extend`. */
172
- dialogs?: string;
173
- /** Add the site's own commands, and answer the Finder's alerts. */
174
- extend?(finder: FinderApi, ctx: AppContext): void;
175
- /** A folder window's size, system px. Default 320 × 223: three columns and two rows of icons. */
176
- window?: Size;
177
- }
178
- /** The Finder, as an application of the shell. */
179
- export declare function finder(options: FinderOptions): AppDefinition<FinderApi>;