vintage-frames 0.17.0 → 0.19.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 (39) hide show
  1. package/README.md +1 -1
  2. package/custom-elements.json +96 -7
  3. package/dist/build/app-file.d.ts +2 -2
  4. package/dist/build/app-file.js +1 -0
  5. package/dist/build/box.d.ts +3 -1
  6. package/dist/build/box.js +6 -3
  7. package/dist/build/frame.js +19 -17
  8. package/dist/build/plugins.d.ts +5 -4
  9. package/dist/build/plugins.js +69 -64
  10. package/dist/components/vf-icon.d.ts +7 -0
  11. package/dist/components/vf-icon.js +101 -81
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.js +15 -14
  14. package/dist/modal-dialog.d.ts +7 -6
  15. package/dist/modal-dialog.js +30 -22
  16. package/dist/shell/app-file.d.ts +43 -5
  17. package/dist/shell/app-file.js +90 -62
  18. package/dist/shell/catalog.d.ts +41 -1
  19. package/dist/shell/catalog.js +174 -153
  20. package/dist/shell/finder.d.ts +27 -9
  21. package/dist/shell/finder.js +335 -277
  22. package/dist/shell/geometry.d.ts +7 -0
  23. package/dist/shell/geometry.js +19 -3
  24. package/dist/shell/index.d.ts +4 -4
  25. package/dist/shell/index.js +12 -12
  26. package/dist/shell/pure.d.ts +1 -1
  27. package/dist/shell/pure.js +6 -6
  28. package/dist/shell/shell.d.ts +14 -10
  29. package/dist/shell/shell.js +144 -117
  30. package/dist/shell/version.d.ts +1 -1
  31. package/dist/shell/version.js +1 -1
  32. package/dist/styles/body-italic-font.d.ts +2 -0
  33. package/dist/styles/body-italic-font.js +10 -0
  34. package/dist/styles/register-embedded-font.d.ts +5 -4
  35. package/dist/styles/register-embedded-font.js +11 -10
  36. package/docs/SPEC.md +2 -2
  37. package/editor/vscode.html-custom-data.json +5 -0
  38. package/editor/web-types.json +11 -1
  39. package/package.json +7 -3
@@ -4,13 +4,27 @@
4
4
  * JSON, and its code, one ES module compressed with zlib. Any PNG tool reads
5
5
  * the chunks and any decoder skips them.
6
6
  *
7
- * This is the reading half, and checking a version against a range. It is
8
- * pure and runs the same under Node and in a page, through the platform's
9
- * compression streams. Writing an app file, and the box, are
10
- * `vintage-frames/build`'s.
7
+ * This is the reading half: the reader, checking a version against a range,
8
+ * putting versions in order, the app API levels and whether this kit runs an
9
+ * application, and the scale every box is drawn at. It is pure and runs the
10
+ * same under Node and in a page, through the platform's compression streams.
11
+ * Writing an app file, and the box, are `vintage-frames/build`'s.
11
12
  */
12
13
  /** The app file format this kit reads and writes. */
13
14
  export declare const APP_FILE_FORMAT = 1;
15
+ /**
16
+ * The app API this kit provides: the level of what a built application can
17
+ * use of it. It goes up when something in it changes or goes away, and never
18
+ * for an addition. app-api.json lists what it covers.
19
+ */
20
+ export declare const APP_API = 2;
21
+ /** The oldest app API this kit still runs. */
22
+ export declare const APP_API_OLDEST = 2;
23
+ /**
24
+ * One system px of an app file's box, in its picture's px: every box of
25
+ * this format is drawn at 3×, each system px a 3 × 3 block.
26
+ */
27
+ export declare const BOX_SCALE = 3;
14
28
  /** The iTXt keywords of the manifest's chunk and the code's. */
15
29
  export declare const MANIFEST_KEYWORD = "vintage-frames.app";
16
30
  export declare const CODE_KEYWORD = "vintage-frames.code";
