@silvery/examples 0.5.6 → 0.17.4

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 (112) hide show
  1. package/dist/UPNG-Cy7ViL8f.mjs +5074 -0
  2. package/dist/__vite-browser-external-2447137e-BML7CYau.mjs +4 -0
  3. package/dist/_banner-DLPxCqVy.mjs +44 -0
  4. package/dist/ansi-CCE2pVS0.mjs +16397 -0
  5. package/dist/apng-HhhBjRGt.mjs +68 -0
  6. package/dist/apng-mwUQbTTF.mjs +3 -0
  7. package/dist/apps/aichat/index.mjs +1299 -0
  8. package/dist/apps/app-todo.mjs +139 -0
  9. package/dist/apps/async-data.mjs +204 -0
  10. package/dist/apps/cli-wizard.mjs +339 -0
  11. package/dist/apps/clipboard.mjs +198 -0
  12. package/dist/apps/components.mjs +864 -0
  13. package/dist/apps/data-explorer.mjs +483 -0
  14. package/dist/apps/dev-tools.mjs +397 -0
  15. package/dist/apps/explorer.mjs +698 -0
  16. package/dist/apps/gallery.mjs +766 -0
  17. package/dist/apps/inline-bench.mjs +115 -0
  18. package/dist/apps/kanban.mjs +280 -0
  19. package/dist/apps/layout-ref.mjs +187 -0
  20. package/dist/apps/outline.mjs +203 -0
  21. package/dist/apps/paste-demo.mjs +189 -0
  22. package/dist/apps/scroll.mjs +86 -0
  23. package/dist/apps/search-filter.mjs +287 -0
  24. package/dist/apps/selection.mjs +355 -0
  25. package/dist/apps/spatial-focus-demo.mjs +388 -0
  26. package/dist/apps/task-list.mjs +258 -0
  27. package/dist/apps/terminal-caps-demo.mjs +315 -0
  28. package/dist/apps/terminal.mjs +872 -0
  29. package/dist/apps/text-selection-demo.mjs +254 -0
  30. package/dist/apps/textarea.mjs +178 -0
  31. package/dist/apps/theme.mjs +661 -0
  32. package/dist/apps/transform.mjs +215 -0
  33. package/dist/apps/virtual-10k.mjs +422 -0
  34. package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
  35. package/dist/backends-Bahh9mKN.mjs +1179 -0
  36. package/dist/backends-CCtCDQ94.mjs +3 -0
  37. package/dist/{cli.mjs → bin/cli.mjs} +21 -25
  38. package/dist/chunk-BSw8zbkd.mjs +37 -0
  39. package/dist/components/counter.mjs +48 -0
  40. package/dist/components/hello.mjs +31 -0
  41. package/dist/components/progress-bar.mjs +59 -0
  42. package/dist/components/select-list.mjs +85 -0
  43. package/dist/components/spinner.mjs +57 -0
  44. package/dist/components/text-input.mjs +62 -0
  45. package/dist/components/virtual-list.mjs +51 -0
  46. package/dist/flexily-zero-adapter-UB-ra8fR.mjs +3374 -0
  47. package/dist/gif-BZaqPPVX.mjs +3 -0
  48. package/dist/gif-BtnXuxLF.mjs +71 -0
  49. package/dist/gifenc-CLRW41dk.mjs +728 -0
  50. package/dist/jsx-runtime-dMs_8fNu.mjs +241 -0
  51. package/dist/key-mapping-5oYQdAQE.mjs +3 -0
  52. package/dist/key-mapping-D4LR1go6.mjs +130 -0
  53. package/dist/layout/dashboard.mjs +1204 -0
  54. package/dist/layout/live-resize.mjs +303 -0
  55. package/dist/layout/overflow.mjs +70 -0
  56. package/dist/layout/text-layout.mjs +335 -0
  57. package/dist/node-NuJ94BWl.mjs +1083 -0
  58. package/dist/plugins-D1KtkT4a.mjs +3057 -0
  59. package/dist/resvg-js-C_8Wps1F.mjs +201 -0
  60. package/dist/src-BTEVGpd9.mjs +23538 -0
  61. package/dist/src-CUUOuRH6.mjs +5322 -0
  62. package/dist/src-CzfRafCQ.mjs +814 -0
  63. package/dist/usingCtx-CsEf0xO3.mjs +57 -0
  64. package/dist/yoga-adapter-BVtQ5OJR.mjs +237 -0
  65. package/package.json +19 -14
  66. package/_banner.tsx +0 -60
  67. package/apps/aichat/components.tsx +0 -469
  68. package/apps/aichat/index.tsx +0 -220
  69. package/apps/aichat/script.ts +0 -460
  70. package/apps/aichat/state.ts +0 -325
  71. package/apps/aichat/types.ts +0 -19
  72. package/apps/app-todo.tsx +0 -201
  73. package/apps/async-data.tsx +0 -196
  74. package/apps/cli-wizard.tsx +0 -332
  75. package/apps/clipboard.tsx +0 -183
  76. package/apps/components.tsx +0 -658
  77. package/apps/data-explorer.tsx +0 -490
  78. package/apps/dev-tools.tsx +0 -395
  79. package/apps/explorer.tsx +0 -731
  80. package/apps/gallery.tsx +0 -653
  81. package/apps/inline-bench.tsx +0 -138
  82. package/apps/kanban.tsx +0 -265
  83. package/apps/layout-ref.tsx +0 -173
  84. package/apps/outline.tsx +0 -160
  85. package/apps/panes/index.tsx +0 -203
  86. package/apps/paste-demo.tsx +0 -185
  87. package/apps/scroll.tsx +0 -77
  88. package/apps/search-filter.tsx +0 -240
  89. package/apps/selection.tsx +0 -342
  90. package/apps/spatial-focus-demo.tsx +0 -368
  91. package/apps/task-list.tsx +0 -271
  92. package/apps/terminal-caps-demo.tsx +0 -334
  93. package/apps/terminal.tsx +0 -800
  94. package/apps/text-selection-demo.tsx +0 -189
  95. package/apps/textarea.tsx +0 -155
  96. package/apps/theme.tsx +0 -515
  97. package/apps/transform.tsx +0 -229
  98. package/apps/virtual-10k.tsx +0 -405
  99. package/apps/vterm-demo/index.tsx +0 -216
  100. package/components/counter.tsx +0 -45
  101. package/components/hello.tsx +0 -34
  102. package/components/progress-bar.tsx +0 -48
  103. package/components/select-list.tsx +0 -50
  104. package/components/spinner.tsx +0 -40
  105. package/components/text-input.tsx +0 -57
  106. package/components/virtual-list.tsx +0 -52
  107. package/dist/cli.d.mts +0 -1
  108. package/dist/cli.mjs.map +0 -1
  109. package/layout/dashboard.tsx +0 -953
  110. package/layout/live-resize.tsx +0 -282
  111. package/layout/overflow.tsx +0 -51
  112. package/layout/text-layout.tsx +0 -283
