@silvery/examples 0.5.2 → 0.5.3

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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/{examples/apps → apps}/aichat/index.tsx +4 -3
  3. package/{examples/apps → apps}/async-data.tsx +4 -4
  4. package/apps/components.tsx +658 -0
  5. package/{examples/apps → apps}/data-explorer.tsx +8 -8
  6. package/{examples/apps → apps}/dev-tools.tsx +35 -19
  7. package/{examples/apps → apps}/inline-bench.tsx +3 -1
  8. package/{examples/apps → apps}/kanban.tsx +20 -22
  9. package/{examples/apps → apps}/layout-ref.tsx +6 -6
  10. package/{examples/apps → apps}/panes/index.tsx +1 -1
  11. package/{examples/apps → apps}/paste-demo.tsx +2 -2
  12. package/{examples/apps → apps}/scroll.tsx +2 -2
  13. package/{examples/apps → apps}/search-filter.tsx +1 -1
  14. package/apps/selection.tsx +342 -0
  15. package/apps/spatial-focus-demo.tsx +368 -0
  16. package/{examples/apps → apps}/task-list.tsx +1 -1
  17. package/apps/terminal-caps-demo.tsx +334 -0
  18. package/apps/text-selection-demo.tsx +189 -0
  19. package/apps/textarea.tsx +155 -0
  20. package/{examples/apps → apps}/theme.tsx +1 -1
  21. package/apps/vterm-demo/index.tsx +216 -0
  22. package/dist/cli.d.mts +1 -0
  23. package/dist/cli.mjs +190 -0
  24. package/dist/cli.mjs.map +1 -0
  25. package/layout/dashboard.tsx +953 -0
  26. package/layout/live-resize.tsx +282 -0
  27. package/layout/overflow.tsx +51 -0
  28. package/layout/text-layout.tsx +283 -0
  29. package/package.json +27 -11
  30. package/bin/cli.ts +0 -294
  31. package/examples/apps/components.tsx +0 -463
  32. package/examples/apps/textarea.tsx +0 -91
  33. /package/{examples/_banner.tsx → _banner.tsx} +0 -0
  34. /package/{examples/apps → apps}/aichat/components.tsx +0 -0
  35. /package/{examples/apps → apps}/aichat/script.ts +0 -0
  36. /package/{examples/apps → apps}/aichat/state.ts +0 -0
  37. /package/{examples/apps → apps}/aichat/types.ts +0 -0
  38. /package/{examples/apps → apps}/app-todo.tsx +0 -0
  39. /package/{examples/apps → apps}/cli-wizard.tsx +0 -0
  40. /package/{examples/apps → apps}/clipboard.tsx +0 -0
  41. /package/{examples/apps → apps}/explorer.tsx +0 -0
  42. /package/{examples/apps → apps}/gallery.tsx +0 -0
  43. /package/{examples/apps → apps}/outline.tsx +0 -0
  44. /package/{examples/apps → apps}/terminal.tsx +0 -0
  45. /package/{examples/apps → apps}/transform.tsx +0 -0
  46. /package/{examples/apps → apps}/virtual-10k.tsx +0 -0
  47. /package/{examples/components → components}/counter.tsx +0 -0
  48. /package/{examples/components → components}/hello.tsx +0 -0
  49. /package/{examples/components → components}/progress-bar.tsx +0 -0
  50. /package/{examples/components → components}/select-list.tsx +0 -0
  51. /package/{examples/components → components}/spinner.tsx +0 -0
  52. /package/{examples/components → components}/text-input.tsx +0 -0
  53. /package/{examples/components → components}/virtual-list.tsx +0 -0
