vintage-frames 0.16.2 → 0.18.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.
@@ -0,0 +1,24 @@
1
+ /**
2
+ * PNG pixels, with no canvas and no dependency: a decoder for the forms art
3
+ * comes in, and an encoder for the box.
4
+ *
5
+ * - Decoding takes 8-bit gray, RGB, gray with alpha and RGBA, and indexed
6
+ * or gray at 1, 2, 4 or 8 bits, non-interlaced, with their transparency.
7
+ * 16-bit and interlaced files are refused, naming what to save them as.
8
+ * - Encoding writes 8-bit RGBA, each row filtered the way that compresses
9
+ * it best.
10
+ */
11
+ /** Straight RGBA, 8 bits a channel, rows top first. */
12
+ export interface Pixels {
13
+ width: number;
14
+ height: number;
15
+ data: Uint8Array;
16
+ }
17
+ /** `parts`, end to end. */
18
+ export declare function concat(parts: readonly Uint8Array[]): Uint8Array;
19
+ /** One chunk, with its length and CRC. */
20
+ export declare function pngChunk(type: string, data: Uint8Array): Uint8Array;
21
+ /** A PNG's pixels. `what` names it in an error: "the artwork". */
22
+ export declare function decodePng(bytes: Uint8Array, what?: string): Promise<Pixels>;
23
+ /** Pixels as an 8-bit RGBA PNG. */
24
+ export declare function encodePng({ width, height, data }: Pixels): Promise<Uint8Array>;
@@ -0,0 +1,116 @@
1
+ import { PNG_SIGNATURE as e, crc32 as t, deflate as n, inflate as r, pngChunks as i } from "../shell/app-file.js";
2
+ //#region src/build/png.ts
3
+ var a = {
4
+ 0: 1,
5
+ 2: 3,
6
+ 3: 1,
7
+ 4: 2,
8
+ 6: 4
9
+ };
10
+ function o(e) {
11
+ let t = new Uint8Array(e.reduce((e, t) => e + t.length, 0)), n = 0;
12
+ for (let r of e) t.set(r, n), n += r.length;
13
+ return t;
14
+ }
15
+ function s(e, n) {
16
+ let r = new Uint8Array(12 + n.length), i = new DataView(r.buffer);
17
+ i.setUint32(0, n.length);
18
+ for (let t = 0; t < 4; t++) r[4 + t] = e.charCodeAt(t);
19
+ return r.set(n, 8), i.setUint32(8 + n.length, t(r.subarray(4, 8 + n.length))), r;
20
+ }
21
+ async function c(e, t = "the PNG") {
22
+ let n = i(e), s = n?.[0];
23
+ if (!n || s?.type !== "IHDR" || s.data.length !== 13) throw Error(`${t} is not a whole PNG`);
24
+ let c = new DataView(s.data.buffer, s.data.byteOffset, 13), l = c.getUint32(0), u = c.getUint32(4), [f, p, m, h, g] = s.data.subarray(8);
25
+ if (f === 16) throw Error(`${t} is a 16-bit PNG: save it as an 8-bit PNG`);
26
+ if (g) throw Error(`${t} is interlaced: save it as a non-interlaced PNG`);
27
+ let _ = a[p], v = p === 0 || p === 3 ? [
28
+ 1,
29
+ 2,
30
+ 4,
31
+ 8
32
+ ].includes(f) : f === 8;
33
+ if (!_ || !v || m || h) throw Error(`${t} is a kind of PNG this kit doesn't read (color type ${p}, ${f} bits): save it as an 8-bit PNG`);
34
+ let y = null, b = null, x = [];
35
+ for (let { type: e, data: t } of n) e === "PLTE" ? y = t : e === "tRNS" ? b = t : e === "IDAT" && x.push(t);
36
+ if (p === 3 && !y) throw Error(`${t} is indexed and has no palette`);
37
+ let S = _ * f, C = Math.ceil(l * S / 8), w = await r(o(x)).catch(() => {
38
+ throw Error(`${t} is not a whole PNG`);
39
+ });
40
+ if (w.length < u * (C + 1)) throw Error(`${t} is not a whole PNG`);
41
+ let T = d(w, u, C, Math.max(1, S >> 3), t), E = (1 << f) - 1, D = (e, t) => {
42
+ let n = t * f;
43
+ return T[e + (n >> 3)] >> 8 - f - (n & 7) & E;
44
+ }, O = (e) => b && b.length >= e * 2 + 2 ? b[e * 2] << 8 | b[e * 2 + 1] : -1, k = new Uint8Array(l * u * 4);
45
+ for (let e = 0; e < u; e++) {
46
+ let n = e * C;
47
+ for (let r = 0; r < l; r++) {
48
+ let i = (e * l + r) * 4;
49
+ if (p === 0) {
50
+ let e = f === 8 ? T[n + r] : D(n, r);
51
+ k[i] = k[i + 1] = k[i + 2] = Math.round(e * 255 / E), k[i + 3] = e === O(0) ? 0 : 255;
52
+ } else if (p === 3) {
53
+ let e = f === 8 ? T[n + r] : D(n, r);
54
+ if (e * 3 + 2 >= y.length) throw Error(`${t} uses a color its palette lacks`);
55
+ k[i] = y[e * 3], k[i + 1] = y[e * 3 + 1], k[i + 2] = y[e * 3 + 2], k[i + 3] = b?.[e] ?? 255;
56
+ } else if (p === 2) {
57
+ let e = n + r * 3;
58
+ k.set(T.subarray(e, e + 3), i);
59
+ let t = T[e] !== O(0) || T[e + 1] !== O(1) || T[e + 2] !== O(2);
60
+ k[i + 3] = t ? 255 : 0;
61
+ } else if (p === 4) {
62
+ let e = n + r * 2;
63
+ k[i] = k[i + 1] = k[i + 2] = T[e], k[i + 3] = T[e + 1];
64
+ } else k.set(T.subarray(n + r * 4, n + r * 4 + 4), i);
65
+ }
66
+ }
67
+ return {
68
+ width: l,
69
+ height: u,
70
+ data: k
71
+ };
72
+ }
73
+ function l(e, t, n) {
74
+ let r = e + t - n, i = Math.abs(r - e), a = Math.abs(r - t), o = Math.abs(r - n);
75
+ return i <= a && i <= o ? e : a <= o ? t : n;
76
+ }
77
+ function u(e, t, n, r) {
78
+ return e === 1 ? t : e === 2 ? n : e === 3 ? t + n >> 1 : e === 4 ? l(t, n, r) : 0;
79
+ }
80
+ function d(e, t, n, r, i) {
81
+ let a = new Uint8Array(t * n);
82
+ for (let o = 0; o < t; o++) {
83
+ let t = e[o * (n + 1)];
84
+ if (t > 4) throw Error(`${i} is not a whole PNG`);
85
+ let s = o * (n + 1) + 1, c = o * n;
86
+ for (let i = 0; i < n; i++) {
87
+ let l = i >= r ? a[c + i - r] : 0, d = o ? a[c - n + i] : 0, f = i >= r && o ? a[c - n + i - r] : 0;
88
+ a[c + i] = e[s + i] + u(t, l, d, f) & 255;
89
+ }
90
+ }
91
+ return a;
92
+ }
93
+ async function f({ width: t, height: r, data: i }) {
94
+ let a = t * 4, c = new Uint8Array(r * (a + 1)), l = Array.from({ length: 5 }, () => new Uint8Array(a));
95
+ for (let e = 0; e < r; e++) {
96
+ let t = e * a, n = 0, r = Infinity;
97
+ for (let o = 0; o < 5; o++) {
98
+ let s = l[o], c = 0;
99
+ for (let n = 0; n < a; n++) {
100
+ let r = n >= 4 ? i[t + n - 4] : 0, l = e ? i[t - a + n] : 0, d = n >= 4 && e ? i[t - a + n - 4] : 0, f = i[t + n] - u(o, r, l, d) & 255;
101
+ s[n] = f, c += f < 128 ? f : 256 - f;
102
+ }
103
+ c < r && (r = c, n = o);
104
+ }
105
+ c[e * (a + 1)] = n, c.set(l[n], e * (a + 1) + 1);
106
+ }
107
+ let d = /* @__PURE__ */ new Uint8Array(13), f = new DataView(d.buffer);
108
+ return f.setUint32(0, t), f.setUint32(4, r), d[8] = 8, d[9] = 6, o([
109
+ e,
110
+ s("IHDR", d),
111
+ s("IDAT", await n(c)),
112
+ s("IEND", /* @__PURE__ */ new Uint8Array())
113
+ ]);
114
+ }
115
+ //#endregion
116
+ export { o as concat, c as decodePng, f as encodePng, s as pngChunk };
@@ -51,7 +51,10 @@ export declare const modalDialogStyles: import("lit").CSSResult;
51
51
  * the reason. Because every close path — Escape, `close()`, the opt-in