@@ -1,658 +0,0 @@
1
- /**
2
- * Components Showcase
3
- *
4
- * A UI component gallery demonstrating silvery's built-in components:
5
- * - Typography: H1-H3, Strong, Muted, Small, Lead, Code, Blockquote, lists
6
- * - Inputs: TextInput, TextArea, SelectList, Toggle with focus cycling
7
- * - Display: ProgressBar, Spinner, Badge, border styles, ModalDialog
8
- */
9
-
10
- import React, { useState, useCallback } from "react"
11
- import {
12
- render,
13
- Box,
14
- Text,
15
- Muted,
16
- useInput,
17
- useApp,
18
- createTerm,
19
- // Typography
20
- H1,
21
- H2,
22
- H3,
23
- P,
24
- Lead,
25
- Small,
26
- Strong,
27
- Em,
28
- Code,
29
- Blockquote,
30
- CodeBlock,
31
- HR,
32
- UL,
33
- OL,
34
- LI,
35
- // Inputs
36
- TextInput,
37
- TextArea,
38
- SelectList,
39
- Toggle,
40
- Button,
41
- // Display
42
- ProgressBar,
43
- Spinner,
44
- Badge,
45
- Divider,
46
- Kbd,
47
- ModalDialog,
48
- type Key,
49
- } from "silvery"
50
- import { ExampleBanner, type ExampleMeta } from "../_banner.js"
51
-
52
- export const meta: ExampleMeta = {
53
- name: "Components",
54
- description: "UI component gallery with typography, inputs, and dialogs",
55
- demo: true,
56
- features: ["Typography", "TextInput", "SelectList", "ModalDialog", "ProgressBar", "focus ring"],
57
- }
58
-
59
- // ============================================================================
60
- // Typography Tab
61
- // ============================================================================
62
-
63
- function TypographyTab({ scrollOffset }: { scrollOffset?: number }) {
64
- return (
65
- <Box flexDirection="column" gap={1} paddingX={1} overflow="scroll" scrollOffset={scrollOffset} flexGrow={1}>
66
- <Box flexDirection="column">
67
- <H1>Getting Started with Silvery</H1>
68
- <Lead>Build modern terminal UIs with React — layout feedback, semantic theming, and 30+ components.</Lead>
69
- </Box>
70
-
71
- <HR />
72
-
73
- <Box flexDirection="row" gap={2}>
74
- <Box flexDirection="column" flexGrow={1} flexBasis={0}>
75
- <H2>Typography</H2>
76
- <Box flexDirection="column">
77
- <Text bold color="$primary">
78
- H1 — Page Title (bold, $primary)
79
- </Text>
80
- <Text bold color="$accent">
81
- H2 — Section Heading (bold, $accent)
82
- </Text>
83
- <Text color="$primary">H3 — Group Heading ($primary)</Text>
84
- <P>P — Body paragraph text</P>
85
- <Lead>Lead — Introductory italic text</Lead>
86
- <Muted>Muted — Secondary information</Muted>
87
- <Small>Small — Fine print and captions</Small>
88
- </Box>
89
- </Box>
90
- <Box flexDirection="column" flexGrow={1} flexBasis={0}>
91
- <H2>Inline Styles</H2>
92
- <Box flexDirection="column">
93
- <Text>
94
- <Strong>Strong</Strong> — bold emphasis
95
- </Text>
96
- <Text>
97
- <Em>Em</Em> — italic emphasis
98
- </Text>
99
- <Text>
100
- <Strong>
101
- <Em>Strong + Em</Em>
102
- </Strong>{" "}
103
- — bold italic
104
- </Text>
105
- <Text>
106
- <Text underline>Underline</Text> — underlined text
107
- </Text>
108
- <Text>
109
- <Text strikethrough>Strikethrough</Text> — deleted text
110
- </Text>
111
- <Text>
112
- <Code>Code</Code> — inline code span
113
- </Text>
114
- <Text>
115
- <Kbd>Kbd</Kbd> — keyboard shortcut
116
- </Text>
117
- </Box>
118
- </Box>
119
- </Box>
120
-
121
- <HR />
122
-
123
- <H2>Semantic Colors</H2>
124
- <Box flexDirection="column">
125
- <Box gap={1}>
126
- <Text backgroundColor="$primary" color="$primary-fg" bold>
127
- {" $primary "}
128
- </Text>
129
- <Text backgroundColor="$accent" color="$accent-fg" bold>
130
- {" $accent "}
131
- </Text>
132
- <Text backgroundColor="$success" color="$success-fg" bold>
133
- {" $success "}
134
- </Text>
135
- <Text backgroundColor="$warning" color="$warning-fg" bold>
136
- {" $warning "}
137
- </Text>
138
- <Text backgroundColor="$error" color="$error-fg" bold>
139
- {" $error "}
140
- </Text>
141
- </Box>
142
- <Box gap={1} marginTop={1}>
143
- <Text color="$primary">{"████"} primary</Text>
144
- <Text color="$accent">{"████"} accent</Text>
145
- <Text color="$success">{"████"} success</Text>
146
- <Text color="$warning">{"████"} warning</Text>
147
- <Text color="$error">{"████"} error</Text>
148
- <Text color="$muted">{"████"} muted</Text>
149
- </Box>
150
- </Box>
151
-
152
- <HR />
153
-
154
- <H2>Block Elements</H2>
155
- <Blockquote>
156
- The best color code is no color code — most components already use the right semantic tokens.
157
- </Blockquote>
158
- <CodeBlock>{"bun add silvery # install\nbun run dev # start dev server"}</CodeBlock>
159
-
160
- <H2>Lists</H2>
161
- <Box flexDirection="row" gap={4}>
162
- <Box flexDirection="column" flexGrow={1} flexBasis={0}>
163
- <H3>Unordered</H3>
164
- <UL>
165
- <LI>
166
- <Strong>SelectList</Strong> — j/k navigation, scroll
167
- </LI>
168
- <LI>
169
- <Strong>TextInput</Strong> — full readline support
170
- </LI>
171
- <LI>
172
- <Strong>ModalDialog</Strong> — overlay with input blocking
173
- </LI>
174
- <LI>
175
- <Strong>ProgressBar</Strong> — determinate + indeterminate
176
- </LI>
177
- </UL>
178
- </Box>
179
- <Box flexDirection="column" flexGrow={1} flexBasis={0}>
180
- <H3>Ordered</H3>
181
- <OL>
182
- <LI>
183
- Install with <Code>bun add silvery</Code>
184
- </LI>
185
- <LI>
186
- Use <Code>$tokens</Code> for semantic colors
187
- </LI>
188
- <LI>
189
- Layout with <Code>flexbox</Code> via Flexily
190
- </LI>
191
- <LI>
192
- Test with <Code>createTermless()</Code>
193
- </LI>
194
- </OL>
195
- </Box>
196
- </Box>
197
-
198
- <Small>silvery v0.0.1 — 38 palettes, 30+ components — silvery.dev</Small>
199
- </Box>
200
- )
201
- }
202
-
203
- // ============================================================================
204
- // Inputs Tab
205
- // ============================================================================
206
-
207
- const frameworkItems = [
208
- { label: "Silvery", value: "silvery" },
209
- { label: "Ink", value: "ink" },
210
- { label: "Blessed", value: "blessed", disabled: true },
211
- { label: "Terminal Kit", value: "terminal-kit" },
212
- { label: "React Curse", value: "react-curse" },
213
- ]
214
-
215
- function InputsTab() {
216
- const [textValue, setTextValue] = useState("")
217
- const [areaValue, setAreaValue] = useState("")
218
- const [selectedFramework, setSelectedFramework] = useState(0)
219
- const [darkMode, setDarkMode] = useState(true)
220
- const [notifications, setNotifications] = useState(false)
221
- const [autoSave, setAutoSave] = useState(true)
222
- const [focusIndex, setFocusIndex] = useState(0)
223
-
224
- const focusableCount = 5
225
-
226
- useInput((_input: string, key: Key) => {
227
- if (key.tab && !key.shift) {
228
- setFocusIndex((prev) => (prev + 1) % focusableCount)
229
- }
230
- if (key.tab && key.shift) {
231
- setFocusIndex((prev) => (prev - 1 + focusableCount) % focusableCount)
232
- }
233
- })
234
-
235
- const resetAll = useCallback(() => {
236
- setTextValue("")
237
- setAreaValue("")
238
- setSelectedFramework(0)
239
- setDarkMode(true)
240
- setNotifications(false)
241
- setAutoSave(true)
242
- }, [])
243
-
244
- return (
245
- <Box flexDirection="column" gap={1} paddingX={1} overflow="scroll" flexGrow={1}>
246
- <Box flexDirection="row" gap={2} flexGrow={1}>
247
- {/* Left column: Input controls */}
248
- <Box flexDirection="column" gap={1} flexGrow={1} flexBasis={0}>
249
- <H2>Text Input</H2>
250
- <TextInput
251
- value={textValue}
252
- onChange={setTextValue}
253
- onSubmit={() => setTextValue("")}
254
- placeholder="Type something..."
255
- prompt="search: "
256
- borderStyle="round"
257
- isActive={focusIndex === 0}
258
- />
259
-
260
- <H2>Text Area</H2>
261
- <TextArea
262
- value={areaValue}
263
- onChange={setAreaValue}
264
- placeholder="Write your thoughts..."
265
- height={4}
266
- borderStyle="round"
267
- isActive={focusIndex === 1}
268
- />
269
-
270
- <H2>Select List</H2>
271
- <Box borderStyle="round" borderColor={focusIndex === 2 ? "$focusborder" : "$border"} paddingX={1}>
272
- <SelectList
273
- items={frameworkItems}
274
- highlightedIndex={selectedFramework}
275
- onHighlight={setSelectedFramework}
276
- isActive={focusIndex === 2}
277
- />
278
- </Box>
279
- </Box>
280
-
281
- {/* Right column: Toggles + Summary */}
282
- <Box flexDirection="column" gap={1} flexGrow={1} flexBasis={0}>
283
- <H2>Toggles</H2>
284
- <Box
285
- flexDirection="column"
286
- borderStyle="round"
287
- borderColor={focusIndex === 3 ? "$focusborder" : "$border"}
288
- paddingX={1}
289
- paddingY={1}
290
- gap={1}
291
- >
292
- <Toggle value={darkMode} onChange={setDarkMode} label="Dark mode" isActive={focusIndex === 3} />
293
- <Toggle value={notifications} onChange={setNotifications} label="Notifications" isActive={false} />
294
- <Toggle value={autoSave} onChange={setAutoSave} label="Auto-save" isActive={false} />
295
- </Box>
296
-
297
- <H2>Button</H2>
298
- <Button label="Reset All" onPress={resetAll} isActive={focusIndex === 4} />
299
-
300
- <HR />
301
-
302
- <H2>Current Values</H2>
303
- <Box flexDirection="column" backgroundColor="$surfacebg" paddingX={1} paddingY={1} borderStyle="round">
304
- <Text color="$surface">
305
- <Strong>Text:</Strong> {textValue || <Muted>(empty)</Muted>}
306
- </Text>
307
- <Text color="$surface">
308
- <Strong>Area:</Strong>{" "}
309
- {areaValue ? areaValue.split("\n")[0] + (areaValue.includes("\n") ? "..." : "") : <Muted>(empty)</Muted>}
310
- </Text>
311
- <Text color="$surface">
312
- <Strong>Framework:</Strong> {frameworkItems[selectedFramework]?.label}
313
- </Text>
314
- <Text color="$surface">
315
- <Strong>Dark mode:</Strong> {darkMode ? "on" : "off"}
316
- </Text>
317
- <Text color="$surface">
318
- <Strong>Notifications:</Strong> {notifications ? "on" : "off"}
319
- </Text>
320
- <Text color="$surface">
321
- <Strong>Auto-save:</Strong> {autoSave ? "on" : "off"}
322
- </Text>
323
- </Box>
324
- </Box>
325
- </Box>
326
-
327
- {/* keyboard hints removed for static screenshots */}
328
- </Box>
329
- )
330
- }
331
-
332
- // ============================================================================
333
- // Display Tab
334
- // ============================================================================
335
-
336
- function DisplayTab({ scrollOffset }: { scrollOffset?: number }) {
337
- const [showModal, setShowModal] = useState(false)
338
- const [selectedBorder, setSelectedBorder] = useState(0)
339
-
340
- const borderStyles = ["round", "bold", "single", "double", "classic"] as const
341
-
342
- useInput((input: string, key: Key) => {
343
- if (key.return && !showModal) {
344
- setShowModal(true)
345
- }
346
- if ((key.escape || input === "q") && showModal) {
347
- setShowModal(false)
348
- }
349
- if (input === "j" && !showModal) {
350
- setSelectedBorder((prev) => Math.min(prev + 1, borderStyles.length - 1))
351
- }
352
- if (input === "k" && !showModal) {
353
- setSelectedBorder((prev) => Math.max(prev - 1, 0))
354
- }
355
- })
356
-
357
- const cell = {
358
- flexGrow: 1,
359
- flexBasis: 0,
360
- borderStyle: "round" as const,
361
- borderColor: "$border",
362
- paddingX: 1,
363
- paddingY: 1,
364
- flexDirection: "column" as const,
365
- gap: 1,
366
- }
367
-
368
- return (
369
- <Box flexDirection="column" gap={1} paddingX={1}>
370
- {/* Row 1 */}
371
- <Box flexDirection="row" gap={1}>
372
- <Box {...cell}>
373
- <Text color="$primary">
374
- <Strong>Progress Bars</Strong>
375
- </Text>
376
- <Box flexDirection="column">
377
- <Box>
378
- <Text color="$muted">{"Build "}</Text>
379
- <Box flexGrow={1}>
380
- <ProgressBar value={1.0} label="✓" />
381
- </Box>
382
- </Box>
383
- <Box>
384
- <Text color="$muted">{"Test "}</Text>
385
- <Box flexGrow={1}>
386
- <ProgressBar value={0.73} />
387
- </Box>
388
- </Box>
389
- <Box>
390
- <Text color="$muted">{"Deploy "}</Text>
391
- <Box flexGrow={1}>
392
- <ProgressBar value={0.35} />
393
- </Box>
394
- </Box>
395
- <Box>
396
- <Text color="$muted">{"Install "}</Text>
397
- <Box flexGrow={1}>
398
- <ProgressBar />
399
- </Box>
400
- </Box>
401
- </Box>
402
- <Box flexDirection="column">
403
- <Spinner type="dots" label="Loading packages..." />
404
- <Spinner type="line" label="Compiling..." />
405
- <Spinner type="arc" label="Optimizing bundle..." />
406
- </Box>
407
- </Box>
408
- <Box {...cell}>
409
- <Text color="$primary">
410
- <Strong>Input Controls</Strong>
411
- </Text>
412
- <Box flexDirection="column" gap={1}>
413
- <Box gap={1}>
414
- <Muted>Search:</Muted>
415
- <Box flexGrow={1}>
416
- <TextInput value="flutter widgets" onChange={() => {}} showUnderline underlineWidth={25} />
417
- </Box>
418
- </Box>
419
- <Box gap={2} wrap="truncate">
420
- <Toggle label="Dark mode" value={true} onChange={() => {}} />
421
- <Toggle label="Notifications" value={false} onChange={() => {}} />
422
- </Box>
423
- </Box>
424
- <SelectList
425
- items={[
426
- { label: "React", value: "react" },
427
- { label: "Vue", value: "vue" },
428
- { label: "Svelte", value: "svelte" },
429
- { label: "Angular", value: "angular" },
430
- ]}
431
- highlightedIndex={0}
432
- onHighlight={() => {}}
433
- isActive={false}
434
- />
435
- </Box>
436
- </Box>
437
-
438
- {/* Row 2: Border Styles | Design Tokens | Modal Dialog */}
439
- <Box flexDirection="row" gap={1}>
440
- {/* Left half: two stacked boxes */}
441
- <Box flexDirection="column" gap={1} flexGrow={1} flexBasis={0}>
442
- <Box {...cell} flexGrow={1} flexBasis={0}>
443
- <Text color="$primary">
444
- <Strong>Border Styles</Strong>
445
- </Text>
446
- <Box flexDirection="column" gap={0}>
447
- {borderStyles.map((style, i) => (
448
- <Box
449
- key={style}
450
- borderStyle={style as any}
451
- borderColor={i === selectedBorder ? "$primary" : "$border"}
452
- borderLeft={true}
453
- borderRight={true}
454
- borderTop={i === 0}
455
- borderBottom={true}
456
- paddingX={1}
457
- >
458
- <Text bold={i === selectedBorder}>
459
- {i === selectedBorder ? "▸ " : " "}
460
- {style}
461
- </Text>
462
- </Box>
463
- ))}
464
- </Box>
465
- </Box>
466
- <Box {...cell} flexGrow={1} flexBasis={0}>
467
- <Text color="$primary">
468
- <Strong>Design Tokens</Strong>
469
- </Text>
470
- <Box flexDirection="row" gap={2}>
471
- <Box flexDirection="column" width={14}>
472
- <Text color="$success">
473
- {"●"} {"$success".padEnd(10)}
474
- </Text>
475
- <Text color="$warning">
476
- {"●"} {"$warning".padEnd(10)}
477
- </Text>
478
- <Text color="$error">
479
- {"●"} {"$error".padEnd(10)}
480
- </Text>
481
- <Text color="$info">
482
- {"●"} {"$info".padEnd(10)}
483
- </Text>
484
- <Text color="$primary">
485
- {"●"} {"$primary".padEnd(10)}
486
- </Text>
487
- <Muted>
488
- {"●"} {"$muted".padEnd(10)}
489
- </Muted>
490
- </Box>
491
- <Box flexDirection="column">
492
- <Text backgroundColor="$primary" color="$primary-fg">
493
- {" $primary "}
494
- </Text>
495
- <Text backgroundColor="$fg" color="$bg">
496
- {" $inverse "}
497
- </Text>
498
- <Text backgroundColor="$muted-bg" color="$fg">
499
- {" $surface "}
500
- </Text>
501
- <Text backgroundColor="$surfacebg" color="$surface">
502
- {" $surfacebg "}
503
- </Text>
504
- </Box>
505
- </Box>
506
- </Box>
507
- </Box>
508
- {/* Right half: Modal Dialog */}
509
- <Box {...cell} backgroundColor="$surfacebg" paddingRight={2}>
510
- <Box justifyContent="space-between" paddingBottom={1}>
511
- <Text color="$primary">
512
- <Strong>Modal Dialog</Strong>
513
- </Text>
514
- <Small color="$muted">Esc to close</Small>
515
- </Box>
516
- <Box flexDirection="column" gap={1}>
517
- <Box gap={1}>
518
- <Muted>Branch:</Muted>
519
- <TextInput value="main" onChange={() => {}} showUnderline underlineWidth={25} isActive={true} />
520
- </Box>
521
- <Box flexDirection="column">
522
- <Text>
523
- <Text color="$success">{"✓"}</Text> All checks passed
524
- </Text>
525
- <Text>
526
- <Text color="$success">{"✓"}</Text> Tests: 247 passed
527
- </Text>
528
- <Text>
529
- <Text color="$warning">{"⚠"}</Text> 2 deprecation warnings
530
- </Text>
531
- <Text>
532
- <Text color="$muted">{"ℹ"}</Text> Deploy target: us-east-1
533
- </Text>
534
- </Box>
535
- <Box gap={2}>
536
- <Text backgroundColor="$primary" color="$primary-fg">
537
- {" Deploy "}
538
- </Text>
539
- <Text backgroundColor="$muted-bg" color="$fg">
540
- {" Cancel "}
541
- </Text>
542
- </Box>
543
- </Box>
544
- </Box>
545
- </Box>
546
-
547
- {showModal && (
548
- <Box position="absolute" display="flex" justifyContent="center" alignItems="center" width="100%" height="100%">
549
- <ModalDialog title="Component Gallery" width={50} footer="ESC or q to close">
550
- <Box flexDirection="column" gap={1}>
551
- <P>
552
- This gallery demonstrates <Strong>silvery</Strong>'s built-in UI components. Every component uses
553
- semantic theme tokens — they adapt to any of the 38 built-in palettes automatically.
554
- </P>
555
- <HR />
556
- <Box flexDirection="column">
557
- <Text color="$success">{"✓ Typography presets (H1-H3, Lead, Muted, Code)"}</Text>
558
- <Text color="$success">{"✓ Input components (TextInput, TextArea, SelectList)"}</Text>
559
- <Text color="$success">{"✓ Display widgets (ProgressBar, Spinner, Badge)"}</Text>
560
- <Text color="$success">{"✓ Layout primitives (Box, Divider, border styles)"}</Text>
561
- <Text color="$success">{"✓ Dialog system (ModalDialog with input blocking)"}</Text>
562
- </Box>
563
- </Box>
564
- </ModalDialog>
565
- </Box>
566
- )}
567
- </Box>
568
- )
569
- }
570
-
571
- // ============================================================================
572
- // App
573
- // ============================================================================
574
-
575
- export function ComponentsApp() {
576
- const { exit } = useApp()
577
- const [activeTab, setActiveTab] = useState("display")
578
- const [scrollOffset, setScrollOffset] = useState(0)
579
-
580
- // Reset scroll when switching tabs
581
- const handleTabChange = useCallback((tab: string) => {
582
- setActiveTab(tab)
583
- setScrollOffset(0)
584
- }, [])
585
-
586
- const tabs = ["display", "inputs", "typography"] as const
587
-
588
- useInput((input: string, key: Key) => {
589
- // Only quit with q when not on the inputs tab (where user may be typing)
590
- if (input === "q" && activeTab !== "inputs") {
591
- exit()
592
- }
593
- if (key.escape && activeTab !== "display") {
594
- exit()
595
- }
596
-
597
- // Tab switching with h/l
598
- if (input === "l" && activeTab !== "inputs") {
599
- const idx = tabs.indexOf(activeTab as (typeof tabs)[number])
600
- handleTabChange(tabs[(idx + 1) % tabs.length]!)
601
- return
602
- }
603
- if (input === "h" && activeTab !== "inputs") {
604
- const idx = tabs.indexOf(activeTab as (typeof tabs)[number])
605
- handleTabChange(tabs[(idx - 1 + tabs.length) % tabs.length]!)
606
- return
607
- }
608
-
609
- // Arrow keys / j/k scroll the active tab content (typography and display tabs)
610
- if (activeTab !== "inputs") {
611
- if (key.downArrow || (activeTab === "typography" && input === "j")) {
612
- setScrollOffset((prev) => prev + 1)
613
- }
614
- if (key.upArrow || (activeTab === "typography" && input === "k")) {
615
- setScrollOffset((prev) => Math.max(0, prev - 1))
616
- }
617
- if (key.pageDown) {
618
- setScrollOffset((prev) => prev + 10)
619
- }
620
- if (key.pageUp) {
621
- setScrollOffset((prev) => Math.max(0, prev - 10))
622
- }
623
- if (key.home || (activeTab === "typography" && input === "g")) {
624
- setScrollOffset(0)
625
- }
626
- if (key.end || (activeTab === "typography" && input === "G")) {
627
- setScrollOffset(999) // will be clamped by scroll phase
628
- }
629
- }
630
- })
631
-
632
- return (
633
- <Box flexDirection="column" flexGrow={1} padding={1}>
634
- {activeTab === "display" && <DisplayTab scrollOffset={scrollOffset} />}
635
- {activeTab === "inputs" && <InputsTab />}
636
- {activeTab === "typography" && <TypographyTab scrollOffset={scrollOffset} />}
637
- </Box>
638
- )
639
- }
640
-
641
- // ============================================================================
642
- // Main
643
- // ============================================================================
644
-
645
- export async function main() {
646
- using term = createTerm()
647
- const { waitUntilExit } = await render(
648
- <ExampleBanner meta={meta} controls="h/l tab Tab cycle inputs j/k navigate Enter modal Esc/q quit">
649
- <ComponentsApp />
650
- </ExampleBanner>,
651
- term,
652
- )
653
- await waitUntilExit()
654
- }
655
-
656
- if (import.meta.main) {
657
- main().catch(console.error)
658
- }