package/bin/cli.ts DELETED
@@ -1,294 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * silvery CLI
4
- *
5
- * Usage:
6
- * bunx silvery — show help
7
- * bunx silvery <name> — run an example by name (fuzzy match)
8
- * bunx silvery examples — list all available examples
9
- * bunx silvery doctor — check terminal capabilities
10
- * bunx silvery --help — show usage help
11
- */
12
-
13
- // =============================================================================
14
- // ANSI helpers (no deps — must work before anything is imported)
15
- // =============================================================================
16
-
17
- const RESET = "\x1b[0m"
18
- const BOLD = "\x1b[1m"
19
- const DIM = "\x1b[2m"
20
- const RED = "\x1b[31m"
21
- const GREEN = "\x1b[32m"
22
- const YELLOW = "\x1b[33m"
23
- const BLUE = "\x1b[34m"
24
- const MAGENTA = "\x1b[35m"
25
- const CYAN = "\x1b[36m"
26
- const WHITE = "\x1b[37m"
27
-
28
- // =============================================================================
29
- // Types
30
- // =============================================================================
31
-
32
- interface Example {
33
- name: string
34
- file: string
35
- description: string
36
- category: string
37
- features?: string[]
38
- }
39
-
40
- // =============================================================================
41
- // Auto-Discovery
42
- // =============================================================================
43
-
44
- const CATEGORY_DIRS = ["components", "apps", "layout", "runtime", "inline", "kitty"] as const
45
-
46
- const CATEGORY_DISPLAY: Record<string, string> = {
47
- kitty: "Kitty Protocol",
48
- }
49
-
50
- const CATEGORY_ORDER: Record<string, number> = {
51
- Components: 0,
52
- Apps: 1,
53
- Layout: 2,
54
- Runtime: 3,
55
- Inline: 4,
56
- "Kitty Protocol": 5,
57
- }
58
-
59
- const CATEGORY_COLOR: Record<string, string> = {
60
- Components: GREEN,
61
- Apps: CYAN,
62
- Layout: MAGENTA,
63
- Runtime: BLUE,
64
- Inline: YELLOW,
65
- "Kitty Protocol": BLUE,
66
- }
67
-
68
- async function discoverExamples(): Promise<Example[]> {
69
- const { resolve, dirname } = await import("node:path")
70
- const { fileURLToPath } = await import("node:url")
71
- const { readdirSync } = await import("node:fs")
72
- const __dirname = dirname(fileURLToPath(import.meta.url))
73
- const examplesDir = resolve(__dirname, "../examples")
74
- const results: Example[] = []
75
-
76
- for (const dir of CATEGORY_DIRS) {
77
- const category = CATEGORY_DISPLAY[dir] ?? dir.charAt(0).toUpperCase() + dir.slice(1)
78
- const dirPath = resolve(examplesDir, dir)
79
-
80
- try {
81
- const files = readdirSync(dirPath).filter((f: string) => f.endsWith(".tsx") && !f.startsWith("_"))
82
- for (const file of files) {
83
- const name = file.replace(/\.tsx$/, "").replace(/-/g, " ")
84
- results.push({
85
- name,
86
- description: "",
87
- file: resolve(dirPath, file),
88
- category,
89
- })
90
- }
91
- } catch {
92
- // Directory doesn't exist — skip
93
- }
94
- }
95
-
96
- // Also scan aichat subdirectory
97
- const aichatDir = resolve(examplesDir, "apps/aichat")
98
- try {
99
- const indexFile = resolve(aichatDir, "index.tsx")
100
- const { stat } = await import("node:fs/promises")
101
- await stat(indexFile)
102
- results.push({
103
- name: "aichat",
104
- description: "AI Coding Agent demo",
105
- file: indexFile,
106
- category: "Apps",
107
- })
108
- } catch {
109
- // No aichat
110
- }
111
-
112
- results.sort((a, b) => {
113
- const catDiff = (CATEGORY_ORDER[a.category] ?? 99) - (CATEGORY_ORDER[b.category] ?? 99)
114
- if (catDiff !== 0) return catDiff
115
- return a.name.localeCompare(b.name)
116
- })
117
-
118
- return results
119
- }
120
-
121
- // =============================================================================
122
- // Formatting
123
- // =============================================================================
124
-
125
- function printHelp(): void {
126
- console.log(`
127
- ${BOLD}${YELLOW}@silvery/examples${RESET} — Try silvery without installing
128
-
129
- ${BOLD}Usage:${RESET}
130
- bunx @silvery/examples ${DIM}<name>${RESET} Run an example by name (fuzzy match)
131
- bunx @silvery/examples List all available examples
132
- bunx @silvery/examples --help Show this help
133
-
134
- ${BOLD}Quick start:${RESET}
135
- bunx @silvery/examples counter Simple counter (Hello World)
136
- bunx @silvery/examples dashboard Responsive layout demo
137
- bunx @silvery/examples kanban Kanban board with keyboard nav
138
- bunx @silvery/examples textarea Rich text editor
139
-
140
- ${DIM}Documentation: https://silvery.dev${RESET}
141
- `)
142
- }
143
-
144
- function printExampleList(examples: Example[]): void {
145
- console.log(`\n${BOLD}${YELLOW} silvery${RESET}${DIM} examples${RESET}\n`)
146
-
147
- let currentCategory = ""
148
-
149
- for (const ex of examples) {
150
- if (ex.category !== currentCategory) {
151
- currentCategory = ex.category
152
- const color = CATEGORY_COLOR[currentCategory] ?? WHITE
153
- console.log(` ${color}${BOLD}${currentCategory}${RESET}`)
154
- }
155
-
156
- const nameStr = `${BOLD}${WHITE}${ex.name}${RESET}`
157
- const descStr = ex.description ? `${DIM}${ex.description}${RESET}` : ""
158
- console.log(` ${nameStr} ${descStr}`)
159
- }
160
-
161
- console.log(`\n ${DIM}Run: bunx @silvery/examples <name>${RESET}\n`)
162
- }
163
-
164
- function findExample(examples: Example[], query: string): Example | undefined {
165
- const q = query.toLowerCase().replace(/-/g, " ")
166
-
167
- const exact = examples.find((ex) => ex.name.toLowerCase() === q)
168
- if (exact) return exact
169
-
170
- const prefix = examples.find((ex) => ex.name.toLowerCase().startsWith(q))
171
- if (prefix) return prefix
172
-
173
- const substring = examples.find((ex) => ex.name.toLowerCase().includes(q))
174
- if (substring) return substring
175
-
176
- return undefined
177
- }
178
-
179
- function printNoMatch(query: string, examples: Example[]): void {
180
- console.error(`\n${RED}${BOLD}Error:${RESET} No example matching "${query}"\n`)
181
- console.error(`${DIM}Available examples:${RESET}`)
182
-
183
- for (const ex of examples) {
184
- console.error(` ${WHITE}${ex.name}${RESET}`)
185
- }
186
-
187
- console.error(`\n${DIM}Run ${BOLD}bunx @silvery/examples${RESET}${DIM} for full list.${RESET}\n`)
188
- }
189
-
190
- // =============================================================================
191
- // Subcommands
192
- // =============================================================================
193
-
194
- async function exampleCommand(args: string[]): Promise<void> {
195
- const examples = await discoverExamples()
196
-
197
- if (args.length === 0 || args[0] === "--list" || args[0] === "-l") {
198
- printExampleList(examples)
199
- return
200
- }
201
-
202
- const query = args.filter((a) => !a.startsWith("--")).join(" ")
203
- if (!query) {
204
- printExampleList(examples)
205
- return
206
- }
207
-
208
- const match = findExample(examples, query)
209
- if (!match) {
210
- printNoMatch(query, examples)
211
- process.exit(1)
212
- }
213
-
214
- console.log(`${DIM}Running ${BOLD}${match.name}${RESET}${DIM}...${RESET}\n`)
215
-
216
- const { spawn } = await import("node:child_process")
217
- const runtime = typeof globalThis.Bun !== "undefined" ? "bun" : "node"
218
- const proc = spawn(runtime, ["run", match.file], { stdio: "inherit" })
219
- proc.on("exit", (code) => process.exit(code ?? 1))
220
- }
221
-
222
- async function doctorCommand(): Promise<void> {
223
- const { resolve, dirname } = await import("node:path")
224
- const { fileURLToPath } = await import("node:url")
225
- const __dirname = dirname(fileURLToPath(import.meta.url))
226
-
227
- const candidates = [
228
- resolve(__dirname, "../../ag-term/src/termtest.ts"),
229
- resolve(__dirname, "../node_modules/@silvery/ag-term/src/termtest.ts"),
230
- ]
231
-
232
- for (const termtestPath of candidates) {
233
- try {
234
- const { stat } = await import("node:fs/promises")
235
- await stat(termtestPath)
236
- const { spawn } = await import("node:child_process")
237
- const runtime = typeof globalThis.Bun !== "undefined" ? "bun" : "node"
238
- const proc = spawn(runtime, ["run", termtestPath], { stdio: "inherit" })
239
- proc.on("exit", (code) => process.exit(code ?? 1))
240
- return
241
- } catch {
242
- continue
243
- }
244
- }
245
-
246
- console.error(`${RED}Error:${RESET} Could not find terminal diagnostics.`)
247
- console.error(`${DIM}Make sure silvery is installed: npm install silvery${RESET}`)
248
- process.exit(1)
249
- }
250
-
251
- // =============================================================================
252
- // Main
253
- // =============================================================================
254
-
255
- async function main(): Promise<void> {
256
- const args = process.argv.slice(2)
257
-
258
- // Top-level flags
259
- if (args.includes("--help") || args.includes("-h")) {
260
- printHelp()
261
- return
262
- }
263
-
264
- // No args → list examples
265
- if (args.length === 0) {
266
- const examples = await discoverExamples()
267
- printExampleList(examples)
268
- return
269
- }
270
-
271
- if (args.includes("--version") || args.includes("-v")) {
272
- try {
273
- const { resolve, dirname } = await import("node:path")
274
- const { fileURLToPath } = await import("node:url")
275
- const { readFileSync } = await import("node:fs")
276
- const __dirname = dirname(fileURLToPath(import.meta.url))
277
- const pkgPath = resolve(__dirname, "../package.json")
278
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"))
279
- console.log(`@silvery/examples ${pkg.version}`)
280
- } catch {
281
- console.log("@silvery/examples (version unknown)")
282
- }
283
- return
284
- }
285
-
286
- // "bunx @silvery/examples counter" → run counter example directly
287
- // "bunx @silvery/examples" → list (handled above by args.length === 0)
288
- await exampleCommand(args)
289
- }
290
-
291
- main().catch((err) => {
292
- console.error(err)
293
- process.exit(1)
294
- })