vintage-frames 0.12.7 → 0.13.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.
@@ -1,6 +1,13 @@
1
- export declare const TROUGH_MOTIF_X: number;
2
- export declare const TROUGH_MOTIF_Y: number;
3
- export declare const TROUGH_RECTS: readonly import("./tile.js").TileRect[];
1
+ /**
2
+ * The trough's 25% lattice is the library's `gray-25` — QuickDraw's
3
+ * `ltGray`, and the UI kit's "Scroll bg" sprite — on its minimal cell: a
4
+ * 4×2-system-px motif with a dot at (0,0) and (2,1). The DOM trough repeats
5
+ * it as a tile drawn at the display's resolution (`--_vf-trough-tile`,
6
+ * written by `ScrollRailController` — 1-bit at every scale, zoom-minted ones
7
+ * included); the SVG span tile below is the forced-colors mask, which
8
+ * repaints the same art in the remapped ink token.
9
+ */
10
+ export declare const TROUGH: import("../../patterns.js").PatternMotif;
4
11
  /**
5
12
  * System 7 scroll rails, drawn by the kit as ordinary shadow DOM — the skin
6
13
  * for the subtree `renderScrollRail()` (src/scroll-rail.ts) produces. The
@@ -17,7 +24,7 @@ export declare const TROUGH_RECTS: readonly import("./tile.js").TileRect[];
17
24
  * is kit art: subpixel layout device-snaps at paint like every other element,
18
25
  * and the whole defect class is unrepresentable. It also renders identically
19
26
  * in every engine (Firefox included), is assertable headless, and lets the
20
- * trough adopt the exact tile raster the other dithers use.
27
+ * trough adopt the repeating tile the other dithers use.
21
28
  *
22
29
  * Geometry (system px) — the classic 16px cell, of which the outermost line
23
30
  * is the component's own 1px frame: the rail element is the 15 inside it —
@@ -52,9 +59,9 @@ export declare const TROUGH_RECTS: readonly import("./tile.js").TileRect[];
52
59
  * through the vfBase token remap like every other element. The arrows are
53
60
  * inline `currentColor` SVG now, so they re-ink with the palette (the old
54
61
  * `::-webkit-scrollbar` skin's one genuine residual — mask-image is ignored
55
- * on scrollbar pseudos — dies here). The trough raster keeps literal ink, so
56
- * it is hidden and the trough repaints as the ink token masked by the same
57
- * art (the vfDots idiom; the span mask keeps the zoom caveat every masked
62
+ * on scrollbar pseudos — dies here). The trough's tile keeps literal ink, so
63
+ * it goes and the trough repaints as the ink token masked by the same art
64
+ * (the vfDots idiom; the span mask keeps the zoom caveat every masked
58
65
  * surface has, an accepted residual).
59
66
  */
60
67
  export declare const vfScrollRail: import("lit").CSSResult;
@@ -1,8 +1,9 @@
1
1
  import { tileImage as e, tileRects as t, vfTileMaskSize as n } from "./tile.js";
2
- import { PATTERNS as r, patternMotif as i } from "../../patterns.js";
3
- import { css as a, unsafeCSS as o } from "lit";
2
+ import { vfRepeatTileSize as r } from "../../tile-grid.js";
3
+ import { PATTERNS as i, patternMotif as a } from "../../patterns.js";
4
+ import { css as o, unsafeCSS as s } from "lit";
4
5
  //#region src/styles/recipes/scroll-rail.ts
