@buntui/extensions 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/framerate.d.ts +3 -0
  2. package/dist/framerate.js +2 -0
  3. package/dist/index.d.ts +6 -0
  4. package/dist/index.js +8 -0
  5. package/dist/logger.d.ts +3 -0
  6. package/dist/logger.js +2 -0
  7. package/dist/matrix.d.ts +3 -0
  8. package/dist/matrix.js +4 -0
  9. package/dist/registry.d.ts +6 -0
  10. package/dist/registry.js +7 -0
  11. package/dist/snake.d.ts +3 -0
  12. package/dist/snake.js +4 -0
  13. package/dist/utils/color.d.ts +10 -0
  14. package/dist/utils/color.js +35 -0
  15. package/dist/videoplayer.d.ts +3 -0
  16. package/dist/videoplayer.js +4 -0
  17. package/dist/widgets/framerate/FrameRateWatcher.d.ts +20 -0
  18. package/dist/widgets/framerate/FrameRateWatcher.js +87 -0
  19. package/dist/widgets/logger/LoggerWidget.d.ts +33 -0
  20. package/dist/widgets/logger/LoggerWidget.js +170 -0
  21. package/dist/widgets/matrix/MatrixWidget.d.ts +13 -0
  22. package/dist/widgets/matrix/MatrixWidget.js +139 -0
  23. package/dist/widgets/matrix/charset.d.ts +1 -0
  24. package/dist/widgets/matrix/charset.js +11 -0
  25. package/dist/widgets/matrix/defaults.d.ts +3 -0
  26. package/dist/widgets/matrix/defaults.js +17 -0
  27. package/dist/widgets/matrix/matrix-column.d.ts +24 -0
  28. package/dist/widgets/matrix/matrix-column.js +58 -0
  29. package/dist/widgets/matrix/types.d.ts +38 -0
  30. package/dist/widgets/matrix/types.js +0 -0
  31. package/dist/widgets/snake/SnakeWidget.d.ts +14 -0
  32. package/dist/widgets/snake/SnakeWidget.js +384 -0
  33. package/dist/widgets/snake/defaults.d.ts +3 -0
  34. package/dist/widgets/snake/defaults.js +19 -0
  35. package/dist/widgets/snake/types.d.ts +25 -0
  36. package/dist/widgets/snake/types.js +0 -0
  37. package/dist/widgets/videoplayer/VideoPlayerWidget.d.ts +16 -0
  38. package/dist/widgets/videoplayer/VideoPlayerWidget.js +465 -0
  39. package/dist/widgets/videoplayer/braille.d.ts +18 -0
  40. package/dist/widgets/videoplayer/braille.js +69 -0
  41. package/dist/widgets/videoplayer/defaults.d.ts +3 -0
  42. package/dist/widgets/videoplayer/defaults.js +17 -0
  43. package/dist/widgets/videoplayer/types.d.ts +21 -0
  44. package/dist/widgets/videoplayer/types.js +0 -0
  45. package/package.json +50 -0