@@ -23,7 +37,11 @@ export interface AppManifest {
23
37
  version: string;
24
38
  /** The kit range the code was built and checked against: `^0.17.0`. */
25
39
  requires: string;
40
+ /** The app API the code was built against, {@link APP_API}. A file from before levels has none. */
41
+ api?: number;
26
42
  author: string;
43
+ /** What the application is, in a sentence or two of plain text. */
44
+ description?: string;
27
45
  /** Its 32 × 32 icon: its PNG as a `data:` URL. */
28
46
  icon: string;
29
47
  }
@@ -62,11 +80,25 @@ export declare function manifestOf(json: unknown): AppManifest | string;
62
80
  /**
63
81
  * The manifest and code an app file carries, or why it isn't one: not a
64
82
  * whole PNG, a chunk missing or doubled, a format this kit doesn't read, a
65
- * field missing, or code that doesn't inflate.
83
+ * field missing or unreadable, or code that doesn't inflate. The reason ends
84
+ * a sentence such as "Meteors.png is not an app file: …", and is for people:
85
+ * its wording can change in any release, so match on nothing in it.
66
86
  */
67
87
  export declare function inspectAppFile(bytes: Uint8Array): Promise<AppFile | string>;
68
88
  /** The manifest and code an app file carries, or null for any other PNG. */
69
89
  export declare function readAppFile(bytes: Uint8Array): Promise<AppFile | null>;
90
+ /**
91
+ * Whether `text` is a version exactly as semver writes one: `0.1.0`,
92
+ * `1.0.0-rc.1`, but not `v0.1.0`, ` 0.1.0` or `01.2.3`.
93
+ */
94
+ export declare function isVersion(text: string): boolean;
95
+ /**
96
+ * Below zero, zero or above as version `a` comes before, with or after `b`,
97
+ * in semver's order: a release after its prereleases, build metadata
98
+ * ignored. Anything that isn't a version, a string or not, comes before
99
+ * every version, so a sort never throws.
100
+ */
101
+ export declare function compareVersions(a: string, b: string): number;
70
102
  /**
71
103
  * Whether `version` meets `range`: an exact version, or a caret range as
72
104
  * npm reads it. `^1.2.3` takes 1.x from 1.2.3 on, `^0.2.3` takes 0.2.x from
@@ -75,3 +107,9 @@ export declare function readAppFile(bytes: Uint8Array): Promise<AppFile | null>;
75
107
  * false.
76
108
  */
77
109
  export declare function satisfies(version: string, range: string): boolean;
110
+ /**
111
+ * Whether this kit runs the application a manifest describes: one built at
112
+ * an app API from {@link APP_API_OLDEST} to {@link APP_API}, or, for a file
113
+ * from before levels, one whose `requires` this kit's version meets.
114
+ */
115
+ export declare function appRuns(manifest: Pick<AppManifest, 'requires' | 'api'>): boolean;
@@ -1,27 +1,30 @@
1
+ import { VERSION as e } from "./version.js";
1
2
  //#region src/shell/app-file.ts
