@teletext/react 0.1.1
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/README.md +175 -0
- package/dist/codec-CwTlBC1h.d.cts +45 -0
- package/dist/codec-CwTlBC1h.d.ts +45 -0
- package/dist/codec.cjs +141 -0
- package/dist/codec.cjs.map +1 -0
- package/dist/codec.d.cts +1 -0
- package/dist/codec.d.ts +1 -0
- package/dist/codec.js +138 -0
- package/dist/codec.js.map +1 -0
- package/dist/index.cjs +428 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +79 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +417 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
+
var __spreadValues = (a, b) => {
|
|
12
|
+
for (var prop in b || (b = {}))
|
|
13
|
+
if (__hasOwnProp.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
if (__getOwnPropSymbols)
|
|
16
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
+
if (__propIsEnum.call(b, prop))
|
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
|
19
|
+
}
|
|
20
|
+
return a;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/codec.ts
|
|
24
|
+
var MAGIC = new Uint8Array([84, 76, 84, 88]);
|
|
25
|
+
var FORMAT_VERSION = 2;
|
|
26
|
+
async function compress(bytes) {
|
|
27
|
+
const stream = new Blob([bytes]).stream().pipeThrough(new CompressionStream("gzip"));
|
|
28
|
+
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
29
|
+
}
|
|
30
|
+
async function decompress(bytes) {
|
|
31
|
+
const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip"));
|
|
32
|
+
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
33
|
+
}
|
|
34
|
+
async function encode(data) {
|
|
35
|
+
const { frames, fps, settings } = data;
|
|
36
|
+
if (frames.length === 0) throw new Error("No frames to encode");
|
|
37
|
+
const rows = frames[0].length;
|
|
38
|
+
const cols = rows > 0 ? frames[0][0].length : 0;
|
|
39
|
+
const charSet = /* @__PURE__ */ new Set();
|
|
40
|
+
charSet.add(" ");
|
|
41
|
+
for (const frame of frames) {
|
|
42
|
+
for (const row of frame) {
|
|
43
|
+
for (const cell of row) {
|
|
44
|
+
charSet.add(cell.char);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const chars = Array.from(charSet);
|
|
49
|
+
if (chars.length > 255) throw new Error("Too many unique characters (max 255)");
|
|
50
|
+
const charToIndex = /* @__PURE__ */ new Map();
|
|
51
|
+
for (let i = 0; i < chars.length; i++) {
|
|
52
|
+
charToIndex.set(chars[i], i);
|
|
53
|
+
}
|
|
54
|
+
const spaceIndex = charToIndex.get(" ");
|
|
55
|
+
const encoder = new TextEncoder();
|
|
56
|
+
const charEntries = chars.map((c) => encoder.encode(c));
|
|
57
|
+
const settingsBytes = encoder.encode(JSON.stringify(settings));
|
|
58
|
+
let headerSize = 13;
|
|
59
|
+
for (const entry of charEntries) {
|
|
60
|
+
headerSize += 1 + entry.length;
|
|
61
|
+
}
|
|
62
|
+
headerSize += 2 + settingsBytes.length;
|
|
63
|
+
const buf = new Uint8Array(headerSize + frames.length * rows * cols * 4);
|
|
64
|
+
const view = new DataView(buf.buffer);
|
|
65
|
+
let off = 0;
|
|
66
|
+
buf.set(MAGIC, off);
|
|
67
|
+
off += 4;
|
|
68
|
+
buf[off++] = FORMAT_VERSION;
|
|
69
|
+
view.setUint16(off, rows);
|
|
70
|
+
off += 2;
|
|
71
|
+
view.setUint16(off, cols);
|
|
72
|
+
off += 2;
|
|
73
|
+
buf[off++] = fps;
|
|
74
|
+
view.setUint16(off, frames.length);
|
|
75
|
+
off += 2;
|
|
76
|
+
buf[off++] = chars.length;
|
|
77
|
+
for (const entry of charEntries) {
|
|
78
|
+
buf[off++] = entry.length;
|
|
79
|
+
buf.set(entry, off);
|
|
80
|
+
off += entry.length;
|
|
81
|
+
}
|
|
82
|
+
view.setUint16(off, settingsBytes.length);
|
|
83
|
+
off += 2;
|
|
84
|
+
buf.set(settingsBytes, off);
|
|
85
|
+
off += settingsBytes.length;
|
|
86
|
+
for (const frame of frames) {
|
|
87
|
+
for (const row of frame) {
|
|
88
|
+
for (const cell of row) {
|
|
89
|
+
const idx = charToIndex.get(cell.char);
|
|
90
|
+
buf[off++] = idx;
|
|
91
|
+
if (idx !== spaceIndex) {
|
|
92
|
+
buf[off++] = cell.r;
|
|
93
|
+
buf[off++] = cell.g;
|
|
94
|
+
buf[off++] = cell.b;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return compress(buf.subarray(0, off));
|
|
100
|
+
}
|
|
101
|
+
async function decode(bytes) {
|
|
102
|
+
const buf = await decompress(bytes);
|
|
103
|
+
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
104
|
+
let off = 0;
|
|
105
|
+
if (buf[0] !== 84 || buf[1] !== 76 || buf[2] !== 84 || buf[3] !== 88) {
|
|
106
|
+
throw new Error("Invalid .teletext file: bad magic bytes");
|
|
107
|
+
}
|
|
108
|
+
off += 4;
|
|
109
|
+
const version = buf[off++];
|
|
110
|
+
if (version !== FORMAT_VERSION) {
|
|
111
|
+
throw new Error(`Unsupported .teletext version: ${version}`);
|
|
112
|
+
}
|
|
113
|
+
const rows = view.getUint16(off);
|
|
114
|
+
off += 2;
|
|
115
|
+
const cols = view.getUint16(off);
|
|
116
|
+
off += 2;
|
|
117
|
+
const fps = buf[off++];
|
|
118
|
+
const frameCount = view.getUint16(off);
|
|
119
|
+
off += 2;
|
|
120
|
+
const charCount = buf[off++];
|
|
121
|
+
const chars = [];
|
|
122
|
+
const td = new TextDecoder();
|
|
123
|
+
for (let i = 0; i < charCount; i++) {
|
|
124
|
+
const len = buf[off++];
|
|
125
|
+
chars.push(td.decode(buf.subarray(off, off + len)));
|
|
126
|
+
off += len;
|
|
127
|
+
}
|
|
128
|
+
const spaceIndex = chars.indexOf(" ");
|
|
129
|
+
const settingsLen = view.getUint16(off);
|
|
130
|
+
off += 2;
|
|
131
|
+
const settings = JSON.parse(td.decode(buf.subarray(off, off + settingsLen)));
|
|
132
|
+
off += settingsLen;
|
|
133
|
+
const frames = [];
|
|
134
|
+
for (let f = 0; f < frameCount; f++) {
|
|
135
|
+
const frame = [];
|
|
136
|
+
for (let r = 0; r < rows; r++) {
|
|
137
|
+
const row = [];
|
|
138
|
+
for (let c = 0; c < cols; c++) {
|
|
139
|
+
const ci = buf[off++];
|
|
140
|
+
if (ci === spaceIndex) {
|
|
141
|
+
row.push({ char: " ", r: 0, g: 0, b: 0 });
|
|
142
|
+
} else {
|
|
143
|
+
row.push({
|
|
144
|
+
char: chars[ci],
|
|
145
|
+
r: buf[off++],
|
|
146
|
+
g: buf[off++],
|
|
147
|
+
b: buf[off++]
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
frame.push(row);
|
|
152
|
+
}
|
|
153
|
+
frames.push(frame);
|
|
154
|
+
}
|
|
155
|
+
return { version: 1, settings, fps, frames };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/loader.ts
|
|
159
|
+
async function loadTeletext(source) {
|
|
160
|
+
if (source instanceof Uint8Array) {
|
|
161
|
+
return decode(source);
|
|
162
|
+
}
|
|
163
|
+
const res = await fetch(source);
|
|
164
|
+
if (!res.ok) throw new Error(`Failed to fetch ${source}: ${res.status}`);
|
|
165
|
+
return decode(new Uint8Array(await res.arrayBuffer()));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/player.ts
|
|
169
|
+
function getFrameIndex(elapsedMs, frameCount, fps, loop) {
|
|
170
|
+
if (frameCount <= 0) return 0;
|
|
171
|
+
const frameDurationMs = 1e3 / fps;
|
|
172
|
+
const totalDurationMs = frameCount * frameDurationMs;
|
|
173
|
+
if (loop) {
|
|
174
|
+
const loopedTime = elapsedMs % totalDurationMs;
|
|
175
|
+
return Math.min(Math.floor(loopedTime / frameDurationMs), frameCount - 1);
|
|
176
|
+
}
|
|
177
|
+
const index = Math.floor(elapsedMs / frameDurationMs);
|
|
178
|
+
return Math.min(index, frameCount - 1);
|
|
179
|
+
}
|
|
180
|
+
function isAnimationComplete(elapsedMs, frameCount, fps) {
|
|
181
|
+
if (frameCount <= 1) return true;
|
|
182
|
+
const totalDurationMs = frameCount * (1e3 / fps);
|
|
183
|
+
return elapsedMs >= totalDurationMs;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/renderers/canvas.ts
|
|
187
|
+
function renderFrame(ctx, frame, opts) {
|
|
188
|
+
var _a;
|
|
189
|
+
if (frame.length === 0) return;
|
|
190
|
+
const rows = frame.length;
|
|
191
|
+
const cols = frame[0].length;
|
|
192
|
+
const fontSize = opts.cellSize;
|
|
193
|
+
const charWidth = fontSize;
|
|
194
|
+
const charHeight = fontSize;
|
|
195
|
+
const font = (_a = opts.fontFamily) != null ? _a : "monospace";
|
|
196
|
+
const bg = opts.backgroundColor;
|
|
197
|
+
const canvas = ctx.canvas;
|
|
198
|
+
const targetWidth = cols * charWidth;
|
|
199
|
+
const targetHeight = rows * charHeight;
|
|
200
|
+
if (canvas.width !== targetWidth || canvas.height !== targetHeight) {
|
|
201
|
+
canvas.width = targetWidth;
|
|
202
|
+
canvas.height = targetHeight;
|
|
203
|
+
}
|
|
204
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
205
|
+
if (bg) {
|
|
206
|
+
ctx.fillStyle = bg;
|
|
207
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
208
|
+
}
|
|
209
|
+
ctx.font = `${fontSize}px ${font}`;
|
|
210
|
+
ctx.textBaseline = "top";
|
|
211
|
+
for (let y = 0; y < rows; y++) {
|
|
212
|
+
for (let x = 0; x < cols; x++) {
|
|
213
|
+
const cell = frame[y][x];
|
|
214
|
+
if (cell.char === " ") continue;
|
|
215
|
+
ctx.fillStyle = `rgb(${cell.r},${cell.g},${cell.b})`;
|
|
216
|
+
ctx.fillText(cell.char, x * charWidth, y * charHeight);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
var TeletextCanvas = react.forwardRef(function TeletextCanvas2({ frame, cellSize = 10, fontFamily, backgroundColor, className, style }, ref) {
|
|
221
|
+
const canvasRef = react.useRef(null);
|
|
222
|
+
react.useImperativeHandle(ref, () => ({
|
|
223
|
+
getCanvas: () => canvasRef.current
|
|
224
|
+
}));
|
|
225
|
+
react.useEffect(() => {
|
|
226
|
+
const canvas = canvasRef.current;
|
|
227
|
+
if (!canvas || frame.length === 0) return;
|
|
228
|
+
const ctx = canvas.getContext("2d");
|
|
229
|
+
if (!ctx) return;
|
|
230
|
+
renderFrame(ctx, frame, { cellSize, fontFamily, backgroundColor });
|
|
231
|
+
}, [frame, cellSize, fontFamily, backgroundColor]);
|
|
232
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
233
|
+
"canvas",
|
|
234
|
+
{
|
|
235
|
+
ref: canvasRef,
|
|
236
|
+
className,
|
|
237
|
+
style: __spreadValues({
|
|
238
|
+
display: "block",
|
|
239
|
+
width: "100%",
|
|
240
|
+
height: "100%",
|
|
241
|
+
objectFit: "contain",
|
|
242
|
+
background: backgroundColor,
|
|
243
|
+
imageRendering: "pixelated"
|
|
244
|
+
}, style)
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
function renderFrame2(frame, options) {
|
|
249
|
+
var _a;
|
|
250
|
+
if (frame.length === 0) return null;
|
|
251
|
+
const rows = frame.map((row, y) => {
|
|
252
|
+
const spans = row.map(
|
|
253
|
+
(cell, x) => react.createElement(
|
|
254
|
+
"span",
|
|
255
|
+
{
|
|
256
|
+
key: x,
|
|
257
|
+
style: { color: `rgb(${cell.r},${cell.g},${cell.b})` }
|
|
258
|
+
},
|
|
259
|
+
cell.char
|
|
260
|
+
)
|
|
261
|
+
);
|
|
262
|
+
return react.createElement("span", { key: y }, ...spans, "\n");
|
|
263
|
+
});
|
|
264
|
+
return react.createElement(
|
|
265
|
+
"pre",
|
|
266
|
+
{
|
|
267
|
+
style: {
|
|
268
|
+
margin: 0,
|
|
269
|
+
lineHeight: 1,
|
|
270
|
+
fontFamily: (_a = options == null ? void 0 : options.fontFamily) != null ? _a : "monospace",
|
|
271
|
+
whiteSpace: "pre"
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
...rows
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
function TeletextDom({
|
|
278
|
+
frame,
|
|
279
|
+
fontFamily,
|
|
280
|
+
backgroundColor,
|
|
281
|
+
className,
|
|
282
|
+
style
|
|
283
|
+
}) {
|
|
284
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
285
|
+
"div",
|
|
286
|
+
{
|
|
287
|
+
className,
|
|
288
|
+
style: __spreadValues({
|
|
289
|
+
background: backgroundColor,
|
|
290
|
+
fontFamily: fontFamily != null ? fontFamily : "monospace",
|
|
291
|
+
overflow: "hidden"
|
|
292
|
+
}, style),
|
|
293
|
+
children: renderFrame2(frame, { fontFamily: fontFamily != null ? fontFamily : "monospace" })
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
function TeletextEmbed({
|
|
298
|
+
data,
|
|
299
|
+
renderer = "canvas",
|
|
300
|
+
autoPlay = false,
|
|
301
|
+
loop = false,
|
|
302
|
+
paused = false,
|
|
303
|
+
fps: fpsOverride,
|
|
304
|
+
fontFamily,
|
|
305
|
+
backgroundColor,
|
|
306
|
+
className,
|
|
307
|
+
style,
|
|
308
|
+
"aria-label": ariaLabel,
|
|
309
|
+
onError
|
|
310
|
+
}) {
|
|
311
|
+
var _a, _b, _c, _d;
|
|
312
|
+
const [resolved, setResolved] = react.useState(null);
|
|
313
|
+
react.useEffect(() => {
|
|
314
|
+
let cancelled = false;
|
|
315
|
+
setResolved(null);
|
|
316
|
+
setFrameIndex(0);
|
|
317
|
+
startTimeRef.current = null;
|
|
318
|
+
cancelAnimationFrame(rafRef.current);
|
|
319
|
+
loadTeletext(data).then((d) => {
|
|
320
|
+
if (!cancelled) setResolved(d);
|
|
321
|
+
}).catch((err) => {
|
|
322
|
+
if (!cancelled) onError == null ? void 0 : onError(err instanceof Error ? err : new Error(String(err)));
|
|
323
|
+
});
|
|
324
|
+
return () => {
|
|
325
|
+
cancelled = true;
|
|
326
|
+
};
|
|
327
|
+
}, [data]);
|
|
328
|
+
const fps = (_a = fpsOverride != null ? fpsOverride : resolved == null ? void 0 : resolved.fps) != null ? _a : 1;
|
|
329
|
+
const frames = (_b = resolved == null ? void 0 : resolved.frames) != null ? _b : [];
|
|
330
|
+
const cellSize = (_c = resolved == null ? void 0 : resolved.settings.cellSize) != null ? _c : 0;
|
|
331
|
+
const isMultiFrame = frames.length > 1;
|
|
332
|
+
const [frameIndex, setFrameIndex] = react.useState(0);
|
|
333
|
+
const startTimeRef = react.useRef(null);
|
|
334
|
+
const rafRef = react.useRef(0);
|
|
335
|
+
const containerRef = react.useRef(null);
|
|
336
|
+
const isVisibleRef = react.useRef(true);
|
|
337
|
+
const currentFrame = (_d = frames[frameIndex]) != null ? _d : frames[0];
|
|
338
|
+
const shouldAnimate = isMultiFrame && !paused && (autoPlay || startTimeRef.current !== null);
|
|
339
|
+
const tick = react.useCallback(() => {
|
|
340
|
+
if (startTimeRef.current === null) return;
|
|
341
|
+
const elapsed = performance.now() - startTimeRef.current;
|
|
342
|
+
const newIndex = getFrameIndex(elapsed, frames.length, fps, loop);
|
|
343
|
+
setFrameIndex(newIndex);
|
|
344
|
+
if (!loop && isAnimationComplete(elapsed, frames.length, fps)) {
|
|
345
|
+
startTimeRef.current = null;
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
rafRef.current = requestAnimationFrame(tick);
|
|
349
|
+
}, [frames.length, fps, loop]);
|
|
350
|
+
react.useEffect(() => {
|
|
351
|
+
if (!isMultiFrame) return;
|
|
352
|
+
if (shouldAnimate && isVisibleRef.current) {
|
|
353
|
+
if (startTimeRef.current === null) {
|
|
354
|
+
startTimeRef.current = performance.now();
|
|
355
|
+
}
|
|
356
|
+
rafRef.current = requestAnimationFrame(tick);
|
|
357
|
+
} else {
|
|
358
|
+
cancelAnimationFrame(rafRef.current);
|
|
359
|
+
}
|
|
360
|
+
return () => cancelAnimationFrame(rafRef.current);
|
|
361
|
+
}, [shouldAnimate, isMultiFrame, tick]);
|
|
362
|
+
react.useEffect(() => {
|
|
363
|
+
if (paused) {
|
|
364
|
+
cancelAnimationFrame(rafRef.current);
|
|
365
|
+
} else if (shouldAnimate && isVisibleRef.current) {
|
|
366
|
+
startTimeRef.current = performance.now() - frameIndex * (1e3 / fps);
|
|
367
|
+
rafRef.current = requestAnimationFrame(tick);
|
|
368
|
+
}
|
|
369
|
+
}, [paused]);
|
|
370
|
+
react.useEffect(() => {
|
|
371
|
+
const el = containerRef.current;
|
|
372
|
+
if (!el) return;
|
|
373
|
+
const observer = new IntersectionObserver(
|
|
374
|
+
([entry]) => {
|
|
375
|
+
isVisibleRef.current = entry.isIntersecting;
|
|
376
|
+
if (!entry.isIntersecting) {
|
|
377
|
+
cancelAnimationFrame(rafRef.current);
|
|
378
|
+
} else if (shouldAnimate && !paused) {
|
|
379
|
+
startTimeRef.current = performance.now() - frameIndex * (1e3 / fps);
|
|
380
|
+
rafRef.current = requestAnimationFrame(tick);
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
{ threshold: 0 }
|
|
384
|
+
);
|
|
385
|
+
observer.observe(el);
|
|
386
|
+
return () => observer.disconnect();
|
|
387
|
+
}, [shouldAnimate, paused]);
|
|
388
|
+
if (!currentFrame) return null;
|
|
389
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
390
|
+
"div",
|
|
391
|
+
{
|
|
392
|
+
ref: containerRef,
|
|
393
|
+
className,
|
|
394
|
+
style,
|
|
395
|
+
role: "img",
|
|
396
|
+
"aria-label": ariaLabel != null ? ariaLabel : "ASCII art animation",
|
|
397
|
+
children: renderer === "canvas" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
398
|
+
TeletextCanvas,
|
|
399
|
+
{
|
|
400
|
+
frame: currentFrame,
|
|
401
|
+
cellSize,
|
|
402
|
+
fontFamily,
|
|
403
|
+
backgroundColor
|
|
404
|
+
}
|
|
405
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
406
|
+
TeletextDom,
|
|
407
|
+
{
|
|
408
|
+
frame: currentFrame,
|
|
409
|
+
fontFamily,
|
|
410
|
+
backgroundColor
|
|
411
|
+
}
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
exports.TeletextCanvas = TeletextCanvas;
|
|
418
|
+
exports.TeletextDom = TeletextDom;
|
|
419
|
+
exports.TeletextEmbed = TeletextEmbed;
|
|
420
|
+
exports.decode = decode;
|
|
421
|
+
exports.encode = encode;
|
|
422
|
+
exports.getFrameIndex = getFrameIndex;
|
|
423
|
+
exports.isAnimationComplete = isAnimationComplete;
|
|
424
|
+
exports.loadTeletext = loadTeletext;
|
|
425
|
+
exports.renderCanvasFrame = renderFrame;
|
|
426
|
+
exports.renderDomFrame = renderFrame2;
|
|
427
|
+
//# sourceMappingURL=index.cjs.map
|
|
428
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/codec.ts","../src/loader.ts","../src/player.ts","../src/renderers/canvas.ts","../src/components/TeletextCanvas.tsx","../src/renderers/dom.ts","../src/components/TeletextDom.tsx","../src/components/TeletextEmbed.tsx"],"names":["forwardRef","TeletextCanvas","useRef","useImperativeHandle","useEffect","jsx","renderFrame","createElement","useState","useCallback"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { TeletextData, TeletextCell, TeletextFrame } from \"./types\";\n\nconst MAGIC = new Uint8Array([0x54, 0x4c, 0x54, 0x58]); // \"TLTX\"\nconst FORMAT_VERSION = 0x02;\n\nasync function compress(bytes: Uint8Array): Promise<Uint8Array> {\n const stream = new Blob([bytes as BlobPart])\n .stream()\n .pipeThrough(new CompressionStream(\"gzip\"));\n return new Uint8Array(await new Response(stream).arrayBuffer());\n}\n\nasync function decompress(bytes: Uint8Array): Promise<Uint8Array> {\n const stream = new Blob([bytes as BlobPart])\n .stream()\n .pipeThrough(new DecompressionStream(\"gzip\"));\n return new Uint8Array(await new Response(stream).arrayBuffer());\n}\n\n/**\n * Encode TeletextData into the compressed .teletext binary format.\n *\n * Binary layout (before gzip):\n * HEADER\n * [0..3] Magic \"TLTX\"\n * [4] Version 0x02\n * [5..6] Rows (uint16 BE)\n * [7..8] Cols (uint16 BE)\n * [9] FPS (uint8)\n * [10..11] Frame count (uint16 BE)\n * [12] Char count N (uint8)\n * [13..] Char table: N × [len:uint8, ...utf8]\n * [..] Settings JSON length (uint16 BE) + UTF-8 bytes\n * PAYLOAD (per frame, per row, per col)\n * char_index: uint8\n * if char_index != space_index: r g b (uint8 each)\n */\nexport async function encode(data: TeletextData): Promise<Uint8Array> {\n const { frames, fps, settings } = data;\n if (frames.length === 0) throw new Error(\"No frames to encode\");\n\n const rows = frames[0].length;\n const cols = rows > 0 ? frames[0][0].length : 0;\n\n // Build char table from all frames\n const charSet = new Set<string>();\n charSet.add(\" \");\n for (const frame of frames) {\n for (const row of frame) {\n for (const cell of row) {\n charSet.add(cell.char);\n }\n }\n }\n\n const chars = Array.from(charSet);\n if (chars.length > 255) throw new Error(\"Too many unique characters (max 255)\");\n\n const charToIndex = new Map<string, number>();\n for (let i = 0; i < chars.length; i++) {\n charToIndex.set(chars[i], i);\n }\n const spaceIndex = charToIndex.get(\" \")!;\n\n const encoder = new TextEncoder();\n const charEntries = chars.map((c) => encoder.encode(c));\n const settingsBytes = encoder.encode(JSON.stringify(settings));\n\n // Calculate header size\n let headerSize = 13; // magic(4)+ver(1)+rows(2)+cols(2)+fps(1)+frames(2)+charCount(1)\n for (const entry of charEntries) {\n headerSize += 1 + entry.length;\n }\n headerSize += 2 + settingsBytes.length;\n\n // Allocate max possible size (4 bytes per cell)\n const buf = new Uint8Array(headerSize + frames.length * rows * cols * 4);\n const view = new DataView(buf.buffer);\n let off = 0;\n\n // Header\n buf.set(MAGIC, off);\n off += 4;\n buf[off++] = FORMAT_VERSION;\n view.setUint16(off, rows);\n off += 2;\n view.setUint16(off, cols);\n off += 2;\n buf[off++] = fps;\n view.setUint16(off, frames.length);\n off += 2;\n buf[off++] = chars.length;\n\n // Char table\n for (const entry of charEntries) {\n buf[off++] = entry.length;\n buf.set(entry, off);\n off += entry.length;\n }\n\n // Settings JSON\n view.setUint16(off, settingsBytes.length);\n off += 2;\n buf.set(settingsBytes, off);\n off += settingsBytes.length;\n\n // Payload\n for (const frame of frames) {\n for (const row of frame) {\n for (const cell of row) {\n const idx = charToIndex.get(cell.char)!;\n buf[off++] = idx;\n if (idx !== spaceIndex) {\n buf[off++] = cell.r;\n buf[off++] = cell.g;\n buf[off++] = cell.b;\n }\n }\n }\n }\n\n return compress(buf.subarray(0, off));\n}\n\n/**\n * Decode a compressed .teletext binary into TeletextData.\n */\nexport async function decode(bytes: Uint8Array): Promise<TeletextData> {\n const buf = await decompress(bytes);\n const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n let off = 0;\n\n // Magic\n if (\n buf[0] !== 0x54 ||\n buf[1] !== 0x4c ||\n buf[2] !== 0x54 ||\n buf[3] !== 0x58\n ) {\n throw new Error(\"Invalid .teletext file: bad magic bytes\");\n }\n off += 4;\n\n const version = buf[off++];\n if (version !== FORMAT_VERSION) {\n throw new Error(`Unsupported .teletext version: ${version}`);\n }\n\n const rows = view.getUint16(off);\n off += 2;\n const cols = view.getUint16(off);\n off += 2;\n const fps = buf[off++];\n const frameCount = view.getUint16(off);\n off += 2;\n\n // Char table\n const charCount = buf[off++];\n const chars: string[] = [];\n const td = new TextDecoder();\n for (let i = 0; i < charCount; i++) {\n const len = buf[off++];\n chars.push(td.decode(buf.subarray(off, off + len)));\n off += len;\n }\n const spaceIndex = chars.indexOf(\" \");\n\n // Settings\n const settingsLen = view.getUint16(off);\n off += 2;\n const settings = JSON.parse(td.decode(buf.subarray(off, off + settingsLen)));\n off += settingsLen;\n\n // Payload\n const frames: TeletextFrame[] = [];\n for (let f = 0; f < frameCount; f++) {\n const frame: TeletextCell[][] = [];\n for (let r = 0; r < rows; r++) {\n const row: TeletextCell[] = [];\n for (let c = 0; c < cols; c++) {\n const ci = buf[off++];\n if (ci === spaceIndex) {\n row.push({ char: \" \", r: 0, g: 0, b: 0 });\n } else {\n row.push({\n char: chars[ci],\n r: buf[off++],\n g: buf[off++],\n b: buf[off++],\n });\n }\n }\n frame.push(row);\n }\n frames.push(frame);\n }\n\n return { version: 1, settings, fps, frames };\n}\n","import type { TeletextData } from \"./types\";\nimport { decode } from \"./codec\";\n\n/**\n * Load a .teletext file from binary bytes or a URL string.\n */\nexport async function loadTeletext(\n source: Uint8Array | string,\n): Promise<TeletextData> {\n if (source instanceof Uint8Array) {\n return decode(source);\n }\n const res = await fetch(source);\n if (!res.ok) throw new Error(`Failed to fetch ${source}: ${res.status}`);\n return decode(new Uint8Array(await res.arrayBuffer()));\n}\n","/**\n * Frame sequencer: given frames[] and fps, returns the correct frame index\n * for a given elapsed time. Pure function, no React dependency.\n */\nexport function getFrameIndex(\n elapsedMs: number,\n frameCount: number,\n fps: number,\n loop: boolean,\n): number {\n if (frameCount <= 0) return 0;\n const frameDurationMs = 1000 / fps;\n const totalDurationMs = frameCount * frameDurationMs;\n\n if (loop) {\n const loopedTime = elapsedMs % totalDurationMs;\n return Math.min(Math.floor(loopedTime / frameDurationMs), frameCount - 1);\n }\n\n const index = Math.floor(elapsedMs / frameDurationMs);\n return Math.min(index, frameCount - 1);\n}\n\n/**\n * Returns whether the animation has finished (only relevant when loop=false).\n */\nexport function isAnimationComplete(\n elapsedMs: number,\n frameCount: number,\n fps: number,\n): boolean {\n if (frameCount <= 1) return true;\n const totalDurationMs = frameCount * (1000 / fps);\n return elapsedMs >= totalDurationMs;\n}\n","import type { TeletextFrame } from \"../types\";\n\nexport interface CanvasRenderOptions {\n cellSize: number;\n fontFamily?: string;\n backgroundColor?: string;\n}\n\n/**\n * Renders a TeletextFrame onto a canvas 2D context.\n * Extracted from the teletext web app's AsciiOutput renderGrid logic.\n */\nexport function renderFrame(\n ctx: CanvasRenderingContext2D,\n frame: TeletextFrame,\n opts: CanvasRenderOptions,\n): void {\n if (frame.length === 0) return;\n\n const rows = frame.length;\n const cols = frame[0].length;\n const fontSize = opts.cellSize;\n const charWidth = fontSize;\n const charHeight = fontSize;\n const font = opts.fontFamily ?? \"monospace\";\n const bg = opts.backgroundColor;\n\n const canvas = ctx.canvas;\n const targetWidth = cols * charWidth;\n const targetHeight = rows * charHeight;\n\n if (canvas.width !== targetWidth || canvas.height !== targetHeight) {\n canvas.width = targetWidth;\n canvas.height = targetHeight;\n }\n\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n if (bg) {\n ctx.fillStyle = bg;\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n }\n\n ctx.font = `${fontSize}px ${font}`;\n ctx.textBaseline = \"top\";\n\n for (let y = 0; y < rows; y++) {\n for (let x = 0; x < cols; x++) {\n const cell = frame[y][x];\n if (cell.char === \" \") continue;\n ctx.fillStyle = `rgb(${cell.r},${cell.g},${cell.b})`;\n ctx.fillText(cell.char, x * charWidth, y * charHeight);\n }\n }\n}\n","import {\n useRef,\n useEffect,\n forwardRef,\n useImperativeHandle,\n type CSSProperties,\n} from \"react\";\nimport type { TeletextFrame } from \"../types\";\nimport { renderFrame } from \"../renderers/canvas\";\n\nexport interface TeletextCanvasProps {\n frame: TeletextFrame;\n cellSize?: number;\n fontFamily?: string;\n backgroundColor?: string;\n className?: string;\n style?: CSSProperties;\n}\n\nexport interface TeletextCanvasHandle {\n getCanvas: () => HTMLCanvasElement | null;\n}\n\nexport const TeletextCanvas = forwardRef<\n TeletextCanvasHandle,\n TeletextCanvasProps\n>(function TeletextCanvas(\n { frame, cellSize = 10, fontFamily, backgroundColor, className, style },\n ref,\n) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n\n useImperativeHandle(ref, () => ({\n getCanvas: () => canvasRef.current,\n }));\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas || frame.length === 0) return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n renderFrame(ctx, frame, { cellSize, fontFamily, backgroundColor });\n }, [frame, cellSize, fontFamily, backgroundColor]);\n\n return (\n <canvas\n ref={canvasRef}\n className={className}\n style={{\n display: \"block\",\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n background: backgroundColor,\n imageRendering: \"pixelated\" as const,\n ...style,\n }}\n />\n );\n});\n","import { createElement, type ReactNode } from \"react\";\nimport type { TeletextFrame } from \"../types\";\n\n/**\n * Renders a TeletextFrame as React DOM elements (SSR-safe).\n * Returns a <pre> wrapping rows of colored <span> elements.\n */\nexport function renderFrame(\n frame: TeletextFrame,\n options?: { fontFamily?: string },\n): ReactNode {\n if (frame.length === 0) return null;\n\n const rows = frame.map((row, y) => {\n const spans = row.map((cell, x) =>\n createElement(\n \"span\",\n {\n key: x,\n style: { color: `rgb(${cell.r},${cell.g},${cell.b})` },\n },\n cell.char,\n ),\n );\n // Add newline between rows (except after last)\n return createElement(\"span\", { key: y }, ...spans, \"\\n\");\n });\n\n return createElement(\n \"pre\",\n {\n style: {\n margin: 0,\n lineHeight: 1,\n fontFamily: options?.fontFamily ?? \"monospace\",\n whiteSpace: \"pre\",\n },\n },\n ...rows,\n );\n}\n","import { type CSSProperties } from \"react\";\nimport type { TeletextFrame } from \"../types\";\nimport { renderFrame } from \"../renderers/dom\";\n\nexport interface TeletextDomProps {\n frame: TeletextFrame;\n fontFamily?: string;\n backgroundColor?: string;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function TeletextDom({\n frame,\n fontFamily,\n backgroundColor,\n className,\n style,\n}: TeletextDomProps) {\n return (\n <div\n className={className}\n style={{\n background: backgroundColor,\n fontFamily: fontFamily ?? \"monospace\",\n overflow: \"hidden\",\n ...style,\n }}\n >\n {renderFrame(frame, { fontFamily: fontFamily ?? \"monospace\" })}\n </div>\n );\n}\n","import {\n useRef,\n useEffect,\n useCallback,\n useState,\n type CSSProperties,\n} from \"react\";\nimport type { TeletextData, TeletextFrame } from \"../types\";\nimport { loadTeletext } from \"../loader\";\nimport { getFrameIndex, isAnimationComplete } from \"../player\";\nimport { TeletextCanvas } from \"./TeletextCanvas\";\nimport { TeletextDom } from \"./TeletextDom\";\n\nexport interface TeletextEmbedProps {\n data: Uint8Array | string;\n renderer?: \"canvas\" | \"dom\";\n autoPlay?: boolean;\n loop?: boolean;\n paused?: boolean;\n fps?: number;\n fontFamily?: string;\n backgroundColor?: string;\n className?: string;\n style?: CSSProperties;\n \"aria-label\"?: string;\n onError?: (error: Error) => void;\n}\n\nexport function TeletextEmbed({\n data,\n renderer = \"canvas\",\n autoPlay = false,\n loop = false,\n paused = false,\n fps: fpsOverride,\n fontFamily,\n backgroundColor,\n className,\n style,\n \"aria-label\": ariaLabel,\n onError,\n}: TeletextEmbedProps) {\n const [resolved, setResolved] = useState<TeletextData | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n setResolved(null);\n setFrameIndex(0);\n startTimeRef.current = null;\n cancelAnimationFrame(rafRef.current);\n loadTeletext(data)\n .then((d) => {\n if (!cancelled) setResolved(d);\n })\n .catch((err) => {\n if (!cancelled) onError?.(err instanceof Error ? err : new Error(String(err)));\n });\n return () => {\n cancelled = true;\n };\n }, [data]);\n\n const fps = fpsOverride ?? resolved?.fps ?? 1;\n const frames = resolved?.frames ?? [];\n const cellSize = resolved?.settings.cellSize ?? 0;\n const isMultiFrame = frames.length > 1;\n\n const [frameIndex, setFrameIndex] = useState(0);\n const startTimeRef = useRef<number | null>(null);\n const rafRef = useRef<number>(0);\n const containerRef = useRef<HTMLDivElement>(null);\n const isVisibleRef = useRef(true);\n\n const currentFrame: TeletextFrame | undefined =\n frames[frameIndex] ?? frames[0];\n\n const shouldAnimate =\n isMultiFrame && !paused && (autoPlay || startTimeRef.current !== null);\n\n const tick = useCallback(() => {\n if (startTimeRef.current === null) return;\n\n const elapsed = performance.now() - startTimeRef.current;\n const newIndex = getFrameIndex(elapsed, frames.length, fps, loop);\n setFrameIndex(newIndex);\n\n if (!loop && isAnimationComplete(elapsed, frames.length, fps)) {\n startTimeRef.current = null;\n return;\n }\n\n rafRef.current = requestAnimationFrame(tick);\n }, [frames.length, fps, loop]);\n\n // Start/stop animation\n useEffect(() => {\n if (!isMultiFrame) return;\n\n if (shouldAnimate && isVisibleRef.current) {\n if (startTimeRef.current === null) {\n startTimeRef.current = performance.now();\n }\n rafRef.current = requestAnimationFrame(tick);\n } else {\n cancelAnimationFrame(rafRef.current);\n }\n\n return () => cancelAnimationFrame(rafRef.current);\n }, [shouldAnimate, isMultiFrame, tick]);\n\n // Pause/resume via paused prop\n useEffect(() => {\n if (paused) {\n cancelAnimationFrame(rafRef.current);\n } else if (shouldAnimate && isVisibleRef.current) {\n // Adjust start time to account for pause\n startTimeRef.current =\n performance.now() - frameIndex * (1000 / fps);\n rafRef.current = requestAnimationFrame(tick);\n }\n }, [paused]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // IntersectionObserver — pause when off-screen\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n isVisibleRef.current = entry.isIntersecting;\n if (!entry.isIntersecting) {\n cancelAnimationFrame(rafRef.current);\n } else if (shouldAnimate && !paused) {\n startTimeRef.current =\n performance.now() - frameIndex * (1000 / fps);\n rafRef.current = requestAnimationFrame(tick);\n }\n },\n { threshold: 0 },\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [shouldAnimate, paused]); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (!currentFrame) return null;\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={style}\n role=\"img\"\n aria-label={ariaLabel ?? \"ASCII art animation\"}\n >\n {renderer === \"canvas\" ? (\n <TeletextCanvas\n frame={currentFrame}\n cellSize={cellSize}\n fontFamily={fontFamily}\n backgroundColor={backgroundColor}\n />\n ) : (\n <TeletextDom\n frame={currentFrame}\n fontFamily={fontFamily}\n backgroundColor={backgroundColor}\n />\n )}\n </div>\n );\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
4
|
+
import { T as TeletextFrame, a as TeletextData } from './codec-CwTlBC1h.cjs';
|
|
5
|
+
export { b as TeletextCell, d as decode, e as encode } from './codec-CwTlBC1h.cjs';
|
|
6
|
+
|
|
7
|
+
interface TeletextEmbedProps {
|
|
8
|
+
data: Uint8Array | string;
|
|
9
|
+
renderer?: "canvas" | "dom";
|
|
10
|
+
autoPlay?: boolean;
|
|
11
|
+
loop?: boolean;
|
|
12
|
+
paused?: boolean;
|
|
13
|
+
fps?: number;
|
|
14
|
+
fontFamily?: string;
|
|
15
|
+
backgroundColor?: string;
|
|
16
|
+
className?: string;
|
|
17
|
+
style?: CSSProperties;
|
|
18
|
+
"aria-label"?: string;
|
|
19
|
+
onError?: (error: Error) => void;
|
|
20
|
+
}
|
|
21
|
+
declare function TeletextEmbed({ data, renderer, autoPlay, loop, paused, fps: fpsOverride, fontFamily, backgroundColor, className, style, "aria-label": ariaLabel, onError, }: TeletextEmbedProps): react_jsx_runtime.JSX.Element | null;
|
|
22
|
+
|
|
23
|
+
interface TeletextCanvasProps {
|
|
24
|
+
frame: TeletextFrame;
|
|
25
|
+
cellSize?: number;
|
|
26
|
+
fontFamily?: string;
|
|
27
|
+
backgroundColor?: string;
|
|
28
|
+
className?: string;
|
|
29
|
+
style?: CSSProperties;
|
|
30
|
+
}
|
|
31
|
+
interface TeletextCanvasHandle {
|
|
32
|
+
getCanvas: () => HTMLCanvasElement | null;
|
|
33
|
+
}
|
|
34
|
+
declare const TeletextCanvas: react.ForwardRefExoticComponent<TeletextCanvasProps & react.RefAttributes<TeletextCanvasHandle>>;
|
|
35
|
+
|
|
36
|
+
interface TeletextDomProps {
|
|
37
|
+
frame: TeletextFrame;
|
|
38
|
+
fontFamily?: string;
|
|
39
|
+
backgroundColor?: string;
|
|
40
|
+
className?: string;
|
|
41
|
+
style?: CSSProperties;
|
|
42
|
+
}
|
|
43
|
+
declare function TeletextDom({ frame, fontFamily, backgroundColor, className, style, }: TeletextDomProps): react_jsx_runtime.JSX.Element;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load a .teletext file from binary bytes or a URL string.
|
|
47
|
+
*/
|
|
48
|
+
declare function loadTeletext(source: Uint8Array | string): Promise<TeletextData>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Frame sequencer: given frames[] and fps, returns the correct frame index
|
|
52
|
+
* for a given elapsed time. Pure function, no React dependency.
|
|
53
|
+
*/
|
|
54
|
+
declare function getFrameIndex(elapsedMs: number, frameCount: number, fps: number, loop: boolean): number;
|
|
55
|
+
/**
|
|
56
|
+
* Returns whether the animation has finished (only relevant when loop=false).
|
|
57
|
+
*/
|
|
58
|
+
declare function isAnimationComplete(elapsedMs: number, frameCount: number, fps: number): boolean;
|
|
59
|
+
|
|
60
|
+
interface CanvasRenderOptions {
|
|
61
|
+
cellSize: number;
|
|
62
|
+
fontFamily?: string;
|
|
63
|
+
backgroundColor?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Renders a TeletextFrame onto a canvas 2D context.
|
|
67
|
+
* Extracted from the teletext web app's AsciiOutput renderGrid logic.
|
|
68
|
+
*/
|
|
69
|
+
declare function renderFrame$1(ctx: CanvasRenderingContext2D, frame: TeletextFrame, opts: CanvasRenderOptions): void;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Renders a TeletextFrame as React DOM elements (SSR-safe).
|
|
73
|
+
* Returns a <pre> wrapping rows of colored <span> elements.
|
|
74
|
+
*/
|
|
75
|
+
declare function renderFrame(frame: TeletextFrame, options?: {
|
|
76
|
+
fontFamily?: string;
|
|
77
|
+
}): ReactNode;
|
|
78
|
+
|
|
79
|
+
export { type CanvasRenderOptions, TeletextCanvas, type TeletextCanvasHandle, type TeletextCanvasProps, TeletextData, TeletextDom, type TeletextDomProps, TeletextEmbed, type TeletextEmbedProps, TeletextFrame, getFrameIndex, isAnimationComplete, loadTeletext, renderFrame$1 as renderCanvasFrame, renderFrame as renderDomFrame };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
4
|
+
import { T as TeletextFrame, a as TeletextData } from './codec-CwTlBC1h.js';
|
|
5
|
+
export { b as TeletextCell, d as decode, e as encode } from './codec-CwTlBC1h.js';
|
|
6
|
+
|
|
7
|
+
interface TeletextEmbedProps {
|
|
8
|
+
data: Uint8Array | string;
|
|
9
|
+
renderer?: "canvas" | "dom";
|
|
10
|
+
autoPlay?: boolean;
|
|
11
|
+
loop?: boolean;
|
|
12
|
+
paused?: boolean;
|
|
13
|
+
fps?: number;
|
|
14
|
+
fontFamily?: string;
|
|
15
|
+
backgroundColor?: string;
|
|
16
|
+
className?: string;
|
|
17
|
+
style?: CSSProperties;
|
|
18
|
+
"aria-label"?: string;
|
|
19
|
+
onError?: (error: Error) => void;
|
|
20
|
+
}
|
|
21
|
+
declare function TeletextEmbed({ data, renderer, autoPlay, loop, paused, fps: fpsOverride, fontFamily, backgroundColor, className, style, "aria-label": ariaLabel, onError, }: TeletextEmbedProps): react_jsx_runtime.JSX.Element | null;
|
|
22
|
+
|
|
23
|
+
interface TeletextCanvasProps {
|
|
24
|
+
frame: TeletextFrame;
|
|
25
|
+
cellSize?: number;
|
|
26
|
+
fontFamily?: string;
|
|
27
|
+
backgroundColor?: string;
|
|
28
|
+
className?: string;
|
|
29
|
+
style?: CSSProperties;
|
|
30
|
+
}
|
|
31
|
+
interface TeletextCanvasHandle {
|
|
32
|
+
getCanvas: () => HTMLCanvasElement | null;
|
|
33
|
+
}
|
|
34
|
+
declare const TeletextCanvas: react.ForwardRefExoticComponent<TeletextCanvasProps & react.RefAttributes<TeletextCanvasHandle>>;
|
|
35
|
+
|
|
36
|
+
interface TeletextDomProps {
|
|
37
|
+
frame: TeletextFrame;
|
|
38
|
+
fontFamily?: string;
|
|
39
|
+
backgroundColor?: string;
|
|
40
|
+
className?: string;
|
|
41
|
+
style?: CSSProperties;
|
|
42
|
+
}
|
|
43
|
+
declare function TeletextDom({ frame, fontFamily, backgroundColor, className, style, }: TeletextDomProps): react_jsx_runtime.JSX.Element;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load a .teletext file from binary bytes or a URL string.
|
|
47
|
+
*/
|
|
48
|
+
declare function loadTeletext(source: Uint8Array | string): Promise<TeletextData>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Frame sequencer: given frames[] and fps, returns the correct frame index
|
|
52
|
+
* for a given elapsed time. Pure function, no React dependency.
|
|
53
|
+
*/
|
|
54
|
+
declare function getFrameIndex(elapsedMs: number, frameCount: number, fps: number, loop: boolean): number;
|
|
55
|
+
/**
|
|
56
|
+
* Returns whether the animation has finished (only relevant when loop=false).
|
|
57
|
+
*/
|
|
58
|
+
declare function isAnimationComplete(elapsedMs: number, frameCount: number, fps: number): boolean;
|
|
59
|
+
|
|
60
|
+
interface CanvasRenderOptions {
|
|
61
|
+
cellSize: number;
|
|
62
|
+
fontFamily?: string;
|
|
63
|
+
backgroundColor?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Renders a TeletextFrame onto a canvas 2D context.
|
|
67
|
+
* Extracted from the teletext web app's AsciiOutput renderGrid logic.
|
|
68
|
+
*/
|
|
69
|
+
declare function renderFrame$1(ctx: CanvasRenderingContext2D, frame: TeletextFrame, opts: CanvasRenderOptions): void;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Renders a TeletextFrame as React DOM elements (SSR-safe).
|
|
73
|
+
* Returns a <pre> wrapping rows of colored <span> elements.
|
|
74
|
+
*/
|
|
75
|
+
declare function renderFrame(frame: TeletextFrame, options?: {
|
|
76
|
+
fontFamily?: string;
|
|
77
|
+
}): ReactNode;
|
|
78
|
+
|
|
79
|
+
export { type CanvasRenderOptions, TeletextCanvas, type TeletextCanvasHandle, type TeletextCanvasProps, TeletextData, TeletextDom, type TeletextDomProps, TeletextEmbed, type TeletextEmbedProps, TeletextFrame, getFrameIndex, isAnimationComplete, loadTeletext, renderFrame$1 as renderCanvasFrame, renderFrame as renderDomFrame };
|