@silvery/examples 0.17.3 → 0.17.5
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/UPNG-ShUlaTDh.mjs +5074 -0
- package/dist/__vite-browser-external-2447137e-Bopa5BFR.mjs +4 -0
- package/dist/_banner-A70_y2Vi.mjs +43 -0
- package/dist/ansi-0VXlUmNn.mjs +16397 -0
- package/dist/apng-B0gRaDVT.mjs +3 -0
- package/dist/apng-BTRDTfDW.mjs +68 -0
- package/dist/apps/aichat/index.mjs +1298 -0
- package/dist/apps/app-todo.mjs +138 -0
- package/dist/apps/async-data.mjs +203 -0
- package/dist/apps/cli-wizard.mjs +338 -0
- package/dist/apps/clipboard.mjs +197 -0
- package/dist/apps/components.mjs +863 -0
- package/dist/apps/data-explorer.mjs +482 -0
- package/dist/apps/dev-tools.mjs +396 -0
- package/dist/apps/explorer.mjs +697 -0
- package/dist/apps/gallery.mjs +765 -0
- package/dist/apps/inline-bench.mjs +115 -0
- package/dist/apps/kanban.mjs +279 -0
- package/dist/apps/layout-ref.mjs +186 -0
- package/dist/apps/outline.mjs +202 -0
- package/dist/apps/paste-demo.mjs +188 -0
- package/dist/apps/scroll.mjs +85 -0
- package/dist/apps/search-filter.mjs +286 -0
- package/dist/apps/selection.mjs +354 -0
- package/dist/apps/spatial-focus-demo.mjs +387 -0
- package/dist/apps/task-list.mjs +257 -0
- package/dist/apps/terminal-caps-demo.mjs +314 -0
- package/dist/apps/terminal.mjs +871 -0
- package/dist/apps/text-selection-demo.mjs +253 -0
- package/dist/apps/textarea.mjs +177 -0
- package/dist/apps/theme.mjs +660 -0
- package/dist/apps/transform.mjs +214 -0
- package/dist/apps/virtual-10k.mjs +421 -0
- package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
- package/dist/backends-Dj-11kZF.mjs +1179 -0
- package/dist/backends-U3QwStfO.mjs +3 -0
- package/dist/{cli.mjs → bin/cli.mjs} +15 -19
- package/dist/chunk-BSw8zbkd.mjs +37 -0
- package/dist/components/counter.mjs +47 -0
- package/dist/components/hello.mjs +30 -0
- package/dist/components/progress-bar.mjs +58 -0
- package/dist/components/select-list.mjs +84 -0
- package/dist/components/spinner.mjs +56 -0
- package/dist/components/text-input.mjs +61 -0
- package/dist/components/virtual-list.mjs +50 -0
- package/dist/flexily-zero-adapter-ByVzLTFP.mjs +3374 -0
- package/dist/gif-B6NGH5gs.mjs +3 -0
- package/dist/gif-CfkOF-iG.mjs +71 -0
- package/dist/gifenc-BI4ihP_T.mjs +728 -0
- package/dist/key-mapping-5oYQdAQE.mjs +3 -0
- package/dist/key-mapping-D4LR1go6.mjs +130 -0
- package/dist/layout/dashboard.mjs +1203 -0
- package/dist/layout/live-resize.mjs +302 -0
- package/dist/layout/overflow.mjs +69 -0
- package/dist/layout/text-layout.mjs +334 -0
- package/dist/node-nsrAOjH4.mjs +1083 -0
- package/dist/plugins-CT0DdV_E.mjs +3056 -0
- package/dist/resvg-js-Cnk2o49d.mjs +201 -0
- package/dist/src-9ZhfQyzD.mjs +814 -0
- package/dist/src-CUUOuRH6.mjs +5322 -0
- package/dist/src-jO3Zuzjj.mjs +23538 -0
- package/dist/usingCtx-CsEf0xO3.mjs +57 -0
- package/dist/yoga-adapter-BSQHuMV9.mjs +237 -0
- package/package.json +21 -14
- package/_banner.tsx +0 -60
- package/apps/aichat/components.tsx +0 -469
- package/apps/aichat/index.tsx +0 -220
- package/apps/aichat/script.ts +0 -460
- package/apps/aichat/state.ts +0 -325
- package/apps/aichat/types.ts +0 -19
- package/apps/app-todo.tsx +0 -201
- package/apps/async-data.tsx +0 -196
- package/apps/cli-wizard.tsx +0 -332
- package/apps/clipboard.tsx +0 -183
- package/apps/components.tsx +0 -658
- package/apps/data-explorer.tsx +0 -490
- package/apps/dev-tools.tsx +0 -395
- package/apps/explorer.tsx +0 -731
- package/apps/gallery.tsx +0 -653
- package/apps/inline-bench.tsx +0 -138
- package/apps/kanban.tsx +0 -265
- package/apps/layout-ref.tsx +0 -173
- package/apps/outline.tsx +0 -160
- package/apps/panes/index.tsx +0 -203
- package/apps/paste-demo.tsx +0 -185
- package/apps/scroll.tsx +0 -80
- package/apps/search-filter.tsx +0 -240
- package/apps/selection.tsx +0 -346
- package/apps/spatial-focus-demo.tsx +0 -372
- package/apps/task-list.tsx +0 -271
- package/apps/terminal-caps-demo.tsx +0 -317
- package/apps/terminal.tsx +0 -784
- package/apps/text-selection-demo.tsx +0 -193
- package/apps/textarea.tsx +0 -155
- package/apps/theme.tsx +0 -515
- package/apps/transform.tsx +0 -229
- package/apps/virtual-10k.tsx +0 -405
- package/apps/vterm-demo/index.tsx +0 -216
- package/components/counter.tsx +0 -49
- package/components/hello.tsx +0 -38
- package/components/progress-bar.tsx +0 -52
- package/components/select-list.tsx +0 -54
- package/components/spinner.tsx +0 -44
- package/components/text-input.tsx +0 -61
- package/components/virtual-list.tsx +0 -56
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs.map +0 -1
- package/layout/dashboard.tsx +0 -953
- package/layout/live-resize.tsx +0 -282
- package/layout/overflow.tsx +0 -51
- package/layout/text-layout.tsx +0 -283
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VTerm Demo -- same ChatApp in inline, fullscreen, and panes modes.
|
|
3
|
-
*
|
|
4
|
-
* Run: bun examples/apps/vterm-demo/index.tsx --mode=inline|fullscreen|panes [--fast]
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React, { useState, useEffect } from "react"
|
|
8
|
-
import { Box, Text, ListView, useWindowSize } from "silvery"
|
|
9
|
-
import { SearchProvider, SearchBar } from "@silvery/ag-react"
|
|
10
|
-
import { run, useInput, type Key } from "silvery/runtime"
|
|
11
|
-
import type { ExampleMeta } from "../../_banner.js"
|
|
12
|
-
import { SCRIPT } from "../aichat/script.js"
|
|
13
|
-
import type { ScriptEntry } from "../aichat/types.js"
|
|
14
|
-
import type { Exchange } from "../aichat/types.js"
|
|
15
|
-
|
|
16
|
-
export const meta: ExampleMeta = {
|
|
17
|
-
name: "VTerm Demo",
|
|
18
|
-
description: "Same chat app in inline, fullscreen, and panes modes",
|
|
19
|
-
demo: true,
|
|
20
|
-
features: ["ListView", "SearchProvider", "inline", "fullscreen", "panes"],
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface ListItemMeta {
|
|
24
|
-
isCursor: boolean
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// ============================================================================
|
|
28
|
-
// Auto-advancing scripted content
|
|
29
|
-
// ============================================================================
|
|
30
|
-
|
|
31
|
-
function useAutoContent(script: ScriptEntry[], fast: boolean): Exchange[] {
|
|
32
|
-
const [exchanges, setExchanges] = useState<Exchange[]>([])
|
|
33
|
-
const [idx, setIdx] = useState(0)
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
if (idx >= script.length) return
|
|
37
|
-
const delay = fast ? 120 : 600 + Math.random() * 1000
|
|
38
|
-
const timer = setTimeout(() => {
|
|
39
|
-
const entry = script[idx]!
|
|
40
|
-
setExchanges((prev) => [...prev, { ...entry, id: idx }])
|
|
41
|
-
setIdx((i) => i + 1)
|
|
42
|
-
}, delay)
|
|
43
|
-
return () => clearTimeout(timer)
|
|
44
|
-
}, [idx, script, fast])
|
|
45
|
-
|
|
46
|
-
return exchanges
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ============================================================================
|
|
50
|
-
// ChatApp — reusable across all modes
|
|
51
|
-
// ============================================================================
|
|
52
|
-
|
|
53
|
-
function ChatApp({
|
|
54
|
-
height,
|
|
55
|
-
active = true,
|
|
56
|
-
surfaceId,
|
|
57
|
-
fast,
|
|
58
|
-
}: {
|
|
59
|
-
height: number
|
|
60
|
-
active?: boolean
|
|
61
|
-
surfaceId?: string
|
|
62
|
-
fast: boolean
|
|
63
|
-
}) {
|
|
64
|
-
const exchanges = useAutoContent(SCRIPT, fast)
|
|
65
|
-
|
|
66
|
-
if (exchanges.length === 0) {
|
|
67
|
-
return (
|
|
68
|
-
<Box paddingX={1}>
|
|
69
|
-
<Text color="$muted">Waiting...</Text>
|
|
70
|
-
</Box>
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<ListView
|
|
76
|
-
items={exchanges}
|
|
77
|
-
height={height}
|
|
78
|
-
getKey={(ex: Exchange) => ex.id}
|
|
79
|
-
scrollTo={exchanges.length - 1}
|
|
80
|
-
active={active}
|
|
81
|
-
surfaceId={surfaceId}
|
|
82
|
-
cache={{
|
|
83
|
-
mode: "virtual",
|
|
84
|
-
isCacheable: (_ex: Exchange, i: number) => i < exchanges.length - 1,
|
|
85
|
-
}}
|
|
86
|
-
search={{ getText: (ex: Exchange) => ex.content }}
|
|
87
|
-
renderItem={(ex: Exchange, _i: number, meta: ListItemMeta) => (
|
|
88
|
-
<Box paddingX={1}>
|
|
89
|
-
<Text>
|
|
90
|
-
{meta.isCursor ? ">" : " "}{" "}
|
|
91
|
-
<Text color={ex.role === "user" ? "$primary" : ex.role === "agent" ? "$success" : "$warning"} bold>
|
|
92
|
-
{ex.role}
|
|
93
|
-
</Text>
|
|
94
|
-
: {ex.content.slice(0, 70)}
|
|
95
|
-
</Text>
|
|
96
|
-
</Box>
|
|
97
|
-
)}
|
|
98
|
-
/>
|
|
99
|
-
)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// ============================================================================
|
|
103
|
-
// Status bar
|
|
104
|
-
// ============================================================================
|
|
105
|
-
|
|
106
|
-
function StatusBar({ mode, count }: { mode: string; count: number }) {
|
|
107
|
-
return (
|
|
108
|
-
<Box paddingX={1}>
|
|
109
|
-
<Text color="$muted">
|
|
110
|
-
[{mode}] {count} exchanges | Ctrl+F search | q quit
|
|
111
|
-
</Text>
|
|
112
|
-
</Box>
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ============================================================================
|
|
117
|
-
// Fullscreen / Inline layout
|
|
118
|
-
// ============================================================================
|
|
119
|
-
|
|
120
|
-
function SingleApp({ mode, fast }: { mode: string; fast: boolean }) {
|
|
121
|
-
const exchanges = useAutoContent(SCRIPT, fast)
|
|
122
|
-
const { rows } = useWindowSize()
|
|
123
|
-
|
|
124
|
-
useInput((_input: string, key: Key) => {
|
|
125
|
-
if (key.escape || _input === "q") return "exit"
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
const listHeight = Math.max(5, rows - 2)
|
|
129
|
-
|
|
130
|
-
return (
|
|
131
|
-
<Box flexDirection="column" height={rows}>
|
|
132
|
-
<ChatApp height={listHeight} fast={fast} />
|
|
133
|
-
<SearchBar />
|
|
134
|
-
<StatusBar mode={mode} count={exchanges.length} />
|
|
135
|
-
</Box>
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// ============================================================================
|
|
140
|
-
// Panes layout
|
|
141
|
-
// ============================================================================
|
|
142
|
-
|
|
143
|
-
function PanesApp({ fast }: { fast: boolean }) {
|
|
144
|
-
const [focus, setFocus] = useState<"left" | "right">("left")
|
|
145
|
-
const exchanges = useAutoContent(SCRIPT, fast)
|
|
146
|
-
const { rows } = useWindowSize()
|
|
147
|
-
|
|
148
|
-
useInput((_input: string, key: Key) => {
|
|
149
|
-
if (key.escape || _input === "q") return "exit"
|
|
150
|
-
if (key.tab) setFocus((f) => (f === "left" ? "right" : "left"))
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
const listHeight = Math.max(5, rows - 4)
|
|
154
|
-
|
|
155
|
-
return (
|
|
156
|
-
<Box flexDirection="column" height={rows}>
|
|
157
|
-
<Box flexDirection="row" flexGrow={1}>
|
|
158
|
-
<Box
|
|
159
|
-
width="50%"
|
|
160
|
-
flexDirection="column"
|
|
161
|
-
borderStyle="single"
|
|
162
|
-
borderColor={focus === "left" ? "$primary" : "$border"}
|
|
163
|
-
overflow="hidden"
|
|
164
|
-
>
|
|
165
|
-
<Box paddingX={1}>
|
|
166
|
-
<Text color={focus === "left" ? "$primary" : "$muted"} bold={focus === "left"}>
|
|
167
|
-
Pane A
|
|
168
|
-
</Text>
|
|
169
|
-
</Box>
|
|
170
|
-
<ChatApp height={listHeight} active={focus === "left"} surfaceId="left" fast={fast} />
|
|
171
|
-
</Box>
|
|
172
|
-
<Box
|
|
173
|
-
width="50%"
|
|
174
|
-
flexDirection="column"
|
|
175
|
-
borderStyle="single"
|
|
176
|
-
borderColor={focus === "right" ? "$primary" : "$border"}
|
|
177
|
-
overflow="hidden"
|
|
178
|
-
>
|
|
179
|
-
<Box paddingX={1}>
|
|
180
|
-
<Text color={focus === "right" ? "$primary" : "$muted"} bold={focus === "right"}>
|
|
181
|
-
Pane B
|
|
182
|
-
</Text>
|
|
183
|
-
</Box>
|
|
184
|
-
<ChatApp height={listHeight} active={focus === "right"} surfaceId="right" fast={fast} />
|
|
185
|
-
</Box>
|
|
186
|
-
</Box>
|
|
187
|
-
<SearchBar />
|
|
188
|
-
<StatusBar mode="panes" count={exchanges.length} />
|
|
189
|
-
</Box>
|
|
190
|
-
)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// ============================================================================
|
|
194
|
-
// Entry
|
|
195
|
-
// ============================================================================
|
|
196
|
-
|
|
197
|
-
export async function main() {
|
|
198
|
-
const args = process.argv.slice(2)
|
|
199
|
-
const fast = args.includes("--fast")
|
|
200
|
-
const modeArg = args.find((a) => a.startsWith("--mode="))?.split("=")[1] ?? "fullscreen"
|
|
201
|
-
const mode = modeArg as "inline" | "fullscreen" | "panes"
|
|
202
|
-
|
|
203
|
-
const runtimeMode = mode === "panes" ? "fullscreen" : mode
|
|
204
|
-
const app = mode === "panes" ? <PanesApp fast={fast} /> : <SingleApp mode={mode} fast={fast} />
|
|
205
|
-
|
|
206
|
-
using handle = await run(<SearchProvider>{app}</SearchProvider>, {
|
|
207
|
-
mode: runtimeMode,
|
|
208
|
-
kitty: false,
|
|
209
|
-
textSizing: false,
|
|
210
|
-
})
|
|
211
|
-
await handle.waitUntilExit()
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (import.meta.main) {
|
|
215
|
-
await main()
|
|
216
|
-
}
|
package/components/counter.tsx
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Counter — Interactive state with useInput
|
|
3
|
-
*
|
|
4
|
-
* The "Hello World" of interactive TUIs: a counter you
|
|
5
|
-
* increment/decrement with j/k.
|
|
6
|
-
*
|
|
7
|
-
* Usage: bun examples/components/counter.tsx
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import React, { useState } from "react"
|
|
11
|
-
import { Box, Text } from "silvery"
|
|
12
|
-
import { run, useInput } from "silvery/runtime"
|
|
13
|
-
|
|
14
|
-
function Counter() {
|
|
15
|
-
const [count, setCount] = useState(0)
|
|
16
|
-
|
|
17
|
-
useInput((input, key) => {
|
|
18
|
-
if (input === "j" || key.downArrow) setCount((c) => c + 1)
|
|
19
|
-
if (input === "k" || key.upArrow) setCount((c) => c - 1)
|
|
20
|
-
if (input === "r") setCount(0)
|
|
21
|
-
if (input === "q" || key.escape) return "exit"
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<Box flexDirection="column" padding={1}>
|
|
26
|
-
<Box gap={1}>
|
|
27
|
-
<Text>Count:</Text>
|
|
28
|
-
<Text bold color={count >= 0 ? "$success" : "$error"}>
|
|
29
|
-
{count}
|
|
30
|
-
</Text>
|
|
31
|
-
</Box>
|
|
32
|
-
<Text color="$muted">j/k: +/- r: reset q: quit</Text>
|
|
33
|
-
</Box>
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const meta = {
|
|
38
|
-
name: "Counter",
|
|
39
|
-
description: "Interactive counter with useState + useInput",
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function main() {
|
|
43
|
-
const handle = await run(<Counter />)
|
|
44
|
-
await handle.waitUntilExit()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (import.meta.main) {
|
|
48
|
-
await main()
|
|
49
|
-
}
|
package/components/hello.tsx
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hello — The simplest silvery app
|
|
3
|
-
*
|
|
4
|
-
* Renders styled text and exits on any keypress.
|
|
5
|
-
*
|
|
6
|
-
* Usage: bun examples/components/hello.tsx
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import React from "react"
|
|
10
|
-
import { Box, Text } from "silvery"
|
|
11
|
-
import { run, useInput } from "silvery/runtime"
|
|
12
|
-
|
|
13
|
-
function Hello() {
|
|
14
|
-
useInput(() => "exit" as const)
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<Box flexDirection="column" padding={1}>
|
|
18
|
-
<Text bold color="$primary">
|
|
19
|
-
Hello, Silvery!
|
|
20
|
-
</Text>
|
|
21
|
-
<Text color="$muted">Press any key to exit.</Text>
|
|
22
|
-
</Box>
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const meta = {
|
|
27
|
-
name: "Hello",
|
|
28
|
-
description: "The simplest silvery app — styled text, exit on keypress",
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export async function main() {
|
|
32
|
-
const handle = await run(<Hello />)
|
|
33
|
-
await handle.waitUntilExit()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (import.meta.main) {
|
|
37
|
-
await main()
|
|
38
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ProgressBar — Determinate and indeterminate progress
|
|
3
|
-
*
|
|
4
|
-
* Press j/k to adjust progress. Shows determinate bars with
|
|
5
|
-
* percentage labels and an indeterminate animated bar.
|
|
6
|
-
*
|
|
7
|
-
* Usage: bun examples/components/progress-bar.tsx
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import React, { useState } from "react"
|
|
11
|
-
import { Box, Text, ProgressBar } from "silvery"
|
|
12
|
-
import { run, useInput } from "silvery/runtime"
|
|
13
|
-
|
|
14
|
-
function ProgressBarDemo() {
|
|
15
|
-
const [progress, setProgress] = useState(0.4)
|
|
16
|
-
|
|
17
|
-
useInput((input, key) => {
|
|
18
|
-
if (input === "j" || key.rightArrow) setProgress((p) => Math.min(1, p + 0.05))
|
|
19
|
-
if (input === "k" || key.leftArrow) setProgress((p) => Math.max(0, p - 0.05))
|
|
20
|
-
if (input === "q" || key.escape) return "exit"
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<Box flexDirection="column" padding={1} gap={1}>
|
|
25
|
-
<Text bold>Determinate</Text>
|
|
26
|
-
<Box width={40}>
|
|
27
|
-
<ProgressBar value={progress} />
|
|
28
|
-
</Box>
|
|
29
|
-
|
|
30
|
-
<Text bold>Indeterminate</Text>
|
|
31
|
-
<Box width={40}>
|
|
32
|
-
<ProgressBar />
|
|
33
|
-
</Box>
|
|
34
|
-
|
|
35
|
-
<Text color="$muted">j/k: adjust q: quit</Text>
|
|
36
|
-
</Box>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const meta = {
|
|
41
|
-
name: "Progress Bar",
|
|
42
|
-
description: "Determinate and indeterminate progress bars",
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function main() {
|
|
46
|
-
const handle = await run(<ProgressBarDemo />)
|
|
47
|
-
await handle.waitUntilExit()
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (import.meta.main) {
|
|
51
|
-
await main()
|
|
52
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SelectList — Keyboard-navigable single-select
|
|
3
|
-
*
|
|
4
|
-
* Navigate with j/k or arrows, confirm with Enter.
|
|
5
|
-
* Disabled items are automatically skipped.
|
|
6
|
-
*
|
|
7
|
-
* Usage: bun examples/components/select-list.tsx
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import React, { useState } from "react"
|
|
11
|
-
import { Box, Text, SelectList } from "silvery"
|
|
12
|
-
import { run, useInput } from "silvery/runtime"
|
|
13
|
-
|
|
14
|
-
const languages = [
|
|
15
|
-
{ label: "TypeScript", value: "ts" },
|
|
16
|
-
{ label: "Rust", value: "rs" },
|
|
17
|
-
{ label: "Go", value: "go" },
|
|
18
|
-
{ label: "Python", value: "py" },
|
|
19
|
-
{ label: "COBOL", value: "cob", disabled: true },
|
|
20
|
-
{ label: "Elixir", value: "ex" },
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
function SelectListDemo() {
|
|
24
|
-
const [selected, setSelected] = useState<string | null>(null)
|
|
25
|
-
|
|
26
|
-
useInput((_, key) => {
|
|
27
|
-
if (key.escape) return "exit"
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<Box flexDirection="column" padding={1} gap={1}>
|
|
32
|
-
<Text bold>Pick a language:</Text>
|
|
33
|
-
<Box borderStyle="round" borderColor="$border" paddingX={1}>
|
|
34
|
-
<SelectList items={languages} onSelect={(opt) => setSelected(opt.value)} />
|
|
35
|
-
</Box>
|
|
36
|
-
{selected && <Text color="$success">Selected: {selected}</Text>}
|
|
37
|
-
<Text color="$muted">j/k: navigate Enter: select Esc: quit</Text>
|
|
38
|
-
</Box>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const meta = {
|
|
43
|
-
name: "Select List",
|
|
44
|
-
description: "Keyboard-navigable single-select list",
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function main() {
|
|
48
|
-
const handle = await run(<SelectListDemo />)
|
|
49
|
-
await handle.waitUntilExit()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (import.meta.main) {
|
|
53
|
-
await main()
|
|
54
|
-
}
|
package/components/spinner.tsx
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spinner — Animated loading indicators
|
|
3
|
-
*
|
|
4
|
-
* Shows all four built-in spinner styles side by side.
|
|
5
|
-
*
|
|
6
|
-
* Usage: bun examples/components/spinner.tsx
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import React from "react"
|
|
10
|
-
import { Box, Text, Spinner } from "silvery"
|
|
11
|
-
import { run, useInput } from "silvery/runtime"
|
|
12
|
-
|
|
13
|
-
function SpinnerDemo() {
|
|
14
|
-
useInput((input, key) => {
|
|
15
|
-
if (input === "q" || key.escape) return "exit"
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<Box flexDirection="column" padding={1} gap={1}>
|
|
20
|
-
<Text bold>Spinner Styles</Text>
|
|
21
|
-
<Box flexDirection="column">
|
|
22
|
-
<Spinner type="dots" label="Loading packages..." />
|
|
23
|
-
<Spinner type="line" label="Compiling..." />
|
|
24
|
-
<Spinner type="arc" label="Optimizing..." />
|
|
25
|
-
<Spinner type="bounce" label="Connecting..." />
|
|
26
|
-
</Box>
|
|
27
|
-
<Text color="$muted">q: quit</Text>
|
|
28
|
-
</Box>
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const meta = {
|
|
33
|
-
name: "Spinner",
|
|
34
|
-
description: "Four animated loading spinner styles",
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export async function main() {
|
|
38
|
-
const handle = await run(<SpinnerDemo />)
|
|
39
|
-
await handle.waitUntilExit()
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (import.meta.main) {
|
|
43
|
-
await main()
|
|
44
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TextInput — Single-line text entry
|
|
3
|
-
*
|
|
4
|
-
* Shows TextInput with placeholder, prompt, and submit handler.
|
|
5
|
-
* Full readline keybindings (Ctrl+A/E/K/U, Alt+B/F) are built in.
|
|
6
|
-
*
|
|
7
|
-
* Usage: bun examples/components/text-input.tsx
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import React, { useState } from "react"
|
|
11
|
-
import { Box, Text, TextInput } from "silvery"
|
|
12
|
-
import { run, useInput } from "silvery/runtime"
|
|
13
|
-
|
|
14
|
-
function TextInputDemo() {
|
|
15
|
-
const [value, setValue] = useState("")
|
|
16
|
-
const [submitted, setSubmitted] = useState<string[]>([])
|
|
17
|
-
|
|
18
|
-
useInput((_, key) => {
|
|
19
|
-
if (key.escape) return "exit"
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<Box flexDirection="column" padding={1} gap={1}>
|
|
24
|
-
<TextInput
|
|
25
|
-
value={value}
|
|
26
|
-
onChange={setValue}
|
|
27
|
-
onSubmit={(val) => {
|
|
28
|
-
setSubmitted((prev) => [...prev, val])
|
|
29
|
-
setValue("")
|
|
30
|
-
}}
|
|
31
|
-
placeholder="Type something and press Enter..."
|
|
32
|
-
prompt="> "
|
|
33
|
-
/>
|
|
34
|
-
{submitted.length > 0 && (
|
|
35
|
-
<Box flexDirection="column">
|
|
36
|
-
<Text color="$muted">Submitted:</Text>
|
|
37
|
-
{submitted.map((s, i) => (
|
|
38
|
-
<Text key={i} color="$success">
|
|
39
|
-
{s}
|
|
40
|
-
</Text>
|
|
41
|
-
))}
|
|
42
|
-
</Box>
|
|
43
|
-
)}
|
|
44
|
-
<Text color="$muted">Enter: submit Esc: quit</Text>
|
|
45
|
-
</Box>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const meta = {
|
|
50
|
-
name: "Text Input",
|
|
51
|
-
description: "Single-line text entry with readline keybindings",
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export async function main() {
|
|
55
|
-
const handle = await run(<TextInputDemo />)
|
|
56
|
-
await handle.waitUntilExit()
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (import.meta.main) {
|
|
60
|
-
await main()
|
|
61
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ListView — Efficient scrollable list
|
|
3
|
-
*
|
|
4
|
-
* Renders 200 items but only materializes visible rows.
|
|
5
|
-
* Built-in j/k navigation, page up/down, Home/End.
|
|
6
|
-
*
|
|
7
|
-
* Usage: bun examples/components/virtual-list.tsx
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import React from "react"
|
|
11
|
-
import { Box, Text, ListView } from "silvery"
|
|
12
|
-
import { run, useInput } from "silvery/runtime"
|
|
13
|
-
|
|
14
|
-
const items = Array.from({ length: 200 }, (_, i) => ({
|
|
15
|
-
id: i,
|
|
16
|
-
name: `Item ${i + 1}`,
|
|
17
|
-
}))
|
|
18
|
-
|
|
19
|
-
function ListViewDemo() {
|
|
20
|
-
useInput((input, key) => {
|
|
21
|
-
if (input === "q" || key.escape) return "exit"
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<Box flexDirection="column" padding={1} gap={1}>
|
|
26
|
-
<Text bold>200 items (virtualized)</Text>
|
|
27
|
-
<ListView
|
|
28
|
-
items={items}
|
|
29
|
-
height={12}
|
|
30
|
-
estimateHeight={1}
|
|
31
|
-
nav
|
|
32
|
-
renderItem={(item, _index, meta) => (
|
|
33
|
-
<Text key={item.id} color={meta.isCursor ? "$primary" : undefined} bold={meta.isCursor}>
|
|
34
|
-
{meta.isCursor ? "> " : " "}
|
|
35
|
-
{item.name}
|
|
36
|
-
</Text>
|
|
37
|
-
)}
|
|
38
|
-
/>
|
|
39
|
-
<Text color="$muted">j/k: navigate q: quit</Text>
|
|
40
|
-
</Box>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const meta = {
|
|
45
|
-
name: "Virtual List",
|
|
46
|
-
description: "Efficient scrollable list with 200 virtualized items",
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export async function main() {
|
|
50
|
-
const handle = await run(<ListViewDemo />)
|
|
51
|
-
await handle.waitUntilExit()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (import.meta.main) {
|
|
55
|
-
await main()
|
|
56
|
-
}
|
package/dist/cli.d.mts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|
package/dist/cli.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../bin/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * silvery CLI\n *\n * Usage:\n * bunx silvery — show help\n * bunx silvery <name> — run an example by name (fuzzy match)\n * bunx silvery examples — list all available examples\n * bunx silvery doctor — check terminal capabilities\n * bunx silvery --help — show usage help\n */\n\n// =============================================================================\n// ANSI helpers (no deps — must work before anything is imported)\n// =============================================================================\n\nconst RESET = \"\\x1b[0m\"\nconst BOLD = \"\\x1b[1m\"\nconst DIM = \"\\x1b[2m\"\nconst RED = \"\\x1b[31m\"\nconst GREEN = \"\\x1b[32m\"\nconst YELLOW = \"\\x1b[33m\"\nconst BLUE = \"\\x1b[34m\"\nconst MAGENTA = \"\\x1b[35m\"\nconst CYAN = \"\\x1b[36m\"\nconst WHITE = \"\\x1b[37m\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface Example {\n name: string\n file: string\n description: string\n category: string\n features?: string[]\n}\n\n// =============================================================================\n// Auto-Discovery\n// =============================================================================\n\nconst CATEGORY_DIRS = [\"components\", \"apps\", \"layout\", \"runtime\", \"inline\", \"kitty\"] as const\n\nconst CATEGORY_DISPLAY: Record<string, string> = {\n kitty: \"Kitty Protocol\",\n}\n\nconst CATEGORY_ORDER: Record<string, number> = {\n Components: 0,\n Apps: 1,\n Layout: 2,\n Runtime: 3,\n Inline: 4,\n \"Kitty Protocol\": 5,\n}\n\nconst CATEGORY_COLOR: Record<string, string> = {\n Components: GREEN,\n Apps: CYAN,\n Layout: MAGENTA,\n Runtime: BLUE,\n Inline: YELLOW,\n \"Kitty Protocol\": BLUE,\n}\n\nasync function discoverExamples(): Promise<Example[]> {\n const { resolve, dirname } = await import(\"node:path\")\n const { fileURLToPath } = await import(\"node:url\")\n const { readdirSync } = await import(\"node:fs\")\n const __dirname = dirname(fileURLToPath(import.meta.url))\n const examplesDir = resolve(__dirname, \"..\")\n const results: Example[] = []\n\n for (const dir of CATEGORY_DIRS) {\n const category = CATEGORY_DISPLAY[dir] ?? dir.charAt(0).toUpperCase() + dir.slice(1)\n const dirPath = resolve(examplesDir, dir)\n\n try {\n const files = readdirSync(dirPath).filter((f: string) => f.endsWith(\".tsx\") && !f.startsWith(\"_\"))\n for (const file of files) {\n const name = file.replace(/\\.tsx$/, \"\").replace(/-/g, \" \")\n results.push({\n name,\n description: \"\",\n file: resolve(dirPath, file),\n category,\n })\n }\n } catch {\n // Directory doesn't exist — skip\n }\n }\n\n // Also scan aichat subdirectory\n const aichatDir = resolve(examplesDir, \"apps/aichat\")\n try {\n const indexFile = resolve(aichatDir, \"index.tsx\")\n const { stat } = await import(\"node:fs/promises\")\n await stat(indexFile)\n results.push({\n name: \"aichat\",\n description: \"AI Coding Agent demo\",\n file: indexFile,\n category: \"Apps\",\n })\n } catch {\n // No aichat\n }\n\n results.sort((a, b) => {\n const catDiff = (CATEGORY_ORDER[a.category] ?? 99) - (CATEGORY_ORDER[b.category] ?? 99)\n if (catDiff !== 0) return catDiff\n return a.name.localeCompare(b.name)\n })\n\n return results\n}\n\n// =============================================================================\n// Formatting\n// =============================================================================\n\nfunction printHelp(): void {\n console.log(`\n${BOLD}${YELLOW}@silvery/examples${RESET} — Try silvery without installing\n\n${BOLD}Usage:${RESET}\n bunx @silvery/examples ${DIM}<name>${RESET} Run an example by name (fuzzy match)\n bunx @silvery/examples List all available examples\n bunx @silvery/examples --help Show this help\n\n${BOLD}Quick start:${RESET}\n bunx @silvery/examples counter Simple counter (Hello World)\n bunx @silvery/examples dashboard Responsive layout demo\n bunx @silvery/examples kanban Kanban board with keyboard nav\n bunx @silvery/examples textarea Rich text editor\n\n${DIM}Documentation: https://silvery.dev${RESET}\n`)\n}\n\nfunction printExampleList(examples: Example[]): void {\n console.log(`\\n${BOLD}${YELLOW} silvery${RESET}${DIM} examples${RESET}\\n`)\n\n let currentCategory = \"\"\n\n for (const ex of examples) {\n if (ex.category !== currentCategory) {\n currentCategory = ex.category\n const color = CATEGORY_COLOR[currentCategory] ?? WHITE\n console.log(` ${color}${BOLD}${currentCategory}${RESET}`)\n }\n\n const nameStr = `${BOLD}${WHITE}${ex.name}${RESET}`\n const descStr = ex.description ? `${DIM}${ex.description}${RESET}` : \"\"\n console.log(` ${nameStr} ${descStr}`)\n }\n\n console.log(`\\n ${DIM}Run: bunx @silvery/examples <name>${RESET}\\n`)\n}\n\nfunction findExample(examples: Example[], query: string): Example | undefined {\n const q = query.toLowerCase().replace(/-/g, \" \")\n\n const exact = examples.find((ex) => ex.name.toLowerCase() === q)\n if (exact) return exact\n\n const prefix = examples.find((ex) => ex.name.toLowerCase().startsWith(q))\n if (prefix) return prefix\n\n const substring = examples.find((ex) => ex.name.toLowerCase().includes(q))\n if (substring) return substring\n\n return undefined\n}\n\nfunction printNoMatch(query: string, examples: Example[]): void {\n console.error(`\\n${RED}${BOLD}Error:${RESET} No example matching \"${query}\"\\n`)\n console.error(`${DIM}Available examples:${RESET}`)\n\n for (const ex of examples) {\n console.error(` ${WHITE}${ex.name}${RESET}`)\n }\n\n console.error(`\\n${DIM}Run ${BOLD}bunx @silvery/examples${RESET}${DIM} for full list.${RESET}\\n`)\n}\n\n// =============================================================================\n// Subcommands\n// =============================================================================\n\nasync function exampleCommand(args: string[]): Promise<void> {\n const examples = await discoverExamples()\n\n if (args.length === 0 || args[0] === \"--list\" || args[0] === \"-l\") {\n printExampleList(examples)\n return\n }\n\n const query = args.filter((a) => !a.startsWith(\"--\")).join(\" \")\n if (!query) {\n printExampleList(examples)\n return\n }\n\n const match = findExample(examples, query)\n if (!match) {\n printNoMatch(query, examples)\n process.exit(1)\n }\n\n console.log(`${DIM}Running ${BOLD}${match.name}${RESET}${DIM}...${RESET}\\n`)\n\n const mod = await import(match.file)\n if (typeof mod.main === \"function\") {\n await mod.main()\n } else {\n console.error(`${RED}Error:${RESET} Example does not export a main() function`)\n process.exit(1)\n }\n}\n\nasync function doctorCommand(): Promise<void> {\n const { resolve, dirname } = await import(\"node:path\")\n const { fileURLToPath } = await import(\"node:url\")\n const __dirname = dirname(fileURLToPath(import.meta.url))\n\n const candidates = [\n resolve(__dirname, \"../../ag-term/src/termtest.ts\"),\n resolve(__dirname, \"../node_modules/@silvery/ag-term/src/termtest.ts\"),\n ]\n\n for (const termtestPath of candidates) {\n try {\n const { stat } = await import(\"node:fs/promises\")\n await stat(termtestPath)\n const mod = await import(termtestPath)\n if (typeof mod.main === \"function\") {\n await mod.main()\n } else {\n // Fallback: module runs on import (legacy pattern)\n }\n return\n } catch {\n continue\n }\n }\n\n console.error(`${RED}Error:${RESET} Could not find terminal diagnostics.`)\n console.error(`${DIM}Make sure silvery is installed: npm install silvery${RESET}`)\n process.exit(1)\n}\n\n// =============================================================================\n// Main\n// =============================================================================\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2)\n\n // Top-level flags\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n printHelp()\n return\n }\n\n // No args → list examples\n if (args.length === 0) {\n const examples = await discoverExamples()\n printExampleList(examples)\n return\n }\n\n if (args.includes(\"--version\") || args.includes(\"-v\")) {\n try {\n const { resolve, dirname } = await import(\"node:path\")\n const { fileURLToPath } = await import(\"node:url\")\n const { readFileSync } = await import(\"node:fs\")\n const __dirname = dirname(fileURLToPath(import.meta.url))\n const pkgPath = resolve(__dirname, \"../package.json\")\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as { version?: string }\n console.log(`@silvery/examples ${pkg.version}`)\n } catch {\n console.log(\"@silvery/examples (version unknown)\")\n }\n return\n }\n\n // \"bunx @silvery/examples counter\" → run counter example directly\n // \"bunx @silvery/examples\" → list (handled above by args.length === 0)\n await exampleCommand(args)\n}\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n"],"mappings":"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"}
|