@glowbox/extras 1.0.0-rc.2
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.
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/dist/gif.d.ts +19 -0
- package/dist/image.d.ts +17 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +511 -0
- package/dist/index.js.map +1 -0
- package/dist/plane.d.ts +18 -0
- package/dist/sample.d.ts +25 -0
- package/dist/text.d.ts +10 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eetu Sutinen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# @glowbox/extras
|
|
2
|
+
|
|
3
|
+
Content helpers for **[@glowbox/led-grid](../led-grid)** — the opt-in content layer the core
|
|
4
|
+
deliberately ships without. Headlined by a **GIF / image animation player**, plus a
|
|
5
|
+
**text** helper. Each helper works with any wrapper: the players return a
|
|
6
|
+
`(d, dt) => void` draw callback; `text()` draws once. They operate on the plain
|
|
7
|
+
`VoxelGrid` / `LedDisplay`, so they also run headlessly.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
yarn add @glowbox/extras # @glowbox/led-grid comes along
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { makeGifPlayer, makeImagePlayer, text } from '@glowbox/extras';
|
|
15
|
+
|
|
16
|
+
// Play a GIF on the front face of the grid:
|
|
17
|
+
display.onFrame(makeGifPlayer('/loop.gif', { plane: 'xy', fit: 'contain' }));
|
|
18
|
+
|
|
19
|
+
// …or a still image:
|
|
20
|
+
display.onFrame(makeImagePlayer('/logo.png', { fit: 'cover' }));
|
|
21
|
+
|
|
22
|
+
// …or draw text in your own frame callback:
|
|
23
|
+
display.onFrame((d) => {
|
|
24
|
+
d.clear();
|
|
25
|
+
text(d, 'HI', { color: '#00e5ff' });
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
With a framework wrapper, hand the callback to the `draw` prop:
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<LedGrid size={[32, 32, 8]} draw={makeGifPlayer('/loop.gif')} />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Players
|
|
36
|
+
|
|
37
|
+
`makeImagePlayer(url, opts)` / `makeGifPlayer(url, opts)` → a draw callback. They load
|
|
38
|
+
asynchronously and draw nothing until ready; GIFs advance by their frame delays and
|
|
39
|
+
loop. GIF frames are decoded with [`gifuct-js`](https://github.com/matt-way/gifuct-js)
|
|
40
|
+
and composited (honouring frame disposal) into full RGBA snapshots.
|
|
41
|
+
|
|
42
|
+
### Options (`PlayerOptions`)
|
|
43
|
+
|
|
44
|
+
| option | default | notes |
|
|
45
|
+
| ----------- | ----------- | -------------------------------------------------------------------- |
|
|
46
|
+
| `plane` | `'xy'` | grid plane to paint on — `'xy'` (faces camera), `'xz'`, `'yz'` |
|
|
47
|
+
| `depth` | middle | index on the plane's normal axis |
|
|
48
|
+
| `fit` | `'contain'` | aspect fit: `'contain'` (letterbox) · `'cover'` (crop) · `'stretch'` |
|
|
49
|
+
| `threshold` | `0.5` | skip cells with coverage below this |
|
|
50
|
+
| `gain` | `1` | multiply painted colour (`>1` blooms in the hologram style) |
|
|
51
|
+
| `clear` | `true` | clear the grid before painting each frame |
|
|
52
|
+
|
|
53
|
+
## Text
|
|
54
|
+
|
|
55
|
+
`text(d, str, opts)` rasterizes `str` with the system sans-serif and threshold-samples
|
|
56
|
+
it onto a grid plane. Options: `plane`, `depth`, `threshold`, `color` (a `Color`), and
|
|
57
|
+
`fontSize` (grid cells; default ~80% of the plane height). No font asset.
|
|
58
|
+
|
|
59
|
+
## Building blocks
|
|
60
|
+
|
|
61
|
+
`sampleImageToGrid(src, nx, ny, fit)` (pure, no DOM) does the image → grid mapping.
|
|
62
|
+
`decodeGif(url)` / `framesFromBuffer(arrayBuffer)` decode a GIF to `GifFrame`s and
|
|
63
|
+
`frameAt(frames, tMs)` picks the frame for a playback time; `decodeImage(url)` decodes a
|
|
64
|
+
still image. `paintImage(grid, src, opts)` plots a decoded image onto a plane. Reuse any of
|
|
65
|
+
them for custom effects.
|
|
66
|
+
|
|
67
|
+
Wrappers: **[@glowbox/svelte](../svelte)** · **[@glowbox/react](../react)** ·
|
|
68
|
+
**[@glowbox/vue](../vue)**. Live demos: <https://eetu.github.io/glowbox/>.
|
package/dist/gif.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DrawFn, PlayerOptions } from './image';
|
|
2
|
+
import { ImageSource } from './sample';
|
|
3
|
+
/** One composited GIF frame: a full-canvas RGBA snapshot + its display delay (ms). */
|
|
4
|
+
export interface GifFrame {
|
|
5
|
+
src: ImageSource;
|
|
6
|
+
delay: number;
|
|
7
|
+
}
|
|
8
|
+
/** Composite decoded GIF frames (from an ArrayBuffer) into full-size RGBA snapshots. */
|
|
9
|
+
export declare function framesFromBuffer(buffer: ArrayBuffer): GifFrame[];
|
|
10
|
+
/** Fetch + decode a GIF URL into composited frames (browser or node with fetch). */
|
|
11
|
+
export declare function decodeGif(url: string): Promise<GifFrame[]>;
|
|
12
|
+
/** Pick the frame index for a time offset (ms) into a looping animation. */
|
|
13
|
+
export declare function frameAt(frames: GifFrame[], elapsedMs: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* Load `url` and return a draw callback that plays the GIF onto the grid plane,
|
|
16
|
+
* advancing frames by their delays and looping. Give the result to
|
|
17
|
+
* `display.onFrame(...)` (or a wrapper's `draw` prop). Draws nothing until loaded.
|
|
18
|
+
*/
|
|
19
|
+
export declare function makeGifPlayer(url: string, opts?: PlayerOptions): DrawFn;
|
package/dist/image.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { LedDisplay } from '@glowbox/led-grid';
|
|
2
|
+
import { PaintOptions } from './plane';
|
|
3
|
+
import { ImageSource } from './sample';
|
|
4
|
+
export interface PlayerOptions extends PaintOptions {
|
|
5
|
+
/** Clear the grid before painting each frame (default true). */
|
|
6
|
+
clear?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** A per-frame draw callback (what the players return). */
|
|
9
|
+
export type DrawFn = (d: LedDisplay, dt: number) => void;
|
|
10
|
+
/** Decode an image URL to raw RGBA via `createImageBitmap` + a 2D canvas (browser). */
|
|
11
|
+
export declare function decodeImage(url: string): Promise<ImageSource>;
|
|
12
|
+
/**
|
|
13
|
+
* Load `url` and return a draw callback that paints it onto the grid plane. Give
|
|
14
|
+
* the result to `display.onFrame(...)` (or a wrapper's `draw` prop). Draws nothing
|
|
15
|
+
* until the image has loaded.
|
|
16
|
+
*/
|
|
17
|
+
export declare function makeImagePlayer(url: string, opts?: PlayerOptions): DrawFn;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { decodeGif, frameAt, framesFromBuffer, type GifFrame, makeGifPlayer } from './gif';
|
|
2
|
+
export { decodeImage, type DrawFn, makeImagePlayer, type PlayerOptions } from './image';
|
|
3
|
+
export { paintImage, type PaintOptions, type Plane } from './plane';
|
|
4
|
+
export { type Fit, type GridSample, type ImageSource, sampleImageToGrid } from './sample';
|
|
5
|
+
export { text, type TextOptions } from './text';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import { parseColor as e } from "@glowbox/led-grid";
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var t = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t.exports), n = /* @__PURE__ */ t(((e) => {
|
|
4
|
+
Object.defineProperty(e, "__esModule", { value: !0 }), e.loop = e.conditional = e.parse = void 0, e.parse = function e(t, n) {
|
|
5
|
+
var r = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {}, i = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : r;
|
|
6
|
+
if (Array.isArray(n)) n.forEach(function(n) {
|
|
7
|
+
return e(t, n, r, i);
|
|
8
|
+
});
|
|
9
|
+
else if (typeof n == "function") n(t, r, i, e);
|
|
10
|
+
else {
|
|
11
|
+
var a = Object.keys(n)[0];
|
|
12
|
+
Array.isArray(n[a]) ? (i[a] = {}, e(t, n[a], r, i[a])) : i[a] = n[a](t, r, i, e);
|
|
13
|
+
}
|
|
14
|
+
return r;
|
|
15
|
+
}, e.conditional = function(e, t) {
|
|
16
|
+
return function(n, r, i, a) {
|
|
17
|
+
t(n, r, i) && a(n, e, r, i);
|
|
18
|
+
};
|
|
19
|
+
}, e.loop = function(e, t) {
|
|
20
|
+
return function(n, r, i, a) {
|
|
21
|
+
for (var o = [], s = n.pos; t(n, r, i);) {
|
|
22
|
+
var c = {};
|
|
23
|
+
if (a(n, e, r, c), n.pos === s) break;
|
|
24
|
+
s = n.pos, o.push(c);
|
|
25
|
+
}
|
|
26
|
+
return o;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
})), r = /* @__PURE__ */ t(((e) => {
|
|
30
|
+
Object.defineProperty(e, "__esModule", { value: !0 }), e.readBits = e.readArray = e.readUnsigned = e.readString = e.peekBytes = e.readBytes = e.peekByte = e.readByte = e.buildStream = void 0, e.buildStream = function(e) {
|
|
31
|
+
return {
|
|
32
|
+
data: e,
|
|
33
|
+
pos: 0
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
var t = function() {
|
|
37
|
+
return function(e) {
|
|
38
|
+
return e.data[e.pos++];
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
e.readByte = t, e.peekByte = function() {
|
|
42
|
+
var e = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
|
|
43
|
+
return function(t) {
|
|
44
|
+
return t.data[t.pos + e];
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
var n = function(e) {
|
|
48
|
+
return function(t) {
|
|
49
|
+
return t.data.subarray(t.pos, t.pos += e);
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
e.readBytes = n, e.peekBytes = function(e) {
|
|
53
|
+
return function(t) {
|
|
54
|
+
return t.data.subarray(t.pos, t.pos + e);
|
|
55
|
+
};
|
|
56
|
+
}, e.readString = function(e) {
|
|
57
|
+
return function(t) {
|
|
58
|
+
return Array.from(n(e)(t)).map(function(e) {
|
|
59
|
+
return String.fromCharCode(e);
|
|
60
|
+
}).join("");
|
|
61
|
+
};
|
|
62
|
+
}, e.readUnsigned = function(e) {
|
|
63
|
+
return function(t) {
|
|
64
|
+
var r = n(2)(t);
|
|
65
|
+
return e ? (r[1] << 8) + r[0] : (r[0] << 8) + r[1];
|
|
66
|
+
};
|
|
67
|
+
}, e.readArray = function(e, t) {
|
|
68
|
+
return function(r, i, a) {
|
|
69
|
+
for (var o = typeof t == "function" ? t(r, i, a) : t, s = n(e), c = Array(o), l = 0; l < o; l++) c[l] = s(r);
|
|
70
|
+
return c;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
var r = function(e, t, n) {
|
|
74
|
+
for (var r = 0, i = 0; i < n; i++) r += e[t + i] && 2 ** (n - i - 1);
|
|
75
|
+
return r;
|
|
76
|
+
};
|
|
77
|
+
e.readBits = function(e) {
|
|
78
|
+
return function(n) {
|
|
79
|
+
for (var i = t()(n), a = Array(8), o = 0; o < 8; o++) a[7 - o] = !!(i & 1 << o);
|
|
80
|
+
return Object.keys(e).reduce(function(t, n) {
|
|
81
|
+
var i = e[n];
|
|
82
|
+
return i.length ? t[n] = r(a, i.index, i.length) : t[n] = a[i.index], t;
|
|
83
|
+
}, {});
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
})), i = /* @__PURE__ */ t(((e) => {
|
|
87
|
+
Object.defineProperty(e, "__esModule", { value: !0 }), e.default = void 0;
|
|
88
|
+
var t = n(), i = r(), a = { blocks: function(e) {
|
|
89
|
+
for (var t = 0, n = [], r = e.data.length, a = 0, o = (0, i.readByte)()(e); o !== t && o; o = (0, i.readByte)()(e)) {
|
|
90
|
+
if (e.pos + o >= r) {
|
|
91
|
+
var s = r - e.pos;
|
|
92
|
+
n.push((0, i.readBytes)(s)(e)), a += s;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
n.push((0, i.readBytes)(o)(e)), a += o;
|
|
96
|
+
}
|
|
97
|
+
for (var c = new Uint8Array(a), l = 0, u = 0; u < n.length; u++) c.set(n[u], l), l += n[u].length;
|
|
98
|
+
return c;
|
|
99
|
+
} }, o = (0, t.conditional)({ gce: [
|
|
100
|
+
{ codes: (0, i.readBytes)(2) },
|
|
101
|
+
{ byteSize: (0, i.readByte)() },
|
|
102
|
+
{ extras: (0, i.readBits)({
|
|
103
|
+
future: {
|
|
104
|
+
index: 0,
|
|
105
|
+
length: 3
|
|
106
|
+
},
|
|
107
|
+
disposal: {
|
|
108
|
+
index: 3,
|
|
109
|
+
length: 3
|
|
110
|
+
},
|
|
111
|
+
userInput: { index: 6 },
|
|
112
|
+
transparentColorGiven: { index: 7 }
|
|
113
|
+
}) },
|
|
114
|
+
{ delay: (0, i.readUnsigned)(!0) },
|
|
115
|
+
{ transparentColorIndex: (0, i.readByte)() },
|
|
116
|
+
{ terminator: (0, i.readByte)() }
|
|
117
|
+
] }, function(e) {
|
|
118
|
+
var t = (0, i.peekBytes)(2)(e);
|
|
119
|
+
return t[0] === 33 && t[1] === 249;
|
|
120
|
+
}), s = (0, t.conditional)({ image: [
|
|
121
|
+
{ code: (0, i.readByte)() },
|
|
122
|
+
{ descriptor: [
|
|
123
|
+
{ left: (0, i.readUnsigned)(!0) },
|
|
124
|
+
{ top: (0, i.readUnsigned)(!0) },
|
|
125
|
+
{ width: (0, i.readUnsigned)(!0) },
|
|
126
|
+
{ height: (0, i.readUnsigned)(!0) },
|
|
127
|
+
{ lct: (0, i.readBits)({
|
|
128
|
+
exists: { index: 0 },
|
|
129
|
+
interlaced: { index: 1 },
|
|
130
|
+
sort: { index: 2 },
|
|
131
|
+
future: {
|
|
132
|
+
index: 3,
|
|
133
|
+
length: 2
|
|
134
|
+
},
|
|
135
|
+
size: {
|
|
136
|
+
index: 5,
|
|
137
|
+
length: 3
|
|
138
|
+
}
|
|
139
|
+
}) }
|
|
140
|
+
] },
|
|
141
|
+
(0, t.conditional)({ lct: (0, i.readArray)(3, function(e, t, n) {
|
|
142
|
+
return 2 ** (n.descriptor.lct.size + 1);
|
|
143
|
+
}) }, function(e, t, n) {
|
|
144
|
+
return n.descriptor.lct.exists;
|
|
145
|
+
}),
|
|
146
|
+
{ data: [{ minCodeSize: (0, i.readByte)() }, a] }
|
|
147
|
+
] }, function(e) {
|
|
148
|
+
return (0, i.peekByte)()(e) === 44;
|
|
149
|
+
}), c = (0, t.conditional)({ text: [
|
|
150
|
+
{ codes: (0, i.readBytes)(2) },
|
|
151
|
+
{ blockSize: (0, i.readByte)() },
|
|
152
|
+
{ preData: function(e, t, n) {
|
|
153
|
+
return (0, i.readBytes)(n.text.blockSize)(e);
|
|
154
|
+
} },
|
|
155
|
+
a
|
|
156
|
+
] }, function(e) {
|
|
157
|
+
var t = (0, i.peekBytes)(2)(e);
|
|
158
|
+
return t[0] === 33 && t[1] === 1;
|
|
159
|
+
}), l = (0, t.conditional)({ application: [
|
|
160
|
+
{ codes: (0, i.readBytes)(2) },
|
|
161
|
+
{ blockSize: (0, i.readByte)() },
|
|
162
|
+
{ id: function(e, t, n) {
|
|
163
|
+
return (0, i.readString)(n.blockSize)(e);
|
|
164
|
+
} },
|
|
165
|
+
a
|
|
166
|
+
] }, function(e) {
|
|
167
|
+
var t = (0, i.peekBytes)(2)(e);
|
|
168
|
+
return t[0] === 33 && t[1] === 255;
|
|
169
|
+
}), u = (0, t.conditional)({ comment: [{ codes: (0, i.readBytes)(2) }, a] }, function(e) {
|
|
170
|
+
var t = (0, i.peekBytes)(2)(e);
|
|
171
|
+
return t[0] === 33 && t[1] === 254;
|
|
172
|
+
});
|
|
173
|
+
e.default = [
|
|
174
|
+
{ header: [{ signature: (0, i.readString)(3) }, { version: (0, i.readString)(3) }] },
|
|
175
|
+
{ lsd: [
|
|
176
|
+
{ width: (0, i.readUnsigned)(!0) },
|
|
177
|
+
{ height: (0, i.readUnsigned)(!0) },
|
|
178
|
+
{ gct: (0, i.readBits)({
|
|
179
|
+
exists: { index: 0 },
|
|
180
|
+
resolution: {
|
|
181
|
+
index: 1,
|
|
182
|
+
length: 3
|
|
183
|
+
},
|
|
184
|
+
sort: { index: 4 },
|
|
185
|
+
size: {
|
|
186
|
+
index: 5,
|
|
187
|
+
length: 3
|
|
188
|
+
}
|
|
189
|
+
}) },
|
|
190
|
+
{ backgroundColorIndex: (0, i.readByte)() },
|
|
191
|
+
{ pixelAspectRatio: (0, i.readByte)() }
|
|
192
|
+
] },
|
|
193
|
+
(0, t.conditional)({ gct: (0, i.readArray)(3, function(e, t) {
|
|
194
|
+
return 2 ** (t.lsd.gct.size + 1);
|
|
195
|
+
}) }, function(e, t) {
|
|
196
|
+
return t.lsd.gct.exists;
|
|
197
|
+
}),
|
|
198
|
+
{ frames: (0, t.loop)([
|
|
199
|
+
o,
|
|
200
|
+
l,
|
|
201
|
+
u,
|
|
202
|
+
s,
|
|
203
|
+
c
|
|
204
|
+
], function(e) {
|
|
205
|
+
var t = (0, i.peekByte)()(e);
|
|
206
|
+
return t === 33 || t === 44;
|
|
207
|
+
}) }
|
|
208
|
+
];
|
|
209
|
+
})), a = /* @__PURE__ */ t(((e) => {
|
|
210
|
+
Object.defineProperty(e, "__esModule", { value: !0 }), e.deinterlace = void 0, e.deinterlace = function(e, t) {
|
|
211
|
+
for (var n = Array(e.length), r = e.length / t, i = function(r, i) {
|
|
212
|
+
var a = e.slice(i * t, (i + 1) * t);
|
|
213
|
+
n.splice.apply(n, [r * t, t].concat(a));
|
|
214
|
+
}, a = [
|
|
215
|
+
0,
|
|
216
|
+
4,
|
|
217
|
+
2,
|
|
218
|
+
1
|
|
219
|
+
], o = [
|
|
220
|
+
8,
|
|
221
|
+
8,
|
|
222
|
+
4,
|
|
223
|
+
2
|
|
224
|
+
], s = 0, c = 0; c < 4; c++) for (var l = a[c]; l < r; l += o[c]) i(l, s), s++;
|
|
225
|
+
return n;
|
|
226
|
+
};
|
|
227
|
+
})), o = /* @__PURE__ */ t(((e) => {
|
|
228
|
+
Object.defineProperty(e, "__esModule", { value: !0 }), e.lzw = void 0, e.lzw = function(e, t, n) {
|
|
229
|
+
var r = 4096, i = -1, a = n, o, s, c, l, u, d, f, p, m, h, g, _, v, y, b, x, S = Array(n), C = Array(r), w = Array(r), T = Array(r + 1);
|
|
230
|
+
for (_ = e, s = 1 << _, u = s + 1, o = s + 2, f = i, l = _ + 1, c = (1 << l) - 1, m = 0; m < s; m++) C[m] = 0, w[m] = m;
|
|
231
|
+
var g = p = v = y = x = b = 0, p, v, y, x, b;
|
|
232
|
+
for (h = 0; h < a;) {
|
|
233
|
+
if (y === 0) {
|
|
234
|
+
if (p < l) {
|
|
235
|
+
g += t[b] << p, p += 8, b++;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (m = g & c, g >>= l, p -= l, m > o || m == u) break;
|
|
239
|
+
if (m == s) {
|
|
240
|
+
l = _ + 1, c = (1 << l) - 1, o = s + 2, f = i;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (f == i) {
|
|
244
|
+
T[y++] = w[m], f = m, v = m;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
for (d = m, m == o && (T[y++] = v, m = f); m > s;) T[y++] = w[m], m = C[m];
|
|
248
|
+
v = w[m] & 255, T[y++] = v, o < r && (C[o] = f, w[o] = v, o++, (o & c) === 0 && o < r && (l++, c += o)), f = d;
|
|
249
|
+
}
|
|
250
|
+
y--, S[x++] = T[y], h++;
|
|
251
|
+
}
|
|
252
|
+
for (h = x; h < a; h++) S[h] = 0;
|
|
253
|
+
return S;
|
|
254
|
+
};
|
|
255
|
+
})), s = (/* @__PURE__ */ t(((e) => {
|
|
256
|
+
Object.defineProperty(e, "__esModule", { value: !0 }), e.decompressFrames = e.decompressFrame = e.parseGIF = void 0;
|
|
257
|
+
var t = d(i()), s = n(), c = r(), l = a(), u = o();
|
|
258
|
+
function d(e) {
|
|
259
|
+
return e && e.__esModule ? e : { default: e };
|
|
260
|
+
}
|
|
261
|
+
e.parseGIF = function(e) {
|
|
262
|
+
var n = new Uint8Array(e);
|
|
263
|
+
return (0, s.parse)((0, c.buildStream)(n), t.default);
|
|
264
|
+
};
|
|
265
|
+
var f = function(e) {
|
|
266
|
+
for (var t = e.pixels.length, n = new Uint8ClampedArray(t * 4), r = 0; r < t; r++) {
|
|
267
|
+
var i = r * 4, a = e.pixels[r], o = e.colorTable[a] || [
|
|
268
|
+
0,
|
|
269
|
+
0,
|
|
270
|
+
0
|
|
271
|
+
];
|
|
272
|
+
n[i] = o[0], n[i + 1] = o[1], n[i + 2] = o[2], n[i + 3] = a === e.transparentIndex ? 0 : 255;
|
|
273
|
+
}
|
|
274
|
+
return n;
|
|
275
|
+
}, p = function(e, t, n) {
|
|
276
|
+
if (!e.image) {
|
|
277
|
+
console.warn("gif frame does not have associated image.");
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
var r = e.image, i = r.descriptor.width * r.descriptor.height, a = (0, u.lzw)(r.data.minCodeSize, r.data.blocks, i);
|
|
281
|
+
r.descriptor.lct.interlaced && (a = (0, l.deinterlace)(a, r.descriptor.width));
|
|
282
|
+
var o = {
|
|
283
|
+
pixels: a,
|
|
284
|
+
dims: {
|
|
285
|
+
top: e.image.descriptor.top,
|
|
286
|
+
left: e.image.descriptor.left,
|
|
287
|
+
width: e.image.descriptor.width,
|
|
288
|
+
height: e.image.descriptor.height
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
return r.descriptor.lct && r.descriptor.lct.exists ? o.colorTable = r.lct : o.colorTable = t, e.gce && (o.delay = (e.gce.delay || 10) * 10, o.disposalType = e.gce.extras.disposal, e.gce.extras.transparentColorGiven && (o.transparentIndex = e.gce.transparentColorIndex)), n && (o.patch = f(o)), o;
|
|
292
|
+
};
|
|
293
|
+
e.decompressFrame = p, e.decompressFrames = function(e, t) {
|
|
294
|
+
return e.frames.filter(function(e) {
|
|
295
|
+
return e.image;
|
|
296
|
+
}).map(function(n) {
|
|
297
|
+
return p(n, e.gct, t);
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
})))();
|
|
301
|
+
function c(e, t, n, r = "contain") {
|
|
302
|
+
let i = new Float32Array(t * n * 3), a = new Float32Array(t * n), { data: o, width: s, height: c } = e, l = t, u = n, d = 0, f = 0;
|
|
303
|
+
if (r !== "stretch" && s > 0 && c > 0) {
|
|
304
|
+
let e = r === "cover" ? Math.max(t / s, n / c) : Math.min(t / s, n / c);
|
|
305
|
+
l = s * e, u = c * e, d = (t - l) / 2, f = (n - u) / 2;
|
|
306
|
+
}
|
|
307
|
+
for (let e = 0; e < n; e++) {
|
|
308
|
+
let n = (e + .5 - f) / u;
|
|
309
|
+
for (let r = 0; r < t; r++) {
|
|
310
|
+
let u = e * t + r, f = (r + .5 - d) / l;
|
|
311
|
+
if (f < 0 || f >= 1 || n < 0 || n >= 1) continue;
|
|
312
|
+
let p = Math.min(s - 1, Math.max(0, f * s | 0)), m = (Math.min(c - 1, Math.max(0, (1 - n) * c | 0)) * s + p) * 4, h = o[m + 3] / 255;
|
|
313
|
+
h <= 0 || (i[u * 3] = o[m] / 255 * h, i[u * 3 + 1] = o[m + 1] / 255 * h, i[u * 3 + 2] = o[m + 2] / 255 * h, a[u] = h);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
nx: t,
|
|
318
|
+
ny: n,
|
|
319
|
+
rgb: i,
|
|
320
|
+
alpha: a
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region src/plane.ts
|
|
325
|
+
function l(e, t) {
|
|
326
|
+
switch (t) {
|
|
327
|
+
case "xz": return {
|
|
328
|
+
dimU: e.nx,
|
|
329
|
+
dimV: e.nz,
|
|
330
|
+
dimW: e.ny,
|
|
331
|
+
at: (e, t, n) => [
|
|
332
|
+
e,
|
|
333
|
+
n,
|
|
334
|
+
t
|
|
335
|
+
]
|
|
336
|
+
};
|
|
337
|
+
case "yz": return {
|
|
338
|
+
dimU: e.ny,
|
|
339
|
+
dimV: e.nz,
|
|
340
|
+
dimW: e.nx,
|
|
341
|
+
at: (e, t, n) => [
|
|
342
|
+
n,
|
|
343
|
+
e,
|
|
344
|
+
t
|
|
345
|
+
]
|
|
346
|
+
};
|
|
347
|
+
default: return {
|
|
348
|
+
dimU: e.nx,
|
|
349
|
+
dimV: e.ny,
|
|
350
|
+
dimW: e.nz,
|
|
351
|
+
at: (e, t, n) => [
|
|
352
|
+
e,
|
|
353
|
+
t,
|
|
354
|
+
n
|
|
355
|
+
]
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function u(e, t, n = {}) {
|
|
360
|
+
let r = n.plane ?? "xy", i = n.threshold ?? .5, a = n.gain ?? 1, { dimU: o, dimV: s, dimW: u, at: d } = l(e, r), f = n.depth ?? u >> 1, { rgb: p, alpha: m } = c(t, o, s, n.fit ?? "contain");
|
|
361
|
+
for (let t = 0; t < s; t++) for (let n = 0; n < o; n++) {
|
|
362
|
+
let r = t * o + n;
|
|
363
|
+
if (m[r] < i) continue;
|
|
364
|
+
let [s, c, l] = d(n, t, f);
|
|
365
|
+
e.plot(s, c, l, [
|
|
366
|
+
p[r * 3] * a,
|
|
367
|
+
p[r * 3 + 1] * a,
|
|
368
|
+
p[r * 3 + 2] * a
|
|
369
|
+
]);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
//#endregion
|
|
373
|
+
//#region src/image.ts
|
|
374
|
+
async function d(e) {
|
|
375
|
+
let t = await fetch(e);
|
|
376
|
+
if (!t.ok) throw Error(`glowbox: failed to load image ${e} (${t.status})`);
|
|
377
|
+
let n = await createImageBitmap(await t.blob()), r = document.createElement("canvas");
|
|
378
|
+
r.width = n.width, r.height = n.height;
|
|
379
|
+
let i = r.getContext("2d");
|
|
380
|
+
if (!i) throw Error("glowbox: 2D canvas unavailable");
|
|
381
|
+
i.drawImage(n, 0, 0), n.close?.();
|
|
382
|
+
let a = i.getImageData(0, 0, r.width, r.height);
|
|
383
|
+
return {
|
|
384
|
+
data: a.data,
|
|
385
|
+
width: a.width,
|
|
386
|
+
height: a.height
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
function f(e, t = {}) {
|
|
390
|
+
let n = null;
|
|
391
|
+
d(e).then((e) => n = e, (e) => console.warn(e));
|
|
392
|
+
let r = t.clear ?? !0;
|
|
393
|
+
return (e) => {
|
|
394
|
+
r && e.clear(), n && u(e, n, t);
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/gif.ts
|
|
399
|
+
function p(e) {
|
|
400
|
+
let t = (0, s.parseGIF)(e), n = (0, s.decompressFrames)(t, !0), r = t.lsd.width, i = t.lsd.height, a = new Uint8ClampedArray(r * i * 4), o = [], c = null;
|
|
401
|
+
for (let e of n) {
|
|
402
|
+
c && (c.type === 2 || c.type === 3) && m(a, r, i, c);
|
|
403
|
+
let { left: t, top: n, width: s, height: l } = e.dims, u = e.patch;
|
|
404
|
+
for (let e = 0; e < l; e++) for (let o = 0; o < s; o++) {
|
|
405
|
+
let c = (e * s + o) * 4;
|
|
406
|
+
if (u[c + 3] === 0) continue;
|
|
407
|
+
let l = t + o, d = n + e;
|
|
408
|
+
if (l < 0 || d < 0 || l >= r || d >= i) continue;
|
|
409
|
+
let f = (d * r + l) * 4;
|
|
410
|
+
a[f] = u[c], a[f + 1] = u[c + 1], a[f + 2] = u[c + 2], a[f + 3] = 255;
|
|
411
|
+
}
|
|
412
|
+
o.push({
|
|
413
|
+
src: {
|
|
414
|
+
data: a.slice(),
|
|
415
|
+
width: r,
|
|
416
|
+
height: i
|
|
417
|
+
},
|
|
418
|
+
delay: e.delay || 100
|
|
419
|
+
}), c = {
|
|
420
|
+
left: t,
|
|
421
|
+
top: n,
|
|
422
|
+
width: s,
|
|
423
|
+
height: l,
|
|
424
|
+
type: e.disposalType
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
return o;
|
|
428
|
+
}
|
|
429
|
+
function m(e, t, n, r) {
|
|
430
|
+
for (let i = r.top; i < r.top + r.height && i < n; i++) for (let n = r.left; n < r.left + r.width && n < t; n++) {
|
|
431
|
+
let r = (i * t + n) * 4;
|
|
432
|
+
e[r] = e[r + 1] = e[r + 2] = e[r + 3] = 0;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
async function h(e) {
|
|
436
|
+
let t = await fetch(e);
|
|
437
|
+
if (!t.ok) throw Error(`glowbox: failed to load gif ${e} (${t.status})`);
|
|
438
|
+
return p(await t.arrayBuffer());
|
|
439
|
+
}
|
|
440
|
+
function g(e, t) {
|
|
441
|
+
let n = e.reduce((e, t) => e + t.delay, 0);
|
|
442
|
+
if (n <= 0) return 0;
|
|
443
|
+
let r = t % n;
|
|
444
|
+
for (let t = 0; t < e.length; t++) {
|
|
445
|
+
if (r < e[t].delay) return t;
|
|
446
|
+
r -= e[t].delay;
|
|
447
|
+
}
|
|
448
|
+
return e.length - 1;
|
|
449
|
+
}
|
|
450
|
+
function _(e, t = {}) {
|
|
451
|
+
let n = null, r = 0;
|
|
452
|
+
h(e).then((e) => n = e, (e) => console.warn(e));
|
|
453
|
+
let i = t.clear ?? !0, a = t;
|
|
454
|
+
return (e, t) => {
|
|
455
|
+
i && e.clear(), !(!n || n.length === 0) && (r += t * 1e3, u(e, n[g(n, r)].src, a));
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
//#endregion
|
|
459
|
+
//#region src/text.ts
|
|
460
|
+
function v(e, t, n, r) {
|
|
461
|
+
let i = document.createElement("canvas");
|
|
462
|
+
i.width = t, i.height = n;
|
|
463
|
+
let a = i.getContext("2d");
|
|
464
|
+
if (!a) throw Error("glowbox: 2D canvas unavailable");
|
|
465
|
+
a.fillStyle = "#000", a.fillRect(0, 0, t, n), a.font = `bold ${Math.max(4, Math.floor(r ?? n * .8))}px sans-serif`, a.fillStyle = "#fff", a.textAlign = "center", a.textBaseline = "middle", a.fillText(e, t / 2, n / 2);
|
|
466
|
+
let o = a.getImageData(0, 0, t, n);
|
|
467
|
+
return {
|
|
468
|
+
data: o.data,
|
|
469
|
+
width: o.width,
|
|
470
|
+
height: o.height
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
var y = /* @__PURE__ */ new Map(), b = 64;
|
|
474
|
+
function x(e, t, n, r) {
|
|
475
|
+
let i = `${e}${t}x${n}${r ?? ""}`, a = y.get(i);
|
|
476
|
+
if (a) return y.delete(i), y.set(i, a), a;
|
|
477
|
+
let { alpha: o } = c(v(e, t, n, r), t, n, "stretch");
|
|
478
|
+
if (y.set(i, o), y.size > b) {
|
|
479
|
+
let e = y.keys().next().value;
|
|
480
|
+
e !== void 0 && y.delete(e);
|
|
481
|
+
}
|
|
482
|
+
return o;
|
|
483
|
+
}
|
|
484
|
+
function S(t, n, r = {}) {
|
|
485
|
+
let i = r.plane ?? "xy", a = r.threshold ?? .5, o = e(r.color ?? [
|
|
486
|
+
1,
|
|
487
|
+
1,
|
|
488
|
+
1
|
|
489
|
+
]), s = i === "yz" ? t.ny : t.nx, c = i === "xy" ? t.ny : t.nz, l = i === "xy" ? t.nz : i === "xz" ? t.ny : t.nx, u = r.depth ?? l >> 1, d = x(n, s, c, r.fontSize);
|
|
490
|
+
for (let e = 0; e < c; e++) for (let n = 0; n < s; n++) {
|
|
491
|
+
if (d[e * s + n] < a) continue;
|
|
492
|
+
let r = i === "xz" ? [
|
|
493
|
+
n,
|
|
494
|
+
u,
|
|
495
|
+
e
|
|
496
|
+
] : i === "yz" ? [
|
|
497
|
+
u,
|
|
498
|
+
n,
|
|
499
|
+
e
|
|
500
|
+
] : [
|
|
501
|
+
n,
|
|
502
|
+
e,
|
|
503
|
+
u
|
|
504
|
+
];
|
|
505
|
+
t.plot(r[0], r[1], r[2], o);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
//#endregion
|
|
509
|
+
export { h as decodeGif, d as decodeImage, g as frameAt, p as framesFromBuffer, _ as makeGifPlayer, f as makeImagePlayer, u as paintImage, c as sampleImageToGrid, S as text };
|
|
510
|
+
|
|
511
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../node_modules/js-binary-schema-parser/lib/index.js","../../../node_modules/js-binary-schema-parser/lib/parsers/uint8.js","../../../node_modules/js-binary-schema-parser/lib/schemas/gif.js","../../../node_modules/gifuct-js/lib/deinterlace.js","../../../node_modules/gifuct-js/lib/lzw.js","../../../node_modules/gifuct-js/lib/index.js","../src/sample.ts","../src/plane.ts","../src/image.ts","../src/gif.ts","../src/text.ts"],"sourcesContent":["\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.loop = exports.conditional = exports.parse = void 0;\n\nvar parse = function parse(stream, schema) {\n var result = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n var parent = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : result;\n\n if (Array.isArray(schema)) {\n schema.forEach(function (partSchema) {\n return parse(stream, partSchema, result, parent);\n });\n } else if (typeof schema === 'function') {\n schema(stream, result, parent, parse);\n } else {\n var key = Object.keys(schema)[0];\n\n if (Array.isArray(schema[key])) {\n parent[key] = {};\n parse(stream, schema[key], result, parent[key]);\n } else {\n parent[key] = schema[key](stream, result, parent, parse);\n }\n }\n\n return result;\n};\n\nexports.parse = parse;\n\nvar conditional = function conditional(schema, conditionFunc) {\n return function (stream, result, parent, parse) {\n if (conditionFunc(stream, result, parent)) {\n parse(stream, schema, result, parent);\n }\n };\n};\n\nexports.conditional = conditional;\n\nvar loop = function loop(schema, continueFunc) {\n return function (stream, result, parent, parse) {\n var arr = [];\n var lastStreamPos = stream.pos;\n\n while (continueFunc(stream, result, parent)) {\n var newParent = {};\n parse(stream, schema, result, newParent); // cases when whole file is parsed but no termination is there and stream position is not getting updated as well\n // it falls into infinite recursion, null check to avoid the same\n\n if (stream.pos === lastStreamPos) {\n break;\n }\n\n lastStreamPos = stream.pos;\n arr.push(newParent);\n }\n\n return arr;\n };\n};\n\nexports.loop = loop;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.readBits = exports.readArray = exports.readUnsigned = exports.readString = exports.peekBytes = exports.readBytes = exports.peekByte = exports.readByte = exports.buildStream = void 0;\n\n// Default stream and parsers for Uint8TypedArray data type\nvar buildStream = function buildStream(uint8Data) {\n return {\n data: uint8Data,\n pos: 0\n };\n};\n\nexports.buildStream = buildStream;\n\nvar readByte = function readByte() {\n return function (stream) {\n return stream.data[stream.pos++];\n };\n};\n\nexports.readByte = readByte;\n\nvar peekByte = function peekByte() {\n var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n return function (stream) {\n return stream.data[stream.pos + offset];\n };\n};\n\nexports.peekByte = peekByte;\n\nvar readBytes = function readBytes(length) {\n return function (stream) {\n return stream.data.subarray(stream.pos, stream.pos += length);\n };\n};\n\nexports.readBytes = readBytes;\n\nvar peekBytes = function peekBytes(length) {\n return function (stream) {\n return stream.data.subarray(stream.pos, stream.pos + length);\n };\n};\n\nexports.peekBytes = peekBytes;\n\nvar readString = function readString(length) {\n return function (stream) {\n return Array.from(readBytes(length)(stream)).map(function (value) {\n return String.fromCharCode(value);\n }).join('');\n };\n};\n\nexports.readString = readString;\n\nvar readUnsigned = function readUnsigned(littleEndian) {\n return function (stream) {\n var bytes = readBytes(2)(stream);\n return littleEndian ? (bytes[1] << 8) + bytes[0] : (bytes[0] << 8) + bytes[1];\n };\n};\n\nexports.readUnsigned = readUnsigned;\n\nvar readArray = function readArray(byteSize, totalOrFunc) {\n return function (stream, result, parent) {\n var total = typeof totalOrFunc === 'function' ? totalOrFunc(stream, result, parent) : totalOrFunc;\n var parser = readBytes(byteSize);\n var arr = new Array(total);\n\n for (var i = 0; i < total; i++) {\n arr[i] = parser(stream);\n }\n\n return arr;\n };\n};\n\nexports.readArray = readArray;\n\nvar subBitsTotal = function subBitsTotal(bits, startIndex, length) {\n var result = 0;\n\n for (var i = 0; i < length; i++) {\n result += bits[startIndex + i] && Math.pow(2, length - i - 1);\n }\n\n return result;\n};\n\nvar readBits = function readBits(schema) {\n return function (stream) {\n var _byte = readByte()(stream); // convert the byte to bit array\n\n\n var bits = new Array(8);\n\n for (var i = 0; i < 8; i++) {\n bits[7 - i] = !!(_byte & 1 << i);\n } // convert the bit array to values based on the schema\n\n\n return Object.keys(schema).reduce(function (res, key) {\n var def = schema[key];\n\n if (def.length) {\n res[key] = subBitsTotal(bits, def.index, def.length);\n } else {\n res[key] = bits[def.index];\n }\n\n return res;\n }, {});\n };\n};\n\nexports.readBits = readBits;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = void 0;\n\nvar _ = require(\"../\");\n\nvar _uint = require(\"../parsers/uint8\");\n\n// a set of 0x00 terminated subblocks\nvar subBlocksSchema = {\n blocks: function blocks(stream) {\n var terminator = 0x00;\n var chunks = [];\n var streamSize = stream.data.length;\n var total = 0;\n\n for (var size = (0, _uint.readByte)()(stream); size !== terminator; size = (0, _uint.readByte)()(stream)) {\n // size becomes undefined for some case when file is corrupted and terminator is not proper \n // null check to avoid recursion\n if (!size) break; // catch corrupted files with no terminator\n\n if (stream.pos + size >= streamSize) {\n var availableSize = streamSize - stream.pos;\n chunks.push((0, _uint.readBytes)(availableSize)(stream));\n total += availableSize;\n break;\n }\n\n chunks.push((0, _uint.readBytes)(size)(stream));\n total += size;\n }\n\n var result = new Uint8Array(total);\n var offset = 0;\n\n for (var i = 0; i < chunks.length; i++) {\n result.set(chunks[i], offset);\n offset += chunks[i].length;\n }\n\n return result;\n }\n}; // global control extension\n\nvar gceSchema = (0, _.conditional)({\n gce: [{\n codes: (0, _uint.readBytes)(2)\n }, {\n byteSize: (0, _uint.readByte)()\n }, {\n extras: (0, _uint.readBits)({\n future: {\n index: 0,\n length: 3\n },\n disposal: {\n index: 3,\n length: 3\n },\n userInput: {\n index: 6\n },\n transparentColorGiven: {\n index: 7\n }\n })\n }, {\n delay: (0, _uint.readUnsigned)(true)\n }, {\n transparentColorIndex: (0, _uint.readByte)()\n }, {\n terminator: (0, _uint.readByte)()\n }]\n}, function (stream) {\n var codes = (0, _uint.peekBytes)(2)(stream);\n return codes[0] === 0x21 && codes[1] === 0xf9;\n}); // image pipeline block\n\nvar imageSchema = (0, _.conditional)({\n image: [{\n code: (0, _uint.readByte)()\n }, {\n descriptor: [{\n left: (0, _uint.readUnsigned)(true)\n }, {\n top: (0, _uint.readUnsigned)(true)\n }, {\n width: (0, _uint.readUnsigned)(true)\n }, {\n height: (0, _uint.readUnsigned)(true)\n }, {\n lct: (0, _uint.readBits)({\n exists: {\n index: 0\n },\n interlaced: {\n index: 1\n },\n sort: {\n index: 2\n },\n future: {\n index: 3,\n length: 2\n },\n size: {\n index: 5,\n length: 3\n }\n })\n }]\n }, (0, _.conditional)({\n lct: (0, _uint.readArray)(3, function (stream, result, parent) {\n return Math.pow(2, parent.descriptor.lct.size + 1);\n })\n }, function (stream, result, parent) {\n return parent.descriptor.lct.exists;\n }), {\n data: [{\n minCodeSize: (0, _uint.readByte)()\n }, subBlocksSchema]\n }]\n}, function (stream) {\n return (0, _uint.peekByte)()(stream) === 0x2c;\n}); // plain text block\n\nvar textSchema = (0, _.conditional)({\n text: [{\n codes: (0, _uint.readBytes)(2)\n }, {\n blockSize: (0, _uint.readByte)()\n }, {\n preData: function preData(stream, result, parent) {\n return (0, _uint.readBytes)(parent.text.blockSize)(stream);\n }\n }, subBlocksSchema]\n}, function (stream) {\n var codes = (0, _uint.peekBytes)(2)(stream);\n return codes[0] === 0x21 && codes[1] === 0x01;\n}); // application block\n\nvar applicationSchema = (0, _.conditional)({\n application: [{\n codes: (0, _uint.readBytes)(2)\n }, {\n blockSize: (0, _uint.readByte)()\n }, {\n id: function id(stream, result, parent) {\n return (0, _uint.readString)(parent.blockSize)(stream);\n }\n }, subBlocksSchema]\n}, function (stream) {\n var codes = (0, _uint.peekBytes)(2)(stream);\n return codes[0] === 0x21 && codes[1] === 0xff;\n}); // comment block\n\nvar commentSchema = (0, _.conditional)({\n comment: [{\n codes: (0, _uint.readBytes)(2)\n }, subBlocksSchema]\n}, function (stream) {\n var codes = (0, _uint.peekBytes)(2)(stream);\n return codes[0] === 0x21 && codes[1] === 0xfe;\n});\nvar schema = [{\n header: [{\n signature: (0, _uint.readString)(3)\n }, {\n version: (0, _uint.readString)(3)\n }]\n}, {\n lsd: [{\n width: (0, _uint.readUnsigned)(true)\n }, {\n height: (0, _uint.readUnsigned)(true)\n }, {\n gct: (0, _uint.readBits)({\n exists: {\n index: 0\n },\n resolution: {\n index: 1,\n length: 3\n },\n sort: {\n index: 4\n },\n size: {\n index: 5,\n length: 3\n }\n })\n }, {\n backgroundColorIndex: (0, _uint.readByte)()\n }, {\n pixelAspectRatio: (0, _uint.readByte)()\n }]\n}, (0, _.conditional)({\n gct: (0, _uint.readArray)(3, function (stream, result) {\n return Math.pow(2, result.lsd.gct.size + 1);\n })\n}, function (stream, result) {\n return result.lsd.gct.exists;\n}), // content frames\n{\n frames: (0, _.loop)([gceSchema, applicationSchema, commentSchema, imageSchema, textSchema], function (stream) {\n var nextCode = (0, _uint.peekByte)()(stream); // rather than check for a terminator, we should check for the existence\n // of an ext or image block to avoid infinite loops\n //var terminator = 0x3B;\n //return nextCode !== terminator;\n\n return nextCode === 0x21 || nextCode === 0x2c;\n })\n}];\nvar _default = schema;\nexports[\"default\"] = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.deinterlace = void 0;\n\n/**\r\n * Deinterlace function from https://github.com/shachaf/jsgif\r\n */\nvar deinterlace = function deinterlace(pixels, width) {\n var newPixels = new Array(pixels.length);\n var rows = pixels.length / width;\n\n var cpRow = function cpRow(toRow, fromRow) {\n var fromPixels = pixels.slice(fromRow * width, (fromRow + 1) * width);\n newPixels.splice.apply(newPixels, [toRow * width, width].concat(fromPixels));\n }; // See appendix E.\n\n\n var offsets = [0, 4, 2, 1];\n var steps = [8, 8, 4, 2];\n var fromRow = 0;\n\n for (var pass = 0; pass < 4; pass++) {\n for (var toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) {\n cpRow(toRow, fromRow);\n fromRow++;\n }\n }\n\n return newPixels;\n};\n\nexports.deinterlace = deinterlace;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.lzw = void 0;\n\n/**\r\n * javascript port of java LZW decompression\r\n * Original java author url: https://gist.github.com/devunwired/4479231\r\n */\nvar lzw = function lzw(minCodeSize, data, pixelCount) {\n var MAX_STACK_SIZE = 4096;\n var nullCode = -1;\n var npix = pixelCount;\n var available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, i, datum, data_size, first, top, bi, pi;\n var dstPixels = new Array(pixelCount);\n var prefix = new Array(MAX_STACK_SIZE);\n var suffix = new Array(MAX_STACK_SIZE);\n var pixelStack = new Array(MAX_STACK_SIZE + 1); // Initialize GIF data stream decoder.\n\n data_size = minCodeSize;\n clear = 1 << data_size;\n end_of_information = clear + 1;\n available = clear + 2;\n old_code = nullCode;\n code_size = data_size + 1;\n code_mask = (1 << code_size) - 1;\n\n for (code = 0; code < clear; code++) {\n prefix[code] = 0;\n suffix[code] = code;\n } // Decode GIF pixel stream.\n\n\n var datum, bits, count, first, top, pi, bi;\n datum = bits = count = first = top = pi = bi = 0;\n\n for (i = 0; i < npix;) {\n if (top === 0) {\n if (bits < code_size) {\n // get the next byte\n datum += data[bi] << bits;\n bits += 8;\n bi++;\n continue;\n } // Get the next code.\n\n\n code = datum & code_mask;\n datum >>= code_size;\n bits -= code_size; // Interpret the code\n\n if (code > available || code == end_of_information) {\n break;\n }\n\n if (code == clear) {\n // Reset decoder.\n code_size = data_size + 1;\n code_mask = (1 << code_size) - 1;\n available = clear + 2;\n old_code = nullCode;\n continue;\n }\n\n if (old_code == nullCode) {\n pixelStack[top++] = suffix[code];\n old_code = code;\n first = code;\n continue;\n }\n\n in_code = code;\n\n if (code == available) {\n pixelStack[top++] = first;\n code = old_code;\n }\n\n while (code > clear) {\n pixelStack[top++] = suffix[code];\n code = prefix[code];\n }\n\n first = suffix[code] & 0xff;\n pixelStack[top++] = first; // add a new string to the table, but only if space is available\n // if not, just continue with current table until a clear code is found\n // (deferred clear code implementation as per GIF spec)\n\n if (available < MAX_STACK_SIZE) {\n prefix[available] = old_code;\n suffix[available] = first;\n available++;\n\n if ((available & code_mask) === 0 && available < MAX_STACK_SIZE) {\n code_size++;\n code_mask += available;\n }\n }\n\n old_code = in_code;\n } // Pop a pixel off the pixel stack.\n\n\n top--;\n dstPixels[pi++] = pixelStack[top];\n i++;\n }\n\n for (i = pi; i < npix; i++) {\n dstPixels[i] = 0; // clear missing pixels\n }\n\n return dstPixels;\n};\n\nexports.lzw = lzw;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.decompressFrames = exports.decompressFrame = exports.parseGIF = void 0;\n\nvar _gif = _interopRequireDefault(require(\"js-binary-schema-parser/lib/schemas/gif\"));\n\nvar _jsBinarySchemaParser = require(\"js-binary-schema-parser\");\n\nvar _uint = require(\"js-binary-schema-parser/lib/parsers/uint8\");\n\nvar _deinterlace = require(\"./deinterlace\");\n\nvar _lzw = require(\"./lzw\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\nvar parseGIF = function parseGIF(arrayBuffer) {\n var byteData = new Uint8Array(arrayBuffer);\n return (0, _jsBinarySchemaParser.parse)((0, _uint.buildStream)(byteData), _gif[\"default\"]);\n};\n\nexports.parseGIF = parseGIF;\n\nvar generatePatch = function generatePatch(image) {\n var totalPixels = image.pixels.length;\n var patchData = new Uint8ClampedArray(totalPixels * 4);\n\n for (var i = 0; i < totalPixels; i++) {\n var pos = i * 4;\n var colorIndex = image.pixels[i];\n var color = image.colorTable[colorIndex] || [0, 0, 0];\n patchData[pos] = color[0];\n patchData[pos + 1] = color[1];\n patchData[pos + 2] = color[2];\n patchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0;\n }\n\n return patchData;\n};\n\nvar decompressFrame = function decompressFrame(frame, gct, buildImagePatch) {\n if (!frame.image) {\n console.warn('gif frame does not have associated image.');\n return;\n }\n\n var image = frame.image; // get the number of pixels\n\n var totalPixels = image.descriptor.width * image.descriptor.height; // do lzw decompression\n\n var pixels = (0, _lzw.lzw)(image.data.minCodeSize, image.data.blocks, totalPixels); // deal with interlacing if necessary\n\n if (image.descriptor.lct.interlaced) {\n pixels = (0, _deinterlace.deinterlace)(pixels, image.descriptor.width);\n }\n\n var resultImage = {\n pixels: pixels,\n dims: {\n top: frame.image.descriptor.top,\n left: frame.image.descriptor.left,\n width: frame.image.descriptor.width,\n height: frame.image.descriptor.height\n }\n }; // color table\n\n if (image.descriptor.lct && image.descriptor.lct.exists) {\n resultImage.colorTable = image.lct;\n } else {\n resultImage.colorTable = gct;\n } // add per frame relevant gce information\n\n\n if (frame.gce) {\n resultImage.delay = (frame.gce.delay || 10) * 10; // convert to ms\n\n resultImage.disposalType = frame.gce.extras.disposal; // transparency\n\n if (frame.gce.extras.transparentColorGiven) {\n resultImage.transparentIndex = frame.gce.transparentColorIndex;\n }\n } // create canvas usable imagedata if desired\n\n\n if (buildImagePatch) {\n resultImage.patch = generatePatch(resultImage);\n }\n\n return resultImage;\n};\n\nexports.decompressFrame = decompressFrame;\n\nvar decompressFrames = function decompressFrames(parsedGif, buildImagePatches) {\n return parsedGif.frames.filter(function (f) {\n return f.image;\n }).map(function (f) {\n return decompressFrame(f, parsedGif.gct, buildImagePatches);\n });\n};\n\nexports.decompressFrames = decompressFrames;","// Pure image → grid sampling. Maps a source image (flat RGBA, as from an\n// ImageData) onto an nx×ny grid plane by nearest-neighbour, honouring an aspect\n// `fit`. No DOM, no WebGL — unit-testable and reusable headlessly.\n\n/** How the image maps into the grid rectangle when aspect ratios differ. */\nexport type Fit = 'contain' | 'cover' | 'stretch';\n\n/** A decoded image: RGBA bytes (row-major, y-down — like `ImageData`). */\nexport interface ImageSource {\n\tdata: Uint8ClampedArray;\n\twidth: number;\n\theight: number;\n}\n\n/** Per-cell colour (0..1 RGB) + coverage (0..1 alpha) for an nx×ny grid plane. */\nexport interface GridSample {\n\tnx: number;\n\tny: number;\n\t/** nx*ny*3, row-major, y-UP (grid row 0 is the bottom). */\n\trgb: Float32Array;\n\t/** nx*ny, y-UP. 0 = outside the image (letterbox) or a transparent source pixel. */\n\talpha: Float32Array;\n}\n\n/**\n * Sample `src` onto an `nx`×`ny` grid. Grid rows run bottom-up (row 0 = bottom),\n * so the image lands upright on a `y`-up display. `fit`:\n * - `contain` (default) — whole image inside the grid, letterboxed (alpha 0 outside).\n * - `cover` — fill the grid, cropping the overflow.\n * - `stretch` — ignore aspect, map edge-to-edge.\n */\nexport function sampleImageToGrid(\n\tsrc: ImageSource,\n\tnx: number,\n\tny: number,\n\tfit: Fit = 'contain'\n): GridSample {\n\tconst rgb = new Float32Array(nx * ny * 3);\n\tconst alpha = new Float32Array(nx * ny);\n\tconst { data, width, height } = src;\n\n\t// The grid-pixel rectangle the image draws into (offset can be negative for cover).\n\tlet drawW = nx;\n\tlet drawH = ny;\n\tlet offX = 0;\n\tlet offY = 0;\n\tif (fit !== 'stretch' && width > 0 && height > 0) {\n\t\tconst scale =\n\t\t\tfit === 'cover' ? Math.max(nx / width, ny / height) : Math.min(nx / width, ny / height);\n\t\tdrawW = width * scale;\n\t\tdrawH = height * scale;\n\t\toffX = (nx - drawW) / 2;\n\t\toffY = (ny - drawH) / 2;\n\t}\n\n\tfor (let gy = 0; gy < ny; gy++) {\n\t\t// Fraction down the draw rect (grid is y-up; the image is y-down → flip).\n\t\tconst fy = (gy + 0.5 - offY) / drawH;\n\t\tfor (let gx = 0; gx < nx; gx++) {\n\t\t\tconst o = gy * nx + gx;\n\t\t\tconst fx = (gx + 0.5 - offX) / drawW;\n\t\t\tif (fx < 0 || fx >= 1 || fy < 0 || fy >= 1) continue; // letterbox\n\t\t\tconst sx = Math.min(width - 1, Math.max(0, (fx * width) | 0));\n\t\t\tconst sy = Math.min(height - 1, Math.max(0, ((1 - fy) * height) | 0));\n\t\t\tconst si = (sy * width + sx) * 4;\n\t\t\tconst a = data[si + 3] / 255;\n\t\t\tif (a <= 0) continue;\n\t\t\trgb[o * 3] = (data[si] / 255) * a;\n\t\t\trgb[o * 3 + 1] = (data[si + 1] / 255) * a;\n\t\t\trgb[o * 3 + 2] = (data[si + 2] / 255) * a;\n\t\t\talpha[o] = a;\n\t\t}\n\t}\n\treturn { nx, ny, rgb, alpha };\n}\n","// Shared plumbing for painting a 2D image onto a plane of the 3D grid: pick which\n// two axes the image's (u, v) map to, at a fixed depth on the third.\nimport type { VoxelGrid } from '@glowbox/led-grid';\n\nimport { type Fit, type ImageSource, sampleImageToGrid } from './sample';\n\n/** Which grid plane the image lands on. `xy` (default) faces the default camera. */\nexport type Plane = 'xy' | 'xz' | 'yz';\n\nexport interface PaintOptions {\n\t/** Grid plane the image maps onto (default `'xy'`). */\n\tplane?: Plane;\n\t/** Index on the plane's normal axis (default: the middle slice). */\n\tdepth?: number;\n\t/** Aspect fit (default `'contain'`). */\n\tfit?: Fit;\n\t/** Skip cells with coverage below this (default 0.5). */\n\tthreshold?: number;\n\t/** Multiply every painted colour (default 1; >1 blooms in the hologram style). */\n\tgain?: number;\n}\n\ninterface Axes {\n\tdimU: number;\n\tdimV: number;\n\tdimW: number;\n\tat(u: number, v: number, w: number): [number, number, number];\n}\n\nfunction planeAxes(g: Pick<VoxelGrid, 'nx' | 'ny' | 'nz'>, plane: Plane): Axes {\n\tswitch (plane) {\n\t\tcase 'xz':\n\t\t\treturn { dimU: g.nx, dimV: g.nz, dimW: g.ny, at: (u, v, w) => [u, w, v] };\n\t\tcase 'yz':\n\t\t\treturn { dimU: g.ny, dimV: g.nz, dimW: g.nx, at: (u, v, w) => [w, u, v] };\n\t\tdefault: // 'xy'\n\t\t\treturn { dimU: g.nx, dimV: g.ny, dimW: g.nz, at: (u, v, w) => [u, v, w] };\n\t}\n}\n\n/** Sample `src` to the plane's dims and plot it onto `g` (does not clear). */\nexport function paintImage(g: VoxelGrid, src: ImageSource, opts: PaintOptions = {}): void {\n\tconst plane = opts.plane ?? 'xy';\n\tconst threshold = opts.threshold ?? 0.5;\n\tconst gain = opts.gain ?? 1;\n\tconst { dimU, dimV, dimW, at } = planeAxes(g, plane);\n\tconst depth = opts.depth ?? dimW >> 1;\n\tconst { rgb, alpha } = sampleImageToGrid(src, dimU, dimV, opts.fit ?? 'contain');\n\tfor (let v = 0; v < dimV; v++)\n\t\tfor (let u = 0; u < dimU; u++) {\n\t\t\tconst i = v * dimU + u;\n\t\t\tif (alpha[i] < threshold) continue;\n\t\t\tconst [x, y, z] = at(u, v, depth);\n\t\t\tg.plot(x, y, z, [rgb[i * 3] * gain, rgb[i * 3 + 1] * gain, rgb[i * 3 + 2] * gain]);\n\t\t}\n}\n","// Still-image player: load an image (PNG/JPG/…), then paint it onto the grid plane\n// every frame. Loads async — draws nothing until ready.\nimport type { LedDisplay } from '@glowbox/led-grid';\n\nimport { paintImage, type PaintOptions } from './plane';\nimport type { ImageSource } from './sample';\n\nexport interface PlayerOptions extends PaintOptions {\n\t/** Clear the grid before painting each frame (default true). */\n\tclear?: boolean;\n}\n\n/** A per-frame draw callback (what the players return). */\nexport type DrawFn = (d: LedDisplay, dt: number) => void;\n\n/** Decode an image URL to raw RGBA via `createImageBitmap` + a 2D canvas (browser). */\nexport async function decodeImage(url: string): Promise<ImageSource> {\n\tconst res = await fetch(url);\n\tif (!res.ok) throw new Error(`glowbox: failed to load image ${url} (${res.status})`);\n\tconst bitmap = await createImageBitmap(await res.blob());\n\tconst canvas = document.createElement('canvas');\n\tcanvas.width = bitmap.width;\n\tcanvas.height = bitmap.height;\n\tconst ctx = canvas.getContext('2d');\n\tif (!ctx) throw new Error('glowbox: 2D canvas unavailable');\n\tctx.drawImage(bitmap, 0, 0);\n\tbitmap.close?.();\n\tconst img = ctx.getImageData(0, 0, canvas.width, canvas.height);\n\treturn { data: img.data, width: img.width, height: img.height };\n}\n\n/**\n * Load `url` and return a draw callback that paints it onto the grid plane. Give\n * the result to `display.onFrame(...)` (or a wrapper's `draw` prop). Draws nothing\n * until the image has loaded.\n */\nexport function makeImagePlayer(url: string, opts: PlayerOptions = {}): DrawFn {\n\tlet src: ImageSource | null = null;\n\tdecodeImage(url).then(\n\t\t(s) => (src = s),\n\t\t(e) => console.warn(e)\n\t);\n\tconst clear = opts.clear ?? true;\n\treturn (d) => {\n\t\tif (clear) d.clear();\n\t\tif (src) paintImage(d, src, opts);\n\t};\n}\n","// GIF animation player: decode a GIF, composite its (possibly partial) frames into\n// full RGBA snapshots honouring frame disposal, then paint the frame for the current\n// time onto the grid every draw. Decoding is via gifuct-js (small, cross-browser —\n// avoids ImageDecoder support gaps). Compositing is pure JS (no canvas), so it runs\n// and is testable in node.\nimport type { LedDisplay } from '@glowbox/led-grid';\nimport { decompressFrames, parseGIF } from 'gifuct-js';\n\nimport { type DrawFn, type PlayerOptions } from './image';\nimport { paintImage, type PaintOptions } from './plane';\nimport type { ImageSource } from './sample';\n\n/** One composited GIF frame: a full-canvas RGBA snapshot + its display delay (ms). */\nexport interface GifFrame {\n\tsrc: ImageSource;\n\tdelay: number;\n}\n\n/** Composite decoded GIF frames (from an ArrayBuffer) into full-size RGBA snapshots. */\nexport function framesFromBuffer(buffer: ArrayBuffer): GifFrame[] {\n\tconst gif = parseGIF(buffer);\n\tconst raw = decompressFrames(gif, true); // buildPatch → frame.patch is RGBA\n\tconst W = gif.lsd.width;\n\tconst H = gif.lsd.height;\n\tconst full = new Uint8ClampedArray(W * H * 4); // accumulating canvas\n\tconst out: GifFrame[] = [];\n\tlet dispose: { left: number; top: number; width: number; height: number; type: number } | null =\n\t\tnull;\n\n\tfor (const fr of raw) {\n\t\t// Apply the previous frame's disposal before drawing this one.\n\t\t// type 2 (restore to background) / 3 (restore to previous, approximated) → clear.\n\t\tif (dispose && (dispose.type === 2 || dispose.type === 3)) clearRegion(full, W, H, dispose);\n\n\t\tconst { left, top, width, height } = fr.dims;\n\t\tconst patch = fr.patch;\n\t\tfor (let y = 0; y < height; y++)\n\t\t\tfor (let x = 0; x < width; x++) {\n\t\t\t\tconst pi = (y * width + x) * 4;\n\t\t\t\tif (patch[pi + 3] === 0) continue; // transparent → keep what's underneath\n\t\t\t\tconst fx = left + x;\n\t\t\t\tconst fy = top + y;\n\t\t\t\tif (fx < 0 || fy < 0 || fx >= W || fy >= H) continue;\n\t\t\t\tconst di = (fy * W + fx) * 4;\n\t\t\t\tfull[di] = patch[pi];\n\t\t\t\tfull[di + 1] = patch[pi + 1];\n\t\t\t\tfull[di + 2] = patch[pi + 2];\n\t\t\t\tfull[di + 3] = 255;\n\t\t\t}\n\t\tout.push({ src: { data: full.slice(), width: W, height: H }, delay: fr.delay || 100 });\n\t\tdispose = { left, top, width, height, type: fr.disposalType };\n\t}\n\treturn out;\n}\n\nfunction clearRegion(\n\tbuf: Uint8ClampedArray,\n\tW: number,\n\tH: number,\n\tr: { left: number; top: number; width: number; height: number }\n) {\n\tfor (let y = r.top; y < r.top + r.height && y < H; y++)\n\t\tfor (let x = r.left; x < r.left + r.width && x < W; x++) {\n\t\t\tconst i = (y * W + x) * 4;\n\t\t\tbuf[i] = buf[i + 1] = buf[i + 2] = buf[i + 3] = 0;\n\t\t}\n}\n\n/** Fetch + decode a GIF URL into composited frames (browser or node with fetch). */\nexport async function decodeGif(url: string): Promise<GifFrame[]> {\n\tconst res = await fetch(url);\n\tif (!res.ok) throw new Error(`glowbox: failed to load gif ${url} (${res.status})`);\n\treturn framesFromBuffer(await res.arrayBuffer());\n}\n\n/** Pick the frame index for a time offset (ms) into a looping animation. */\nexport function frameAt(frames: GifFrame[], elapsedMs: number): number {\n\tconst total = frames.reduce((a, f) => a + f.delay, 0);\n\tif (total <= 0) return 0;\n\tlet t = elapsedMs % total;\n\tfor (let i = 0; i < frames.length; i++) {\n\t\tif (t < frames[i].delay) return i;\n\t\tt -= frames[i].delay;\n\t}\n\treturn frames.length - 1;\n}\n\n/**\n * Load `url` and return a draw callback that plays the GIF onto the grid plane,\n * advancing frames by their delays and looping. Give the result to\n * `display.onFrame(...)` (or a wrapper's `draw` prop). Draws nothing until loaded.\n */\nexport function makeGifPlayer(url: string, opts: PlayerOptions = {}): DrawFn {\n\tlet frames: GifFrame[] | null = null;\n\tlet elapsed = 0;\n\tdecodeGif(url).then(\n\t\t(f) => (frames = f),\n\t\t(e) => console.warn(e)\n\t);\n\tconst clear = opts.clear ?? true;\n\tconst paint: PaintOptions = opts;\n\treturn (d: LedDisplay, dt: number) => {\n\t\tif (clear) d.clear();\n\t\tif (!frames || frames.length === 0) return;\n\t\telapsed += dt * 1000; // dt is seconds\n\t\tpaintImage(d, frames[frameAt(frames, elapsed)].src, paint);\n\t};\n}\n","// Text helper: rasterize a string with a 2D canvas, then threshold-sample it onto a\n// grid plane. No font asset (uses the system sans-serif). Draws once — call it from\n// your per-frame callback (it does not clear). The rasterize+sample step (a fresh\n// canvas + getImageData) is memoized on (str, dims, fontSize), so calling it every\n// frame with the same text is cheap — only the plot loop re-runs.\nimport { type Color, parseColor, type VoxelGrid } from '@glowbox/led-grid';\n\nimport { type PaintOptions } from './plane';\nimport { type ImageSource, sampleImageToGrid } from './sample';\n\nexport interface TextOptions extends Omit<PaintOptions, 'gain'> {\n\t/** Lit colour (default white). */\n\tcolor?: Color;\n\t/** Font size in grid cells (default: ~80% of the plane's V dimension). */\n\tfontSize?: number;\n}\n\n// Rasterize `str` to an nx×ny RGBA buffer (white glyphs on black), centred.\nfunction rasterizeText(str: string, nx: number, ny: number, fontSize?: number): ImageSource {\n\tconst canvas = document.createElement('canvas');\n\tcanvas.width = nx;\n\tcanvas.height = ny;\n\tconst ctx = canvas.getContext('2d');\n\tif (!ctx) throw new Error('glowbox: 2D canvas unavailable');\n\tctx.fillStyle = '#000';\n\tctx.fillRect(0, 0, nx, ny);\n\tconst px = Math.max(4, Math.floor(fontSize ?? ny * 0.8));\n\tctx.font = `bold ${px}px sans-serif`;\n\tctx.fillStyle = '#fff';\n\tctx.textAlign = 'center';\n\tctx.textBaseline = 'middle';\n\tctx.fillText(str, nx / 2, ny / 2);\n\tconst img = ctx.getImageData(0, 0, nx, ny);\n\treturn { data: img.data, width: img.width, height: img.height };\n}\n\n// Memoize the expensive part — rasterizing + sampling to an alpha mask — keyed on the\n// text and target dims. A scroller that feeds changing substrings would grow this\n// unbounded, so cap it and evict least-recently-used (Map keeps insertion order; a hit\n// re-inserts to refresh recency).\nconst RASTER_CACHE = new Map<string, Float32Array>();\nconst RASTER_CACHE_MAX = 64;\nfunction rasterAlpha(str: string, dimU: number, dimV: number, fontSize?: number): Float32Array {\n\tconst key = `${str}\u0000${dimU}x${dimV}\u0000${fontSize ?? ''}`;\n\tconst hit = RASTER_CACHE.get(key);\n\tif (hit) {\n\t\tRASTER_CACHE.delete(key);\n\t\tRASTER_CACHE.set(key, hit);\n\t\treturn hit;\n\t}\n\tconst raster = rasterizeText(str, dimU, dimV, fontSize);\n\tconst { alpha } = sampleImageToGrid(raster, dimU, dimV, 'stretch');\n\tRASTER_CACHE.set(key, alpha);\n\tif (RASTER_CACHE.size > RASTER_CACHE_MAX) {\n\t\tconst oldest = RASTER_CACHE.keys().next().value;\n\t\tif (oldest !== undefined) RASTER_CACHE.delete(oldest);\n\t}\n\treturn alpha;\n}\n\n/** Draw `str` onto the grid plane (default the xy face, mid-depth), in `color`. */\nexport function text(g: VoxelGrid, str: string, opts: TextOptions = {}): void {\n\tconst plane = opts.plane ?? 'xy';\n\tconst threshold = opts.threshold ?? 0.5;\n\tconst color = parseColor(opts.color ?? [1, 1, 1]);\n\t// Plane dims (mirror plane.ts, but we rasterize at those dims for crisp glyphs).\n\tconst dimU = plane === 'yz' ? g.ny : g.nx;\n\tconst dimV = plane === 'xy' ? g.ny : g.nz;\n\tconst dimW = plane === 'xy' ? g.nz : plane === 'xz' ? g.ny : g.nx;\n\tconst depth = opts.depth ?? dimW >> 1;\n\tconst alpha = rasterAlpha(str, dimU, dimV, opts.fontSize);\n\tfor (let v = 0; v < dimV; v++)\n\t\tfor (let u = 0; u < dimU; u++) {\n\t\t\tif (alpha[v * dimU + u] < threshold) continue;\n\t\t\tconst at: [number, number, number] =\n\t\t\t\tplane === 'xz' ? [u, depth, v] : plane === 'yz' ? [depth, u, v] : [u, v, depth];\n\t\t\tg.plot(at[0], at[1], at[2], color);\n\t\t}\n}\n"],"x_google_ignoreList":[0,1,2,3,4,5],"mappings":";;;CAiEA,AA/DA,OAAO,eAAe,GAAS,cAAc,EAC3C,OAAO,GACT,CAAC,GACD,EAAQ,OAAO,EAAQ,cAAc,EAAQ,QAAQ,KAAK,GA0B1D,EAAQ,QAAQ,SAxBK,EAAM,GAAQ,GAAQ;EACzC,IAAI,IAAS,UAAU,SAAS,KAAK,UAAU,OAAO,KAAA,IAAY,UAAU,KAAK,CAAC,GAC9E,IAAS,UAAU,SAAS,KAAK,UAAU,OAAO,KAAA,IAAY,UAAU,KAAK;EAEjF,IAAI,MAAM,QAAQ,CAAM,GACtB,EAAO,QAAQ,SAAU,GAAY;GACnC,OAAO,EAAM,GAAQ,GAAY,GAAQ,CAAM;EACjD,CAAC;OACI,IAAI,OAAO,KAAW,YAC3B,EAAO,GAAQ,GAAQ,GAAQ,CAAK;OAC/B;GACL,IAAI,IAAM,OAAO,KAAK,CAAM,CAAC,CAAC;GAE9B,AAAI,MAAM,QAAQ,EAAO,EAAI,KAC3B,EAAO,KAAO,CAAC,GACf,EAAM,GAAQ,EAAO,IAAM,GAAQ,EAAO,EAAI,KAE9C,EAAO,KAAO,EAAO,EAAI,CAAC,GAAQ,GAAQ,GAAQ,CAAK;EAE3D;EAEA,OAAO;CACT,GAYA,EAAQ,cAAc,SARiB,GAAQ,GAAe;EAC5D,OAAO,SAAU,GAAQ,GAAQ,GAAQ,GAAO;GAC9C,AAAI,EAAc,GAAQ,GAAQ,CAAM,KACtC,EAAM,GAAQ,GAAQ,GAAQ,CAAM;EAExC;CACF,GA0BA,EAAQ,OAAO,SAtBU,GAAQ,GAAc;EAC7C,OAAO,SAAU,GAAQ,GAAQ,GAAQ,GAAO;GAI9C,KAHA,IAAI,IAAM,CAAC,GACP,IAAgB,EAAO,KAEpB,EAAa,GAAQ,GAAQ,CAAM,IAAG;IAC3C,IAAI,IAAY,CAAC;IAIjB,IAHA,EAAM,GAAQ,GAAQ,GAAQ,CAAS,GAGnC,EAAO,QAAQ,GACjB;IAIF,AADA,IAAgB,EAAO,KACvB,EAAI,KAAK,CAAS;GACpB;GAEA,OAAO;EACT;CACF;;CChDA,AAbA,OAAO,eAAe,GAAS,cAAc,EAC3C,OAAO,GACT,CAAC,GACD,EAAQ,WAAW,EAAQ,YAAY,EAAQ,eAAe,EAAQ,aAAa,EAAQ,YAAY,EAAQ,YAAY,EAAQ,WAAW,EAAQ,WAAW,EAAQ,cAAc,KAAK,GAU5L,EAAQ,cAAc,SAPiB,GAAW;EAChD,OAAO;GACL,MAAM;GACN,KAAK;EACP;CACF;CAIA,IAAI,IAAW,WAAoB;EACjC,OAAO,SAAU,GAAQ;GACvB,OAAO,EAAO,KAAK,EAAO;EAC5B;CACF;CAWA,AATA,EAAQ,WAAW,GASnB,EAAQ,WAAW,WAPgB;EACjC,IAAI,IAAS,UAAU,SAAS,KAAK,UAAU,OAAO,KAAA,IAAY,UAAU,KAAK;EACjF,OAAO,SAAU,GAAQ;GACvB,OAAO,EAAO,KAAK,EAAO,MAAM;EAClC;CACF;CAIA,IAAI,IAAY,SAAmB,GAAQ;EACzC,OAAO,SAAU,GAAQ;GACvB,OAAO,EAAO,KAAK,SAAS,EAAO,KAAK,EAAO,OAAO,CAAM;EAC9D;CACF;CA6CA,AA3CA,EAAQ,YAAY,GAQpB,EAAQ,YAAY,SANe,GAAQ;EACzC,OAAO,SAAU,GAAQ;GACvB,OAAO,EAAO,KAAK,SAAS,EAAO,KAAK,EAAO,MAAM,CAAM;EAC7D;CACF,GAYA,EAAQ,aAAa,SARgB,GAAQ;EAC3C,OAAO,SAAU,GAAQ;GACvB,OAAO,MAAM,KAAK,EAAU,CAAM,CAAC,CAAC,CAAM,CAAC,CAAC,CAAC,IAAI,SAAU,GAAO;IAChE,OAAO,OAAO,aAAa,CAAK;GAClC,CAAC,CAAC,CAAC,KAAK,EAAE;EACZ;CACF,GAWA,EAAQ,eAAe,SAPkB,GAAc;EACrD,OAAO,SAAU,GAAQ;GACvB,IAAI,IAAQ,EAAU,CAAC,CAAC,CAAC,CAAM;GAC/B,OAAO,KAAgB,EAAM,MAAM,KAAK,EAAM,MAAM,EAAM,MAAM,KAAK,EAAM;EAC7E;CACF,GAkBA,EAAQ,YAAY,SAde,GAAU,GAAa;EACxD,OAAO,SAAU,GAAQ,GAAQ,GAAQ;GAKvC,KAAK,IAJD,IAAQ,OAAO,KAAgB,aAAa,EAAY,GAAQ,GAAQ,CAAM,IAAI,GAClF,IAAS,EAAU,CAAQ,GAC3B,IAAU,MAAM,CAAK,GAEhB,IAAI,GAAG,IAAI,GAAO,KACzB,EAAI,KAAK,EAAO,CAAM;GAGxB,OAAO;EACT;CACF;CAIA,IAAI,IAAe,SAAsB,GAAM,GAAY,GAAQ;EAGjE,KAAK,IAFD,IAAS,GAEJ,IAAI,GAAG,IAAI,GAAQ,KAC1B,KAAU,EAAK,IAAa,MAAe,MAAG,IAAS,IAAI;EAG7D,OAAO;CACT;CA4BA,EAAQ,WAAW,SA1Bc,GAAQ;EACvC,OAAO,SAAU,GAAQ;GAMvB,KAAK,IALD,IAAQ,EAAS,CAAC,CAAC,CAAM,GAGzB,IAAW,MAAM,CAAC,GAEb,IAAI,GAAG,IAAI,GAAG,KACrB,EAAK,IAAI,KAAK,CAAC,EAAE,IAAQ,KAAK;GAIhC,OAAO,OAAO,KAAK,CAAM,CAAC,CAAC,OAAO,SAAU,GAAK,GAAK;IACpD,IAAI,IAAM,EAAO;IAQjB,OANI,EAAI,SACN,EAAI,KAAO,EAAa,GAAM,EAAI,OAAO,EAAI,MAAM,IAEnD,EAAI,KAAO,EAAK,EAAI,QAGf;GACT,GAAG,CAAC,CAAC;EACP;CACF;;CClHA,AAHA,OAAO,eAAe,GAAS,cAAc,EAC3C,OAAO,GACT,CAAC,GACD,EAAQ,UAAa,KAAK;CAE1B,IAAI,IAAA,EAAA,GAEA,IAAA,EAAA,GAGA,IAAkB,EACpB,QAAQ,SAAgB,GAAQ;EAM9B,KAAK,IALD,IAAa,GACb,IAAS,CAAC,GACV,IAAa,EAAO,KAAK,QACzB,IAAQ,GAEH,KAAQ,GAAG,EAAM,SAAA,CAAU,CAAC,CAAC,CAAM,GAAG,MAAS,KAGjD,GAH6D,KAAQ,GAAG,EAAM,SAAA,CAAU,CAAC,CAAC,CAAM,GAAG;GAKxG,IAAI,EAAO,MAAM,KAAQ,GAAY;IACnC,IAAI,IAAgB,IAAa,EAAO;IAExC,AADA,EAAO,MAAM,GAAG,EAAM,UAAA,CAAW,CAAa,CAAC,CAAC,CAAM,CAAC,GACvD,KAAS;IACT;GACF;GAGA,AADA,EAAO,MAAM,GAAG,EAAM,UAAA,CAAW,CAAI,CAAC,CAAC,CAAM,CAAC,GAC9C,KAAS;EACX;EAKA,KAAK,IAHD,IAAS,IAAI,WAAW,CAAK,GAC7B,IAAS,GAEJ,IAAI,GAAG,IAAI,EAAO,QAAQ,KAEjC,AADA,EAAO,IAAI,EAAO,IAAI,CAAM,GAC5B,KAAU,EAAO,EAAE,CAAC;EAGtB,OAAO;CACT,EACF,GAEI,KAAa,GAAG,EAAE,YAAA,CAAa,EACjC,KAAK;EAAC,EACJ,QAAQ,GAAG,EAAM,UAAA,CAAW,CAAC,EAC/B;EAAG,EACD,WAAW,GAAG,EAAM,SAAA,CAAU,EAChC;EAAG,EACD,SAAS,GAAG,EAAM,SAAA,CAAU;GAC1B,QAAQ;IACN,OAAO;IACP,QAAQ;GACV;GACA,UAAU;IACR,OAAO;IACP,QAAQ;GACV;GACA,WAAW,EACT,OAAO,EACT;GACA,uBAAuB,EACrB,OAAO,EACT;EACF,CAAC,EACH;EAAG,EACD,QAAQ,GAAG,EAAM,aAAA,CAAc,EAAI,EACrC;EAAG,EACD,wBAAwB,GAAG,EAAM,SAAA,CAAU,EAC7C;EAAG,EACD,aAAa,GAAG,EAAM,SAAA,CAAU,EAClC;CAAC,EACH,GAAG,SAAU,GAAQ;EACnB,IAAI,KAAS,GAAG,EAAM,UAAA,CAAW,CAAC,CAAC,CAAC,CAAM;EAC1C,OAAO,EAAM,OAAO,MAAQ,EAAM,OAAO;CAC3C,CAAC,GAEG,KAAe,GAAG,EAAE,YAAA,CAAa,EACnC,OAAO;EAAC,EACN,OAAO,GAAG,EAAM,SAAA,CAAU,EAC5B;EAAG,EACD,YAAY;GAAC,EACX,OAAO,GAAG,EAAM,aAAA,CAAc,EAAI,EACpC;GAAG,EACD,MAAM,GAAG,EAAM,aAAA,CAAc,EAAI,EACnC;GAAG,EACD,QAAQ,GAAG,EAAM,aAAA,CAAc,EAAI,EACrC;GAAG,EACD,SAAS,GAAG,EAAM,aAAA,CAAc,EAAI,EACtC;GAAG,EACD,MAAM,GAAG,EAAM,SAAA,CAAU;IACvB,QAAQ,EACN,OAAO,EACT;IACA,YAAY,EACV,OAAO,EACT;IACA,MAAM,EACJ,OAAO,EACT;IACA,QAAQ;KACN,OAAO;KACP,QAAQ;IACV;IACA,MAAM;KACJ,OAAO;KACP,QAAQ;IACV;GACF,CAAC,EACH;EAAC,EACH;GAAI,GAAG,EAAE,YAAA,CAAa,EACpB,MAAM,GAAG,EAAM,UAAA,CAAW,GAAG,SAAU,GAAQ,GAAQ,GAAQ;GAC7D,OAAgB,MAAG,EAAO,WAAW,IAAI,OAAO;EAClD,CAAC,EACH,GAAG,SAAU,GAAQ,GAAQ,GAAQ;GACnC,OAAO,EAAO,WAAW,IAAI;EAC/B,CAAC;EAAG,EACF,MAAM,CAAC,EACL,cAAc,GAAG,EAAM,SAAA,CAAU,EACnC,GAAG,CAAe,EACpB;CAAC,EACH,GAAG,SAAU,GAAQ;EACnB,QAAQ,GAAG,EAAM,SAAA,CAAU,CAAC,CAAC,CAAM,MAAM;CAC3C,CAAC,GAEG,KAAc,GAAG,EAAE,YAAA,CAAa,EAClC,MAAM;EAAC,EACL,QAAQ,GAAG,EAAM,UAAA,CAAW,CAAC,EAC/B;EAAG,EACD,YAAY,GAAG,EAAM,SAAA,CAAU,EACjC;EAAG,EACD,SAAS,SAAiB,GAAQ,GAAQ,GAAQ;GAChD,QAAQ,GAAG,EAAM,UAAA,CAAW,EAAO,KAAK,SAAS,CAAC,CAAC,CAAM;EAC3D,EACF;EAAG;CAAe,EACpB,GAAG,SAAU,GAAQ;EACnB,IAAI,KAAS,GAAG,EAAM,UAAA,CAAW,CAAC,CAAC,CAAC,CAAM;EAC1C,OAAO,EAAM,OAAO,MAAQ,EAAM,OAAO;CAC3C,CAAC,GAEG,KAAqB,GAAG,EAAE,YAAA,CAAa,EACzC,aAAa;EAAC,EACZ,QAAQ,GAAG,EAAM,UAAA,CAAW,CAAC,EAC/B;EAAG,EACD,YAAY,GAAG,EAAM,SAAA,CAAU,EACjC;EAAG,EACD,IAAI,SAAY,GAAQ,GAAQ,GAAQ;GACtC,QAAQ,GAAG,EAAM,WAAA,CAAY,EAAO,SAAS,CAAC,CAAC,CAAM;EACvD,EACF;EAAG;CAAe,EACpB,GAAG,SAAU,GAAQ;EACnB,IAAI,KAAS,GAAG,EAAM,UAAA,CAAW,CAAC,CAAC,CAAC,CAAM;EAC1C,OAAO,EAAM,OAAO,MAAQ,EAAM,OAAO;CAC3C,CAAC,GAEG,KAAiB,GAAG,EAAE,YAAA,CAAa,EACrC,SAAS,CAAC,EACR,QAAQ,GAAG,EAAM,UAAA,CAAW,CAAC,EAC/B,GAAG,CAAe,EACpB,GAAG,SAAU,GAAQ;EACnB,IAAI,KAAS,GAAG,EAAM,UAAA,CAAW,CAAC,CAAC,CAAC,CAAM;EAC1C,OAAO,EAAM,OAAO,MAAQ,EAAM,OAAO;CAC3C,CAAC;CAoDD,EAAQ,UAAa;EAnDP,EACZ,QAAQ,CAAC,EACP,YAAY,GAAG,EAAM,WAAA,CAAY,CAAC,EACpC,GAAG,EACD,UAAU,GAAG,EAAM,WAAA,CAAY,CAAC,EAClC,CAAC,EACH;EAAG,EACD,KAAK;GAAC,EACJ,QAAQ,GAAG,EAAM,aAAA,CAAc,EAAI,EACrC;GAAG,EACD,SAAS,GAAG,EAAM,aAAA,CAAc,EAAI,EACtC;GAAG,EACD,MAAM,GAAG,EAAM,SAAA,CAAU;IACvB,QAAQ,EACN,OAAO,EACT;IACA,YAAY;KACV,OAAO;KACP,QAAQ;IACV;IACA,MAAM,EACJ,OAAO,EACT;IACA,MAAM;KACJ,OAAO;KACP,QAAQ;IACV;GACF,CAAC,EACH;GAAG,EACD,uBAAuB,GAAG,EAAM,SAAA,CAAU,EAC5C;GAAG,EACD,mBAAmB,GAAG,EAAM,SAAA,CAAU,EACxC;EAAC,EACH;GAAI,GAAG,EAAE,YAAA,CAAa,EACpB,MAAM,GAAG,EAAM,UAAA,CAAW,GAAG,SAAU,GAAQ,GAAQ;GACrD,OAAgB,MAAG,EAAO,IAAI,IAAI,OAAO;EAC3C,CAAC,EACH,GAAG,SAAU,GAAQ,GAAQ;GAC3B,OAAO,EAAO,IAAI,IAAI;EACxB,CAAC;EACD,EACE,SAAS,GAAG,EAAE,KAAA,CAAM;GAAC;GAAW;GAAmB;GAAe;GAAa;EAAU,GAAG,SAAU,GAAQ;GAC5G,IAAI,KAAY,GAAG,EAAM,SAAA,CAAU,CAAC,CAAC,CAAM;GAK3C,OAAO,MAAa,MAAQ,MAAa;EAC3C,CAAC,EACH;CAEqB;;CCxLrB,AAhCA,OAAO,eAAe,GAAS,cAAc,EAC3C,OAAO,GACT,CAAC,GACD,EAAQ,cAAc,KAAK,GA6B3B,EAAQ,cAAc,SAxBiB,GAAQ,GAAO;EAcpD,KAAK,IAbD,IAAgB,MAAM,EAAO,MAAM,GACnC,IAAO,EAAO,SAAS,GAEvB,IAAQ,SAAe,GAAO,GAAS;GACzC,IAAI,IAAa,EAAO,MAAM,IAAU,IAAQ,IAAU,KAAK,CAAK;GACpE,EAAU,OAAO,MAAM,GAAW,CAAC,IAAQ,GAAO,CAAK,CAAC,CAAC,OAAO,CAAU,CAAC;EAC7E,GAGI,IAAU;GAAC;GAAG;GAAG;GAAG;EAAC,GACrB,IAAQ;GAAC;GAAG;GAAG;GAAG;EAAC,GACnB,IAAU,GAEL,IAAO,GAAG,IAAO,GAAG,KAC3B,KAAK,IAAI,IAAQ,EAAQ,IAAO,IAAQ,GAAM,KAAS,EAAM,IAE3D,AADA,EAAM,GAAO,CAAO,GACpB;EAIJ,OAAO;CACT;;CCqFA,AAnHA,OAAO,eAAe,GAAS,cAAc,EAC3C,OAAO,GACT,CAAC,GACD,EAAQ,MAAM,KAAK,GAgHnB,EAAQ,MAAM,SA1GS,GAAa,GAAM,GAAY;EACpD,IAAI,IAAiB,MACjB,IAAW,IACX,IAAO,GACP,GAAW,GAAO,GAAW,GAAW,GAAoB,GAAS,GAAU,GAAM,GAAM,GAAG,GAAO,GAAW,GAAO,GAAK,GAAI,GAChI,IAAgB,MAAM,CAAU,GAChC,IAAa,MAAM,CAAc,GACjC,IAAa,MAAM,CAAc,GACjC,IAAiB,MAAM,IAAiB,CAAC;EAU7C,KARA,IAAY,GACZ,IAAQ,KAAK,GACb,IAAqB,IAAQ,GAC7B,IAAY,IAAQ,GACpB,IAAW,GACX,IAAY,IAAY,GACxB,KAAa,KAAK,KAAa,GAE1B,IAAO,GAAG,IAAO,GAAO,KAE3B,AADA,EAAO,KAAQ,GACf,EAAO,KAAQ;EAIjB,IAAI,IACI,IAAe,IAAQ,IAAM,IAAK,IAAK,GADpC,GAAa,GAAO,GAAK,GAAI;EAGxC,KAAK,IAAI,GAAG,IAAI,IAAO;GACrB,IAAI,MAAQ,GAAG;IACb,IAAI,IAAO,GAAW;KAIpB,AAFA,KAAS,EAAK,MAAO,GACrB,KAAQ,GACR;KACA;IACF;IAOA,IAJA,IAAO,IAAQ,GACf,MAAU,GACV,KAAQ,GAEJ,IAAO,KAAa,KAAQ,GAC9B;IAGF,IAAI,KAAQ,GAAO;KAKjB,AAHA,IAAY,IAAY,GACxB,KAAa,KAAK,KAAa,GAC/B,IAAY,IAAQ,GACpB,IAAW;KACX;IACF;IAEA,IAAI,KAAY,GAAU;KAGxB,AAFA,EAAW,OAAS,EAAO,IAC3B,IAAW,GACX,IAAQ;KACR;IACF;IASA,KAPA,IAAU,GAEN,KAAQ,MACV,EAAW,OAAS,GACpB,IAAO,IAGF,IAAO,IAEZ,AADA,EAAW,OAAS,EAAO,IAC3B,IAAO,EAAO;IAmBhB,AAhBA,IAAQ,EAAO,KAAQ,KACvB,EAAW,OAAS,GAIhB,IAAY,MACd,EAAO,KAAa,GACpB,EAAO,KAAa,GACpB,MAEK,IAAY,OAAe,KAAK,IAAY,MAC/C,KACA,KAAa,KAIjB,IAAW;GACb;GAKA,AAFA,KACA,EAAU,OAAQ,EAAW,IAC7B;EACF;EAEA,KAAK,IAAI,GAAI,IAAI,GAAM,KACrB,EAAU,KAAK;EAGjB,OAAO;CACT;;CC9GA,AAHA,OAAO,eAAe,GAAS,cAAc,EAC3C,OAAO,GACT,CAAC,GACD,EAAQ,mBAAmB,EAAQ,kBAAkB,EAAQ,WAAW,KAAK;CAE7E,IAAI,IAAO,EAAA,EAAA,CAAyE,GAEhF,IAAA,EAAA,GAEA,IAAA,EAAA,GAEA,IAAA,EAAA,GAEA,IAAA,EAAA;CAEJ,SAAS,EAAuB,GAAK;EAAE,OAAO,KAAO,EAAI,aAAa,IAAM,EAAE,SAAW,EAAI;CAAG;CAOhG,EAAQ,WAAW,SALc,GAAa;EAC5C,IAAI,IAAW,IAAI,WAAW,CAAW;EACzC,QAAQ,GAAG,EAAsB,MAAA,EAAQ,GAAG,EAAM,YAAA,CAAa,CAAQ,GAAG,EAAK,OAAU;CAC3F;CAIA,IAAI,IAAgB,SAAuB,GAAO;EAIhD,KAAK,IAHD,IAAc,EAAM,OAAO,QAC3B,IAAY,IAAI,kBAAkB,IAAc,CAAC,GAE5C,IAAI,GAAG,IAAI,GAAa,KAAK;GACpC,IAAI,IAAM,IAAI,GACV,IAAa,EAAM,OAAO,IAC1B,IAAQ,EAAM,WAAW,MAAe;IAAC;IAAG;IAAG;GAAC;GAIpD,AAHA,EAAU,KAAO,EAAM,IACvB,EAAU,IAAM,KAAK,EAAM,IAC3B,EAAU,IAAM,KAAK,EAAM,IAC3B,EAAU,IAAM,KAAK,MAAe,EAAM,mBAAyB,IAAN;EAC/D;EAEA,OAAO;CACT,GAEI,IAAkB,SAAyB,GAAO,GAAK,GAAiB;EAC1E,IAAI,CAAC,EAAM,OAAO;GAChB,QAAQ,KAAK,2CAA2C;GACxD;EACF;EAEA,IAAI,IAAQ,EAAM,OAEd,IAAc,EAAM,WAAW,QAAQ,EAAM,WAAW,QAExD,KAAU,GAAG,EAAK,IAAA,CAAK,EAAM,KAAK,aAAa,EAAM,KAAK,QAAQ,CAAW;EAEjF,AAAI,EAAM,WAAW,IAAI,eACvB,KAAU,GAAG,EAAa,YAAA,CAAa,GAAQ,EAAM,WAAW,KAAK;EAGvE,IAAI,IAAc;GACR;GACR,MAAM;IACJ,KAAK,EAAM,MAAM,WAAW;IAC5B,MAAM,EAAM,MAAM,WAAW;IAC7B,OAAO,EAAM,MAAM,WAAW;IAC9B,QAAQ,EAAM,MAAM,WAAW;GACjC;EACF;EAwBA,OAtBI,EAAM,WAAW,OAAO,EAAM,WAAW,IAAI,SAC/C,EAAY,aAAa,EAAM,MAE/B,EAAY,aAAa,GAIvB,EAAM,QACR,EAAY,SAAS,EAAM,IAAI,SAAS,MAAM,IAE9C,EAAY,eAAe,EAAM,IAAI,OAAO,UAExC,EAAM,IAAI,OAAO,0BACnB,EAAY,mBAAmB,EAAM,IAAI,yBAKzC,MACF,EAAY,QAAQ,EAAc,CAAW,IAGxC;CACT;CAYA,AAVA,EAAQ,kBAAkB,GAU1B,EAAQ,mBAAmB,SARsB,GAAW,GAAmB;EAC7E,OAAO,EAAU,OAAO,OAAO,SAAU,GAAG;GAC1C,OAAO,EAAE;EACX,CAAC,CAAC,CAAC,IAAI,SAAU,GAAG;GAClB,OAAO,EAAgB,GAAG,EAAU,KAAK,CAAiB;EAC5D,CAAC;CACH;;ACvEA,SAAgB,EACf,GACA,GACA,GACA,IAAW,WACE;CACb,IAAM,IAAM,IAAI,aAAa,IAAK,IAAK,CAAC,GAClC,IAAQ,IAAI,aAAa,IAAK,CAAE,GAChC,EAAE,SAAM,UAAO,cAAW,GAG5B,IAAQ,GACR,IAAQ,GACR,IAAO,GACP,IAAO;CACX,IAAI,MAAQ,aAAa,IAAQ,KAAK,IAAS,GAAG;EACjD,IAAM,IACL,MAAQ,UAAU,KAAK,IAAI,IAAK,GAAO,IAAK,CAAM,IAAI,KAAK,IAAI,IAAK,GAAO,IAAK,CAAM;EAIvF,AAHA,IAAQ,IAAQ,GAChB,IAAQ,IAAS,GACjB,KAAQ,IAAK,KAAS,GACtB,KAAQ,IAAK,KAAS;CACvB;CAEA,KAAK,IAAI,IAAK,GAAG,IAAK,GAAI,KAAM;EAE/B,IAAM,KAAM,IAAK,KAAM,KAAQ;EAC/B,KAAK,IAAI,IAAK,GAAG,IAAK,GAAI,KAAM;GAC/B,IAAM,IAAI,IAAK,IAAK,GACd,KAAM,IAAK,KAAM,KAAQ;GAC/B,IAAI,IAAK,KAAK,KAAM,KAAK,IAAK,KAAK,KAAM,GAAG;GAC5C,IAAM,IAAK,KAAK,IAAI,IAAQ,GAAG,KAAK,IAAI,GAAI,IAAK,IAAS,CAAC,CAAC,GAEtD,KADK,KAAK,IAAI,IAAS,GAAG,KAAK,IAAI,IAAK,IAAI,KAAM,IAAU,CAAC,CACvD,IAAK,IAAQ,KAAM,GACzB,IAAI,EAAK,IAAK,KAAK;GACrB,KAAK,MACT,EAAI,IAAI,KAAM,EAAK,KAAM,MAAO,GAChC,EAAI,IAAI,IAAI,KAAM,EAAK,IAAK,KAAK,MAAO,GACxC,EAAI,IAAI,IAAI,KAAM,EAAK,IAAK,KAAK,MAAO,GACxC,EAAM,KAAK;EACZ;CACD;CACA,OAAO;EAAE;EAAI;EAAI;EAAK;CAAM;AAC7B;;;AC7CA,SAAS,EAAU,GAAwC,GAAoB;CAC9E,QAAQ,GAAR;EACC,KAAK,MACJ,OAAO;GAAE,MAAM,EAAE;GAAI,MAAM,EAAE;GAAI,MAAM,EAAE;GAAI,KAAK,GAAG,GAAG,MAAM;IAAC;IAAG;IAAG;GAAC;EAAE;EACzE,KAAK,MACJ,OAAO;GAAE,MAAM,EAAE;GAAI,MAAM,EAAE;GAAI,MAAM,EAAE;GAAI,KAAK,GAAG,GAAG,MAAM;IAAC;IAAG;IAAG;GAAC;EAAE;EACzE,SACC,OAAO;GAAE,MAAM,EAAE;GAAI,MAAM,EAAE;GAAI,MAAM,EAAE;GAAI,KAAK,GAAG,GAAG,MAAM;IAAC;IAAG;IAAG;GAAC;EAAE;CAC1E;AACD;AAGA,SAAgB,EAAW,GAAc,GAAkB,IAAqB,CAAC,GAAS;CACzF,IAAM,IAAQ,EAAK,SAAS,MACtB,IAAY,EAAK,aAAa,IAC9B,IAAO,EAAK,QAAQ,GACpB,EAAE,SAAM,SAAM,SAAM,UAAO,EAAU,GAAG,CAAK,GAC7C,IAAQ,EAAK,SAAS,KAAQ,GAC9B,EAAE,QAAK,aAAU,EAAkB,GAAK,GAAM,GAAM,EAAK,OAAO,SAAS;CAC/E,KAAK,IAAI,IAAI,GAAG,IAAI,GAAM,KACzB,KAAK,IAAI,IAAI,GAAG,IAAI,GAAM,KAAK;EAC9B,IAAM,IAAI,IAAI,IAAO;EACrB,IAAI,EAAM,KAAK,GAAW;EAC1B,IAAM,CAAC,GAAG,GAAG,KAAK,EAAG,GAAG,GAAG,CAAK;EAChC,EAAE,KAAK,GAAG,GAAG,GAAG;GAAC,EAAI,IAAI,KAAK;GAAM,EAAI,IAAI,IAAI,KAAK;GAAM,EAAI,IAAI,IAAI,KAAK;EAAI,CAAC;CAClF;AACF;;;ACvCA,eAAsB,EAAY,GAAmC;CACpE,IAAM,IAAM,MAAM,MAAM,CAAG;CAC3B,IAAI,CAAC,EAAI,IAAI,MAAU,MAAM,iCAAiC,EAAI,IAAI,EAAI,OAAO,EAAE;CACnF,IAAM,IAAS,MAAM,kBAAkB,MAAM,EAAI,KAAK,CAAC,GACjD,IAAS,SAAS,cAAc,QAAQ;CAE9C,AADA,EAAO,QAAQ,EAAO,OACtB,EAAO,SAAS,EAAO;CACvB,IAAM,IAAM,EAAO,WAAW,IAAI;CAClC,IAAI,CAAC,GAAK,MAAU,MAAM,gCAAgC;CAE1D,AADA,EAAI,UAAU,GAAQ,GAAG,CAAC,GAC1B,EAAO,QAAQ;CACf,IAAM,IAAM,EAAI,aAAa,GAAG,GAAG,EAAO,OAAO,EAAO,MAAM;CAC9D,OAAO;EAAE,MAAM,EAAI;EAAM,OAAO,EAAI;EAAO,QAAQ,EAAI;CAAO;AAC/D;AAOA,SAAgB,EAAgB,GAAa,IAAsB,CAAC,GAAW;CAC9E,IAAI,IAA0B;CAC9B,EAAY,CAAG,CAAC,CAAC,MACf,MAAO,IAAM,IACb,MAAM,QAAQ,KAAK,CAAC,CACtB;CACA,IAAM,IAAQ,EAAK,SAAS;CAC5B,QAAQ,MAAM;EAEb,AADI,KAAO,EAAE,MAAM,GACf,KAAK,EAAW,GAAG,GAAK,CAAI;CACjC;AACD;;;AC5BA,SAAgB,EAAiB,GAAiC;CACjE,IAAM,KAAA,GAAA,EAAA,SAAA,CAAe,CAAM,GACrB,KAAA,GAAA,EAAA,iBAAA,CAAuB,GAAK,EAAI,GAChC,IAAI,EAAI,IAAI,OACZ,IAAI,EAAI,IAAI,QACZ,IAAO,IAAI,kBAAkB,IAAI,IAAI,CAAC,GACtC,IAAkB,CAAC,GACrB,IACH;CAED,KAAK,IAAM,KAAM,GAAK;EAGrB,AAAI,MAAY,EAAQ,SAAS,KAAK,EAAQ,SAAS,MAAI,EAAY,GAAM,GAAG,GAAG,CAAO;EAE1F,IAAM,EAAE,SAAM,QAAK,UAAO,cAAW,EAAG,MAClC,IAAQ,EAAG;EACjB,KAAK,IAAI,IAAI,GAAG,IAAI,GAAQ,KAC3B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAO,KAAK;GAC/B,IAAM,KAAM,IAAI,IAAQ,KAAK;GAC7B,IAAI,EAAM,IAAK,OAAO,GAAG;GACzB,IAAM,IAAK,IAAO,GACZ,IAAK,IAAM;GACjB,IAAI,IAAK,KAAK,IAAK,KAAK,KAAM,KAAK,KAAM,GAAG;GAC5C,IAAM,KAAM,IAAK,IAAI,KAAM;GAI3B,AAHA,EAAK,KAAM,EAAM,IACjB,EAAK,IAAK,KAAK,EAAM,IAAK,IAC1B,EAAK,IAAK,KAAK,EAAM,IAAK,IAC1B,EAAK,IAAK,KAAK;EAChB;EAED,AADA,EAAI,KAAK;GAAE,KAAK;IAAE,MAAM,EAAK,MAAM;IAAG,OAAO;IAAG,QAAQ;GAAE;GAAG,OAAO,EAAG,SAAS;EAAI,CAAC,GACrF,IAAU;GAAE;GAAM;GAAK;GAAO;GAAQ,MAAM,EAAG;EAAa;CAC7D;CACA,OAAO;AACR;AAEA,SAAS,EACR,GACA,GACA,GACA,GACC;CACD,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,EAAE,UAAU,IAAI,GAAG,KAClD,KAAK,IAAI,IAAI,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,SAAS,IAAI,GAAG,KAAK;EACxD,IAAM,KAAK,IAAI,IAAI,KAAK;EACxB,EAAI,KAAK,EAAI,IAAI,KAAK,EAAI,IAAI,KAAK,EAAI,IAAI,KAAK;CACjD;AACF;AAGA,eAAsB,EAAU,GAAkC;CACjE,IAAM,IAAM,MAAM,MAAM,CAAG;CAC3B,IAAI,CAAC,EAAI,IAAI,MAAU,MAAM,+BAA+B,EAAI,IAAI,EAAI,OAAO,EAAE;CACjF,OAAO,EAAiB,MAAM,EAAI,YAAY,CAAC;AAChD;AAGA,SAAgB,EAAQ,GAAoB,GAA2B;CACtE,IAAM,IAAQ,EAAO,QAAQ,GAAG,MAAM,IAAI,EAAE,OAAO,CAAC;CACpD,IAAI,KAAS,GAAG,OAAO;CACvB,IAAI,IAAI,IAAY;CACpB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAO,QAAQ,KAAK;EACvC,IAAI,IAAI,EAAO,EAAE,CAAC,OAAO,OAAO;EAChC,KAAK,EAAO,EAAE,CAAC;CAChB;CACA,OAAO,EAAO,SAAS;AACxB;AAOA,SAAgB,EAAc,GAAa,IAAsB,CAAC,GAAW;CAC5E,IAAI,IAA4B,MAC5B,IAAU;CACd,EAAU,CAAG,CAAC,CAAC,MACb,MAAO,IAAS,IAChB,MAAM,QAAQ,KAAK,CAAC,CACtB;CACA,IAAM,IAAQ,EAAK,SAAS,IACtB,IAAsB;CAC5B,QAAQ,GAAe,MAAe;EACrC,AAAI,KAAO,EAAE,MAAM,GACf,GAAC,KAAU,EAAO,WAAW,OACjC,KAAW,IAAK,KAChB,EAAW,GAAG,EAAO,EAAQ,GAAQ,CAAO,EAAE,CAAC,KAAK,CAAK;CAC1D;AACD;;;ACzFA,SAAS,EAAc,GAAa,GAAY,GAAY,GAAgC;CAC3F,IAAM,IAAS,SAAS,cAAc,QAAQ;CAE9C,AADA,EAAO,QAAQ,GACf,EAAO,SAAS;CAChB,IAAM,IAAM,EAAO,WAAW,IAAI;CAClC,IAAI,CAAC,GAAK,MAAU,MAAM,gCAAgC;CAQ1D,AAPA,EAAI,YAAY,QAChB,EAAI,SAAS,GAAG,GAAG,GAAI,CAAE,GAEzB,EAAI,OAAO,QADA,KAAK,IAAI,GAAG,KAAK,MAAM,KAAY,IAAK,EAAG,CACnC,EAAG,gBACtB,EAAI,YAAY,QAChB,EAAI,YAAY,UAChB,EAAI,eAAe,UACnB,EAAI,SAAS,GAAK,IAAK,GAAG,IAAK,CAAC;CAChC,IAAM,IAAM,EAAI,aAAa,GAAG,GAAG,GAAI,CAAE;CACzC,OAAO;EAAE,MAAM,EAAI;EAAM,OAAO,EAAI;EAAO,QAAQ,EAAI;CAAO;AAC/D;AAMA,IAAM,oBAAe,IAAI,IAA0B,GAC7C,IAAmB;AACzB,SAAS,EAAY,GAAa,GAAc,GAAc,GAAiC;CAC9F,IAAM,IAAM,GAAG,EAAI,GAAG,EAAK,GAAG,EAAK,GAAG,KAAY,MAC5C,IAAM,EAAa,IAAI,CAAG;CAChC,IAAI,GAGH,OAFA,EAAa,OAAO,CAAG,GACvB,EAAa,IAAI,GAAK,CAAG,GAClB;CAGR,IAAM,EAAE,aAAU,EADH,EAAc,GAAK,GAAM,GAAM,CACV,GAAQ,GAAM,GAAM,SAAS;CAEjE,IADA,EAAa,IAAI,GAAK,CAAK,GACvB,EAAa,OAAO,GAAkB;EACzC,IAAM,IAAS,EAAa,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;EAC1C,AAAI,MAAW,KAAA,KAAW,EAAa,OAAO,CAAM;CACrD;CACA,OAAO;AACR;AAGA,SAAgB,EAAK,GAAc,GAAa,IAAoB,CAAC,GAAS;CAC7E,IAAM,IAAQ,EAAK,SAAS,MACtB,IAAY,EAAK,aAAa,IAC9B,IAAQ,EAAW,EAAK,SAAS;EAAC;EAAG;EAAG;CAAC,CAAC,GAE1C,IAAO,MAAU,OAAO,EAAE,KAAK,EAAE,IACjC,IAAO,MAAU,OAAO,EAAE,KAAK,EAAE,IACjC,IAAO,MAAU,OAAO,EAAE,KAAK,MAAU,OAAO,EAAE,KAAK,EAAE,IACzD,IAAQ,EAAK,SAAS,KAAQ,GAC9B,IAAQ,EAAY,GAAK,GAAM,GAAM,EAAK,QAAQ;CACxD,KAAK,IAAI,IAAI,GAAG,IAAI,GAAM,KACzB,KAAK,IAAI,IAAI,GAAG,IAAI,GAAM,KAAK;EAC9B,IAAI,EAAM,IAAI,IAAO,KAAK,GAAW;EACrC,IAAM,IACL,MAAU,OAAO;GAAC;GAAG;GAAO;EAAC,IAAI,MAAU,OAAO;GAAC;GAAO;GAAG;EAAC,IAAI;GAAC;GAAG;GAAG;EAAK;EAC/E,EAAE,KAAK,EAAG,IAAI,EAAG,IAAI,EAAG,IAAI,CAAK;CAClC;AACF"}
|
package/dist/plane.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { VoxelGrid } from '@glowbox/led-grid';
|
|
2
|
+
import { Fit, ImageSource } from './sample';
|
|
3
|
+
/** Which grid plane the image lands on. `xy` (default) faces the default camera. */
|
|
4
|
+
export type Plane = 'xy' | 'xz' | 'yz';
|
|
5
|
+
export interface PaintOptions {
|
|
6
|
+
/** Grid plane the image maps onto (default `'xy'`). */
|
|
7
|
+
plane?: Plane;
|
|
8
|
+
/** Index on the plane's normal axis (default: the middle slice). */
|
|
9
|
+
depth?: number;
|
|
10
|
+
/** Aspect fit (default `'contain'`). */
|
|
11
|
+
fit?: Fit;
|
|
12
|
+
/** Skip cells with coverage below this (default 0.5). */
|
|
13
|
+
threshold?: number;
|
|
14
|
+
/** Multiply every painted colour (default 1; >1 blooms in the hologram style). */
|
|
15
|
+
gain?: number;
|
|
16
|
+
}
|
|
17
|
+
/** Sample `src` to the plane's dims and plot it onto `g` (does not clear). */
|
|
18
|
+
export declare function paintImage(g: VoxelGrid, src: ImageSource, opts?: PaintOptions): void;
|
package/dist/sample.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** How the image maps into the grid rectangle when aspect ratios differ. */
|
|
2
|
+
export type Fit = 'contain' | 'cover' | 'stretch';
|
|
3
|
+
/** A decoded image: RGBA bytes (row-major, y-down — like `ImageData`). */
|
|
4
|
+
export interface ImageSource {
|
|
5
|
+
data: Uint8ClampedArray;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
/** Per-cell colour (0..1 RGB) + coverage (0..1 alpha) for an nx×ny grid plane. */
|
|
10
|
+
export interface GridSample {
|
|
11
|
+
nx: number;
|
|
12
|
+
ny: number;
|
|
13
|
+
/** nx*ny*3, row-major, y-UP (grid row 0 is the bottom). */
|
|
14
|
+
rgb: Float32Array;
|
|
15
|
+
/** nx*ny, y-UP. 0 = outside the image (letterbox) or a transparent source pixel. */
|
|
16
|
+
alpha: Float32Array;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sample `src` onto an `nx`×`ny` grid. Grid rows run bottom-up (row 0 = bottom),
|
|
20
|
+
* so the image lands upright on a `y`-up display. `fit`:
|
|
21
|
+
* - `contain` (default) — whole image inside the grid, letterboxed (alpha 0 outside).
|
|
22
|
+
* - `cover` — fill the grid, cropping the overflow.
|
|
23
|
+
* - `stretch` — ignore aspect, map edge-to-edge.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sampleImageToGrid(src: ImageSource, nx: number, ny: number, fit?: Fit): GridSample;
|
package/dist/text.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Color, VoxelGrid } from '@glowbox/led-grid';
|
|
2
|
+
import { PaintOptions } from './plane';
|
|
3
|
+
export interface TextOptions extends Omit<PaintOptions, 'gain'> {
|
|
4
|
+
/** Lit colour (default white). */
|
|
5
|
+
color?: Color;
|
|
6
|
+
/** Font size in grid cells (default: ~80% of the plane's V dimension). */
|
|
7
|
+
fontSize?: number;
|
|
8
|
+
}
|
|
9
|
+
/** Draw `str` onto the grid plane (default the xy face, mid-depth), in `color`. */
|
|
10
|
+
export declare function text(g: VoxelGrid, str: string, opts?: TextOptions): void;
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@glowbox/extras",
|
|
3
|
+
"version": "1.0.0-rc.2",
|
|
4
|
+
"description": "Content helpers for @glowbox/led-grid — GIF/image animation player + text, painted onto the LED grid.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"glowbox",
|
|
7
|
+
"led",
|
|
8
|
+
"gif",
|
|
9
|
+
"image",
|
|
10
|
+
"voxel",
|
|
11
|
+
"3d",
|
|
12
|
+
"animation"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"module": "./dist/index.js",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/eetu/glowbox.git",
|
|
34
|
+
"directory": "packages/extras"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://eetu.github.io/glowbox/",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "vite build",
|
|
39
|
+
"prepack": "vite build",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"lint": "eslint .",
|
|
42
|
+
"lint:fix": "eslint . --fix",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"size": "size-limit"
|
|
45
|
+
},
|
|
46
|
+
"size-limit": [
|
|
47
|
+
{
|
|
48
|
+
"name": "esm (incl. bundled gifuct-js; core external)",
|
|
49
|
+
"path": "dist/index.js",
|
|
50
|
+
"ignore": [
|
|
51
|
+
"@glowbox/led-grid"
|
|
52
|
+
],
|
|
53
|
+
"limit": "6 kB"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@glowbox/led-grid": "^1.0.0-rc.2"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@anarkisti/eslint-config": "^1",
|
|
61
|
+
"@vitest/browser": "^4.1",
|
|
62
|
+
"@vitest/browser-playwright": "^4.1",
|
|
63
|
+
"eslint": "^10",
|
|
64
|
+
"gifuct-js": "^2",
|
|
65
|
+
"playwright": "^1.61",
|
|
66
|
+
"typescript": "^6",
|
|
67
|
+
"vite": "^8.1",
|
|
68
|
+
"vite-plugin-dts": "^5",
|
|
69
|
+
"vitest": "^4.1"
|
|
70
|
+
}
|
|
71
|
+
}
|