vintage-frames 0.18.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.
@@ -0,0 +1,10 @@
1
+ import { PIXEL_GRID_METRICS as e, registerEmbeddedFont as t } from "./register-embedded-font.js";
2
+ import { VF_BODY_FAMILY as n } from "./body-font.js";
3
+ //#region src/styles/body-italic-font.ts
4
+ var r = "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";
5
+ function i() {
6
+ t(n, r, e, "italic");
7
+ }
8
+ i();
9
+ //#endregion
10
+ export { i as registerBodyItalicFace };
@@ -53,12 +53,13 @@ export interface EmbeddedFontMetrics {
53
53
  */
54
54
  export declare const PIXEL_GRID_METRICS: EmbeddedFontMetrics;
55
55
  /**
56
- * Register `family` from an inlined base64 WOFF2 exactly once per session.
56
+ * Register `family` from an inlined base64 WOFF2 exactly once per session —
57
+ * its `style` face, `normal` unless the WOFF2 is the family's italic.
57
58
  *
58
59
  * Idempotent and environment-safe: it no-ops under SSR (no `document`), when
59
- * the CSS Font Loading API is unavailable, or when the family is already
60
- * present (duplicate bundles / HMR). A failed decode releases the family so a
60
+ * the CSS Font Loading API is unavailable, or when the face is already
61
+ * present (duplicate bundles / HMR). A failed decode releases the face so a
61
62
  * later explicit call can retry, and on any failure components simply keep the
62
63
  * fallback stack — so this can never break rendering.
63
64
  */
64
- export declare function registerEmbeddedFont(family: string, woff2Base64: string, metrics?: EmbeddedFontMetrics): void;
65
+ export declare function registerEmbeddedFont(family: string, woff2Base64: string, metrics?: EmbeddedFontMetrics, style?: 'normal' | 'italic'): void;
@@ -4,26 +4,27 @@ var e = /* @__PURE__ */ new Set(), t = {
4
4
  descentOverride: "25%",
5
5
  lineGapOverride: "0%"
6
6
  };
7
- function n(t, n, r) {
8
- if (e.has(t) || typeof document > "u" || !("fonts" in document)) return;
9
- for (let n of document.fonts) if (n.family === t) {
10
- e.add(t);
7
+ function n(t, n, r, i = "normal") {
8
+ let a = `${t} ${i}`;
9
+ if (e.has(a) || typeof document > "u" || !("fonts" in document)) return;
10
+ for (let n of document.fonts) if (n.family === t && n.style === i) {
11
+ e.add(a);
11
12
  return;
12
13
  }
13
- e.add(t);
14
- let i = `url(data:font/woff2;base64,${n}) format('woff2')`;
14
+ e.add(a);
15
+ let o = `url(data:font/woff2;base64,${n}) format('woff2')`;
15
16
  try {
16
- let n = new FontFace(t, i, {
17
- style: "normal",
17
+ let n = new FontFace(t, o, {
18
+ style: i,
18
19
  weight: "100 900",
19
20
  display: "swap",
20
21
  ...r
21
22
  });
22
23
  document.fonts.add(n), n.load().then(() => {}, () => {
23
- document.fonts.delete(n), e.delete(t);
24
+ document.fonts.delete(n), e.delete(a);
24
25
  });
25
26
  } catch {
26
- e.delete(t);
27
+ e.delete(a);
27
28
  }
28
29
  }
29
30
  //#endregion
package/docs/SPEC.md CHANGED
@@ -480,7 +480,7 @@ A raster image — the reference art's pixel icons — on the kit's grid, treati
480
480
 
481
481
  #### `vf-icon` (`VfIcon`, vf-icon.ts)
482
482
  The Finder icon — pixel art in a reserved cell with its name on a plate below, as one selectable, movable, renameable unit. `vf-img` puts a picture on the grid; this makes it the thing the Finder manipulates.
483
- - **Attributes/props:** `label`: string (the name; empty draws no plate — that is the "no label" setting, rather than a second attribute that could disagree with it). `size`: `large` (default, 32×32) | `small` (16×16) — picks the slot that paints *and* the cell it paints in. `selectable`, `selected`, `movable`, `editable`: boolean. `color`: boolean — declares the art a **color icon**, so selection darkens instead of inverting (see Visual). `open`: boolean — the icon's window is on screen, and the art paints as the derived open ghost (below). `target`: boolean (reflected) — the destination highlight under a drag: the selected treatment of the *art* alone (`invert(1)`, or `brightness(0.5)` under `color`) with none of `selected`'s semantics — no `vf-select`, no outside listener, no `aria-selected`, the plate untouched; the page sets it on the folder a `vf-drag` reports the pointer over. `width`: number (system px, **even**; the cell/grid pitch, which a longer name overflows rather than being bound by). `maxlength`: number (31).
483
+ - **Attributes/props:** `label`: string (the name; empty draws no plate — that is the "no label" setting, rather than a second attribute that could disagree with it). `size`: `large` (default, 32×32) | `small` (16×16) — picks the slot that paints *and* the cell it paints in. `selectable`, `selected`, `movable`, `editable`: boolean. `color`: boolean — declares the art a **color icon**, so selection darkens instead of inverting (see Visual). `open`: boolean — the icon's window is on screen, and the art paints as the derived open ghost (below). `alias`: boolean (reflected) — the icon stands for another item: its name, plate and rename field alike, is set in the body face's italic (`registerBodyItalicFace`, built from the face's own glyphs, each row shifted right on the grid, never a synthesized oblique), and the plate widens to the slant's last pixel, read from the run's ink in a canvas set in the plate's font. `target`: boolean (reflected) — the destination highlight under a drag: the selected treatment of the *art* alone (`invert(1)`, or `brightness(0.5)` under `color`) with none of `selected`'s semantics — no `vf-select`, no outside listener, no `aria-selected`, the plate untouched; the page sets it on the folder a `vf-drag` reports the pointer over. `width`: number (system px, **even**; the cell/grid pitch, which a longer name overflows rather than being bound by). `maxlength`: number (31).
484
484
  - **Visual:** `display: inline-block`. A column: the art cell (`--_cell` system px square, `overflow: hidden`, art centered), then, directly under it (`--vf-icon-gap`, 0 by default), the name plate — body face on a `--vf-icon-label-height` (12px) line box, the Finder's own plate height: the face's 16px em centers in it, 3px above the ascenders and descenders on the bottom edge, exactly as the Finder drew it — 1px horizontal padding, **opaque `--vf-white`** in its own right rather than `--vf-surface`: on the desktop dither the name reads because it sits on a plate, and `--vf-surface` is unset out there (`Example screen (1-bit).png`, the "Macintosh HD" icon). Selected: the plate takes the `--vf-highlight` pair and the art takes `filter: invert(1)` — a System 7 icon is ink and opaque white on a transparent surround, i.e. precisely an image plus its mask, so inverting flips the two and leaves the surround alone. That is the complete classic selected appearance for 1-bit art. Color art would invert into a photographic negative, which System 7 never showed: declare `color` and selection **darkens** instead — Icon Utilities' ttSelected transform, every color blended halfway toward black (`filter: brightness(0.5)`), whites going gray, the surround still untouched. The declaration is the consumer's, since the component cannot tell color art from a mask by looking at a slotted image; it covers whatever the cell shows (the open ghost included, so a color icon has one selected treatment open or closed), and the plate inverts either way — the selected name was inverted whatever the art.
485
485
  - **The open ghost is derived, not shipped** (`open`; `open-art.ts`). The art redraws as the Finder's open-window ghost — outline held in solid black, interior re-filled with the scrollbar trough's 4×2 dot lattice (a dot at (0,0) and (2,1), 25%) on opaque white, surround untouched — derived from the slotted art itself, by compositing alone: a silhouette via `source-in`; a 1px 4-neighborhood erosion via four `destination-in` draws of the silhouette shifted one pixel each way (off-canvas composites as transparent, so the raster's own edge erodes too and an interior hole rings itself); the lattice via `createPattern` anchored at the art's top-left, where 32 and 16 being whole multiples of the tile keeps a field of icons in one phase; then the fill composed over the un-eroded silhouette, so the ring the erosion removed reads as the outline. Never `getImageData` — a canvas that drew a cross-origin image is tainted, which forbids *reading* pixels but not drawing or displaying them, so the ghost works for CORS-less sources too — and never `ctx.filter` (Safari's shaky corner; compositing operators are universal). Keeping the interior *opaque white* is what lets a selected open icon invert under the same `filter: invert(1)` with no second treatment. The slot stays in the tree while the ghost paints, hidden via a class gated on a successful derivation — art the pipeline cannot draw (nothing slotted, a failed load, an inline `<svg>`) keeps rendering as itself. The ghost re-derives on slotchange, the art's `load`/`error`, and `open`/`size` changes, keyed by `currentSrc` so a refresh that changed nothing is a string compare; the source's `alt` is carried onto the ghost (`role="img"` + `aria-label`) so a graphic that names its icon keeps doing so while its slot is hidden. The ghost displays on `vf-img`'s terms: natural raster size in system px, `image-rendering: pixelated`, one image pixel per system pixel.
486
486
  - **Behavior:** clicking selects, Shift/⌘ toggles, and a press elsewhere clears — a `DocumentListenersController` capture-phase listener attached only while selectable *and* selected, which is what makes single-selection work with no container owning the set. For an icon alone, elsewhere is anywhere outside it. For an icon in a field, only a press on another icon or on a field (`vf-icon-field`, or a `role="listbox"` element of the page's own) clears it; a press on the menu bar, a menu, a dialog, a window's title bar or another window leaves the selection, so a menu command still has its icons to act on and a selection survives an application switch. Either way the press calls off a pending rename and a half-finished tap pair. Double-click fires `vf-open`, and its keyboard route is ⌘O / ⌘↓ (Ctrl off the Mac) — the System 7 Open shortcuts, added because a double-click is a pointer-only gesture (§1). **The instrument never changes what the icon means**, so a finger or a pen opens it too, on a *tap pair* the icon classifies from its own pointer events rather than from `dblclick`: two taps on the icon, the second pressed within `TAP_PAIR_MS` (500) of the first and no further than `TAP_PAIR_SLOP_PX` (24 CSS px) from it, `vf-open` firing on the second release. A platform reserves the double-tap for its zoom and may deliver no `dblclick` for one, and a movable icon's press is `preventDefault()`ed for the drag, which can suppress it as well; the pair depends on neither. A tap is a press released where it landed — one that became a drag, or that the platform cancelled, is not half of a pair — and a pair does not chain. The mouse keeps the platform's `dblclick`, which follows the user's own double-click setting; each press takes exactly one of the two routes, so a platform that also delivers a `dblclick` for a double-tap opens the icon once. The frame sets `touch-action: manipulation`, which takes the double-tap zoom off the icon and leaves panning and pinching to the page. Both are additions in the kit's idiom — System 7 had the mouse alone. Return is deliberately not one: the Finder's Return renamed, never opened, so it starts the edit on an editable icon and does nothing on a non-editable one. A double-click opens the icon **wherever it lands, the name included** — the handler is on the frame, and the name is as much the icon as the art is. `editable`: a press on the plate of an ALREADY-selected icon opens a rename field overlaying it (the press that does the selecting never does); the whole name starts selected, Return commits, Escape reverts, and the hidden plate keeps rendering the draft so the box widens as you type. **The two pointer gestures on a name are the same press**, and only the second one tells them apart, so the rename *waits*: the press arms a field that opens `RENAME_DELAY_MS` (800ms, `src/motion.ts`) later, and a press landing inside that window — wherever in the icon it falls — calls it off, leaving the double-click to open with no rename flashing up behind it. The window is long deliberately: no API reports what the browser counts as a double-click (it follows a user-movable platform setting), and reading a lone click as a pair costs only a wait, while reading a pair as a lone click renames when the user meant to open. A press that travels into a drag, a press outside, and any key call it off too — so a name drags like the art does, and Return opens the field at once, having no second half to wait for. `movable` — **not `draggable`**, which is a global HTML attribute and an `HTMLElement` accessor (the same trap as `align`, §5 Group G) — drags via `DragController` on the `vf-window` delegate shape (the frame is the handle and sets `touch-action: none` while movable, as the title bars do), plus arrow-key nudging (1 system px, 8 with Shift) because a pointer-only gesture is the kind of gap the kit closes (§1). Focus is the dashed rule below the plate, gated on `FocusRuleController` rather than `:focus-visible`: the host focuses itself so the press-drag can own the pointer.
@@ -538,6 +538,11 @@
538
538
  "description": "The icon's window is on screen, so the art paints as the Finder's open\nghost — outline held, interior re-filled with the kit's loose dither —\nderived in the client from the slotted art itself (see the class doc).\nSet it when handling `vf-open`, clear it when the window goes away.\nSelection inverts the ghost exactly as it inverts the art.",
539
539
  "values": []
540
540
  },
541
+ {
542
+ "name": "alias",
543
+ "description": "The icon is an alias, an item standing for another: its name is set in\nitalics, the body face's own, and the plate widens to the slant's last\npixel.",
544
+ "values": []
545
+ },
541
546
  {
542
547
  "name": "movable",
543
548
  "description": "Drag to move — `movable`, never `draggable`, which is a platform attribute\nand accessor (see the class doc). Arrow keys move a focused icon too, which\nmeans pairing this with selectable: focus is what that grants.",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
3
  "name": "vintage-frames",
4
- "version": "0.18.0",
4
+ "version": "0.19.0",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -1191,6 +1191,11 @@
1191
1191
  "description": "The icon's window is on screen, so the art paints as the Finder's open\nghost — outline held, interior re-filled with the kit's loose dither —\nderived in the client from the slotted art itself (see the class doc).\nSet it when handling `vf-open`, clear it when the window goes away.\nSelection inverts the ghost exactly as it inverts the art.",
1192
1192
  "value": { "type": "boolean", "default": "false" }
1193
1193
  },
1194
+ {
1195
+ "name": "alias",
1196
+ "description": "The icon is an alias, an item standing for another: its name is set in\nitalics, the body face's own, and the plate widens to the slant's last\npixel.",
1197
+ "value": { "type": "boolean", "default": "false" }
1198
+ },
1194
1199
  {
1195
1200
  "name": "movable",
1196
1201
  "description": "Drag to move — `movable`, never `draggable`, which is a platform attribute\nand accessor (see the class doc). Arrow keys move a focused icon too, which\nmeans pairing this with selectable: focus is what that grants.",
@@ -1317,6 +1322,11 @@
1317
1322
  "description": "The icon's window is on screen, so the art paints as the Finder's open\nghost — outline held, interior re-filled with the kit's loose dither —\nderived in the client from the slotted art itself (see the class doc).\nSet it when handling `vf-open`, clear it when the window goes away.\nSelection inverts the ghost exactly as it inverts the art.",
1318
1323
  "type": "boolean"
1319
1324
  },
1325
+ {
1326
+ "name": "alias",
1327
+ "description": "The icon is an alias, an item standing for another: its name is set in\nitalics, the body face's own, and the plate widens to the slant's last\npixel.",
1328
+ "type": "boolean"
1329
+ },
1320
1330
  {
1321
1331
  "name": "movable",
1322
1332
  "description": "Drag to move — `movable`, never `draggable`, which is a platform attribute\nand accessor (see the class doc). Arrow keys move a focused icon too, which\nmeans pairing this with selectable: focus is what that grants.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vintage-frames",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Lit web components that emulate the look and feel of classic Mac OS System 7",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -20,6 +20,8 @@
20
20
  "**/display-font.ts",
21
21
  "**/body-font.js",
22
22
  "**/body-font.ts",
23
+ "**/body-italic-font.js",
24
+ "**/body-italic-font.ts",
23
25
  "**/*.css"
24
26
  ],
25
27
  "main": "./dist/index.js",
@@ -74,8 +76,10 @@
74
76
  "build:pages": "vite build --config vite.pages.config.ts",
75
77
  "preview:pages": "vite preview --config vite.pages.config.ts",
76
78
  "prepack": "npm run build && npm run verify:manifest",
77
- "version": "npm run analyze && node scripts/write-version.mjs && git add custom-elements.json editor src/shell/version.ts",
78
- "analyze": "cem analyze",
79
+ "preversion": "npm run check:app-api",
80
+ "version": "npm run analyze && node scripts/write-version.mjs && git add custom-elements.json editor app-api.json src/shell/version.ts",
81
+ "analyze": "cem analyze && node scripts/app-api.mjs",
82
+ "check:app-api": "npm run analyze && node scripts/check-app-api.mjs",
79
83
  "verify:manifest": "node scripts/verify-manifest.mjs",
80
84
  "typecheck": "tsc --noEmit",
81
85
  "preview": "vite preview",