2
- var e = 1, t = "vintage-frames.app", n = "vintage-frames.code", r = [
3
+ var t = 1, n = 2, r = 2, i = 3, a = "vintage-frames.app", o = "vintage-frames.code", s = [
3
4
  "id",
4
5
  "name",
5
6
  "version",
6
7
  "requires",
8
+ "api",
7
9
  "author",
10
+ "description",
8
11
  "icon"
9
- ], i = Uint8Array.of(137, 80, 78, 71, 13, 10, 26, 10), a = Uint32Array.from({ length: 256 }, (e, t) => {
12
+ ], c = Uint8Array.of(137, 80, 78, 71, 13, 10, 26, 10), l = Uint32Array.from({ length: 256 }, (e, t) => {
10
13
  let n = t;
11
14
  for (let e = 0; e < 8; e++) n = n & 1 ? 3988292384 ^ n >>> 1 : n >>> 1;
12
15
  return n >>> 0;
13
16
  });
14
- function o(e) {
17
+ function u(e) {
15
18
  let t = 4294967295;
16
- for (let n of e) t = a[(t ^ n) & 255] ^ t >>> 8;
19
+ for (let n of e) t = l[(t ^ n) & 255] ^ t >>> 8;
17
20
  return (t ^ 4294967295) >>> 0;
18
21
  }
19
- function s(e) {
20
- if (e.length < 8 || i.some((t, n) => e[n] !== t)) return null;
22
+ function d(e) {
23
+ if (e.length < 8 || c.some((t, n) => e[n] !== t)) return null;
21
24
  let t = new DataView(e.buffer, e.byteOffset, e.byteLength), n = [], r = 8;
22
25
  for (; r + 12 <= e.length;) {
23
26
  let i = r + 12 + t.getUint32(r);
24
- if (i > e.length || o(e.subarray(r + 4, i - 4)) !== t.getUint32(i - 4)) return null;
27
+ if (i > e.length || u(e.subarray(r + 4, i - 4)) !== t.getUint32(i - 4)) return null;
25
28
  let a = String.fromCharCode(...e.subarray(r + 4, r + 8));
26
29
  if (n.push({
27
30
  type: a,
@@ -33,85 +36,91 @@ function s(e) {
33
36
  }
34
37
  return null;
35
38
  }
36
- function c(e) {
39
+ function f(e) {
37
40
  let t = e.indexOf(0);
38
41
  return t > 0 ? String.fromCharCode(...e.subarray(0, t)) : "";
39
42
  }
40
- var l = new TextDecoder("utf-8", { fatal: !0 });
41
- async function u(e) {
43
+ var p = new TextDecoder("utf-8", { fatal: !0 });
44
+ async function m(e) {
42
45
  let t = e.indexOf(0), n = e[t + 1], r = e[t + 2], i = e.indexOf(0, t + 3), a = i < 0 ? -1 : e.indexOf(0, i + 1);
43
46
  if (t < 1 || a < 0 || n > 1 || r !== 0) throw Error("a broken iTXt chunk");
44
47
  let o = e.subarray(a + 1);
45
- return l.decode(n ? await d(o) : o);
48
+ return p.decode(n ? await h(o) : o);
46
49
  }
47
- async function d(e) {
50
+ async function h(e) {
48
51
  let t = new Blob([e.slice()]).stream().pipeThrough(new DecompressionStream("deflate"));
49
52
  return new Uint8Array(await new Response(t).arrayBuffer());
50
53
  }
51
- async function f(e) {
54
+ async function g(e) {
52
55
  let t = new Blob([e.slice()]).stream().pipeThrough(new CompressionStream("deflate"));
53
56
  return new Uint8Array(await new Response(t).arrayBuffer());
54
57
  }
55
- function p(e) {
58
+ function _(e) {
56
59
  if (!e || typeof e != "object") return "its manifest is not an object";
57
60
  let t = e;
58
61
  if (t.format !== 1) return typeof t.format == "number" ? `it is format ${t.format}, and this kit reads format 1` : "its manifest has no format";
59
- let n = {
60
- format: 1,
61
- id: "",
62
- name: "",
63
- version: "",
64
- requires: "",
65
- author: "",
66
- icon: ""
67
- };
68
- for (let e of r) {
62
+ let n = { format: 1 };
63
+ for (let e of s) {
69
64
  let r = t[e];
65
+ if (e === "api") {
66
+ if (r === void 0) continue;
67
+ if (!Number.isInteger(r) || r < 1) return `its api, ${JSON.stringify(r)}, is not a level`;
68
+ n.api = r;
69
+ continue;
70
+ }
71
+ if (e === "description") {
72
+ if (r !== void 0 && typeof r != "string") return "its description is not text";
73
+ typeof r == "string" && r.trim() && (n.description = r);
74
+ continue;
75
+ }
70
76
  if (typeof r != "string" || !r) return `its manifest has no ${e}`;
77
+ if (e === "version" && !S(r)) return `its version, ${r}, is not a version`;
78
+ if (e === "requires" && !C(r)) return `its requires, ${r}, is not a range`;
71
79
  n[e] = r;
72
80
  }
73
81
  return n;
74
82
  }
75
- async function m(e) {
76
- let r = s(e);
77
- if (!r) return "it is not a whole PNG";
78
- let i = /* @__PURE__ */ new Map();
79
- for (let { type: e, data: t } of r) {
83
+ async function v(e) {
84
+ let t = d(e);
85
+ if (!t) return "it is not a whole PNG";
86
+ let n = /* @__PURE__ */ new Map();
87
+ for (let { type: e, data: r } of t) {
80
88
  if (e !== "iTXt") continue;
81
- let n = c(t);
82
- if (n === "vintage-frames.app" || n === "vintage-frames.code") {
83
- if (i.has(n)) return `it has two ${n} chunks`;
84
- i.set(n, t);
89
+ let t = f(r);
90
+ if (t === "vintage-frames.app" || t === "vintage-frames.code") {
91
+ if (n.has(t)) return `it has two ${t} chunks`;
92
+ n.set(t, r);
85
93
  }
86
94
  }
87
- let a = i.get(t), o = i.get(n);
88
- if (!a && !o) return "it carries no application";
89
- if (!a) return "its manifest is missing";
90
- if (!o) return "its code is missing";
91
- let l;
95
+ let r = n.get(a), i = n.get(o);
96
+ if (!r && !i) return "it carries no application";
97
+ if (!r) return "its manifest is missing";
98
+ if (!i) return "its code is missing";
99
+ let s;
92
100
  try {
93
- l = JSON.parse(await u(a));
101
+ s = JSON.parse(await m(r));
94
102
  } catch {
95
103
  return "its manifest does not read";
96
104
  }
97
- let d = p(l);
98
- if (typeof d == "string") return d;
105
+ let c = _(s);
106
+ if (typeof c == "string") return c;
99
107
  try {
100
108
  return {
101
- manifest: d,
102
- code: await u(o)
109
+ manifest: c,
110
+ code: await m(i)
103
111
  };
104
112
  } catch {
105
113
  return "its code does not inflate";
106
114
  }
107
115
  }
108
- async function h(e) {
109
- let t = await m(e);
116
+ async function y(e) {
117
+ let t = await v(e);
110
118
  return typeof t == "string" ? null : t;
111
119
  }
112
- var g = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/;
113
- function _(e) {
114
- let t = g.exec(e.trim());
120
+ var b = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
121
+ function x(e) {
122
+ if (typeof e != "string") return null;
123
+ let t = b.exec(e.trim().replace(/^v/, ""));
115
124
  return t ? {
116
125
  core: [
117
126
  Number(t[1]),
@@ -121,7 +130,18 @@ function _(e) {
121
130
  pre: t[4] ? t[4].split(".") : []
122
131
  } : null;
123
132
  }
124
- function v(e, t) {
133
+ function S(e) {
134
+ return b.test(e);
135
+ }
136
+ function C(e) {
137
+ return S(e.startsWith("^") ? e.slice(1) : e);
138
+ }
139
+ function w(e) {
140
+ if (typeof e != "string") return null;
141
+ let t = e.trim();
142
+ return x(t.startsWith("^") ? t.slice(1) : t);
143
+ }
144
+ function T(e, t) {
125
145
  for (let n = 0; n < 3; n++) if (e.core[n] !== t.core[n]) return e.core[n] - t.core[n];
126
146
  if (!e.pre.length || !t.pre.length) return t.pre.length - e.pre.length;
127
147
  for (let n = 0; n < Math.max(e.pre.length, t.pre.length); n++) {
@@ -134,28 +154,36 @@ function v(e, t) {
134
154
  }
135
155
  return 0;
136
156
  }
137
- function y(e, t) {
138
- let n = t.trim().startsWith("^"), r = _(e), i = _(n ? t.trim().slice(1) : t);
139
- if (!r || !i) return !1;
140
- if (!n) return v(r, i) === 0;
141
- if (r.pre.length && !(i.pre.length && r.core.every((e, t) => e === i.core[t]))) return !1;
142
- let [a, o, s] = i.core, c = {
143
- core: a ? [
144
- a + 1,
157
+ function E(e, t) {
158
+ let n = x(e), r = x(t);
159
+ return n && r ? T(n, r) : !!n - +!!r;
160
+ }
161
+ function D(e, t) {
162
+ let n = x(e), r = w(t);
163
+ if (!n || !r) return !1;
164
+ if (!t.trim().startsWith("^")) return T(n, r) === 0;
165
+ if (n.pre.length && !(r.pre.length && n.core.every((e, t) => e === r.core[t]))) return !1;
166
+ let [i, a, o] = r.core, s = {
167
+ core: i ? [
168
+ i + 1,
145
169
  0,
146
170
  0
147
- ] : o ? [
171
+ ] : a ? [
148
172
  0,
149
- o + 1,
173
+ a + 1,
150
174
  0
151
175
  ] : [
152
176
  0,
153
177
  0,
154
- s + 1
178
+ o + 1
155
179
  ],
156
180
  pre: []
157
181
  };
158
- return v(r, i) >= 0 && v(r, c) < 0;
182
+ return T(n, r) >= 0 && T(n, s) < 0;
183
+ }
184
+ function O(t) {
185
+ let { api: n } = t;
186
+ return n === void 0 ? D(e, t.requires) : Number.isInteger(n) && n >= 2 && n <= 2;
159
187
  }
160
188
  //#endregion
161
- export { e as APP_FILE_FORMAT, n as CODE_KEYWORD, t as MANIFEST_KEYWORD, i as PNG_SIGNATURE, o as crc32, f as deflate, d as inflate, m as inspectAppFile, c as itxtKeyword, p as manifestOf, s as pngChunks, h as readAppFile, y as satisfies };
189
+ export { n as APP_API, r as APP_API_OLDEST, t as APP_FILE_FORMAT, i as BOX_SCALE, o as CODE_KEYWORD, a as MANIFEST_KEYWORD, c as PNG_SIGNATURE, O as appRuns, E as compareVersions, u as crc32, g as deflate, h as inflate, v as inspectAppFile, S as isVersion, f as itxtKeyword, _ as manifestOf, d as pngChunks, y as readAppFile, D as satisfies };
@@ -13,6 +13,12 @@
13
13
  * never renamed, moved, copied or removed; nothing is made in the Trash.
14
14
  * Deleting is a move into the Trash, and only Empty Trash removes. A
15
15
  * volume's position is stored on a record of its own id.
16
+ * - The System Folder is kept the same way when the site names it: a folder
17
+ * on the startup disk (the desktop without one) holding Apple Menu Items
18
+ * and Startup Items, listed after the volumes. What they hold is anyone's,
19
+ * and the shell reads it: the Apple menu, and what opens at startup.
20
+ * - An alias stands for another item: its data names the original's id, and
21
+ * `resolve` finds the original wherever it moved, until it is removed.
16
22
  * - A folder never goes into itself or a folder inside it. A parent with no
17
23
  * record reads as the desktop.
18
24
  * - An item whose kind no application registers is left out of the listing
@@ -30,17 +36,25 @@ import type { Point } from './geometry.js';
30
36
  export declare const TRASH = "trash";
31
37
  /** The startup disk's id. */
32
38
  export declare const DISK = "disk";
39
+ /** The System Folder's id: kept on the startup disk when the site names it. */
40
+ export declare const SYSTEM_FOLDER = "system-folder";
41
+ /** Apple Menu Items' id, in the System Folder: what it holds is the Apple menu. */
42
+ export declare const APPLE_MENU_ITEMS = "apple-menu-items";
43
+ /** Startup Items' id, in the System Folder: what it holds opens at startup. */
44
+ export declare const STARTUP_ITEMS = "startup-items";
33
45
  /** A folder's kind. */
34
46
  export declare const FOLDER = "folder";
35
47
  /** An application's icon: the kind the shell registers, whose data names the application it opens. */
36
48
  export declare const APP_KIND = "app";
49
+ /** An alias: the kind the shell registers, whose data names the item it stands for. */
50
+ export declare const ALIAS_KIND = "alias";
37
51
  /** The name a new folder takes, counted up where it is taken. */
38
52
  export declare const UNTITLED_FOLDER = "untitled folder";
39
53
  /** One item of the catalog. */
40
54
  export interface Item {
41
55
  id: string;
42
56
  name: string;
43
- /** `folder`, a volume's (`trash`, `disk`), the shell's `app`, or a kind an application registers. */
57
+ /** `folder`, a volume's (`trash`, `disk`), the shell's `app` or `alias`, or a kind an application registers. */
44
58
  kind: string;
45
59
  /** A folder's id, a volume's, or null for the desktop. */
46
60
  parent: string | null;
@@ -78,9 +92,17 @@ export interface Volumes {
78
92
  trash?: string | false;
79
93
  disk?: string | false;
80
94
  }
95
+ /** The System Folder and its two folders, by name. A site that names them has them. */
96
+ export interface SystemFolders {
97
+ folder: string;
98
+ appleMenu: string;
99
+ startup: string;
100
+ }
81
101
  export interface CatalogOptions {
82
102
  storage: CatalogStorage | null;
83
103
  volumes?: Volumes;
104
+ /** The System Folder, on the startup disk. Unset, none. */
105
+ system?: SystemFolders;
84
106
  /** Whether an application registers `kind`. Folders are always listed. Default: every kind. */
85
107
  listed?(kind: string): boolean;
86
108
  /** A kind's payload hooks. */
@@ -109,10 +131,22 @@ export interface CatalogArchive {
109
131
  export declare const isContainerKind: (kind: string) => boolean;
110
132
  /** Whether `id` names a volume. */
111
133
  export declare const isVolume: (id: string | null | undefined) => boolean;
134
+ /**
135
+ * Whether `id` names a container the catalog keeps, never renamed, moved,
136
+ * copied or removed: a volume, the System Folder or a folder in it.
137
+ */
138
+ export declare const isKept: (id: string | null | undefined) => boolean;
112
139
  /** The application an `app` item stands for: its `data.app`, or null. */
113
140
  export declare function appIdOf(item: Item): string | null;
141
+ /** The id of the item an alias stands for: its `data.original`, or null for anything but an alias. */
142
+ export declare function originalOf(item: Item): string | null;
114
143
  /** The item with `id`, or null. */
115
144
  export declare function itemOf(state: CatalogState, id: string | null | undefined): Item | null;
145
+ /**
146
+ * The item `id` names, or the original an alias stands for, through an alias
147
+ * of an alias too. Null once the original is gone, and for a chain that loops.
148
+ */
149
+ export declare function resolve(state: CatalogState, id: string | null | undefined): Item | null;
116
150
  /** The container `parent` resolves to: that container while it is listed, else the desktop (null). */
117
151
  export declare function containerOf(state: CatalogState, parent: string | null | undefined): string | null;
118
152
  /** The items directly in a container (null: the desktop), in listing order. */
@@ -142,6 +176,12 @@ export declare function nextFolderName(state: CatalogState, parent: string | nul
142
176
  * then " copy 2", " copy 3", …
143
177
  */
144
178
  export declare function copyName(state: CatalogState, parent: string | null, name: string, kind: string): string;
179
+ /** The name for an alias of `name` in a container: "name alias", then "name alias 2", … while an alias there has it. */
180
+ export declare function aliasName(state: CatalogState, parent: string | null, name: string): string;
181
+ /** What Apple Menu Items holds, by name: the Apple menu's entries. None without the System Folder. */
182
+ export declare const appleMenuItemsOf: (state: CatalogState) => Item[];
183
+ /** What Startup Items holds, by name: what opens at startup. None without the System Folder. */
184
+ export declare const startupItemsOf: (state: CatalogState) => Item[];
145
185
  /** The catalog's operations, over its listing. */
146
186
  export interface Catalog {
147
187
  get(): CatalogState;