@@ -0,0 +1,465 @@
1
+ import { widgets, } from '@buntui/core';
2
+ import { encodeBrailleFrame, isVideoFile } from './braille';
3
+ import { DEFAULT_VIDEOPLAYER_COLOR_SCHEME, DEFAULT_VIDEOPLAYER_OPTIONS } from './defaults';
4
+ export class VideoPlayerWidget extends widgets.InteractiveWidget {
5
+ #x;
6
+ #y;
7
+ #width;
8
+ #height;
9
+ #src;
10
+ #colorScheme;
11
+ #loop;
12
+ #threshold;
13
+ #invert;
14
+ #targetFps;
15
+ #audioSrc;
16
+ #audioTempFile;
17
+ #frameData;
18
+ #frameCols = 0;
19
+ #frameRows = 0;
20
+ #frameCount = 0;
21
+ #fps = 30;
22
+ #frameInterval = 33;
23
+ #playerState = 'loading';
24
+ #currentFrame = 0;
25
+ #accumulator = 0;
26
+ #loadingProgress = '';
27
+ #audioProcess;
28
+ #ffmpegProcess;
29
+ #loading = false;
30
+ get #frameOffsetMs() {
31
+ return (this.#currentFrame / this.#fps) * 1000;
32
+ }
33
+ constructor(options = {}) {
34
+ super();
35
+ const resolved = { ...DEFAULT_VIDEOPLAYER_OPTIONS, ...options };
36
+ const rect = this.initRect(resolved.x, resolved.y, resolved.width, resolved.height);
37
+ this.#x = rect.x;
38
+ this.#y = rect.y;
39
+ this.#width = rect.width;
40
+ this.#height = rect.height;
41
+ const schemeOverride = resolved.colorScheme ?? {};
42
+ this.#colorScheme = {
43
+ dotRgba: schemeOverride.dotRgba ?? DEFAULT_VIDEOPLAYER_COLOR_SCHEME.dotRgba,
44
+ bgRgba: schemeOverride.bgRgba ?? DEFAULT_VIDEOPLAYER_COLOR_SCHEME.bgRgba,
45
+ textRgba: schemeOverride.textRgba ?? DEFAULT_VIDEOPLAYER_COLOR_SCHEME.textRgba,
46
+ };
47
+ this.#src = resolved.src;
48
+ this.#loop = resolved.loop ?? false;
49
+ this.#threshold = resolved.threshold ?? 128;
50
+ this.#invert = resolved.invert ?? false;
51
+ this.#targetFps = resolved.fps ?? 30;
52
+ if (resolved.audioSrc) {
53
+ this.#audioSrc = resolved.audioSrc;
54
+ }
55
+ else if (resolved.src && isVideoFile(resolved.src)) {
56
+ this.#audioSrc = resolved.src;
57
+ }
58
+ else if (resolved.src) {
59
+ this.#audioSrc = resolved.src.replace(/\.bin$/v, '.mp3');
60
+ }
61
+ else {
62
+ this.#audioSrc = undefined;
63
+ }
64
+ if (resolved.data) {
65
+ this.#parseBinData(resolved.data);
66
+ }
67
+ else if (!resolved.src) {
68
+ this.#playerState = 'error';
69
+ }
70
+ }
71
+ mounted() {
72
+ super.mounted();
73
+ }
74
+ unmounted() {
75
+ this.#stopAudio();
76
+ this.#killFfmpeg();
77
+ this.#cleanupTempAudio();
78
+ super.unmounted();
79
+ }
80
+ updateRect(rect) {
81
+ if (rect.x !== undefined) {
82
+ this.#x = rect.x;
83
+ }
84
+ if (rect.y !== undefined) {
85
+ this.#y = rect.y;
86
+ }
87
+ if (rect.width !== undefined) {
88
+ this.#width = rect.width;
89
+ }
90
+ if (rect.height !== undefined) {
91
+ this.#height = rect.height;
92
+ }
93
+ }
94
+ containsPoint(x, y) {
95
+ return x >= this.#x
96
+ && x < this.#x + this.#width
97
+ && y >= this.#y
98
+ && y < this.#y + this.#height;
99
+ }
100
+ get rect() {
101
+ return {
102
+ x: this.#x, y: this.#y, width: this.#width, height: this.#height,
103
+ };
104
+ }
105
+ handleKey(event) {
106
+ const { key } = event;
107
+ if (!key) {
108
+ return;
109
+ }
110
+ if (key === ' ') {
111
+ switch (this.#playerState) {
112
+ case 'ready':
113
+ case 'ended': {
114
+ if (this.#playerState === 'ended') {
115
+ this.#currentFrame = 0;
116
+ }
117
+ this.#playerState = 'playing';
118
+ this.#accumulator = 0;
119
+ this.#startAudio(0);
120
+ break;
121
+ }
122
+ case 'playing': {
123
+ this.#playerState = 'paused';
124
+ this.#stopAudio();
125
+ break;
126
+ }
127
+ case 'paused': {
128
+ this.#startAudio(this.#frameOffsetMs);
129
+ this.#playerState = 'playing';
130
+ this.#accumulator = 0;
131
+ break;
132
+ }
133
+ case 'loading':
134
+ case 'error': {
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ else if (key === 'r' || key === 'R') {
140
+ this.#currentFrame = 0;
141
+ this.#stopAudio();
142
+ this.#playerState = 'ready';
143
+ }
144
+ }
145
+ update(dt) {
146
+ if (this.#playerState !== 'playing' || !this.#frameData) {
147
+ return;
148
+ }
149
+ this.#accumulator += dt;
150
+ if (this.#accumulator >= this.#frameInterval) {
151
+ this.#accumulator -= this.#frameInterval;
152
+ this.#currentFrame++;
153
+ if (this.#currentFrame >= this.#frameCount) {
154
+ if (this.#loop) {
155
+ this.#currentFrame = 0;
156
+ this.#startAudio(0);
157
+ }
158
+ else {
159
+ this.#currentFrame = this.#frameCount - 1;
160
+ this.#playerState = 'ended';
161
+ this.#stopAudio();
162
+ }
163
+ }
164
+ }
165
+ }
166
+ emitDrawCommands(buffer) {
167
+ const w = this.#width;
168
+ const h = this.#height;
169
+ if (w <= 0 || h <= 0) {
170
+ return;
171
+ }
172
+ const absX = this.#x;
173
+ const absY = this.#y;
174
+ const { bgRgba } = this.#colorScheme;
175
+ buffer.pushClip(absX, absY, w, h);
176
+ buffer.drawRect({
177
+ x: absX, y: absY, width: w, height: h, bgRgba,
178
+ });
179
+ if (this.#playerState === 'loading') {
180
+ if (!this.#loading && this.#src && !this.#frameData) {
181
+ this.#loading = true;
182
+ this.#load().catch(() => {
183
+ this.#playerState = 'error';
184
+ });
185
+ }
186
+ const message = this.#loadingProgress || (this.#src ? `Loading: ${this.#src}` : 'Loading...');
187
+ this.#drawOverlay(buffer, absX, absY, w, h, message);
188
+ buffer.popClip();
189
+ return;
190
+ }
191
+ if (this.#playerState === 'error') {
192
+ const message = this.#src ? `Error: ${this.#src}` : 'No src or data provided';
193
+ this.#drawOverlay(buffer, absX, absY, w, h, message);
194
+ buffer.popClip();
195
+ return;
196
+ }
197
+ // Render current frame — batch by row to stay within DrawListBuffer limits
198
+ if (this.#frameData && this.#frameCols > 0 && this.#frameRows > 0) {
199
+ const offsetX = Math.max(0, Math.floor((w - this.#frameCols) / 2));
200
+ const offsetY = Math.max(0, Math.floor((h - this.#frameRows) / 2));
201
+ const rowSize = this.#frameCols;
202
+ const frameStart = this.#currentFrame * (rowSize * this.#frameRows);
203
+ const { dotRgba } = this.#colorScheme;
204
+ for (let row = 0; row < this.#frameRows; row++) {
205
+ const renderY = absY + offsetY + row;
206
+ if (renderY < absY || renderY >= absY + h) {
207
+ continue;
208
+ }
209
+ let rowText = '';
210
+ for (let col = 0; col < this.#frameCols; col++) {
211
+ const index = frameStart + (row * rowSize) + col;
212
+ const brailleByte = this.#frameData[index];
213
+ rowText += brailleByte === 0 ? ' ' : String.fromCodePoint(0x28_00 + brailleByte);
214
+ }
215
+ buffer.drawText({
216
+ x: absX + offsetX,
217
+ y: renderY,
218
+ text: rowText,
219
+ fgRgba: dotRgba,
220
+ bgRgba,
221
+ });
222
+ }
223
+ }
224
+ // State overlay
225
+ switch (this.#playerState) {
226
+ case 'ready': {
227
+ const hint = this.#audioSrc ? 'SPACE to play (with audio)' : 'SPACE to play';
228
+ this.#drawOverlay(buffer, absX, absY, w, h, hint);
229
+ break;
230
+ }
231
+ case 'paused': {
232
+ this.#drawOverlay(buffer, absX, absY, w, h, 'PAUSED');
233
+ break;
234
+ }
235
+ case 'ended': {
236
+ this.#drawOverlay(buffer, absX, absY, w, h, 'END SPACE=replay R=reset');
237
+ break;
238
+ }
239
+ case 'playing': {
240
+ break;
241
+ }
242
+ }
243
+ // Progress bar at bottom
244
+ if (this.#frameCount > 0 && h > 2) {
245
+ const progress = (this.#currentFrame + 1) / this.#frameCount;
246
+ const barWidth = Math.max(1, Math.floor(w * progress));
247
+ buffer.drawRect({
248
+ x: absX, y: absY + h - 1, width: barWidth, height: 1, bgRgba: this.#colorScheme.textRgba,
249
+ });
250
+ }
251
+ buffer.popClip();
252
+ }
253
+ #drawOverlay(buffer, absX, absY, w, h, text) {
254
+ buffer.drawText({
255
+ x: absX + Math.max(0, Math.floor((w - text.length) / 2)),
256
+ y: absY + Math.floor(h / 2),
257
+ text,
258
+ fgRgba: this.#colorScheme.textRgba,
259
+ bgRgba: this.#colorScheme.bgRgba,
260
+ });
261
+ }
262
+ #parseBinData(raw) {
263
+ if (raw.length < 12 || raw[0] !== 0xBA || raw[1] !== 0xAD || raw[2] !== 0x01) {
264
+ this.#playerState = 'error';
265
+ return;
266
+ }
267
+ this.#fps = raw[3];
268
+ this.#frameCols = raw[4] | (raw[5] << 8);
269
+ this.#frameRows = raw[6] | (raw[7] << 8);
270
+ const frameArea = this.#frameCols * this.#frameRows;
271
+ this.#frameCount = raw[8] | (raw[9] << 8) | (raw[10] << 16) | (raw[11] << 24);
272
+ this.#frameInterval = Math.floor(1000 / this.#fps);
273
+ const expectedSize = 12 + (this.#frameCount * frameArea);
274
+ if (raw.length < expectedSize) {
275
+ this.#playerState = 'error';
276
+ return;
277
+ }
278
+ this.#frameData = raw.slice(12, expectedSize);
279
+ this.#playerState = 'ready';
280
+ }
281
+ async #load() {
282
+ const src = this.#src;
283
+ if (src.endsWith('.bin')) {
284
+ await this.#loadFromBinFile(src);
285
+ }
286
+ else if (isVideoFile(src)) {
287
+ await this.#loadFromVideo(src);
288
+ }
289
+ else {
290
+ this.#playerState = 'error';
291
+ }
292
+ }
293
+ async #loadFromBinFile(path) {
294
+ try {
295
+ const file = Bun.file(path);
296
+ const buffer = await file.arrayBuffer();
297
+ this.#parseBinData(new Uint8Array(buffer));
298
+ }
299
+ catch {
300
+ this.#playerState = 'error';
301
+ }
302
+ }
303
+ async #loadFromVideo(path) {
304
+ const cols = Math.max(1, this.#width);
305
+ const rows = Math.max(1, this.#height);
306
+ const pixelW = cols * 2;
307
+ const pixelH = rows * 4;
308
+ const rawFrameSize = pixelW * pixelH;
309
+ this.#fps = this.#targetFps;
310
+ this.#frameInterval = Math.floor(1000 / this.#fps);
311
+ this.#loadingProgress = 'Decoding 0 frames...';
312
+ try {
313
+ const proc = Bun.spawn([
314
+ 'ffmpeg',
315
+ '-i',
316
+ path,
317
+ '-vf',
318
+ `scale=${pixelW}:${pixelH}`,
319
+ '-pix_fmt',
320
+ 'gray',
321
+ '-r',
322
+ String(this.#fps),
323
+ '-f',
324
+ 'rawvideo',
325
+ '-v',
326
+ 'quiet',
327
+ 'pipe:1',
328
+ ], {
329
+ stdout: 'pipe',
330
+ stderr: 'ignore',
331
+ });
332
+ this.#ffmpegProcess = proc;
333
+ const chunks = [];
334
+ let frameCount = 0;
335
+ const reader = proc.stdout.getReader();
336
+ let leftover = new Uint8Array(0);
337
+ while (true) {
338
+ // eslint-disable-next-line no-await-in-loop
339
+ const { done, value } = await reader.read();
340
+ if (done) {
341
+ break;
342
+ }
343
+ const merged = new Uint8Array(leftover.length + value.length);
344
+ merged.set(leftover);
345
+ merged.set(value, leftover.length);
346
+ let offset = 0;
347
+ while (offset + rawFrameSize <= merged.length) {
348
+ const rawFrame = merged.slice(offset, offset + rawFrameSize);
349
+ chunks.push(encodeBrailleFrame(rawFrame, cols, rows, this.#threshold, this.#invert));
350
+ frameCount++;
351
+ offset += rawFrameSize;
352
+ if (frameCount % 100 === 0) {
353
+ this.#loadingProgress = `Decoding ${frameCount} frames...`;
354
+ }
355
+ }
356
+ leftover = merged.slice(offset);
357
+ }
358
+ this.#ffmpegProcess = undefined;
359
+ const exitCode = await proc.exited;
360
+ if (exitCode !== 0 && frameCount === 0) {
361
+ this.#playerState = 'error';
362
+ return;
363
+ }
364
+ // Assemble frame data
365
+ const frameArea = cols * rows;
366
+ this.#frameCols = cols;
367
+ this.#frameRows = rows;
368
+ this.#frameCount = frameCount;
369
+ this.#frameData = new Uint8Array(frameCount * frameArea);
370
+ let writeOffset = 0;
371
+ for (const chunk of chunks) {
372
+ this.#frameData.set(chunk, writeOffset);
373
+ writeOffset += chunk.length;
374
+ }
375
+ // Extract audio to temp WAV for byte-accurate seeking
376
+ try {
377
+ const temporaryAudio = `${path}.buntui-audio.wav`;
378
+ const audioProc = Bun.spawn([
379
+ 'ffmpeg',
380
+ '-i',
381
+ path,
382
+ '-vn',
383
+ '-f',
384
+ 'wav',
385
+ '-y',
386
+ temporaryAudio,
387
+ ], {
388
+ stdout: 'ignore',
389
+ stderr: 'ignore',
390
+ });
391
+ const audioExit = await audioProc.exited;
392
+ if (audioExit === 0) {
393
+ this.#audioSrc = temporaryAudio;
394
+ this.#audioTempFile = temporaryAudio;
395
+ }
396
+ }
397
+ catch {
398
+ // Audio extraction failed, keep original source
399
+ }
400
+ this.#loadingProgress = '';
401
+ this.#playerState = 'ready';
402
+ }
403
+ catch {
404
+ this.#playerState = 'error';
405
+ }
406
+ }
407
+ #killFfmpeg() {
408
+ if (this.#ffmpegProcess) {
409
+ try {
410
+ this.#ffmpegProcess.kill();
411
+ }
412
+ catch {
413
+ // Process may have already exited
414
+ }
415
+ this.#ffmpegProcess = undefined;
416
+ }
417
+ }
418
+ #cleanupTempAudio() {
419
+ if (this.#audioTempFile) {
420
+ try {
421
+ void Bun.file(this.#audioTempFile).unlink();
422
+ }
423
+ catch {
424
+ // File may already be gone
425
+ }
426
+ this.#audioTempFile = undefined;
427
+ }
428
+ }
429
+ #startAudio(offsetMs) {
430
+ if (!this.#audioSrc) {
431
+ return;
432
+ }
433
+ this.#stopAudio();
434
+ const args = ['ffplay', '-nodisp', '-autoexit', '-loglevel', 'quiet'];
435
+ if (offsetMs > 0) {
436
+ args.push('-ss', String(offsetMs / 1000));
437
+ }
438
+ args.push(this.#audioSrc);
439
+ try {
440
+ this.#audioProcess = Bun.spawn(args, {
441
+ stdin: 'ignore',
442
+ stdout: 'ignore',
443
+ stderr: 'ignore',
444
+ });
445
+ }
446
+ catch {
447
+ // Ffplay not available, skip audio
448
+ }
449
+ }
450
+ #stopAudio() {
451
+ if (this.#audioProcess) {
452
+ try {
453
+ this.#audioProcess.kill();
454
+ }
455
+ catch {
456
+ // Process may have already exited
457
+ }
458
+ this.#audioProcess = undefined;
459
+ }
460
+ }
461
+ }
462
+ export function createVideoPlayerWidget(options) {
463
+ return new VideoPlayerWidget(options);
464
+ }
465
+ export default VideoPlayerWidget;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Braille dot bit positions within a 2x4 cell grid.
3
+ * Each entry: [dx, dy, bitIndex]
4
+ * Maps pixel positions to the Unicode braille dot encoding.
5
+ */
6
+ export declare const BRAILLE_DOTS: ReadonlyArray<readonly [number, number, number]>;
7
+ export declare function isVideoFile(path: string): boolean;
8
+ /**
9
+ * Encode a raw grayscale frame into braille dot patterns.
10
+ * Each 2x4 pixel block becomes one braille cell byte.
11
+ *
12
+ * @param pixels - Raw grayscale pixel data (width * height bytes)
13
+ * @param cols - Number of braille columns (width / 2)
14
+ * @param rows - Number of braille rows (height / 4)
15
+ * @param threshold - Brightness threshold (0-255). Pixels darker -> dots
16
+ * @param invert - If true, bright pixels become dots instead
17
+ */
18
+ export declare function encodeBrailleFrame(pixels: Uint8Array, cols: number, rows: number, threshold: number, invert: boolean): Uint8Array;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Braille dot bit positions within a 2x4 cell grid.
3
+ * Each entry: [dx, dy, bitIndex]
4
+ * Maps pixel positions to the Unicode braille dot encoding.
5
+ */
6
+ export const BRAILLE_DOTS = [
7
+ [0, 0, 0],
8
+ [0, 1, 1],
9
+ [0, 2, 2],
10
+ [1, 0, 3],
11
+ [1, 1, 4],
12
+ [1, 2, 5],
13
+ [0, 3, 6],
14
+ [1, 3, 7],
15
+ ];
16
+ const VIDEO_EXTENSIONS = new Set([
17
+ '.mp4',
18
+ '.mkv',
19
+ '.avi',
20
+ '.webm',
21
+ '.mov',
22
+ '.flv',
23
+ '.wmv',
24
+ '.ts',
25
+ '.mts',
26
+ '.m4v',
27
+ '.ogv',
28
+ '.3gp',
29
+ ]);
30
+ export function isVideoFile(path) {
31
+ const dot = path.lastIndexOf('.');
32
+ if (dot === -1) {
33
+ return false;
34
+ }
35
+ return VIDEO_EXTENSIONS.has(path.slice(dot).toLowerCase());
36
+ }
37
+ /**
38
+ * Encode a raw grayscale frame into braille dot patterns.
39
+ * Each 2x4 pixel block becomes one braille cell byte.
40
+ *
41
+ * @param pixels - Raw grayscale pixel data (width * height bytes)
42
+ * @param cols - Number of braille columns (width / 2)
43
+ * @param rows - Number of braille rows (height / 4)
44
+ * @param threshold - Brightness threshold (0-255). Pixels darker -> dots
45
+ * @param invert - If true, bright pixels become dots instead
46
+ */
47
+ export function encodeBrailleFrame(pixels, cols, rows, threshold, invert) {
48
+ const pixelW = cols * 2;
49
+ const pixelH = rows * 4;
50
+ const result = new Uint8Array(cols * rows);
51
+ for (let row = 0; row < rows; row++) {
52
+ for (let col = 0; col < cols; col++) {
53
+ let byte = 0;
54
+ for (const [dx, dy, bit] of BRAILLE_DOTS) {
55
+ const px = (col * 2) + dx;
56
+ const py = (row * 4) + dy;
57
+ if (px < pixelW && py < pixelH) {
58
+ const brightness = pixels[(py * pixelW) + px];
59
+ const isOn = invert ? brightness >= threshold : brightness < threshold;
60
+ if (isOn) { // eslint-disable-line max-depth
61
+ byte |= (1 << bit);
62
+ }
63
+ }
64
+ }
65
+ result[(row * cols) + col] = byte;
66
+ }
67
+ }
68
+ return result;
69
+ }
@@ -0,0 +1,3 @@
1
+ import type { VideoPlayerColorScheme, VideoPlayerWidgetOptions } from './types';
2
+ export declare const DEFAULT_VIDEOPLAYER_COLOR_SCHEME: VideoPlayerColorScheme;
3
+ export declare const DEFAULT_VIDEOPLAYER_OPTIONS: VideoPlayerWidgetOptions;
@@ -0,0 +1,17 @@
1
+ import { rgbToRgba } from '@buntui/core';
2
+ export const DEFAULT_VIDEOPLAYER_COLOR_SCHEME = {
3
+ dotRgba: rgbToRgba(0xFF, 0xFF, 0xFF),
4
+ bgRgba: rgbToRgba(0x00, 0x00, 0x00),
5
+ textRgba: rgbToRgba(0x88, 0x88, 0x88),
6
+ };
7
+ export const DEFAULT_VIDEOPLAYER_OPTIONS = {
8
+ x: 0,
9
+ y: 0,
10
+ width: '100%',
11
+ height: '100%',
12
+ colorScheme: DEFAULT_VIDEOPLAYER_COLOR_SCHEME,
13
+ loop: false,
14
+ threshold: 128,
15
+ invert: false,
16
+ fps: 30,
17
+ };
@@ -0,0 +1,21 @@
1
+ import type { TuiSizeValue } from '@buntui/core';
2
+ export type VideoPlayerState = 'loading' | 'ready' | 'playing' | 'paused' | 'ended' | 'error';
3
+ export type VideoPlayerColorScheme = {
4
+ dotRgba: number;
5
+ bgRgba: number;
6
+ textRgba: number;
7
+ };
8
+ export type VideoPlayerWidgetOptions = {
9
+ x?: TuiSizeValue;
10
+ y?: TuiSizeValue;
11
+ width?: TuiSizeValue;
12
+ height?: TuiSizeValue;
13
+ src?: string;
14
+ audioSrc?: string;
15
+ data?: Uint8Array;
16
+ colorScheme?: Partial<VideoPlayerColorScheme>;
17
+ loop?: boolean;
18
+ threshold?: number;
19
+ invert?: boolean;
20
+ fps?: number;
21
+ };
File without changes
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@buntui/extensions",
3
+ "version": "0.0.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/AlphaFoxz/buntui.git",
7
+ "directory": "packages/extensions"
8
+ },
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./matrix": {
17
+ "types": "./dist/matrix.d.ts",
18
+ "default": "./dist/matrix.js"
19
+ },
20
+ "./framerate": {
21
+ "types": "./dist/framerate.d.ts",
22
+ "default": "./dist/framerate.js"
23
+ },
24
+ "./snake": {
25
+ "types": "./dist/snake.d.ts",
26
+ "default": "./dist/snake.js"
27
+ },
28
+ "./videoplayer": {
29
+ "types": "./dist/videoplayer.d.ts",
30
+ "default": "./dist/videoplayer.js"
31
+ },
32
+ "./logger": {
33
+ "types": "./dist/logger.d.ts",
34
+ "default": "./dist/logger.js"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist/"
39
+ ],
40
+ "sideEffects": false,
41
+ "scripts": {
42
+ "build": "tsc --project ./tsconfig.json"
43
+ },
44
+ "dependencies": {
45
+ "@buntui/core": "^0.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/bun": "^1.3.14"
49
+ }
50
+ }