@nick-skriabin/glyph 0.1.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/dist/index.cjs +3570 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +407 -0
- package/dist/index.d.ts +407 -0
- package/dist/index.js +3539 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import React, { ReactElement, ReactNode } from 'react';
|
|
2
|
+
import { Node } from 'yoga-layout';
|
|
3
|
+
|
|
4
|
+
type NamedColor = "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright";
|
|
5
|
+
type HexColor = `#${string}`;
|
|
6
|
+
type RGBColor = {
|
|
7
|
+
r: number;
|
|
8
|
+
g: number;
|
|
9
|
+
b: number;
|
|
10
|
+
};
|
|
11
|
+
type Color = NamedColor | HexColor | RGBColor | number;
|
|
12
|
+
type DimensionValue = number | `${number}%`;
|
|
13
|
+
type BorderStyle = "none" | "single" | "double" | "round" | "ascii";
|
|
14
|
+
type WrapMode = "wrap" | "truncate" | "ellipsis" | "none";
|
|
15
|
+
type TextAlign = "left" | "center" | "right";
|
|
16
|
+
interface Style {
|
|
17
|
+
width?: DimensionValue;
|
|
18
|
+
height?: DimensionValue;
|
|
19
|
+
minWidth?: number;
|
|
20
|
+
minHeight?: number;
|
|
21
|
+
maxWidth?: number;
|
|
22
|
+
maxHeight?: number;
|
|
23
|
+
padding?: number;
|
|
24
|
+
paddingX?: number;
|
|
25
|
+
paddingY?: number;
|
|
26
|
+
paddingTop?: number;
|
|
27
|
+
paddingRight?: number;
|
|
28
|
+
paddingBottom?: number;
|
|
29
|
+
paddingLeft?: number;
|
|
30
|
+
gap?: number;
|
|
31
|
+
flexDirection?: "row" | "column";
|
|
32
|
+
flexWrap?: "nowrap" | "wrap";
|
|
33
|
+
justifyContent?: "flex-start" | "center" | "flex-end" | "space-between" | "space-around";
|
|
34
|
+
alignItems?: "flex-start" | "center" | "flex-end" | "stretch";
|
|
35
|
+
flexGrow?: number;
|
|
36
|
+
flexShrink?: number;
|
|
37
|
+
position?: "relative" | "absolute";
|
|
38
|
+
top?: DimensionValue;
|
|
39
|
+
right?: DimensionValue;
|
|
40
|
+
bottom?: DimensionValue;
|
|
41
|
+
left?: DimensionValue;
|
|
42
|
+
inset?: DimensionValue;
|
|
43
|
+
zIndex?: number;
|
|
44
|
+
pointerEvents?: "auto" | "none";
|
|
45
|
+
bg?: Color;
|
|
46
|
+
border?: BorderStyle;
|
|
47
|
+
borderColor?: Color;
|
|
48
|
+
clip?: boolean;
|
|
49
|
+
color?: Color;
|
|
50
|
+
bold?: boolean;
|
|
51
|
+
dim?: boolean;
|
|
52
|
+
italic?: boolean;
|
|
53
|
+
underline?: boolean;
|
|
54
|
+
wrap?: WrapMode;
|
|
55
|
+
textAlign?: TextAlign;
|
|
56
|
+
}
|
|
57
|
+
interface LayoutRect {
|
|
58
|
+
x: number;
|
|
59
|
+
y: number;
|
|
60
|
+
width: number;
|
|
61
|
+
height: number;
|
|
62
|
+
innerX: number;
|
|
63
|
+
innerY: number;
|
|
64
|
+
innerWidth: number;
|
|
65
|
+
innerHeight: number;
|
|
66
|
+
}
|
|
67
|
+
interface Key {
|
|
68
|
+
name: string;
|
|
69
|
+
sequence: string;
|
|
70
|
+
ctrl?: boolean;
|
|
71
|
+
alt?: boolean;
|
|
72
|
+
shift?: boolean;
|
|
73
|
+
}
|
|
74
|
+
interface RenderOptions {
|
|
75
|
+
stdout?: NodeJS.WriteStream;
|
|
76
|
+
stdin?: NodeJS.ReadStream;
|
|
77
|
+
debug?: boolean;
|
|
78
|
+
}
|
|
79
|
+
interface AppHandle {
|
|
80
|
+
unmount(): void;
|
|
81
|
+
exit(code?: number): void;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
declare function render(element: ReactElement, opts?: RenderOptions): AppHandle;
|
|
85
|
+
|
|
86
|
+
interface BoxProps {
|
|
87
|
+
style?: Style;
|
|
88
|
+
children?: ReactNode;
|
|
89
|
+
focusable?: boolean;
|
|
90
|
+
}
|
|
91
|
+
declare function Box({ children, style, focusable }: BoxProps): React.JSX.Element;
|
|
92
|
+
|
|
93
|
+
interface TextProps {
|
|
94
|
+
style?: Style;
|
|
95
|
+
children?: ReactNode;
|
|
96
|
+
wrap?: Style["wrap"];
|
|
97
|
+
}
|
|
98
|
+
declare function Text({ children, style, wrap }: TextProps): React.JSX.Element;
|
|
99
|
+
|
|
100
|
+
interface InputProps {
|
|
101
|
+
value?: string;
|
|
102
|
+
defaultValue?: string;
|
|
103
|
+
onChange?: (value: string) => void;
|
|
104
|
+
placeholder?: string;
|
|
105
|
+
style?: Style;
|
|
106
|
+
/** Style when focused (merged with style) */
|
|
107
|
+
focusedStyle?: Style;
|
|
108
|
+
/** Enable multiline editing (Enter inserts newlines, Up/Down navigate lines). */
|
|
109
|
+
multiline?: boolean;
|
|
110
|
+
}
|
|
111
|
+
declare function Input(props: InputProps): React.JSX.Element;
|
|
112
|
+
|
|
113
|
+
interface FocusScopeProps {
|
|
114
|
+
trap?: boolean;
|
|
115
|
+
children?: ReactNode;
|
|
116
|
+
}
|
|
117
|
+
declare function FocusScope({ trap, children }: FocusScopeProps): React.JSX.Element;
|
|
118
|
+
|
|
119
|
+
interface SpacerProps {
|
|
120
|
+
size?: number;
|
|
121
|
+
}
|
|
122
|
+
declare function Spacer({ size }: SpacerProps): React.JSX.Element;
|
|
123
|
+
|
|
124
|
+
interface KeybindProps {
|
|
125
|
+
/** Key descriptor, e.g. "q", "escape", "ctrl+c", "ctrl+shift+a" */
|
|
126
|
+
keypress: string;
|
|
127
|
+
/** Handler called when the key matches */
|
|
128
|
+
onPress: () => void;
|
|
129
|
+
/** Only fire when this focus ID is active */
|
|
130
|
+
whenFocused?: string;
|
|
131
|
+
/** Only fire when nothing is focused or the focused element doesn't consume the key */
|
|
132
|
+
global?: boolean;
|
|
133
|
+
/** Disabled state */
|
|
134
|
+
disabled?: boolean;
|
|
135
|
+
}
|
|
136
|
+
declare function Keybind({ keypress, onPress, whenFocused, disabled, }: KeybindProps): null;
|
|
137
|
+
|
|
138
|
+
interface PortalProps {
|
|
139
|
+
children?: ReactNode;
|
|
140
|
+
zIndex?: number;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Portal renders children as a fullscreen absolute overlay on top of the main tree.
|
|
144
|
+
* Useful for modals, notifications, and dropdowns that should paint above all other content.
|
|
145
|
+
*/
|
|
146
|
+
declare function Portal({ children, zIndex }: PortalProps): React.JSX.Element;
|
|
147
|
+
|
|
148
|
+
interface ButtonProps {
|
|
149
|
+
onPress?: () => void;
|
|
150
|
+
style?: Style;
|
|
151
|
+
focusedStyle?: Style;
|
|
152
|
+
children?: ReactNode;
|
|
153
|
+
disabled?: boolean;
|
|
154
|
+
}
|
|
155
|
+
declare function Button({ onPress, style, focusedStyle, children, disabled, }: ButtonProps): React.JSX.Element;
|
|
156
|
+
|
|
157
|
+
interface ScrollViewProps {
|
|
158
|
+
children?: ReactNode;
|
|
159
|
+
style?: Style;
|
|
160
|
+
/** Controlled scroll offset (rows from top) */
|
|
161
|
+
scrollOffset?: number;
|
|
162
|
+
/** Callback when scroll offset should change (controlled mode) */
|
|
163
|
+
onScroll?: (offset: number) => void;
|
|
164
|
+
/** Initial offset for uncontrolled mode */
|
|
165
|
+
defaultScrollOffset?: number;
|
|
166
|
+
/** Lines to scroll per arrow key press (default: 1) */
|
|
167
|
+
scrollStep?: number;
|
|
168
|
+
/** Disable keyboard scrolling */
|
|
169
|
+
disableKeyboard?: boolean;
|
|
170
|
+
/** Auto-scroll to focused element (default: true) */
|
|
171
|
+
scrollToFocus?: boolean;
|
|
172
|
+
/** Show scrollbar when content is scrollable (default: true) */
|
|
173
|
+
showScrollbar?: boolean;
|
|
174
|
+
}
|
|
175
|
+
declare function ScrollView({ children, style, scrollOffset: controlledOffset, onScroll, defaultScrollOffset, scrollStep, disableKeyboard, scrollToFocus, showScrollbar, }: ScrollViewProps): React.JSX.Element;
|
|
176
|
+
|
|
177
|
+
interface ListItemInfo {
|
|
178
|
+
index: number;
|
|
179
|
+
selected: boolean;
|
|
180
|
+
focused: boolean;
|
|
181
|
+
}
|
|
182
|
+
interface ListProps {
|
|
183
|
+
/** Total number of items */
|
|
184
|
+
count: number;
|
|
185
|
+
/** Render function for each item */
|
|
186
|
+
renderItem: (info: ListItemInfo) => ReactNode;
|
|
187
|
+
/** Controlled selected index */
|
|
188
|
+
selectedIndex?: number;
|
|
189
|
+
/** Callback when selected index should change */
|
|
190
|
+
onSelectionChange?: (index: number) => void;
|
|
191
|
+
/** Callback when enter is pressed on selected item */
|
|
192
|
+
onSelect?: (index: number) => void;
|
|
193
|
+
/** Initial index for uncontrolled mode */
|
|
194
|
+
defaultSelectedIndex?: number;
|
|
195
|
+
/** Set of disabled indices that are skipped during navigation */
|
|
196
|
+
disabledIndices?: Set<number>;
|
|
197
|
+
/** Outer box style */
|
|
198
|
+
style?: Style;
|
|
199
|
+
/** Whether the list is focusable (default: true) */
|
|
200
|
+
focusable?: boolean;
|
|
201
|
+
}
|
|
202
|
+
declare function List({ count, renderItem, selectedIndex: controlledIndex, onSelectionChange, onSelect, defaultSelectedIndex, disabledIndices, style, focusable, }: ListProps): React.JSX.Element;
|
|
203
|
+
|
|
204
|
+
interface MenuItem {
|
|
205
|
+
label: string;
|
|
206
|
+
value: string;
|
|
207
|
+
disabled?: boolean;
|
|
208
|
+
}
|
|
209
|
+
interface MenuProps {
|
|
210
|
+
items: MenuItem[];
|
|
211
|
+
/** Controlled selected index */
|
|
212
|
+
selectedIndex?: number;
|
|
213
|
+
/** Callback when selected index changes */
|
|
214
|
+
onSelectionChange?: (index: number) => void;
|
|
215
|
+
/** Callback when enter is pressed on item */
|
|
216
|
+
onSelect?: (value: string, index: number) => void;
|
|
217
|
+
/** Initial index for uncontrolled mode */
|
|
218
|
+
defaultSelectedIndex?: number;
|
|
219
|
+
/** Outer box style */
|
|
220
|
+
style?: Style;
|
|
221
|
+
/** Color for the selected item indicator and text (default: "cyan") */
|
|
222
|
+
highlightColor?: Color;
|
|
223
|
+
/** Whether the menu is focusable (default: true) */
|
|
224
|
+
focusable?: boolean;
|
|
225
|
+
}
|
|
226
|
+
declare function Menu({ items, selectedIndex, onSelectionChange, onSelect, defaultSelectedIndex, style, highlightColor, focusable, }: MenuProps): React.JSX.Element;
|
|
227
|
+
|
|
228
|
+
interface ProgressProps {
|
|
229
|
+
/** Progress value 0..1. Omit when indeterminate. */
|
|
230
|
+
value?: number;
|
|
231
|
+
/** Animate as indeterminate (marquee). Default false. */
|
|
232
|
+
indeterminate?: boolean;
|
|
233
|
+
/** Width of the progress bar. Default "100%". */
|
|
234
|
+
width?: DimensionValue;
|
|
235
|
+
/** Optional label text displayed before the bar. */
|
|
236
|
+
label?: string;
|
|
237
|
+
/** Show percentage text after the bar. Default false. */
|
|
238
|
+
showPercent?: boolean;
|
|
239
|
+
/** Outer container style. */
|
|
240
|
+
style?: Style;
|
|
241
|
+
/** Character(s) for the filled portion. Default "█". */
|
|
242
|
+
filled?: string;
|
|
243
|
+
/** Character(s) for the empty portion. Default "░". */
|
|
244
|
+
empty?: string;
|
|
245
|
+
}
|
|
246
|
+
declare function Progress({ value, indeterminate, width, label, showPercent, style, filled, empty, }: ProgressProps): React.JSX.Element;
|
|
247
|
+
|
|
248
|
+
interface SpinnerProps {
|
|
249
|
+
/** Animation frames. Defaults to braille dots. */
|
|
250
|
+
frames?: string[];
|
|
251
|
+
/** Interval between frames in ms. Default 80. */
|
|
252
|
+
intervalMs?: number;
|
|
253
|
+
/** Optional label text next to the spinner. */
|
|
254
|
+
label?: string;
|
|
255
|
+
/** Style applied to the spinner character. */
|
|
256
|
+
style?: Style;
|
|
257
|
+
}
|
|
258
|
+
declare function Spinner({ frames, intervalMs, label, style, }: SpinnerProps): React.JSX.Element;
|
|
259
|
+
|
|
260
|
+
type ToastVariant = "info" | "success" | "warning" | "error";
|
|
261
|
+
interface Toast {
|
|
262
|
+
id: string;
|
|
263
|
+
message: string;
|
|
264
|
+
title?: string;
|
|
265
|
+
variant?: ToastVariant;
|
|
266
|
+
durationMs?: number;
|
|
267
|
+
}
|
|
268
|
+
type ToastPosition = "top-right" | "top-left" | "bottom-right" | "bottom-left";
|
|
269
|
+
interface ToastHostProps {
|
|
270
|
+
/** Where toasts appear. Default "bottom-right". */
|
|
271
|
+
position?: ToastPosition;
|
|
272
|
+
/** Max visible toasts. Default 5. */
|
|
273
|
+
maxVisible?: number;
|
|
274
|
+
children?: ReactNode;
|
|
275
|
+
}
|
|
276
|
+
declare function useToast(): (toast: Omit<Toast, "id">) => void;
|
|
277
|
+
declare function ToastHost({ position, maxVisible, children, }: ToastHostProps): React.JSX.Element;
|
|
278
|
+
|
|
279
|
+
interface SelectItem {
|
|
280
|
+
label: string;
|
|
281
|
+
value: string;
|
|
282
|
+
disabled?: boolean;
|
|
283
|
+
}
|
|
284
|
+
interface SelectProps {
|
|
285
|
+
/** List of selectable items */
|
|
286
|
+
items: SelectItem[];
|
|
287
|
+
/** Currently selected value (controlled) */
|
|
288
|
+
value?: string;
|
|
289
|
+
/** Callback when selection changes */
|
|
290
|
+
onChange?: (value: string) => void;
|
|
291
|
+
/** Placeholder text when nothing is selected */
|
|
292
|
+
placeholder?: string;
|
|
293
|
+
/** Trigger box style */
|
|
294
|
+
style?: Style;
|
|
295
|
+
/** Trigger style when focused */
|
|
296
|
+
focusedStyle?: Style;
|
|
297
|
+
/** Dropdown overlay style overrides */
|
|
298
|
+
dropdownStyle?: Style;
|
|
299
|
+
/** Highlight color for the selected item in the dropdown (default: "cyan") */
|
|
300
|
+
highlightColor?: Color;
|
|
301
|
+
/** Max visible items in the dropdown before scrolling (default: 8) */
|
|
302
|
+
maxVisible?: number;
|
|
303
|
+
/** Enable type-to-filter when dropdown is open (default: true) */
|
|
304
|
+
searchable?: boolean;
|
|
305
|
+
/** Disabled state */
|
|
306
|
+
disabled?: boolean;
|
|
307
|
+
}
|
|
308
|
+
declare function Select({ items, value, onChange, placeholder, style, focusedStyle, dropdownStyle, highlightColor, maxVisible, searchable, disabled, }: SelectProps): React.JSX.Element;
|
|
309
|
+
|
|
310
|
+
interface CheckboxProps {
|
|
311
|
+
/** Whether the checkbox is checked */
|
|
312
|
+
checked: boolean;
|
|
313
|
+
/** Called when the checkbox is toggled */
|
|
314
|
+
onChange: (checked: boolean) => void;
|
|
315
|
+
/** Label text displayed next to the checkbox */
|
|
316
|
+
label?: string;
|
|
317
|
+
/** Style for the checkbox container */
|
|
318
|
+
style?: Style;
|
|
319
|
+
/** Style when focused */
|
|
320
|
+
focusedStyle?: Style;
|
|
321
|
+
/** Whether the checkbox is disabled */
|
|
322
|
+
disabled?: boolean;
|
|
323
|
+
/** Custom character for checked state (default: "✓") */
|
|
324
|
+
checkedChar?: string;
|
|
325
|
+
/** Custom character for unchecked state (default: " ") */
|
|
326
|
+
uncheckedChar?: string;
|
|
327
|
+
}
|
|
328
|
+
declare function Checkbox({ checked, onChange, label, style, focusedStyle, disabled, checkedChar, uncheckedChar, }: CheckboxProps): React.JSX.Element;
|
|
329
|
+
|
|
330
|
+
interface RadioItem<T = string> {
|
|
331
|
+
/** Display label */
|
|
332
|
+
label: string;
|
|
333
|
+
/** Value returned on selection */
|
|
334
|
+
value: T;
|
|
335
|
+
/** Whether this option is disabled */
|
|
336
|
+
disabled?: boolean;
|
|
337
|
+
}
|
|
338
|
+
interface RadioProps<T = string> {
|
|
339
|
+
/** Radio options */
|
|
340
|
+
items: RadioItem<T>[];
|
|
341
|
+
/** Currently selected value */
|
|
342
|
+
value: T | undefined;
|
|
343
|
+
/** Called when selection changes */
|
|
344
|
+
onChange: (value: T) => void;
|
|
345
|
+
/** Style for the radio group container */
|
|
346
|
+
style?: Style;
|
|
347
|
+
/** Style for each radio item */
|
|
348
|
+
itemStyle?: Style;
|
|
349
|
+
/** Style for the focused item */
|
|
350
|
+
focusedItemStyle?: Style;
|
|
351
|
+
/** Style for the selected item */
|
|
352
|
+
selectedItemStyle?: Style;
|
|
353
|
+
/** Whether the entire group is disabled */
|
|
354
|
+
disabled?: boolean;
|
|
355
|
+
/** Layout direction (default: "column") */
|
|
356
|
+
direction?: "row" | "column";
|
|
357
|
+
/** Gap between items (default: 0) */
|
|
358
|
+
gap?: number;
|
|
359
|
+
/** Custom character for selected state (default: "●") */
|
|
360
|
+
selectedChar?: string;
|
|
361
|
+
/** Custom character for unselected state (default: "○") */
|
|
362
|
+
unselectedChar?: string;
|
|
363
|
+
}
|
|
364
|
+
declare function Radio<T = string>({ items, value, onChange, style, itemStyle, focusedItemStyle, selectedItemStyle, disabled, direction, gap, selectedChar, unselectedChar, }: RadioProps<T>): React.JSX.Element;
|
|
365
|
+
|
|
366
|
+
declare function useInput(handler: (key: Key) => void, deps?: any[]): void;
|
|
367
|
+
|
|
368
|
+
type GlyphNodeType = "box" | "text" | "input";
|
|
369
|
+
interface GlyphNode {
|
|
370
|
+
type: GlyphNodeType;
|
|
371
|
+
props: Record<string, any>;
|
|
372
|
+
style: Style;
|
|
373
|
+
children: GlyphNode[];
|
|
374
|
+
rawTextChildren: GlyphTextInstance[];
|
|
375
|
+
parent: GlyphNode | null;
|
|
376
|
+
yogaNode: Node | null;
|
|
377
|
+
text: string | null;
|
|
378
|
+
layout: LayoutRect;
|
|
379
|
+
focusId: string | null;
|
|
380
|
+
hidden: boolean;
|
|
381
|
+
}
|
|
382
|
+
interface GlyphTextInstance {
|
|
383
|
+
type: "raw-text";
|
|
384
|
+
text: string;
|
|
385
|
+
parent: GlyphNode | null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
interface UseFocusResult {
|
|
389
|
+
focused: boolean;
|
|
390
|
+
focus(): void;
|
|
391
|
+
}
|
|
392
|
+
declare function useFocus(nodeRef?: {
|
|
393
|
+
current: GlyphNode | null;
|
|
394
|
+
}): UseFocusResult;
|
|
395
|
+
|
|
396
|
+
declare function useLayout(nodeRef?: {
|
|
397
|
+
current: GlyphNode | null;
|
|
398
|
+
}): LayoutRect;
|
|
399
|
+
|
|
400
|
+
interface UseAppResult {
|
|
401
|
+
exit(code?: number): void;
|
|
402
|
+
columns: number;
|
|
403
|
+
rows: number;
|
|
404
|
+
}
|
|
405
|
+
declare function useApp(): UseAppResult;
|
|
406
|
+
|
|
407
|
+
export { type AppHandle, type BorderStyle, Box, type BoxProps, Button, type ButtonProps, Checkbox, type CheckboxProps, type Color, type DimensionValue, FocusScope, type FocusScopeProps, type HexColor, Input, type InputProps, type Key, Keybind, type KeybindProps, type LayoutRect, List, type ListItemInfo, type ListProps, Menu, type MenuItem, type MenuProps, type NamedColor, Portal, type PortalProps, Progress, type ProgressProps, type RGBColor, Radio, type RadioItem, type RadioProps, type RenderOptions, ScrollView, type ScrollViewProps, Select, type SelectItem, type SelectProps, Spacer, type SpacerProps, Spinner, type SpinnerProps, type Style, Text, type TextAlign, type TextProps, type Toast, ToastHost, type ToastHostProps, type ToastPosition, type ToastVariant, type WrapMode, render, useApp, useFocus, useInput, useLayout, useToast };
|