vuetty 0.1.2 → 0.2.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.
package/README.md CHANGED
@@ -1,3 +1,15 @@
1
+ <p align="center">
2
+ <a href="https://tterrasson.github.io/vuetty/" target="_blank" rel="noopener noreferrer">
3
+ <img src="docs/public/images/logo.webp" alt="Vuetty Logo" width="300" />
4
+ </a>
5
+ </p>
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/vuetty">
8
+ <img src="https://img.shields.io/npm/v/vuetty?logo=npm&style=plastic" alt="npm package">
9
+ </a>
10
+ </p>
11
+ <br/>
12
+
1
13
  # Vuetty
2
14
 
3
15
  A Vue.js custom renderer for building Terminal User Interfaces (TUIs). Vuetty brings Vue's reactive system and component model to the terminal, allowing you to build interactive command-line applications using familiar Vue.js syntax.
@@ -5,14 +5,13 @@
5
5
  */
6
6
  import { plugin } from 'bun';
7
7
  import { compileSFC } from './compiler-core.js';
8
- import { readFileSync } from 'node:fs';
9
8
 
10
9
  plugin({
11
10
  name: 'vuetty-bun-loader',
12
11
 
13
12
  setup(build) {
14
13
  build.onLoad({ filter: /\.vue$/ }, async ({ path }) => {
15
- const source = readFileSync(path, 'utf-8');
14
+ const source = await Bun.file(path).text();
16
15
  const { code, errors } = compileSFC(source, path);
17
16
 
18
17
  if (errors.length > 0) {
package/dist/index.d.ts CHANGED
@@ -1,7 +1,11 @@
1
- import type { Component, DefineComponent } from "vue";
1
+ import type { Component, DefineComponent, ComputedRef } from "vue";
2
+
3
+ // ============================================================================
4
+ // Core Options and Classes
5
+ // ============================================================================
2
6
 
3
7
  export interface VuettyOptions {
4
- theme?: Record<string, any>;
8
+ theme?: Partial<Theme>;
5
9
  debugServer?: boolean | Record<string, any>;
6
10
  viewport?: Record<string, any>;
7
11
  forceColors?: boolean;
@@ -27,26 +31,478 @@ export class CommentNode extends TUINode {}
27
31
  export function createTUIRenderer(options?: Record<string, any>): any;
28
32
  export function renderToString(root: any): string;
29
33
 
30
- export const Box: DefineComponent<any, any, any>;
31
- export const TextBox: DefineComponent<any, any, any>;
32
- export const Row: DefineComponent<any, any, any>;
33
- export const Col: DefineComponent<any, any, any>;
34
- export const Divider: DefineComponent<any, any, any>;
35
- export const Spacer: DefineComponent<any, any, any>;
36
- export const Newline: DefineComponent<any, any, any>;
37
- export const Spinner: DefineComponent<any, any, any>;
38
- export const ProgressBar: DefineComponent<any, any, any>;
39
- export const TextInput: DefineComponent<any, any, any>;
40
- export const SelectInput: DefineComponent<any, any, any>;
41
- export const Checkbox: DefineComponent<any, any, any>;
42
- export const Radiobox: DefineComponent<any, any, any>;
43
- export const Table: DefineComponent<any, any, any>;
44
- export const Markdown: DefineComponent<any, any, any>;
45
- export const Image: DefineComponent<any, any, any>;
46
- export const BigText: DefineComponent<any, any, any>;
47
- export const Gradient: DefineComponent<any, any, any>;
48
- export const Button: DefineComponent<any, any, any>;
49
- export const Tree: DefineComponent<any, any, any>;
34
+ // ============================================================================
35
+ // Color Utilities
36
+ // ============================================================================
37
+
38
+ export interface RGB {
39
+ r: number;
40
+ g: number;
41
+ b: number;
42
+ }
43
+
44
+ export interface HSL {
45
+ h: number;
46
+ s: number;
47
+ l: number;
48
+ }
49
+
50
+ export function parseColor(color: string): RGB | null;
51
+ export function interpolateColor(colors: string[], ratio: number, interpolation?: 'rgb' | 'hsv'): string;
52
+ export function adjustBrightness(color: RGB, factor: number): RGB;
53
+ export function hslToRgb(h: number, s: number, l: number): RGB;
54
+ export function rgbToHex(r: number, g: number, b: number): string;
55
+ export function rgbToHsl(r: number, g: number, b: number): HSL;
56
+
57
+ // ============================================================================
58
+ // Effect Registry
59
+ // ============================================================================
60
+
61
+ export interface EffectFunction {
62
+ (text: string, effectProps: Record<string, any>, frame: number): string;
63
+ }
64
+
65
+ export interface EffectDefinition {
66
+ fn: EffectFunction;
67
+ animated: boolean;
68
+ defaultInterval: number;
69
+ }
70
+
71
+ export const effectRegistry: {
72
+ register(name: string, effectFn: EffectFunction, options?: { animated?: boolean; defaultInterval?: number }): void;
73
+ get(name: string): EffectDefinition | null;
74
+ has(name: string): boolean;
75
+ unregister(name: string): void;
76
+ getAll(): string[];
77
+ };
78
+
79
+ // ============================================================================
80
+ // Theme System
81
+ // ============================================================================
82
+
83
+ export interface ButtonVariantConfig {
84
+ bg: string;
85
+ color: string;
86
+ bold: boolean;
87
+ focusBg?: string;
88
+ }
89
+
90
+ export interface Theme {
91
+ background?: string;
92
+ foreground?: string;
93
+ primary?: string;
94
+ secondary?: string;
95
+ success?: string;
96
+ warning?: string;
97
+ danger?: string;
98
+ info?: string;
99
+ components?: {
100
+ box?: {
101
+ color?: string;
102
+ bg?: string | null;
103
+ };
104
+ textBox?: {
105
+ color?: string;
106
+ bg?: string | null;
107
+ };
108
+ textInput?: {
109
+ color?: string;
110
+ borderColor?: string;
111
+ bg?: string | null;
112
+ focusColor?: string;
113
+ errorColor?: string;
114
+ };
115
+ button?: {
116
+ variants?: {
117
+ primary?: ButtonVariantConfig;
118
+ secondary?: ButtonVariantConfig;
119
+ danger?: ButtonVariantConfig;
120
+ warning?: ButtonVariantConfig;
121
+ info?: ButtonVariantConfig;
122
+ success?: ButtonVariantConfig;
123
+ };
124
+ };
125
+ checkbox?: {
126
+ color?: string;
127
+ checkedColor?: string;
128
+ uncheckedColor?: string;
129
+ };
130
+ radiobox?: {
131
+ color?: string;
132
+ selectedColor?: string;
133
+ unselectedColor?: string;
134
+ };
135
+ tabs?: {
136
+ focusColor?: string;
137
+ activeColor?: string;
138
+ highlightColor?: string;
139
+ };
140
+ };
141
+ [key: string]: any; // Allow custom theme properties
142
+ }
143
+
144
+ export function createTheme(userTheme?: Partial<Theme>): Theme;
145
+ export const DEFAULT_THEME: Theme;
146
+ export function resolveThemeColor(theme: Theme, path: string): string | null;
147
+
148
+ // ============================================================================
149
+ // Layout Prop Interfaces
150
+ // ============================================================================
151
+
152
+ export interface FlexContainerProps {
153
+ justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly' | null;
154
+ alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline' | null;
155
+ alignContent?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'space-between' | 'space-around' | null;
156
+ flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse' | null;
157
+ flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse' | null;
158
+ gap?: number | null;
159
+ }
160
+
161
+ export interface FlexItemProps {
162
+ flex?: number | string | null;
163
+ flexGrow?: number | null;
164
+ flexShrink?: number | null;
165
+ flexBasis?: number | string | null;
166
+ alignSelf?: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline' | null;
167
+ }
168
+
169
+ export interface DimensionProps {
170
+ width?: number | string | null;
171
+ height?: number | string | null;
172
+ minWidth?: number | null;
173
+ maxWidth?: number | null;
174
+ minHeight?: number | null;
175
+ maxHeight?: number | null;
176
+ }
177
+
178
+ export interface PaddingProps {
179
+ padding?: number | null;
180
+ paddingLeft?: number | null;
181
+ paddingRight?: number | null;
182
+ paddingTop?: number | null;
183
+ paddingBottom?: number | null;
184
+ }
185
+
186
+ export interface MarginProps {
187
+ margin?: number | null;
188
+ marginLeft?: number | null;
189
+ marginRight?: number | null;
190
+ marginTop?: number | null;
191
+ marginBottom?: number | null;
192
+ }
193
+
194
+ export type BoxProps = FlexItemProps & DimensionProps & PaddingProps & MarginProps;
195
+ export type LayoutProps = FlexContainerProps & FlexItemProps & DimensionProps & PaddingProps & MarginProps;
196
+
197
+ export interface StyleProps {
198
+ color?: string;
199
+ bg?: string;
200
+ bold?: boolean;
201
+ italic?: boolean;
202
+ underline?: boolean;
203
+ dim?: boolean;
204
+ }
205
+
206
+ // ============================================================================
207
+ // Component Prop Interfaces
208
+ // ============================================================================
209
+
210
+ // Layout Components
211
+ export interface RowProps extends LayoutProps {
212
+ justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';
213
+ alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline';
214
+ flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
215
+ gap?: number;
216
+ responsive?: boolean;
217
+ }
218
+
219
+ export interface ColProps extends LayoutProps {
220
+ flex?: number | string;
221
+ gap?: number;
222
+ justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';
223
+ alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline';
224
+ flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
225
+ responsive?: boolean;
226
+ }
227
+
228
+ // Visual Container Components
229
+ export type BorderStyle =
230
+ | 'rounded'
231
+ | 'square'
232
+ | 'double'
233
+ | 'classic'
234
+ | 'bold'
235
+ | 'dashed'
236
+ | 'sparse'
237
+ | 'light'
238
+ | 'button'
239
+ | {
240
+ topLeft: string;
241
+ topRight: string;
242
+ bottomLeft: string;
243
+ bottomRight: string;
244
+ horizontal: string;
245
+ vertical: string;
246
+ };
247
+
248
+ export interface BoxComponentProps extends BoxProps, StyleProps {
249
+ border?: boolean;
250
+ borderStyle?: BorderStyle;
251
+ title?: string | null;
252
+ titleAlign?: 'left' | 'center' | 'right';
253
+ titlePadding?: number;
254
+ align?: 'left' | 'center' | 'right';
255
+ }
256
+
257
+ export interface TextBoxProps extends BoxProps, StyleProps {
258
+ effect?: string | null;
259
+ effectProps?: Record<string, any> | null;
260
+ animated?: boolean;
261
+ animationInterval?: number | null;
262
+ }
263
+
264
+ // Simple Display Components
265
+ export interface DividerProps extends BoxProps, Pick<StyleProps, 'color'> {
266
+ char?: string;
267
+ length?: number;
268
+ }
269
+
270
+ export interface SpacerProps {
271
+ count?: number;
272
+ }
273
+
274
+ export interface NewlineProps {
275
+ count?: number;
276
+ }
277
+
278
+ // Interactive Input Components
279
+ export interface TextInputProps extends BoxProps, StyleProps {
280
+ modelValue?: string;
281
+ multiline?: boolean;
282
+ rows?: number;
283
+ minRows?: number;
284
+ maxRows?: number;
285
+ autoResize?: boolean;
286
+ wrapLines?: boolean;
287
+ label?: string;
288
+ placeholder?: string;
289
+ hint?: string | boolean;
290
+ borderColor?: string;
291
+ focusColor?: string;
292
+ errorColor?: string;
293
+ pattern?: RegExp;
294
+ required?: boolean;
295
+ maxLength?: number;
296
+ disabled?: boolean;
297
+ readonly?: boolean;
298
+ }
299
+
300
+ export interface ListItem {
301
+ value?: any;
302
+ label?: string;
303
+ disabled?: boolean;
304
+ }
305
+
306
+ export interface SelectInputProps extends BoxProps, Pick<StyleProps, 'color' | 'bg' | 'bold' | 'dim'> {
307
+ modelValue?: any | any[];
308
+ options?: Array<ListItem | string | number>;
309
+ label?: string;
310
+ height?: number;
311
+ marker?: string;
312
+ highlightMarker?: string;
313
+ disabled?: boolean;
314
+ multiple?: boolean;
315
+ focusColor?: string;
316
+ selectedColor?: string;
317
+ highlightColor?: string;
318
+ hint?: string | boolean;
319
+ }
320
+
321
+ export interface ListComponentProps extends BoxProps, Pick<StyleProps, 'color' | 'bg' | 'bold' | 'dim'> {
322
+ items?: Array<ListItem | string | number>;
323
+ label?: string;
324
+ marker?: string;
325
+ highlightedValue?: string | number | object | null;
326
+ highlightColor?: string;
327
+ }
328
+
329
+ export interface CheckboxProps extends BoxProps, Pick<StyleProps, 'color' | 'bg' | 'bold' | 'dim'> {
330
+ modelValue?: any[];
331
+ options?: Array<ListItem | string | number>;
332
+ label?: string;
333
+ direction?: 'vertical' | 'horizontal';
334
+ height?: number;
335
+ itemSpacing?: number;
336
+ disabled?: boolean;
337
+ focusColor?: string;
338
+ selectedColor?: string;
339
+ highlightColor?: string;
340
+ hint?: string | boolean;
341
+ }
342
+
343
+ export interface RadioboxProps extends BoxProps, Pick<StyleProps, 'color' | 'bg' | 'bold' | 'dim'> {
344
+ modelValue?: string | number | object | null;
345
+ options?: Array<ListItem | string | number>;
346
+ label?: string;
347
+ direction?: 'vertical' | 'horizontal';
348
+ height?: number;
349
+ itemSpacing?: number;
350
+ disabled?: boolean;
351
+ focusColor?: string;
352
+ selectedColor?: string;
353
+ highlightColor?: string;
354
+ hint?: string | boolean;
355
+ }
356
+
357
+ export type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'warning' | 'info' | 'success';
358
+
359
+ export interface ButtonComponentProps extends BoxProps, Pick<StyleProps, 'bold' | 'italic' | 'dim'> {
360
+ label: string;
361
+ variant?: ButtonVariant;
362
+ color?: string;
363
+ bg?: string;
364
+ disabled?: boolean;
365
+ focusColor?: string;
366
+ focusBg?: string | null;
367
+ fullWidth?: boolean;
368
+ }
369
+
370
+ export interface TabItem {
371
+ value?: any;
372
+ label?: string;
373
+ disabled?: boolean;
374
+ }
375
+
376
+ export interface TabsProps extends BoxProps, Pick<StyleProps, 'color' | 'bg'> {
377
+ modelValue?: string | number | null;
378
+ tabs: Array<TabItem | string | number>;
379
+ disabled?: boolean;
380
+ focusColor?: string;
381
+ activeColor?: string;
382
+ highlightColor?: string;
383
+ panelBorder?: boolean;
384
+ panelBorderStyle?: string;
385
+ panelPadding?: number;
386
+ hint?: string | boolean;
387
+ }
388
+
389
+ export interface TableProps extends BoxProps, Pick<StyleProps, 'color' | 'bg' | 'bold' | 'dim'> {
390
+ modelValue?: number | null;
391
+ headers?: any[];
392
+ rows?: any[][];
393
+ label?: string;
394
+ height?: number;
395
+ columnWidths?: number[] | null;
396
+ striped?: boolean;
397
+ showHeader?: boolean;
398
+ disabled?: boolean;
399
+ focusColor?: string;
400
+ selectedColor?: string;
401
+ highlightColor?: string;
402
+ headerColor?: string;
403
+ stripedColor?: string;
404
+ hint?: string | boolean;
405
+ }
406
+
407
+ // Display Components
408
+ export type SpinnerType = 'dots' | 'line' | 'arc' | 'arrow' | 'bounce' | 'clock' | 'box';
409
+
410
+ export interface SpinnerProps extends BoxProps, StyleProps {
411
+ type?: SpinnerType;
412
+ modelValue?: boolean;
413
+ interval?: number;
414
+ label?: string;
415
+ labelPosition?: 'left' | 'right';
416
+ }
417
+
418
+ export interface ProgressBarProps extends BoxProps, StyleProps {
419
+ value?: number;
420
+ max?: number;
421
+ char?: string;
422
+ emptyChar?: string;
423
+ showPercentage?: boolean;
424
+ label?: string;
425
+ labelPosition?: 'left' | 'right' | 'above' | 'below';
426
+ brackets?: boolean;
427
+ emptyColor?: string;
428
+ percentageColor?: string;
429
+ }
430
+
431
+ export interface GradientProps extends BoxProps {
432
+ name?: string | null;
433
+ colors?: string[] | null;
434
+ interpolation?: 'rgb' | 'hsv';
435
+ }
436
+
437
+ export interface BigTextProps extends BoxProps, StyleProps {
438
+ font?: string;
439
+ horizontalLayout?: string;
440
+ align?: 'left' | 'center' | 'right';
441
+ }
442
+
443
+ export interface TreeNode {
444
+ name: string;
445
+ children?: TreeNode[];
446
+ [key: string]: any;
447
+ }
448
+
449
+ export interface TreeProps extends BoxProps, Pick<StyleProps, 'color' | 'bg'> {
450
+ data?: TreeNode[];
451
+ branchColor?: string;
452
+ folderColor?: string;
453
+ fileColor?: string | null;
454
+ showIcons?: boolean;
455
+ treeStyle?: string | Record<string, string>;
456
+ }
457
+
458
+ export interface MarkdownProps extends BoxProps, StyleProps {
459
+ source?: string;
460
+ codeTheme?: string;
461
+ enableTables?: boolean;
462
+ enableLists?: boolean;
463
+ enableCodeBlocks?: boolean;
464
+ }
465
+
466
+ export interface ImageProps extends BoxProps {
467
+ src: string | Buffer;
468
+ width?: number | string | null;
469
+ height?: number | string | null;
470
+ preserveAspectRatio?: boolean;
471
+ alt?: string;
472
+ errorColor?: string;
473
+ errorBorderStyle?: string;
474
+ }
475
+
476
+ // ============================================================================
477
+ // Component Exports
478
+ // ============================================================================
479
+
480
+ export const Box: DefineComponent<BoxComponentProps>;
481
+ export const TextBox: DefineComponent<TextBoxProps>;
482
+ export const Row: DefineComponent<RowProps>;
483
+ export const Col: DefineComponent<ColProps>;
484
+ export const Divider: DefineComponent<DividerProps>;
485
+ export const Spacer: DefineComponent<SpacerProps>;
486
+ export const Newline: DefineComponent<NewlineProps>;
487
+ export const Spinner: DefineComponent<SpinnerProps>;
488
+ export const ProgressBar: DefineComponent<ProgressBarProps>;
489
+ export const TextInput: DefineComponent<TextInputProps>;
490
+ export const SelectInput: DefineComponent<SelectInputProps>;
491
+ export const Checkbox: DefineComponent<CheckboxProps>;
492
+ export const Radiobox: DefineComponent<RadioboxProps>;
493
+ export const Table: DefineComponent<TableProps>;
494
+ export const Markdown: DefineComponent<MarkdownProps>;
495
+ export const Image: DefineComponent<ImageProps>;
496
+ export const BigText: DefineComponent<BigTextProps>;
497
+ export const Gradient: DefineComponent<GradientProps>;
498
+ export const Button: DefineComponent<ButtonComponentProps>;
499
+ export const Tree: DefineComponent<TreeProps>;
500
+ export const List: DefineComponent<ListComponentProps>;
501
+ export const Tabs: DefineComponent<TabsProps>;
502
+
503
+ // ============================================================================
504
+ // BigText Utilities
505
+ // ============================================================================
50
506
 
51
507
  export function clearBigTextCache(): void;
52
508
  export function getBigTextCacheStats(): {
@@ -54,26 +510,78 @@ export function getBigTextCacheStats(): {
54
510
  final: { size: number; maxSize: number };
55
511
  };
56
512
 
57
- export function createTheme(userTheme?: Record<string, any>): Record<string, any>;
58
- export const DEFAULT_THEME: Record<string, any>;
59
- export function resolveThemeColor(
60
- theme: Record<string, any>,
61
- path: string
62
- ): string | null;
513
+ // ============================================================================
514
+ // Injection Keys
515
+ // ============================================================================
63
516
 
64
517
  export const VUETTY_INPUT_MANAGER_KEY: string;
65
518
  export const VUETTY_THEME_KEY: string;
519
+ export const VUETTY_RENDERER_KEY: string;
520
+ export const VUETTY_ROUTER_KEY: string;
521
+ export const VUETTY_VIEWPORT_STATE_KEY: string;
522
+ export const VUETTY_INSTANCE_KEY: string;
523
+
524
+ // ============================================================================
525
+ // Render Handler System
526
+ // ============================================================================
527
+
528
+ export interface RenderContextOptions {
529
+ node: any;
530
+ depth: number;
531
+ absX: number;
532
+ absY: number;
533
+ inRow: boolean;
534
+ renderNodeFn: (node: any, depth: number, options: any) => string;
535
+ }
536
+
537
+ export interface RenderChildOptions {
538
+ parentAbsX?: number;
539
+ yOffset?: number;
540
+ inRow?: boolean;
541
+ }
66
542
 
67
543
  export class RenderContext {
68
- constructor(options: any);
69
- [key: string]: any;
544
+ node: any;
545
+ depth: number;
546
+ absX: number;
547
+ absY: number;
548
+ inRow: boolean;
549
+
550
+ constructor(options: RenderContextOptions);
551
+
552
+ get props(): Record<string, any>;
553
+ get text(): string;
554
+ get children(): any[];
555
+ get metrics(): any;
556
+
557
+ getEffectiveWidth(): number | null;
558
+ renderChild(child: any, options?: RenderChildOptions): string;
70
559
  }
560
+
71
561
  export class RenderHandler {
72
562
  render(ctx: RenderContext): string;
73
563
  }
564
+
74
565
  export const renderHandlerRegistry: {
75
566
  register(type: string, handler: RenderHandler): void;
76
567
  get(type: string): RenderHandler | null;
77
568
  has(type: string): boolean;
78
569
  unregister(type: string): void;
79
570
  };
571
+
572
+ // ============================================================================
573
+ // Theme Composable
574
+ // ============================================================================
575
+
576
+ export interface UseThemeReturn {
577
+ theme: ComputedRef<Theme | null>;
578
+ themeName: ComputedRef<string | null>;
579
+ isThemeSet: ComputedRef<boolean>;
580
+ setTheme: (themeName: string) => void;
581
+ getTheme: (themeName: string) => Theme | null;
582
+ listThemes: () => string[];
583
+ getAllThemes: () => Record<string, Theme>;
584
+ findThemes: (pattern: string) => Array<{ name: string; theme: Theme }>;
585
+ }
586
+
587
+ export function useTheme(): UseThemeReturn;