silvery 0.3.0 → 0.4.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.
Files changed (120) hide show
  1. package/README.md +41 -145
  2. package/dist/chalk.js +3 -0
  3. package/dist/chalk.js.map +11 -0
  4. package/dist/index.js +340 -0
  5. package/dist/index.js.map +282 -0
  6. package/dist/ink.js +129 -0
  7. package/dist/ink.js.map +140 -0
  8. package/dist/runtime.js +394 -0
  9. package/dist/runtime.js.map +286 -0
  10. package/dist/theme.js +343 -0
  11. package/dist/theme.js.map +286 -0
  12. package/dist/ui/animation.js +3 -0
  13. package/dist/ui/animation.js.map +15 -0
  14. package/dist/ui/ansi.js +3 -0
  15. package/dist/ui/ansi.js.map +10 -0
  16. package/dist/ui/cli.js +8 -0
  17. package/dist/ui/cli.js.map +14 -0
  18. package/dist/ui/display.js +4 -0
  19. package/dist/ui/display.js.map +10 -0
  20. package/dist/ui/image.js +4 -0
  21. package/dist/ui/image.js.map +15 -0
  22. package/dist/ui/input.js +3 -0
  23. package/dist/ui/input.js.map +11 -0
  24. package/dist/ui/progress.js +8 -0
  25. package/dist/ui/progress.js.map +20 -0
  26. package/dist/ui/react.js +3 -0
  27. package/dist/ui/react.js.map +15 -0
  28. package/dist/ui/utils.js +3 -0
  29. package/dist/ui/utils.js.map +10 -0
  30. package/dist/ui/wrappers.js +14 -0
  31. package/dist/ui/wrappers.js.map +19 -0
  32. package/dist/ui.js +17 -0
  33. package/dist/ui.js.map +20 -0
  34. package/package.json +67 -15
  35. package/src/index.ts +67 -1
  36. package/src/runtime.ts +4 -0
  37. package/src/theme.ts +4 -0
  38. package/src/ui/animation.ts +2 -0
  39. package/src/ui/ansi.ts +2 -0
  40. package/src/ui/cli.ts +2 -0
  41. package/src/ui/display.ts +2 -0
  42. package/src/ui/image.ts +2 -0
  43. package/src/ui/input.ts +2 -0
  44. package/src/ui/progress.ts +2 -0
  45. package/src/ui/react.ts +2 -0
  46. package/src/ui/utils.ts +2 -0
  47. package/src/ui/wrappers.ts +2 -0
  48. package/src/ui.ts +4 -0
  49. package/examples/CLAUDE.md +0 -75
  50. package/examples/_banner.tsx +0 -60
  51. package/examples/cli.ts +0 -228
  52. package/examples/index.md +0 -101
  53. package/examples/inline/inline-nontty.tsx +0 -98
  54. package/examples/inline/inline-progress.tsx +0 -79
  55. package/examples/inline/inline-simple.tsx +0 -63
  56. package/examples/inline/scrollback.tsx +0 -185
  57. package/examples/interactive/_input-debug.tsx +0 -110
  58. package/examples/interactive/_stdin-test.ts +0 -71
  59. package/examples/interactive/_textarea-bare.tsx +0 -45
  60. package/examples/interactive/aichat/components.tsx +0 -468
  61. package/examples/interactive/aichat/index.tsx +0 -207
  62. package/examples/interactive/aichat/script.ts +0 -460
  63. package/examples/interactive/aichat/state.ts +0 -326
  64. package/examples/interactive/aichat/types.ts +0 -19
  65. package/examples/interactive/app-todo.tsx +0 -198
  66. package/examples/interactive/async-data.tsx +0 -208
  67. package/examples/interactive/cli-wizard.tsx +0 -332
  68. package/examples/interactive/clipboard.tsx +0 -183
  69. package/examples/interactive/components.tsx +0 -463
  70. package/examples/interactive/data-explorer.tsx +0 -506
  71. package/examples/interactive/dev-tools.tsx +0 -379
  72. package/examples/interactive/explorer.tsx +0 -747
  73. package/examples/interactive/gallery.tsx +0 -652
  74. package/examples/interactive/inline-bench.tsx +0 -136
  75. package/examples/interactive/kanban.tsx +0 -267
  76. package/examples/interactive/layout-ref.tsx +0 -185
  77. package/examples/interactive/outline.tsx +0 -171
  78. package/examples/interactive/paste-demo.tsx +0 -198
  79. package/examples/interactive/scroll.tsx +0 -77
  80. package/examples/interactive/search-filter.tsx +0 -240
  81. package/examples/interactive/task-list.tsx +0 -279
  82. package/examples/interactive/terminal.tsx +0 -798
  83. package/examples/interactive/textarea.tsx +0 -103
  84. package/examples/interactive/theme.tsx +0 -336
  85. package/examples/interactive/transform.tsx +0 -256
  86. package/examples/interactive/virtual-10k.tsx +0 -413
  87. package/examples/kitty/canvas.tsx +0 -519
  88. package/examples/kitty/generate-samples.ts +0 -236
  89. package/examples/kitty/image-component.tsx +0 -273
  90. package/examples/kitty/images.tsx +0 -604
  91. package/examples/kitty/input.tsx +0 -371
  92. package/examples/kitty/keys.tsx +0 -378
  93. package/examples/kitty/paint.tsx +0 -1017
  94. package/examples/layout/dashboard.tsx +0 -551
  95. package/examples/layout/live-resize.tsx +0 -290
  96. package/examples/layout/overflow.tsx +0 -51
  97. package/examples/playground/README.md +0 -69
  98. package/examples/playground/build.ts +0 -61
  99. package/examples/playground/index.html +0 -420
  100. package/examples/playground/playground-app.tsx +0 -416
  101. package/examples/runtime/elm-counter.tsx +0 -206
  102. package/examples/runtime/hello-runtime.tsx +0 -73
  103. package/examples/runtime/pipe-composition.tsx +0 -184
  104. package/examples/runtime/run-counter.tsx +0 -78
  105. package/examples/runtime/runtime-counter.tsx +0 -197
  106. package/examples/screenshots/generate.tsx +0 -563
  107. package/examples/scrollback-perf.tsx +0 -230
  108. package/examples/viewer.tsx +0 -654
  109. package/examples/web/build.ts +0 -365
  110. package/examples/web/canvas-app.tsx +0 -80
  111. package/examples/web/canvas.html +0 -89
  112. package/examples/web/dom-app.tsx +0 -81
  113. package/examples/web/dom.html +0 -113
  114. package/examples/web/showcase-app.tsx +0 -107
  115. package/examples/web/showcase.html +0 -34
  116. package/examples/web/showcases/index.tsx +0 -56
  117. package/examples/web/viewer-app.tsx +0 -555
  118. package/examples/web/viewer.html +0 -30
  119. package/examples/web/xterm-app.tsx +0 -105
  120. package/examples/web/xterm.html +0 -118