52
52
  * {@link lightDismiss} click outside — routes through the native `close`
53
53
  * event, an Escape-close no longer leaves a stale origin behind, so the next
54
- * open re-derives it.
54
+ * open re-derives it. That event is queued: a modal opened again before it
55
+ * comes (`close()` then `show()` in one turn) runs the funnel at the open,
56
+ * inside `show()`, so the close's `vf-close` still comes before the open's
57
+ * `vf-show`, and the late event changes nothing.
55
58
  *
56
59
  * It also owns the two keyboard rules every classic dialog followed (see
57
60
  * {@link defaultButton} and {@link initialFocusTarget}): Return or Enter
@@ -242,11 +245,9 @@ export declare class VfModalDialog extends LitElement {
242
245
  /** Native `cancel` (Escape): remember the reason; `close` follows. */
243
246
  protected _onNativeCancel(): void;
244
247
  /**
245
- * Native `close` — the single teardown funnel for every close path. Drops
246
- * the written origin so the next open re-derives it from the box it will
247
- * actually have (a stated `top`/`left` is the author's or the user's and
248
- * survives, and is re-applied by that open), syncs `open`, and fires
249
- * `vf-close` with the reason and the value the close carried.
248
+ * Native `close`, which every close path ends in: syncs `open` and runs the
249
+ * close funnel. An open that came before the event has run the funnel for
250
+ * this close already, and the event passes.
250
251
  */
251
252
  protected _onNativeClose(): void;
252
253
  }