5
- var s = i(r["gray-25"]), c = s.width, l = s.height, u = s.rects, d = e(c, l, t(u)), f = a`
6
+ var c = a(i["gray-25"]), l = e(c.width, c.height, t(c.rects)), u = o`
6
7
  /* Hide the native bar, keep the native scrolling. Both lines ship:
7
8
  scrollbar-width reaches Chromium 121+/Firefox/Safari 18.2+, the pseudo
8
9
  covers older WebKit — the last scrollbar pseudo left in the kit.
@@ -117,25 +118,16 @@ var s = i(r["gray-25"]), c = s.width, l = s.height, u = s.rects, d = e(c, l, t(u
117
118
  min-width: 0;
118
119
  min-height: 0;
119
120
  }
120
- /* The trough: the token base color under the dither, clipping the raster's
121
- overdraw (the art is ceiled up to whole motifs of the measured track). */
121
+ /* The trough: the dither over the token base color — the repeating tile
122
+ ScrollRailController writes (src/tile-grid.ts), 1-bit at every scale. */
122
123
  .vf-rail-trough {
123
124
  position: absolute;
124
125
  inset: 0;
125
- overflow: hidden;
126
126
  background-color: var(--vf-scrollbar-track, var(--vf-white, #fff));
127
- pointer-events: none;
128
- }
129
- /* The dither raster ScrollRailController sizes and writes — one image px
130
- per system px, magnified nearest-neighbor (the vf-img idiom), so the
131
- lattice is 1-bit at every scale. */
132
- .vf-rail-trough-art {
133
- position: absolute;
134
- top: 0;
135
- left: 0;
136
- background-size: 100% 100%;
137
- background-repeat: no-repeat;
127
+ background-image: var(--_vf-trough-tile, none);
128
+ ${r}
138
129
  image-rendering: pixelated;
130
+ pointer-events: none;
139
131
  }
140
132
  /* The fixed System 7 thumb (never proportional): a white 16px box spanning
141
133
  the channel. Its border reads as inset 1px from each channel rail — the
@@ -207,21 +199,19 @@ var s = i(r["gray-25"]), c = s.width, l = s.height, u = s.rects, d = e(c, l, t(u
207
199
  display: none;
208
200
  }
209
201
 
210
- /* Forced colors: the raster keeps literal ink, so it hides and the trough
202
+ /* Forced colors: the tile keeps literal ink, so it goes and the trough
211
203
  repaints as the remapped ink token masked by the same art (the vfDots
212
204
  idiom — and like every masked tile, the span mask keeps the zoom
213
205
  caveat). Everything else re-inks through the vfBase token remap; the
214
206
  arrows are currentColor SVG and follow the palette by themselves. */
215
207
  @media (forced-colors: active) {
216
208
  .vf-rail-trough {
209
+ background-image: none;
217
210
  background-color: var(--vf-black, #000);
218
- mask-image: ${o(d)};
219
- ${n(c, l)}
220
- }
221
- .vf-rail-trough-art {
222
- display: none;
211
+ mask-image: ${s(l)};
212
+ ${n(c.width, c.height)}
223
213
  }
224
214
  }
225
215
  `;
226
216
  //#endregion
227
- export { c as TROUGH_MOTIF_X, l as TROUGH_MOTIF_Y, u as TROUGH_RECTS, f as vfScrollRail };
217
+ export { c as TROUGH, u as vfScrollRail };
@@ -61,8 +61,9 @@ export type TileRect = readonly [x: number, y: number, w: number, h: number, fil
61
61
  export declare function tileRects(rects: readonly TileRect[]): string;
62
62
  /**
63
63
  * The motif tiled over `spanWidth × spanHeight` system px as a RASTER image —
64
- * a PNG data URI at one image px per system px — for the whole-surface fill a
65
- * converted tiled surface paints (src/tile-grid.ts).
64
+ * a PNG data URI at `density` image px per system px (one by default) — the
65
+ * encoder behind the tile every kit surface repeats (`repeatTile`,
66
+ * src/tile-grid.ts).
66
67
  *
67
68
  * A raster because of how engines rasterize a fill's art, not how they place
68
69
  * its box. Chromium rasterizes an SVG image at the box's *stored* (layout)
@@ -79,8 +80,15 @@ export declare function tileRects(rects: readonly TileRect[]): string;
79
80
  * out as a data URI and released — kit art only, so there is nothing
80
81
  * cross-origin to taint). Where canvas is unavailable the SVG tile is
81
82
  * returned instead, so the declaration still paints.
83
+ *
84
+ * `density` draws each system px as a `density × density` block of whole
85
+ * image px. An image drawn at the device px per system px it will be shown
86
+ * at is copied 1:1 onto the device grid, so no engine's image filter has
87
+ * anything to decide — Safari smooths a magnified image on its repeating
88
+ * path whatever `image-rendering` says. It is still one pattern bit per
89
+ * system px; only who scales it changes.
82
90
  */
83
- export declare function tileRaster(motifWidth: number, motifHeight: number, rects: readonly TileRect[], spanWidth?: number, spanHeight?: number): string;
91
+ export declare function tileRaster(motifWidth: number, motifHeight: number, rects: readonly TileRect[], spanWidth?: number, spanHeight?: number, density?: number): string;
84
92
  /**
85
93
  * `background-size` for a REPEATING tile — stated as the motif, sized as the
86
94
  * span that motif tiles at ({@link tileSpan}).
@@ -11,18 +11,18 @@ function i(e, t, n, i = r(e), a = r(t)) {
11
11
  function a(e) {
12
12
  return e.map(([e, t, n, r, i]) => `%3Crect ${(e ? `x='${e}' ` : "") + (t ? `y='${t}' ` : "")}width='${n}' height='${r}'${i ? ` fill='${encodeURIComponent(i)}'` : ""}/%3E`).join("");
13
13
  }
14
- function o(e, t, n, o = r(e), s = r(t)) {
15
- let c = () => i(e, t, a(n), o, s);
16
- if (typeof document > "u") return c();
17
- let l = document.createElement("canvas");
18
- l.width = e, l.height = t;
19
- let u = l.getContext("2d");
20
- if (!u) return c();
21
- for (let [e, t, r, i, a] of n) u.fillStyle = a ?? "#000000", u.fillRect(e, t, r, i);
22
- let d = document.createElement("canvas");
23
- d.width = o, d.height = s;
24
- let f = d.getContext("2d"), p = f?.createPattern(l, "repeat");
25
- return !f || !p ? c() : (f.fillStyle = p, f.fillRect(0, 0, o, s), `url("${d.toDataURL("image/png")}")`);
14
+ function o(e, t, n, o = r(e), s = r(t), c = 1) {
15
+ let l = () => i(e, t, a(n), o, s);
16
+ if (typeof document > "u") return l();
17
+ let u = document.createElement("canvas");
18
+ u.width = e * c, u.height = t * c;
19
+ let d = u.getContext("2d");
20
+ if (!d) return l();
21
+ for (let [e, t, r, i, a] of n) d.fillStyle = a ?? "#000000", d.fillRect(e * c, t * c, r * c, i * c);
22
+ let f = document.createElement("canvas");
23
+ f.width = o * c, f.height = s * c;
24
+ let p = f.getContext("2d"), m = p?.createPattern(u, "repeat");
25
+ return !p || !m ? l() : (p.fillStyle = m, p.fillRect(0, 0, f.width, f.height), `url("${f.toDataURL("image/png")}")`);
26
26
  }
27
27
  function s(t, n = t) {
28
28
  return e`
@@ -1,51 +1,39 @@
1
- import { type CSSResult, type TemplateResult } from 'lit';
1
+ import { type CSSResult, type ReactiveController, type ReactiveControllerHost, type TemplateResult } from 'lit';
2
2
  import { type TileRect } from './styles/recipes/tile.js';
3
3
  /**
4
- * The tiled-fill machinery that replaces CSS `background-repeat` on the kit's
5
- * convertible tiled surfaces (the desktop dither, the windoid dots, the swatch
6
- * checker, the barber stripes).
4
+ * The tiled-fill machinery behind the kit's patterned surfaces — the desktop
5
+ * and container patterns, the windoid dots, the swatch checker, the barber
6
+ * stripes and the scroll trough.
7
7
  *
8
- * A CSS repeating fill is ONE paint-snapped box holding N *unsnapped* repeats,
9
- * each placed at `k × tileSize` where the tile size is a single stored length
10
- * the engine quantizes to its layout grid (Chromium 1/64 CSS px, Gecko 1/60).
11
- * The error compounds linearly with `k` until a motif boundary has walked a
12
- * whole device pixel and the 1-bit art smears to gray. The span construction
13
- * (`tileSpan`, src/styles/recipes/tile.ts) makes the stored length exact for
14
- * every scale the *density* ladder derives — but zoom mints scales with
15
- * arbitrary prime denominators (20/17 at Safari's 85%, 30/23 at its 115%),
16
- * and no finite lattice can hold those. ZOOM-TILE-DRIFT.md is the analysis;
17
- * TILE-GRID-PLAN.md the plan this module implements.
8
+ * **Kit art is a CSS repeat of one pre-scaled tile** ({@link repeatTile}):
9
+ * the motif laid over {@link REPEAT_TILE} system px square and drawn at n
10
+ * image px per system px, n = `--vf-scale × trueDpr` — whole by the scale
11
+ * contract — so the engine copies the tile to the device grid 1:1 and has no
12
+ * filtering to choose. The surface states the tile's size as
13
+ * {@link vfRepeatTileSize}, a length every engine holds exactly at every
14
+ * density and zoom, so each repeat lands exactly. The tile depends only on
15
+ * the art and n: it is encoded once per pair for the whole page, drawn again
16
+ * when zoom or the display changes n, and never on a resize.
17
+ * {@link RepeatTileController} keeps a box's tile current.
18
18
  *
19
- * Two rendering paths, chosen by whether the surface's pattern token is
20
- * overridden ({@link patternOverride}):
19
+ * Why 120, and why pre-scaled (TILE-REPEAT-PLAN.md, in git history at
20
+ * commit 5bc45c2, has the measurements): Safari smooths a repeated image it
21
+ * has to magnify, whatever
22
+ * `image-rendering` says, so the image must already be at device
23
+ * resolution; and WebKit on a 3× display now and then draws one small tile a
24
+ * device px narrow and resamples it, which tiles of 192 device px and up
25
+ * never showed. The repeat replaced one raster the size of the whole surface,
26
+ * re-encoded as it was resized.
21
27
  *
22
- * **Kit art (token unset): one whole-surface raster** — a single `.vf-tile-raster`
23
- * element carrying the motif tiled over the surface's declared system-px size
24
- * as a raster image at one image px per system px (`tileRaster`), stretched
25
- * `background-size: 100% 100%` under `image-rendering: pixelated`. Exact at
26
- * every scale, holdable or not, for two measured reasons: nearest-neighbor
27
- * sampling can only produce source colors, and ONE box has no interior
28
- * boundaries — Chromium paints an image background to the box's *unsnapped*
29
- * layout rect with antialiased edges, so any fill assembled from several
30
- * boxes blends a one-device-px line at every interior seam at a zoom-minted
31
- * scale, no matter how exactly the boxes were placed. (That measurement is
32
- * what buried both this plan's original per-tile form and SVG art of any
33
- * kind: Chromium rasterizes SVG images at the fractional layout size and
34
- * ignores `image-rendering` for them.) The box's own edge is the one place a
35
- * partial device pixel remains — the same status every component box already
36
- * has. The sampling stays exact because the box's stored length errs at most
37
- * 1/128 CSS px over the WHOLE surface: the relative error shrinks as the
38
- * surface grows, so the nearest-neighbor grid cannot slip a pixel at any
39
- * realistic size.
40
- *
41
- * **Consumer art (token set): the placed tile grid** — a flat set of
42
- * absolutely positioned `.vf-tile` boxes, each `T × T` system px at
28
+ * **Consumer art (a pattern token set): the placed tile grid** — a flat set
29
+ * of absolutely positioned `.vf-tile` boxes, each `T × T` system px at
43
30
  * `left: calc(var(--vf-scale, 1) * kT px)`: one single-multiplication length
44
31
  * per tile, quantized once, so each box paint-snaps onto the device grid
45
32
  * independently and the documented tile geometry (`--vf-*-pattern` art on a
46
- * 30- or 60-system-px tile) renders verbatim. Positions never accumulate
47
- * error; at a zoom-minted scale what remains is the bounded per-seam blend
48
- * above — a hairline per tile boundary, never surface-wide smear. Raster
33
+ * 30- or 60-system-px tile) renders verbatim. A token is any CSS image —
34
+ * SVG included — so it cannot be pre-scaled the way kit art is. Positions
35
+ * never accumulate error; at a scale the layout grid cannot hold, what
36
+ * remains is a hairline per tile boundary, never surface-wide smear. Raster
49
37
  * consumer art additionally magnifies nearest-neighbor (the `vf-img` idiom).
50
38
  *
51
39
  * The trap that must not come back: NEVER lay tiles out with CSS grid,
@@ -55,22 +43,18 @@ import { type TileRect } from './styles/recipes/tile.js';
55
43
  * returns. Flat absolute placement with one multiplication per tile is the
56
44
  * correctness, not a style choice.
57
45
  *
58
- * Wiring: the *container* — the layer the fill used to paint on, or a
46
+ * Wiring the grid: the *container* — the layer the fill paints on, or a
59
47
  * dedicated child filling it — takes the `vf-tile-grid` class (clip and
60
48
  * pointer transparency; it must be positioned, which every kit layer already
61
- * is). For the grid path it resolves `--_vf-tile-image` — the pattern-token
62
- * `var()` each tile stretches over its box, stated once so no data URI is
63
- * repeated per tile — and {@link tileGrid} renders the tiles. For the raster
64
- * path the component renders a `.vf-tile-raster` child whose
65
- * `background-image` it writes from `tileRaster(...)`, regenerated only when
66
- * the declared system-px size changes (density and zoom re-render nothing:
67
- * the box lengths are live against `--vf-scale`).
49
+ * is). It resolves `--_vf-tile-image` — the pattern-token `var()` each tile
50
+ * stretches over its box, stated once so no data URI is repeated per tile —
51
+ * and {@link tileGrid} renders the tiles.
68
52
  *
69
- * Forced colors keeps each surface's existing layer-level branches (flat
70
- * Canvas desktop, span-masked dots and barber, the swatch's
71
- * forced-color-adjust) — no mask pipeline rasterizes exactly at a zoom-minted
72
- * scale (image-rendering does not reach masks), so forced-colors-plus-zoom
73
- * remains the one accepted residual, unchanged from before this work.
53
+ * Forced colors keeps each surface's existing branches (flat Canvas desktop,
54
+ * span-masked dots, barber and trough, the swatch's forced-color-adjust) — no
55
+ * mask pipeline rasterizes exactly at a zoom-minted scale (image-rendering
56
+ * does not reach masks), so forced-colors-plus-zoom remains the one accepted
57
+ * residual.
74
58
  */
75
59
  export declare const vfTileGrid: CSSResult;
76
60
  /**
@@ -89,9 +73,9 @@ export declare function tileGrid({ cols, rows, tile, }: {
89
73
  }): TemplateResult[];
90
74
  /**
91
75
  * The consumer's override of a pattern token, or `''` when the token is unset
92
- * and the surface should paint its own art — how a converted surface picks
93
- * between its whole-surface raster (kit art) and its placed tile grid
94
- * (consumer art at the documented tile geometry).
76
+ * and the surface should paint its own art — how a surface picks between its
77
+ * repeating kit tile and its placed tile grid (consumer art at the
78
+ * documented tile geometry).
95
79
  *
96
80
  * Read with `getComputedStyle` at update time, not observed live: a pattern
97
81
  * token swapped at runtime without touching the component wants a nudge (any
@@ -99,15 +83,65 @@ export declare function tileGrid({ cols, rows, tile, }: {
99
83
  * contract the component's other measured inputs follow.
100
84
  */
101
85
  export declare function patternOverride(el: Element, token: string): string;
86
+ /** A motif as rect data over the cell it tiles at, in system px. */
87
+ export interface TileMotif {
88
+ readonly width: number;
89
+ readonly height: number;
90
+ readonly rects: readonly TileRect[];
91
+ }
92
+ /**
93
+ * The side of a kit repeat tile in system px: a whole number of every motif
94
+ * the kit draws (8 × 15 — the 2-, 4-, 8- and 12-px cells all divide it), and
95
+ * a whole number of layout px in every engine at every density and zoom
96
+ * (120n device px over the engine's ratio: 40n at 3×, 60n at 2×).
97
+ */
98
+ export declare const REPEAT_TILE = 120;
99
+ /**
100
+ * `background-size` for a {@link repeatTile}: {@link REPEAT_TILE} system px
101
+ * square, live against `--vf-scale`.
102
+ */
103
+ export declare const vfRepeatTileSize: CSSResult;
102
104
  /**
103
- * A one-entry cache for a surface's whole-surface raster, keyed by its
104
- * system-px size — so every render with an unchanged size (all of them except
105
- * a declared-size change) reuses the encoded image instead of re-running
106
- * `tileRaster`'s canvas encode. A surface whose *art* can change under the
107
- * same size (a `pattern` attribute, src/pattern-fill.ts) passes an `id` for
108
- * it — the fixed-motif surfaces leave it empty.
105
+ * The motif laid over {@link REPEAT_TILE} system px square and drawn at `n`
106
+ * image px per system px, as a CSS `url()` — the image a
107
+ * {@link vfRepeatTileSize} repeat copies 1:1 at that density. Cached for the
108
+ * page by the motif's content and `n`.
109
+ */
110
+ export declare function repeatTile(motif: TileMotif, n: number): string;
111
+ /**
112
+ * Device px per system px at `el`: its `--vf-scale` (a consumer override
113
+ * included) times the true device pixel ratio — the `n` a {@link repeatTile}
114
+ * shown there is drawn at.
115
+ */
116
+ export declare function devicePxAt(el: Element): number;
117
+ /** What a component hands {@link RepeatTileController}. */
118
+ export interface RepeatTileOptions {
119
+ /** The element the tile's custom property is written on. */
120
+ getBox: () => HTMLElement | null | undefined;
121
+ /** The art to tile, or null to write nothing (a consumer token owns the
122
+ * surface, or the component isn't showing it). Return the same object for
123
+ * the same art: an unchanged motif and density cost nothing. */
124
+ getArt: () => TileMotif | null | undefined;
125
+ /** The custom property the surface's `background-image` reads. */
126
+ property: string;
127
+ }
128
+ /**
129
+ * Keeps a box's {@link repeatTile} current: writes it as a custom property on
130
+ * the box's inline style after each host update, drawn for the density at
131
+ * the box, and again when zoom or the display changes that density
132
+ * ({@link onScaleChange}); removes it the moment there is nothing to paint.
133
+ * A custom-property channel rather than `background-image` itself, so a
134
+ * surface's forced-colors rule can out-cascade it without `!important`.
135
+ *
136
+ * The surface's stylesheet does the rest: `background-image` from the
137
+ * property, {@link vfRepeatTileSize}, `background-repeat: repeat`, and
138
+ * `image-rendering: pixelated` for the frame between a zoom and the new
139
+ * tile.
109
140
  */
110
- export declare class TileRasterCache {
141
+ export declare class RepeatTileController implements ReactiveController {
111
142
  #private;
112
- for(motifWidth: number, motifHeight: number, rects: readonly TileRect[], width: number, height: number, id?: string): string;
143
+ constructor(host: ReactiveControllerHost, opts: RepeatTileOptions);
144
+ hostConnected(): void;
145
+ hostDisconnected(): void;
146
+ hostUpdated(): void;
113
147
  }
package/dist/tile-grid.js CHANGED
@@ -1,53 +1,86 @@
1
- import { sysLength as e } from "./scale.js";
2
- import { tileRaster as t } from "./styles/recipes/tile.js";
3
- import { css as n, html as r } from "lit";
1
+ import { truePixelRatio as e } from "./zoom.js";
2
+ import { effectiveScale as t, onScaleChange as n, sysLength as r } from "./scale.js";
3
+ import { tileRaster as i } from "./styles/recipes/tile.js";
4
+ import { css as a, html as o } from "lit";
4
5
  //#region src/tile-grid.ts
5
- var i = n`
6
+ var s = a`
6
7
  .vf-tile-grid {
7
8
  /* Clip the overdraw: a fill overdrawn past a box that is not a whole
8
- number of tiles (or a raster ceiled up to one) crops here. */
9
+ number of tiles crops here. */
9
10
  overflow: hidden;
10
11
  pointer-events: none;
11
12
  }
12
- .vf-tile,
13
- .vf-tile-raster {
13
+ .vf-tile {
14
14
  position: absolute;
15
15
  /* The box IS the size — never the image's intrinsic size, and never an
16
16
  intra-box repeat: a consumer tile smaller than its box repeating
17
17
  inside it would re-import the accumulation this module removes. */
18
+ background-image: var(--_vf-tile-image);
18
19
  background-size: 100% 100%;
19
20
  background-repeat: no-repeat;
20
- /* Raster art samples nearest-neighbor — the exactness mechanism for the
21
- kit's own tiles and the vf-img idiom for consumer raster art. A no-op
22
- for SVG (which is why kit art is raster; see tileRaster). */
21
+ /* Consumer raster art samples nearest-neighbor — the vf-img idiom. A
22
+ no-op for SVG. */
23
23
  image-rendering: pixelated;
24
24
  }
25
- .vf-tile {
26
- background-image: var(--_vf-tile-image);
27
- }
28
- .vf-tile-raster {
29
- top: 0;
30
- left: 0;
31
- }
32
25
  `;
33
- function a({ cols: t, rows: n, tile: i }) {
34
- let a = e(i), o = [];
35
- for (let s = 0; s < n; s++) for (let n = 0; n < t; n++) o.push(r`<span
26
+ function c({ cols: e, rows: t, tile: n }) {
27
+ let i = r(n), a = [];
28
+ for (let s = 0; s < t; s++) for (let t = 0; t < e; t++) a.push(o`<span
36
29
  class="vf-tile"
37
- style="left:${e(n * i)};top:${e(s * i)};width:${a};height:${a}"
30
+ style="left:${r(t * n)};top:${r(s * n)};width:${i};height:${i}"
38
31
  ></span>`);
39
- return o;
32
+ return a;
40
33
  }
41
- function o(e, t) {
34
+ function l(e, t) {
42
35
  return typeof getComputedStyle > "u" ? "" : getComputedStyle(e).getPropertyValue(t).trim();
43
36
  }
44
- var s = class {
45
- #e = "";
46
- #t = "";
47
- for(e, n, r, i, a, o = "") {
48
- let s = `${o}|${i}x${a}`;
49
- return this.#e !== s && (this.#e = s, this.#t = t(e, n, r, i, a)), this.#t;
37
+ var u = 120, d = a`
38
+ background-size: calc(var(--vf-scale, 1) * ${120}px)
39
+ calc(var(--vf-scale, 1) * ${120}px);
40
+ `, f = /* @__PURE__ */ new Map();
41
+ function p(e, t) {
42
+ let n = `${e.width}×${e.height}|${t}|${e.rects.join(";")}`, r = f.get(n);
43
+ return r || (r = i(e.width, e.height, e.rects, 120, 120, t), f.set(n, r)), r;
44
+ }
45
+ function m(n) {
46
+ return Math.max(1, Math.round(t(n) * e()));
47
+ }
48
+ var h = class {
49
+ #e;
50
+ #t;
51
+ #n = null;
52
+ #r = null;
53
+ #i = 0;
54
+ #a = "";
55
+ constructor(e, t) {
56
+ this.#e = t, e.addController(this);
57
+ }
58
+ hostConnected() {
59
+ this.#t = n(() => this.#o());
60
+ }
61
+ hostDisconnected() {
62
+ this.#t?.(), this.#t = void 0;
63
+ }
64
+ hostUpdated() {
65
+ this.#o();
66
+ }
67
+ #o() {
68
+ let e = this.#e.getBox() ?? null;
69
+ e !== this.#n && (this.#s(), this.#n = e);
70
+ let t = this.#e.getArt() ?? null;
71
+ if (!e || !t) {
72
+ this.#s();
73
+ return;
74
+ }
75
+ let n = m(e);
76
+ if (t === this.#r && n === this.#i && this.#a) return;
77
+ this.#r = t, this.#i = n;
78
+ let r = p(t, n);
79
+ r !== this.#a && (this.#a = r, e.style.setProperty(this.#e.property, r));
80
+ }
81
+ #s() {
82
+ this.#r = null, this.#i = 0, this.#a && (this.#a = "", this.#n?.style.removeProperty(this.#e.property));
50
83
  }
51
84
  };
52
85
  //#endregion
53
- export { s as TileRasterCache, o as patternOverride, a as tileGrid, i as vfTileGrid };
86
+ export { u as REPEAT_TILE, h as RepeatTileController, m as devicePxAt, l as patternOverride, p as repeatTile, c as tileGrid, d as vfRepeatTileSize, s as vfTileGrid };
@@ -22,8 +22,7 @@ import type { ReactiveController, ReactiveControllerHost } from 'lit';
22
22
  * runs on every update.
23
23
  *
24
24
  * The height comes along from the same observation, for a box measured on
25
- * both axes: `PatternFillController` (src/pattern-fill.ts) sizes a pattern
26
- * raster to a box whose extent nobody declared.
25
+ * both axes.
27
26
  */
28
27
  export declare class TrackWidthController implements ReactiveController {
29
28
  private readonly host;