@@ -1,208 +0,0 @@
1
- /**
2
- * Async Data Example
3
- *
4
- * Demonstrates React Suspense for async data loading:
5
- * - Suspense boundaries with fallback UI
6
- * - Multiple independent suspending components
7
- * - Error handling with ErrorBoundary
8
- */
9
-
10
- import React, { Suspense, useState, use } from "react"
11
- import {
12
- render,
13
- Box,
14
- Text,
15
- H1,
16
- Kbd,
17
- Muted,
18
- useInput,
19
- useApp,
20
- createTerm,
21
- ErrorBoundary,
22
- type Key,
23
- } from "../../src/index.js"
24
- import { ExampleBanner, type ExampleMeta } from "../_banner.js"
25
-
26
- export const meta: ExampleMeta = {
27
- name: "Async Data",
28
- description: "React Suspense with independent data sources and error boundaries",
29
- features: ["React Suspense", "use() hook", "ErrorBoundary"],
30
- }
31
-
32
- // ============================================================================
33
- // Data Fetching (simulated)
34
- // ============================================================================
35
-
36
- // Cache for promises (React's use() requires stable promise references)
37
- const cache = new Map<string, Promise<unknown>>()
38
-
39
- function fetchData<T>(key: string, ms: number, data: T): Promise<T> {
40
- if (!cache.has(key)) {
41
- cache.set(key, new Promise<T>((resolve) => setTimeout(() => resolve(data), ms)))
42
- }
43
- return cache.get(key) as Promise<T>
44
- }
45
-
46
- function clearCache() {
47
- cache.clear()
48
- }
49
-
50
- // ============================================================================
51
- // Async Components
52
- // ============================================================================
53
-
54
- interface UserData {
55
- name: string
56
- email: string
57
- role: string
58
- }
59
-
60
- function UserProfile() {
61
- const user = use(
62
- fetchData<UserData>("user", 1500, {
63
- name: "Alice Chen",
64
- email: "alice@example.com",
65
- role: "Developer",
66
- }),
67
- )
68
-
69
- return (
70
- <Box flexDirection="column" borderStyle="round" borderColor="$success" padding={1}>
71
- <H1 color="$success">User Profile</H1>
72
- <Text>Name: {user.name}</Text>
73
- <Text>Email: {user.email}</Text>
74
- <Text>Role: {user.role}</Text>
75
- </Box>
76
- )
77
- }
78
-
79
- interface StatsData {
80
- projects: number
81
- commits: number
82
- reviews: number
83
- }
84
-
85
- function Statistics() {
86
- const stats = use(
87
- fetchData<StatsData>("stats", 2500, {
88
- projects: 12,
89
- commits: 847,
90
- reviews: 156,
91
- }),
92
- )
93
-
94
- return (
95
- <Box flexDirection="column" borderStyle="round" borderColor="$primary" padding={1}>
96
- <H1>Statistics</H1>
97
- <Text>Projects: {stats.projects}</Text>
98
- <Text>Commits: {stats.commits}</Text>
99
- <Text>Reviews: {stats.reviews}</Text>
100
- </Box>
101
- )
102
- }
103
-
104
- interface Activity {
105
- id: number
106
- action: string
107
- time: string
108
- }
109
-
110
- function RecentActivity() {
111
- const activities = use(
112
- fetchData<Activity[]>("activity", 3500, [
113
- { id: 1, action: "Merged PR #423", time: "2h ago" },
114
- { id: 2, action: "Reviewed PR #421", time: "4h ago" },
115
- { id: 3, action: "Created issue #89", time: "1d ago" },
116
- ]),
117
- )
118
-
119
- return (
120
- <Box flexDirection="column" borderStyle="round" borderColor="$warning" padding={1}>
121
- <H1 color="$warning">Recent Activity</H1>
122
- {activities.map((a) => (
123
- <Text key={a.id}>
124
- <Text dim>{a.time}</Text> {a.action}
125
- </Text>
126
- ))}
127
- </Box>
128
- )
129
- }
130
-
131
- // Loading fallbacks
132
- function LoadingBox({ label, color }: { label: string; color: string }) {
133
- return (
134
- <Box borderStyle="round" borderColor="$border" padding={1}>
135
- <Text color="$muted">Loading {label}...</Text>
136
- </Box>
137
- )
138
- }
139
-
140
- // ============================================================================
141
- // Main App
142
- // ============================================================================
143
-
144
- export function AsyncDataApp(): JSX.Element {
145
- const { exit } = useApp()
146
- const [refreshKey, setRefreshKey] = useState(0)
147
-
148
- useInput((input: string, key: Key) => {
149
- if (input === "q" || key.escape) {
150
- exit()
151
- return
152
- }
153
- if (input === "r") {
154
- // Refresh: clear cache and force re-render
155
- clearCache()
156
- setRefreshKey((k) => k + 1)
157
- }
158
- })
159
-
160
- return (
161
- <Box flexDirection="column" padding={1} key={refreshKey}>
162
- <Box flexGrow={1} flexDirection="row" gap={1}>
163
- {/* Each Suspense boundary loads independently */}
164
- <ErrorBoundary fallback={<Text color="$error">User error</Text>}>
165
- <Suspense fallback={<LoadingBox label="user" color="green" />}>
166
- <UserProfile />
167
- </Suspense>
168
- </ErrorBoundary>
169
-
170
- <ErrorBoundary fallback={<Text color="$error">Stats error</Text>}>
171
- <Suspense fallback={<LoadingBox label="stats" color="blue" />}>
172
- <Statistics />
173
- </Suspense>
174
- </ErrorBoundary>
175
-
176
- <ErrorBoundary fallback={<Text color="$error">Activity error</Text>}>
177
- <Suspense fallback={<LoadingBox label="activity" color="yellow" />}>
178
- <RecentActivity />
179
- </Suspense>
180
- </ErrorBoundary>
181
- </Box>
182
-
183
- <Muted>
184
- {" "}
185
- <Kbd>r</Kbd> refresh <Kbd>Esc/q</Kbd> quit
186
- </Muted>
187
- </Box>
188
- )
189
- }
190
-
191
- // ============================================================================
192
- // Main
193
- // ============================================================================
194
-
195
- async function main() {
196
- using term = createTerm()
197
- const { waitUntilExit } = await render(
198
- <ExampleBanner meta={meta} controls="r refresh Esc/q quit">
199
- <AsyncDataApp />
200
- </ExampleBanner>,
201
- term,
202
- )
203
- await waitUntilExit()
204
- }
205
-
206
- if (import.meta.main) {
207
- main().catch(console.error)
208
- }
@@ -1,332 +0,0 @@
1
- /**
2
- * CLI Wizard Example
3
- *
4
- * A multi-step project scaffolding wizard demonstrating:
5
- * - SelectList for option selection with keyboard navigation
6
- * - TextInput for free-form text entry
7
- * - ProgressBar for visual progress feedback
8
- * - Step-by-step flow with state transitions
9
- *
10
- * Usage: bun run examples/interactive/cli-wizard.tsx
11
- *
12
- * Controls:
13
- * j/k or Up/Down - Navigate options (step 1)
14
- * Enter - Confirm selection / submit input
15
- * Type - Enter project name (step 2)
16
- * q or Esc - Quit (when not typing)
17
- */
18
-
19
- import React, { useState, useCallback, useEffect } from "react"
20
- import {
21
- render,
22
- Box,
23
- Text,
24
- SelectList,
25
- TextInput,
26
- ProgressBar,
27
- Spinner,
28
- useInput,
29
- useApp,
30
- createTerm,
31
- H1,
32
- Muted,
33
- Lead,
34
- Kbd,
35
- Code,
36
- type Key,
37
- type SelectOption,
38
- } from "../../src/index.js"
39
- import { ExampleBanner, type ExampleMeta } from "../_banner.js"
40
-
41
- export const meta: ExampleMeta = {
42
- name: "CLI Wizard",
43
- description: "Multi-step project scaffolding wizard with selection, input, and progress",
44
- demo: true,
45
- features: ["SelectList", "TextInput", "ProgressBar", "Spinner", "useInput()"],
46
- }
47
-
48
- // ============================================================================
49
- // Types
50
- // ============================================================================
51
-
52
- type WizardStep = "framework" | "name" | "installing" | "done"
53
-
54
- interface WizardState {
55
- step: WizardStep
56
- framework: string | null
57
- projectName: string
58
- progress: number
59
- }
60
-
61
- // ============================================================================
62
- // Data
63
- // ============================================================================
64
-
65
- const FRAMEWORKS: SelectOption[] = [
66
- { label: "React — A JavaScript library for building user interfaces", value: "react" },
67
- { label: "Vue — The progressive JavaScript framework", value: "vue" },
68
- { label: "Svelte — Cybernetically enhanced web apps", value: "svelte" },
69
- { label: "Solid — Simple and performant reactivity", value: "solid" },
70
- {
71
- label: "Angular — Platform for building web applications",
72
- value: "angular",
73
- disabled: true,
74
- },
75
- ]
76
-
77
- const INSTALL_STEPS = [
78
- "Resolving dependencies...",
79
- "Downloading packages...",
80
- "Linking dependencies...",
81
- "Building project...",
82
- "Generating types...",
83
- "Setting up config...",
84
- "Done!",
85
- ]
86
-
87
- // ============================================================================
88
- // Components
89
- // ============================================================================
90
-
91
- /** Step indicator showing current position in the wizard */
92
- function StepIndicator({ current, total }: { current: number; total: number }): JSX.Element {
93
- return (
94
- <Box paddingX={1} marginBottom={1}>
95
- {Array.from({ length: total }, (_, i) => {
96
- const isDone = i < current
97
- const isCurrent = i === current
98
- const dot = isDone ? "\u25cf" : isCurrent ? "\u25cb" : "\u25cb"
99
- const color = isDone ? "$success" : isCurrent ? "$primary" : "$muted"
100
- return (
101
- <Text key={i} color={color} bold={isCurrent}>
102
- {dot}
103
- {i < total - 1 ? " \u2500 " : ""}
104
- </Text>
105
- )
106
- })}
107
- </Box>
108
- )
109
- }
110
-
111
- /** Step 1: Framework selection */
112
- function FrameworkStep({ onSelect }: { onSelect: (option: SelectOption) => void }): JSX.Element {
113
- return (
114
- <Box flexDirection="column" paddingX={1}>
115
- <Box marginBottom={1}>
116
- <H1>? Select a framework:</H1>
117
- </Box>
118
- <SelectList items={FRAMEWORKS} onSelect={onSelect} />
119
- <Box marginTop={1}>
120
- <Lead>(Angular is coming soon)</Lead>
121
- </Box>
122
- </Box>
123
- )
124
- }
125
-
126
- /** Step 2: Project name input */
127
- function NameStep({
128
- value,
129
- onChange,
130
- onSubmit,
131
- framework,
132
- }: {
133
- value: string
134
- onChange: (v: string) => void
135
- onSubmit: (v: string) => void
136
- framework: string
137
- }): JSX.Element {
138
- return (
139
- <Box flexDirection="column" paddingX={1}>
140
- <Box marginBottom={1}>
141
- <H1>? Project name</H1>
142
- <Muted> ({framework})</Muted>
143
- <H1>:</H1>
144
- </Box>
145
- <Box>
146
- <Text color="$muted">{"\u276f "}</Text>
147
- <TextInput value={value} onChange={onChange} onSubmit={onSubmit} prompt="" />
148
- </Box>
149
- <Box marginTop={1}>
150
- <Muted>Press Enter to confirm</Muted>
151
- </Box>
152
- </Box>
153
- )
154
- }
155
-
156
- /** Step 3: Installation progress */
157
- function InstallStep({ progress, stepIndex }: { progress: number; stepIndex: number }): JSX.Element {
158
- const currentStep = INSTALL_STEPS[Math.min(stepIndex, INSTALL_STEPS.length - 1)]!
159
-
160
- return (
161
- <Box flexDirection="column" paddingX={1}>
162
- <Box marginBottom={1}>
163
- <Spinner type="dots" />
164
- <Text bold color="$warning">
165
- {" "}
166
- Installing dependencies...
167
- </Text>
168
- </Box>
169
-
170
- <Box marginBottom={1}>
171
- <ProgressBar value={progress} color="$primary" label="" />
172
- </Box>
173
-
174
- <Muted>{currentStep}</Muted>
175
- </Box>
176
- )
177
- }
178
-
179
- /** Step 4: Completion summary */
180
- function DoneStep({ framework, projectName }: { framework: string; projectName: string }): JSX.Element {
181
- return (
182
- <Box flexDirection="column" paddingX={1}>
183
- <Box marginBottom={1}>
184
- <H1 color="$success">{"\u2714"} Project created successfully!</H1>
185
- </Box>
186
-
187
- <Box flexDirection="column" borderStyle="round" borderColor="$success" paddingX={2} paddingY={1}>
188
- <Box>
189
- <Muted>Framework: </Muted>
190
- <Text bold>{framework}</Text>
191
- </Box>
192
- <Box>
193
- <Muted>Project: </Muted>
194
- <Text bold>{projectName}</Text>
195
- </Box>
196
- <Box>
197
- <Muted>Location: </Muted>
198
- <Text bold>./{projectName}/</Text>
199
- </Box>
200
- </Box>
201
-
202
- <Box flexDirection="column" marginTop={1}>
203
- <Muted>Get started:</Muted>
204
- <Code>
205
- {" "}cd {projectName}
206
- </Code>
207
- <Code>{" "}bun install</Code>
208
- <Code>{" "}bun dev</Code>
209
- </Box>
210
-
211
- <Box marginTop={1}>
212
- <Muted>
213
- Press <Kbd>q</Kbd> or <Kbd>Esc</Kbd> to exit
214
- </Muted>
215
- </Box>
216
- </Box>
217
- )
218
- }
219
-
220
- // ============================================================================
221
- // Main App
222
- // ============================================================================
223
-
224
- export function CliWizard(): JSX.Element {
225
- const { exit } = useApp()
226
- const [state, setState] = useState<WizardState>({
227
- step: "framework",
228
- framework: null,
229
- projectName: "",
230
- progress: 0,
231
- })
232
-
233
- // Handle framework selection
234
- const handleFrameworkSelect = useCallback((option: SelectOption) => {
235
- setState((prev) => ({
236
- ...prev,
237
- step: "name",
238
- framework: option.value,
239
- projectName: `my-${option.value}-app`,
240
- }))
241
- }, [])
242
-
243
- // Handle project name change
244
- const handleNameChange = useCallback((value: string) => {
245
- setState((prev) => ({ ...prev, projectName: value }))
246
- }, [])
247
-
248
- // Handle project name submission
249
- const handleNameSubmit = useCallback((value: string) => {
250
- if (value.trim()) {
251
- setState((prev) => ({ ...prev, step: "installing", progress: 0 }))
252
- }
253
- }, [])
254
-
255
- // Simulate installation progress
256
- useEffect(() => {
257
- if (state.step !== "installing") return
258
-
259
- const timer = setInterval(() => {
260
- setState((prev) => {
261
- const next = prev.progress + 0.08 + Math.random() * 0.04
262
- if (next >= 1) {
263
- clearInterval(timer)
264
- return { ...prev, step: "done", progress: 1 }
265
- }
266
- return { ...prev, progress: next }
267
- })
268
- }, 200)
269
-
270
- return () => clearInterval(timer)
271
- }, [state.step])
272
-
273
- // Global quit handler (only when not in text input step)
274
- useInput((input: string, key: Key) => {
275
- if (state.step === "name") return // TextInput handles its own input
276
- if (input === "q" || key.escape) {
277
- exit()
278
- }
279
- })
280
-
281
- // Map progress to step index for display
282
- const installStepIndex = Math.floor(state.progress * (INSTALL_STEPS.length - 1))
283
-
284
- const stepNumber = state.step === "framework" ? 0 : state.step === "name" ? 1 : state.step === "installing" ? 2 : 3
285
-
286
- return (
287
- <Box flexDirection="column" flexGrow={1}>
288
- <Box borderStyle="single" borderColor="$primary" paddingX={2} marginBottom={1}>
289
- <H1>create-app</H1>
290
- <Muted> v1.0.0</Muted>
291
- </Box>
292
-
293
- <StepIndicator current={stepNumber} total={4} />
294
-
295
- {state.step === "framework" && <FrameworkStep onSelect={handleFrameworkSelect} />}
296
-
297
- {state.step === "name" && state.framework && (
298
- <NameStep
299
- value={state.projectName}
300
- onChange={handleNameChange}
301
- onSubmit={handleNameSubmit}
302
- framework={state.framework}
303
- />
304
- )}
305
-
306
- {state.step === "installing" && <InstallStep progress={state.progress} stepIndex={installStepIndex} />}
307
-
308
- {state.step === "done" && state.framework && (
309
- <DoneStep framework={state.framework} projectName={state.projectName} />
310
- )}
311
- </Box>
312
- )
313
- }
314
-
315
- // ============================================================================
316
- // Main
317
- // ============================================================================
318
-
319
- async function main() {
320
- using term = createTerm()
321
- const { waitUntilExit } = await render(
322
- <ExampleBanner meta={meta} controls="j/k navigate Enter select q/Esc quit">
323
- <CliWizard />
324
- </ExampleBanner>,
325
- term,
326
- )
327
- await waitUntilExit()
328
- }
329
-
330
- if (import.meta.main) {
331
- main().catch(console.error)
332
- }