@@ -112,7 +112,7 @@ var p = 24, m = /* @__PURE__ */ new Set([
112
112
  this.#o = null, !(!t || e.target !== this._dialog || !this.lightDismiss) && (this.#n = "outside", this.close());
113
113
  }, this.#f = () => {
114
114
  this.#o = null;
115
- }, this.#p = null, this.#y = () => this.settle(), this.addEventListener("submit", this.#a);
115
+ }, this.#p = null, this.#m = !1, this.#b = () => this.settle(), this.addEventListener("submit", this.#a);
116
116
  }
117
117
  #a;
118
118
  #o;
@@ -133,41 +133,42 @@ var p = 24, m = /* @__PURE__ */ new Set([
133
133
  #d;
134
134
  #f;
135
135
  #p;
136
+ #m;
136
137
  show() {
137
- this.open = !0, this.hasUpdated && this.#v();
138
+ this.open = !0, this.hasUpdated && this.#y();
138
139
  }
139
140
  close(e) {
140
- this.open && e !== void 0 && (this.#r = e), this.open = !1, this.hasUpdated && this.#v();
141
+ this.open && e !== void 0 && (this.#r = e), this.open = !1, this.hasUpdated && this.#y();
141
142
  }
142
143
  closeFromBox(e) {
143
144
  this.open && (this.#i = e), this.close();
144
145
  }
145
146
  updated(e) {
146
- e.has("open") && this.#v(), (e.has("top") || e.has("left")) && this.settle(), this.#t();
147
+ e.has("open") && this.#y(), (e.has("top") || e.has("left")) && this.settle(), this.#t();
147
148
  }
148
- #m() {
149
+ #h() {
149
150
  let e = document.documentElement;
150
151
  return {
151
152
  width: o(e.clientWidth || window.innerWidth, this),
152
153
  height: o(e.clientHeight || window.innerHeight, this)
153
154
  };
154
155
  }
155
- #h(e, t) {
156
- let n = this.#m(), r = o(this._dialog?.offsetWidth ?? 0, this);
156
+ #g(e, t) {
157
+ let n = this.#h(), r = o(this._dialog?.offsetWidth ?? 0, this);
157
158
  return {
158
159
  x: Math.min(Math.max(e, p - r), n.width - p),
159
160
  y: Math.min(Math.max(t, 0), Math.max(0, n.height - p))
160
161
  };
161
162
  }
162
- #g() {
163
- let e = this.#m(), t = this._dialog?.getBoundingClientRect();
163
+ #_() {
164
+ let e = this.#h(), t = this._dialog?.getBoundingClientRect();
164
165
  return {
165
166
  x: (e.width - o(t?.width ?? 0, this)) / 2,
166
167
  y: (e.height - o(t?.height ?? 0, this)) / 2
167
168
  };
168
169
  }
169
170
  placeAt(e, t) {
170
- let n = this.#h(e, t);
171
+ let n = this.#g(e, t);
171
172
  this.left = i(n.x, this), this.top = i(n.y, this);
172
173
  }
173
174
  settle() {
@@ -176,10 +177,10 @@ var p = 24, m = /* @__PURE__ */ new Set([
176
177
  let t = this.left != null || this.top != null ? {
177
178
  x: this.left ?? 0,
178
179
  y: this.top ?? 0
179
- } : this.#g(), n = this.#h(t.x, t.y);
180
+ } : this.#_(), n = this.#g(t.x, t.y);
180
181
  e.style.left = a(i(n.x, this)), e.style.top = a(i(n.y, this)), e.style.right = "auto", e.style.bottom = "auto", e.style.margin = "0";
181
182
  }
182
- #_() {
183
+ #v() {
183
184
  let e = this._dialog?.style;
184
185
  if (e) for (let t of [
185
186
  "left",
@@ -189,27 +190,31 @@ var p = 24, m = /* @__PURE__ */ new Set([
189
190
  "margin"
190
191
  ]) e.removeProperty(t);
191
192
  }
192
- #v() {
193
+ #y() {
193
194
  let e = this._dialog;
194
195
  if (e) {
195
196
  if (this.open && !e.open) {
196
- this.#p = document.activeElement, this.returnValue = "", e.showModal();
197
+ if (this.#m) {
198
+ this.#T(), this.#y();
199
+ return;
200
+ }
201
+ this.#p = document.activeElement, this.returnValue = "", this.#n = null, this.#r = null, this.#i = null, e.showModal(), this.#m = !0;
197
202
  let n = this.#c();
198
- this.settle(), this.#S(e), this.#s.attach(), t(this, "vf-show", { focus: n }), n && _(n) && n.select?.();
203
+ this.settle(), this.#C(e), this.#s.attach(), t(this, "vf-show", { focus: n }), n && _(n) && n.select?.();
199
204
  } else !this.open && e.open && e.close();
200
205
  }
201
206
  }
202
- #y;
203
207
  #b;
204
208
  #x;
205
- #S(e) {
206
- typeof ResizeObserver < "u" && !this.#b && (this.#b = new ResizeObserver(this.#y), this.#b.observe(e)), window.addEventListener("resize", this.#y), this.#x ??= r(this.#y);
209
+ #S;
210
+ #C(e) {
211
+ typeof ResizeObserver < "u" && !this.#x && (this.#x = new ResizeObserver(this.#b), this.#x.observe(e)), window.addEventListener("resize", this.#b), this.#S ??= r(this.#b);
207
212
  }
208
- #C() {
209
- this.#b?.disconnect(), this.#b = void 0, window.removeEventListener("resize", this.#y), this.#x?.(), this.#x = void 0;
213
+ #w() {
214
+ this.#x?.disconnect(), this.#x = void 0, window.removeEventListener("resize", this.#b), this.#S?.(), this.#S = void 0;
210
215
  }
211
216
  disconnectedCallback() {
212
- super.disconnectedCallback(), this.#C();
217
+ super.disconnectedCallback(), this.#w();
213
218
  let e = this._dialog;
214
219
  if (e?.open) {
215
220
  e.close();
@@ -222,8 +227,11 @@ var p = 24, m = /* @__PURE__ */ new Set([
222
227
  this.#n = "escape";
223
228
  }
224
229
  _onNativeClose() {
230
+ !this.#m || this._dialog?.open || (this.open = !1, this.#T());
231
+ }
232
+ #T() {
225
233
  let e = this.#n ?? "close", n = this.#r, r = this.#i;
226
- this.#n = null, this.#r = null, this.#i = null, n !== null && (this.returnValue = n), this.#p = null, this.#o = null, this.#s.detach(), this.#C(), this.open = !1, this.#_(), t(this, "vf-close", {
234
+ this.#n = null, this.#r = null, this.#i = null, this.#m = !1, n !== null && (this.returnValue = n), this.#p = null, this.#o = null, this.#s.detach(), this.#w(), this.#v(), t(this, "vf-close", {
227
235
  reason: e,
228
236
  returnValue: n,
229
237
  ...r
@@ -0,0 +1,99 @@
1
+ /**
2
+ * App files: an application on the shell as one PNG. The picture is the
3
+ * application's box; two iTXt chunks before IEND carry its manifest, as
4
+ * JSON, and its code, one ES module compressed with zlib. Any PNG tool reads
5
+ * the chunks and any decoder skips them.
6
+ *
7
+ * This is the reading half: the reader, checking a version against a range,
8
+ * putting versions in order, and the scale every box is drawn at. It is
9
+ * pure and runs the same under Node and in a page, through the platform's
10
+ * compression streams. Writing an app file, and the box, are
11
+ * `vintage-frames/build`'s.
12
+ */
13
+ /** The app file format this kit reads and writes. */
14
+ export declare const APP_FILE_FORMAT = 1;
15
+ /**
16
+ * One system px of an app file's box, in its picture's px: every box of
17
+ * this format is drawn at 3×, each system px a 3 × 3 block.
18
+ */
19
+ export declare const BOX_SCALE = 3;
20
+ /** The iTXt keywords of the manifest's chunk and the code's. */
21
+ export declare const MANIFEST_KEYWORD = "vintage-frames.app";
22
+ export declare const CODE_KEYWORD = "vintage-frames.code";
23
+ /** What an app file says about its application. */
24
+ export interface AppManifest {
25
+ /** The app file's own format, {@link APP_FILE_FORMAT}. */
26
+ format: number;
27
+ id: string;
28
+ name: string;
29
+ version: string;
30
+ /** The kit range the code was built and checked against: `^0.17.0`. */
31
+ requires: string;
32
+ author: string;
33
+ /** What the application is, in a sentence or two of plain text. */
34
+ description?: string;
35
+ /** Its 32 × 32 icon: its PNG as a `data:` URL. */
36
+ icon: string;
37
+ }
38
+ /** An application, read from its app file. */
39
+ export interface AppFile {
40
+ manifest: AppManifest;
41
+ /** One ES module. Its default export is the application's factory. */
42
+ code: string;
43
+ }
44
+ /** The eight bytes every PNG starts with. */
45
+ export declare const PNG_SIGNATURE: Uint8Array<ArrayBuffer>;
46
+ /** The CRC-32 a PNG chunk ends with, over its type and data. */
47
+ export declare function crc32(bytes: Uint8Array): number;
48
+ /** One chunk of a PNG. */
49
+ export interface PngChunk {
50
+ type: string;
51
+ data: Uint8Array;
52
+ /** Where it sits in the file: its length's first byte, and the byte after its CRC. */
53
+ start: number;
54
+ end: number;
55
+ }
56
+ /**
57
+ * A PNG's chunks, IEND last, or null when it isn't a whole PNG: the
58
+ * signature is wrong, a chunk runs past the end, a CRC doesn't match, or
59
+ * there is no IEND. Anything after IEND is left out.
60
+ */
61
+ export declare function pngChunks(bytes: Uint8Array): PngChunk[] | null;
62
+ /** An iTXt chunk's keyword. */
63
+ export declare function itxtKeyword(data: Uint8Array): string;
64
+ /** zlib data, inflated. */
65
+ export declare function inflate(bytes: Uint8Array): Promise<Uint8Array>;
66
+ /** zlib data, deflated. */
67
+ export declare function deflate(bytes: Uint8Array): Promise<Uint8Array>;
68
+ /** A parsed manifest as one this kit reads, or what is wrong with it. */
69
+ export declare function manifestOf(json: unknown): AppManifest | string;
70
+ /**
71
+ * The manifest and code an app file carries, or why it isn't one: not a
72
+ * whole PNG, a chunk missing or doubled, a format this kit doesn't read, a
73
+ * field missing or unreadable, or code that doesn't inflate. The reason ends
74
+ * a sentence such as "Meteors.png is not an app file: …", and is for people:
75
+ * its wording can change in any release, so match on nothing in it.
76
+ */
77
+ export declare function inspectAppFile(bytes: Uint8Array): Promise<AppFile | string>;
78
+ /** The manifest and code an app file carries, or null for any other PNG. */
79
+ export declare function readAppFile(bytes: Uint8Array): Promise<AppFile | null>;
80
+ /**
81
+ * Whether `text` is a version exactly as semver writes one: `0.1.0`,
82
+ * `1.0.0-rc.1`, but not `v0.1.0`, ` 0.1.0` or `01.2.3`.
83
+ */
84
+ export declare function isVersion(text: string): boolean;
85
+ /**
86
+ * Below zero, zero or above as version `a` comes before, with or after `b`,
87
+ * in semver's order: a release after its prereleases, build metadata
88
+ * ignored. Anything that isn't a version, a string or not, comes before
89
+ * every version, so a sort never throws.
90
+ */
91
+ export declare function compareVersions(a: string, b: string): number;
92
+ /**
93
+ * Whether `version` meets `range`: an exact version, or a caret range as
94
+ * npm reads it. `^1.2.3` takes 1.x from 1.2.3 on, `^0.2.3` takes 0.2.x from
95
+ * 0.2.3 on, and `^0.0.3` takes 0.0.3 alone. A prerelease meets a range only
96
+ * when the range names a prerelease of the same version. Anything else is
97
+ * false.
98
+ */
99
+ export declare function satisfies(version: string, range: string): boolean;
@@ -0,0 +1,177 @@
1
+ //#region src/shell/app-file.ts
2
+ var e = 1, t = 3, n = "vintage-frames.app", r = "vintage-frames.code", i = [
3
+ "id",
4
+ "name",
5
+ "version",
6
+ "requires",
7
+ "author",
8
+ "description",
9
+ "icon"
10
+ ], a = Uint8Array.of(137, 80, 78, 71, 13, 10, 26, 10), o = Uint32Array.from({ length: 256 }, (e, t) => {
11
+ let n = t;
12
+ for (let e = 0; e < 8; e++) n = n & 1 ? 3988292384 ^ n >>> 1 : n >>> 1;
13
+ return n >>> 0;
14
+ });
15
+ function s(e) {
16
+ let t = 4294967295;
17
+ for (let n of e) t = o[(t ^ n) & 255] ^ t >>> 8;
18
+ return (t ^ 4294967295) >>> 0;
19
+ }
20
+ function c(e) {
21
+ if (e.length < 8 || a.some((t, n) => e[n] !== t)) return null;
22
+ let t = new DataView(e.buffer, e.byteOffset, e.byteLength), n = [], r = 8;
23
+ for (; r + 12 <= e.length;) {
24
+ let i = r + 12 + t.getUint32(r);
25
+ if (i > e.length || s(e.subarray(r + 4, i - 4)) !== t.getUint32(i - 4)) return null;
26
+ let a = String.fromCharCode(...e.subarray(r + 4, r + 8));
27
+ if (n.push({
28
+ type: a,
29
+ data: e.subarray(r + 8, i - 4),
30
+ start: r,
31
+ end: i
32
+ }), a === "IEND") return n;
33
+ r = i;
34
+ }
35
+ return null;
36
+ }
37
+ function l(e) {
38
+ let t = e.indexOf(0);
39
+ return t > 0 ? String.fromCharCode(...e.subarray(0, t)) : "";
40
+ }
41
+ var u = new TextDecoder("utf-8", { fatal: !0 });
42
+ async function d(e) {
43
+ 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);
44
+ if (t < 1 || a < 0 || n > 1 || r !== 0) throw Error("a broken iTXt chunk");
45
+ let o = e.subarray(a + 1);
46
+ return u.decode(n ? await f(o) : o);
47
+ }
48
+ async function f(e) {
49
+ let t = new Blob([e.slice()]).stream().pipeThrough(new DecompressionStream("deflate"));
50
+ return new Uint8Array(await new Response(t).arrayBuffer());
51
+ }
52
+ async function p(e) {
53
+ let t = new Blob([e.slice()]).stream().pipeThrough(new CompressionStream("deflate"));
54
+ return new Uint8Array(await new Response(t).arrayBuffer());
55
+ }
56
+ function m(e) {
57
+ if (!e || typeof e != "object") return "its manifest is not an object";
58
+ let t = e;
59
+ 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";
60
+ let n = { format: 1 };
61
+ for (let e of i) {
62
+ let r = t[e];
63
+ if (e === "description") {
64
+ if (r !== void 0 && typeof r != "string") return "its description is not text";
65
+ typeof r == "string" && r.trim() && (n.description = r);
66
+ continue;
67
+ }
68
+ if (typeof r != "string" || !r) return `its manifest has no ${e}`;
69
+ if (e === "version" && !y(r)) return `its version, ${r}, is not a version`;
70
+ if (e === "requires" && !b(r)) return `its requires, ${r}, is not a range`;
71
+ n[e] = r;
72
+ }
73
+ return n;
74
+ }
75
+ async function h(e) {
76
+ let t = c(e);
77
+ if (!t) return "it is not a whole PNG";
78
+ let i = /* @__PURE__ */ new Map();
79
+ for (let { type: e, data: n } of t) {
80
+ if (e !== "iTXt") continue;
81
+ let t = l(n);
82
+ if (t === "vintage-frames.app" || t === "vintage-frames.code") {
83
+ if (i.has(t)) return `it has two ${t} chunks`;
84
+ i.set(t, n);
85
+ }
86
+ }
87
+ let a = i.get(n), o = i.get(r);
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 s;
92
+ try {
93
+ s = JSON.parse(await d(a));
94
+ } catch {
95
+ return "its manifest does not read";
96
+ }
97
+ let u = m(s);
98
+ if (typeof u == "string") return u;
99
+ try {
100
+ return {
101
+ manifest: u,
102
+ code: await d(o)
103
+ };
104
+ } catch {
105
+ return "its code does not inflate";
106
+ }
107
+ }
108
+ async function g(e) {
109
+ let t = await h(e);
110
+ return typeof t == "string" ? null : t;
111
+ }
112
+ var _ = /^(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-]+)*))?$/;
113
+ function v(e) {
114
+ if (typeof e != "string") return null;
115
+ let t = _.exec(e.trim().replace(/^v/, ""));
116
+ return t ? {
117
+ core: [
118
+ Number(t[1]),
119
+ Number(t[2]),
120
+ Number(t[3])
121
+ ],
122
+ pre: t[4] ? t[4].split(".") : []
123
+ } : null;
124
+ }
125
+ function y(e) {
126
+ return _.test(e);
127
+ }
128
+ function b(e) {
129
+ return y(e.startsWith("^") ? e.slice(1) : e);
130
+ }
131
+ function x(e) {
132
+ if (typeof e != "string") return null;
133
+ let t = e.trim();
134
+ return v(t.startsWith("^") ? t.slice(1) : t);
135
+ }
136
+ function S(e, t) {
137
+ for (let n = 0; n < 3; n++) if (e.core[n] !== t.core[n]) return e.core[n] - t.core[n];
138
+ if (!e.pre.length || !t.pre.length) return t.pre.length - e.pre.length;
139
+ for (let n = 0; n < Math.max(e.pre.length, t.pre.length); n++) {
140
+ let r = e.pre[n], i = t.pre[n];
141
+ if (r === void 0) return -1;
142
+ if (i === void 0) return 1;
143
+ if (r === i) continue;
144
+ let a = /^\d+$/.test(r), o = /^\d+$/.test(i);
145
+ return a && o ? Number(r) - Number(i) : a === o ? r < i ? -1 : 1 : a ? -1 : 1;
146
+ }
147
+ return 0;
148
+ }
149
+ function C(e, t) {
150
+ let n = v(e), r = v(t);
151
+ return n && r ? S(n, r) : !!n - +!!r;
152
+ }
153
+ function w(e, t) {
154
+ let n = v(e), r = x(t);
155
+ if (!n || !r) return !1;
156
+ if (!t.trim().startsWith("^")) return S(n, r) === 0;
157
+ if (n.pre.length && !(r.pre.length && n.core.every((e, t) => e === r.core[t]))) return !1;
158
+ let [i, a, o] = r.core, s = {
159
+ core: i ? [
160
+ i + 1,
161
+ 0,
162
+ 0
163
+ ] : a ? [
164
+ 0,
165
+ a + 1,
166
+ 0
167
+ ] : [
168
+ 0,
169
+ 0,
170
+ o + 1
171
+ ],
172
+ pre: []
173
+ };
174
+ return S(n, r) >= 0 && S(n, s) < 0;
175
+ }
176
+ //#endregion
177
+ export { e as APP_FILE_FORMAT, t as BOX_SCALE, r as CODE_KEYWORD, n as MANIFEST_KEYWORD, a as PNG_SIGNATURE, C as compareVersions, s as crc32, p as deflate, f as inflate, h as inspectAppFile, y as isVersion, l as itxtKeyword, m as manifestOf, c as pngChunks, g as readAppFile, w as satisfies };
@@ -25,3 +25,6 @@ export type { LocalStorageStateOptions, SavedSession, SavedWindow, SessionSnapsh
25
25
  export { BAND, CASCADE_SLOTS, CASCADE_STEP, ICON_CELL, ICON_COLUMN, ICON_INSET, ICON_ROW, NEAR, WINDOW_ORIGIN, cascadedBox, cascadeFrom, cascadeSlot, centeredBox, cleanUp, collisions, desktopLattice, fieldExtent, fillOrder, folderLattice, frameOf, isPin, latticeCell, latticeSlot, nearBox, nextFreeCell, pinOf, pinTo, trashCell, windowOrigin, } from './geometry.js';
26
26
  export type { Bands, Box, CascadeOptions, EdgePin, Frame, Lattice, LatticeOptions, Pin, Point, Policy, Size, } from './geometry.js';
27
27
  export { startClock, formatDate, formatTime, DATE_HOLD_MS } from './clock.js';
28
+ export { APP_FILE_FORMAT, BOX_SCALE, compareVersions, inspectAppFile, readAppFile, satisfies } from './app-file.js';
29
+ export type { AppFile, AppManifest } from './app-file.js';
30
+ export { VERSION } from './version.js';
@@ -7,4 +7,6 @@ import { createShell as ae, defineApp as oe } from "./shell.js";
7
7
  import { fileByDrag as se } from "./filing.js";
8
8
  import { FINDER as ce, finder as le } from "./finder.js";
9
9
  import { indexedDbStorage as ue } from "./storage.js";
10
- export { e as APP_KIND, b as BAND, x as CASCADE_SLOTS, S as CASCADE_STEP, te as DATE_HOLD_MS, t as DISK, ce as FINDER, n as FOLDER, C as ICON_CELL, w as ICON_COLUMN, T as ICON_INSET, E as ICON_ROW, D as NEAR, r as TRASH, i as UNTITLED_FOLDER, O as WINDOW_ORIGIN, a as appIdOf, k as cascadeFrom, A as cascadeSlot, j as cascadedBox, M as centeredBox, o as childrenOf, N as cleanUp, P as collisions, s as containerOf, c as copyName, l as createCatalog, ae as createShell, Y as createWindowManager, oe as defineApp, u as descendantsOf, F as desktopLattice, d as enclosingFolders, I as fieldExtent, se as fileByDrag, L as fillOrder, le as finder, X as focusInto, R as folderLattice, ne as formatDate, re as formatTime, z as frameOf, ue as indexedDbStorage, f as isContainerKind, p as isInside, B as isPin, m as isTrashed, h as isVolume, g as itemCount, _ as itemOf, V as latticeCell, H as latticeSlot, Z as localStorageState, v as memoryStorage, Q as mergeSession, U as nearBox, y as nextFolderName, W as nextFreeCell, $ as openWindowsOf, G as pinOf, K as pinTo, ee as readSession, ie as startClock, q as trashCell, J as windowOrigin };
10
+ import { APP_FILE_FORMAT as de, BOX_SCALE as fe, compareVersions as pe, inspectAppFile as me, readAppFile as he, satisfies as ge } from "./app-file.js";
11
+ import { VERSION as _e } from "./version.js";
12
+ export { de as APP_FILE_FORMAT, e as APP_KIND, b as BAND, fe as BOX_SCALE, x as CASCADE_SLOTS, S as CASCADE_STEP, te as DATE_HOLD_MS, t as DISK, ce as FINDER, n as FOLDER, C as ICON_CELL, w as ICON_COLUMN, T as ICON_INSET, E as ICON_ROW, D as NEAR, r as TRASH, i as UNTITLED_FOLDER, _e as VERSION, O as WINDOW_ORIGIN, a as appIdOf, k as cascadeFrom, A as cascadeSlot, j as cascadedBox, M as centeredBox, o as childrenOf, N as cleanUp, P as collisions, pe as compareVersions, s as containerOf, c as copyName, l as createCatalog, ae as createShell, Y as createWindowManager, oe as defineApp, u as descendantsOf, F as desktopLattice, d as enclosingFolders, I as fieldExtent, se as fileByDrag, L as fillOrder, le as finder, X as focusInto, R as folderLattice, ne as formatDate, re as formatTime, z as frameOf, ue as indexedDbStorage, me as inspectAppFile, f as isContainerKind, p as isInside, B as isPin, m as isTrashed, h as isVolume, g as itemCount, _ as itemOf, V as latticeCell, H as latticeSlot, Z as localStorageState, v as memoryStorage, Q as mergeSession, U as nearBox, y as nextFolderName, W as nextFreeCell, $ as openWindowsOf, G as pinOf, K as pinTo, he as readAppFile, ee as readSession, ge as satisfies, ie as startClock, q as trashCell, J as windowOrigin };
@@ -1,10 +1,13 @@
1
1
  /**
2
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.
3
+ * catalog, the geometry, the saved session, the app file reader and the
4
+ * kit's version. They import nothing from the kit and touch no DOM at
5
+ * import, so a site's own pure modules and their tests import them under
6
+ * Node. `vintage-frames/shell` exports the same names.
7
7
  */
8
8
  export * from './catalog.js';
9
9
  export * from './geometry.js';
10
10
  export * from './state.js';
11
+ export { APP_FILE_FORMAT, BOX_SCALE, compareVersions, inspectAppFile, readAppFile, satisfies } from './app-file.js';
12
+ export type { AppFile, AppManifest } from './app-file.js';
13
+ export { VERSION } from './version.js';
@@ -1,4 +1,6 @@
1
1
  import { APP_KIND as e, DISK as t, FOLDER as n, TRASH as r, UNTITLED_FOLDER as i, appIdOf as a, childrenOf as o, containerOf as s, copyName as c, createCatalog as l, descendantsOf as u, enclosingFolders as d, isContainerKind as f, isInside as p, isTrashed as m, isVolume as h, itemCount as g, itemOf as _, memoryStorage as v, nextFolderName as y } from "./catalog.js";
2
2
  import { BAND as b, CASCADE_SLOTS as x, CASCADE_STEP as S, ICON_CELL as C, ICON_COLUMN as w, ICON_INSET as T, ICON_ROW as E, NEAR as D, WINDOW_ORIGIN as O, cascadeFrom as k, cascadeSlot as A, cascadedBox as j, centeredBox as M, cleanUp as N, collisions as P, desktopLattice as F, fieldExtent as I, fillOrder as L, folderLattice as R, frameOf as z, isPin as B, latticeCell as V, latticeSlot as H, nearBox as U, nextFreeCell as W, pinOf as G, pinTo as K, trashCell as q, windowOrigin as J } from "./geometry.js";
3
3
  import { localStorageState as Y, mergeSession as X, openWindowsOf as Z, readSession as Q } from "./state.js";
4
- export { e as APP_KIND, b as BAND, x as CASCADE_SLOTS, S as CASCADE_STEP, t as DISK, n as FOLDER, C as ICON_CELL, w as ICON_COLUMN, T as ICON_INSET, E as ICON_ROW, D as NEAR, r as TRASH, i as UNTITLED_FOLDER, O as WINDOW_ORIGIN, a as appIdOf, k as cascadeFrom, A as cascadeSlot, j as cascadedBox, M as centeredBox, o as childrenOf, N as cleanUp, P as collisions, s as containerOf, c as copyName, l as createCatalog, u as descendantsOf, F as desktopLattice, d as enclosingFolders, I as fieldExtent, L as fillOrder, R as folderLattice, z as frameOf, f as isContainerKind, p as isInside, B as isPin, m as isTrashed, h as isVolume, g as itemCount, _ as itemOf, V as latticeCell, H as latticeSlot, Y as localStorageState, v as memoryStorage, X as mergeSession, U as nearBox, y as nextFolderName, W as nextFreeCell, Z as openWindowsOf, G as pinOf, K as pinTo, Q as readSession, q as trashCell, J as windowOrigin };
4
+ import { APP_FILE_FORMAT as $, BOX_SCALE as ee, compareVersions as te, inspectAppFile as ne, readAppFile as re, satisfies as ie } from "./app-file.js";
5
+ import { VERSION as ae } from "./version.js";
6
+ export { $ as APP_FILE_FORMAT, e as APP_KIND, b as BAND, ee as BOX_SCALE, x as CASCADE_SLOTS, S as CASCADE_STEP, t as DISK, n as FOLDER, C as ICON_CELL, w as ICON_COLUMN, T as ICON_INSET, E as ICON_ROW, D as NEAR, r as TRASH, i as UNTITLED_FOLDER, ae as VERSION, O as WINDOW_ORIGIN, a as appIdOf, k as cascadeFrom, A as cascadeSlot, j as cascadedBox, M as centeredBox, o as childrenOf, N as cleanUp, P as collisions, te as compareVersions, s as containerOf, c as copyName, l as createCatalog, u as descendantsOf, F as desktopLattice, d as enclosingFolders, I as fieldExtent, L as fillOrder, R as folderLattice, z as frameOf, ne as inspectAppFile, f as isContainerKind, p as isInside, B as isPin, m as isTrashed, h as isVolume, g as itemCount, _ as itemOf, V as latticeCell, H as latticeSlot, Y as localStorageState, v as memoryStorage, X as mergeSession, U as nearBox, y as nextFolderName, W as nextFreeCell, Z as openWindowsOf, G as pinOf, K as pinTo, re as readAppFile, Q as readSession, ie as satisfies, q as trashCell, J as windowOrigin };
@@ -144,7 +144,7 @@ function d(l, d) {
144
144
  let n = (r) => {
145
145
  r.target === t && (t.removeEventListener("vf-close", n), e(r.detail.returnValue));
146
146
  };
147
- t.addEventListener("vf-close", n), t.show();
147
+ t.show(), t.addEventListener("vf-close", n);
148
148
  });
149
149
  },
150
150
  gate(e, t) {
@@ -0,0 +1,2 @@
1
+ /** The kit's version, as published: package.json's `version`. `npm version` rewrites it (the `version` script). */
2
+ export declare const VERSION = "0.18.0";
@@ -0,0 +1,4 @@
1
+ //#region src/shell/version.ts
2
+ var e = "0.18.0";
3
+ //#endregion
4
+ export { e as VERSION };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * VF Body as data. GENERATED by fonts/manifest-to-font.py from
3
+ * fonts/VF-Body.glyphs.txt: edit the manifest and rebuild, never this file.
4
+ *
5
+ * Each character's advance, placement and ink, in whole design px, for
6
+ * drawing the kit's type without a browser. The ink is the kit's own
7
+ * re-drawn strike in the style of Geneva 9pt, the design Susan Kare
8
+ * created for Apple's original Macintosh, and the same artwork the face's
9
+ * woff2 is built from (fonts/README.md).
10
+ */
11
+ import type { Strike } from './strike.js';
12
+ export declare const VF_BODY_STRIKE: Strike;