silvery 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +41 -145
  2. package/package.json +64 -12
  3. package/src/index.ts +67 -1
  4. package/src/runtime.ts +4 -0
  5. package/src/theme.ts +4 -0
  6. package/src/ui/animation.ts +2 -0
  7. package/src/ui/ansi.ts +2 -0
  8. package/src/ui/cli.ts +2 -0
  9. package/src/ui/display.ts +2 -0
  10. package/src/ui/image.ts +2 -0
  11. package/src/ui/input.ts +2 -0
  12. package/src/ui/progress.ts +2 -0
  13. package/src/ui/react.ts +2 -0
  14. package/src/ui/utils.ts +2 -0
  15. package/src/ui/wrappers.ts +2 -0
  16. package/src/ui.ts +4 -0
  17. package/examples/CLAUDE.md +0 -75
  18. package/examples/_banner.tsx +0 -60
  19. package/examples/cli.ts +0 -228
  20. package/examples/index.md +0 -101
  21. package/examples/inline/inline-nontty.tsx +0 -98
  22. package/examples/inline/inline-progress.tsx +0 -79
  23. package/examples/inline/inline-simple.tsx +0 -63
  24. package/examples/inline/scrollback.tsx +0 -185
  25. package/examples/interactive/_input-debug.tsx +0 -110
  26. package/examples/interactive/_stdin-test.ts +0 -71
  27. package/examples/interactive/_textarea-bare.tsx +0 -45
  28. package/examples/interactive/aichat/components.tsx +0 -468
  29. package/examples/interactive/aichat/index.tsx +0 -207
  30. package/examples/interactive/aichat/script.ts +0 -460
  31. package/examples/interactive/aichat/state.ts +0 -326
  32. package/examples/interactive/aichat/types.ts +0 -19
  33. package/examples/interactive/app-todo.tsx +0 -198
  34. package/examples/interactive/async-data.tsx +0 -208
  35. package/examples/interactive/cli-wizard.tsx +0 -332
  36. package/examples/interactive/clipboard.tsx +0 -183
  37. package/examples/interactive/components.tsx +0 -463
  38. package/examples/interactive/data-explorer.tsx +0 -506
  39. package/examples/interactive/dev-tools.tsx +0 -379
  40. package/examples/interactive/explorer.tsx +0 -747
  41. package/examples/interactive/gallery.tsx +0 -652
  42. package/examples/interactive/inline-bench.tsx +0 -136
  43. package/examples/interactive/kanban.tsx +0 -267
  44. package/examples/interactive/layout-ref.tsx +0 -185
  45. package/examples/interactive/outline.tsx +0 -171
  46. package/examples/interactive/paste-demo.tsx +0 -198
  47. package/examples/interactive/scroll.tsx +0 -77
  48. package/examples/interactive/search-filter.tsx +0 -240
  49. package/examples/interactive/task-list.tsx +0 -279
  50. package/examples/interactive/terminal.tsx +0 -798
  51. package/examples/interactive/textarea.tsx +0 -103
  52. package/examples/interactive/theme.tsx +0 -336
  53. package/examples/interactive/transform.tsx +0 -256
  54. package/examples/interactive/virtual-10k.tsx +0 -413
  55. package/examples/kitty/canvas.tsx +0 -519
  56. package/examples/kitty/generate-samples.ts +0 -236
  57. package/examples/kitty/image-component.tsx +0 -273
  58. package/examples/kitty/images.tsx +0 -604
  59. package/examples/kitty/input.tsx +0 -371
  60. package/examples/kitty/keys.tsx +0 -378
  61. package/examples/kitty/paint.tsx +0 -1017
  62. package/examples/layout/dashboard.tsx +0 -551
  63. package/examples/layout/live-resize.tsx +0 -290
  64. package/examples/layout/overflow.tsx +0 -51
  65. package/examples/playground/README.md +0 -69
  66. package/examples/playground/build.ts +0 -61
  67. package/examples/playground/index.html +0 -420
  68. package/examples/playground/playground-app.tsx +0 -416
  69. package/examples/runtime/elm-counter.tsx +0 -206
  70. package/examples/runtime/hello-runtime.tsx +0 -73
  71. package/examples/runtime/pipe-composition.tsx +0 -184
  72. package/examples/runtime/run-counter.tsx +0 -78
  73. package/examples/runtime/runtime-counter.tsx +0 -197
  74. package/examples/screenshots/generate.tsx +0 -563
  75. package/examples/scrollback-perf.tsx +0 -230
  76. package/examples/viewer.tsx +0 -654
  77. package/examples/web/build.ts +0 -365
  78. package/examples/web/canvas-app.tsx +0 -80
  79. package/examples/web/canvas.html +0 -89
  80. package/examples/web/dom-app.tsx +0 -81
  81. package/examples/web/dom.html +0 -113
  82. package/examples/web/showcase-app.tsx +0 -107
  83. package/examples/web/showcase.html +0 -34
  84. package/examples/web/showcases/index.tsx +0 -56
  85. package/examples/web/viewer-app.tsx +0 -555
  86. package/examples/web/viewer.html +0 -30
  87. package/examples/web/xterm-app.tsx +0 -105
  88. package/examples/web/xterm.html +0 -118
package/README.md CHANGED
@@ -2,181 +2,77 @@
2
2
 
3
3
  **Polished Terminal UIs in React.**
4
4
 
5
- Responsive layouts, scrollable containers, 100x+ faster incremental updates, and full support for modern terminal capabilities. 30+ components from TextInput to VirtualList. Pure TypeScript, no WASM.
5
+ Ink-compatible React renderer for terminals same `Box`, `Text`, `useInput` API you know. Plus everything you wish Ink had.
6
6
 
7
- ```
8
- npm install silvery react
9
- ```
7
+ > **Note:** Under active development. APIs may change. Feedback welcome.
10
8
 
11
- > **Status:** Alpha — under active development. APIs may change. Early adopters and feedback welcome.
9
+ ```console
10
+ $ npm install silvery react
11
+ ```
12
12
 
13
13
  ```tsx
14
14
  import { useState } from "react"
15
- import { render, Box, Text, useInput, useContentRect, createTerm } from "silvery"
15
+ import { render, Box, Text, useInput } from "silvery"
16
16
 
17
- function App() {
18
- const { width } = useContentRect()
17
+ function Counter() {
19
18
  const [count, setCount] = useState(0)
20
-
21
19
  useInput((input) => {
22
20
  if (input === "j") setCount((c) => c + 1)
23
- if (input === "k") setCount((c) => c - 1)
24
- if (input === "q") return "exit"
25
21
  })
26
-
27
22
  return (
28
- <Box flexDirection="column" padding={1}>
29
- <Text bold>Counter ({width} cols wide)</Text>
23
+ <Box borderStyle="round" padding={1}>
30
24
  <Text>Count: {count}</Text>
31
- <Text dim>j/k = change, q = quit</Text>
32
25
  </Box>
33
26
  )
34
27
  }
35
28
 
36
- using term = createTerm()
37
- await render(<App />, term).run()
38
- ```
39
-
40
- ## Renderer
41
-
42
- ### Responsive layout
43
-
44
- `useContentRect()` returns actual dimensions synchronously -- no post-layout effect, no `{width: 0, height: 0}` on first render. Components adapt to their available space immediately.
45
-
46
- ```tsx
47
- function Responsive() {
48
- const { width } = useContentRect()
49
- return width > 80 ? <FullDashboard /> : <CompactView />
50
- }
51
- ```
52
-
53
- ### Scrollable containers
54
-
55
- `overflow="scroll"` with `scrollTo` -- the framework handles measurement, clipping, and scroll position. No manual virtualization needed.
56
-
57
- ```tsx
58
- <Box height={20} overflow="scroll" scrollTo={selectedIndex}>
59
- {items.map((item) => (
60
- <Card key={item.id} item={item} />
61
- ))}
62
- </Box>
63
- ```
64
-
65
- ### Per-node dirty tracking
66
-
67
- Seven independent dirty flags per node. When a user presses a key, only the affected nodes re-render -- bypassing React reconciliation entirely for unchanged subtrees. Typical interactive updates complete in ~170 microseconds for 1000 nodes, compared to full-tree re-renders.
68
-
69
- ### Multi-target rendering
70
-
71
- Terminal today, Canvas 2D and DOM experimental. Same React components, different rendering backends.
72
-
73
- ## Framework Layers (Optional)
74
-
75
- ### Input layer stack
76
-
77
- DOM-style event bubbling with modal isolation. Opening a dialog automatically captures input -- no manual guard checks in every handler.
78
-
79
- ```tsx
80
- <InputLayerProvider>
81
- <Board />
82
- {isOpen && <Dialog />} {/* Dialog captures input; Board doesn't see it */}
83
- </InputLayerProvider>
29
+ await render(<Counter />).run()
84
30
  ```
85
31
 
86
- ### Spatial focus navigation
32
+ ### Familiar
87
33
 
88
- Tree-based focus with scopes, arrow-key directional movement, click-to-focus, and `useFocusWithin`. Go beyond tab-order.
34
+ - **React 18 + 19** hooks, refs, effects, suspense all works
35
+ - **Flexbox layout** — `Box` with `flexDirection`, `padding`, `gap`, `flexGrow`, just like Ink
36
+ - **Ink/Chalk compatible** — same component model, `@silvery/ink` compatibility layer for migration
89
37
 
90
- ### Command and keybinding system
38
+ ### Better
91
39
 
92
- Named commands with IDs, help text, configurable keybindings, and runtime introspection. Build discoverable, AI-automatable interfaces.
93
-
94
- ```tsx
95
- const MyComponent = withCommands(BaseComponent, () => [
96
- { id: "save", label: "Save", keys: ["ctrl+s"], action: () => save() },
97
- { id: "quit", label: "Quit", keys: ["q", "ctrl+c"], action: () => exit() },
98
- ])
99
- ```
100
-
101
- ### Mouse support
102
-
103
- SGR mouse protocol with DOM-style event props -- `onClick`, `onMouseDown`, `onWheel`, hit testing, drag support.
104
-
105
- ### Multi-line text editing
106
-
107
- Built-in `TextArea` with word wrap, scrolling, cursor movement, selection, and undo/redo via `EditContext`.
108
-
109
- ### 30+ built-in components
110
-
111
- TextArea, TextInput, VirtualList, SelectList, Table, CommandPalette, ModalDialog, Tabs, TreeView, SplitView, Toast, Image, and more -- all with built-in scrolling, focus, and input handling.
112
-
113
- ### Theme system
114
-
115
- `@silvery/theme` with 38 built-in palettes and semantic color tokens (`$primary`, `$error`, `$border`, etc.) that adapt automatically.
116
-
117
- ### TEA state machines
118
-
119
- Optional [Elm Architecture](https://guide.elm-lang.org/architecture/) alongside React hooks. Pure `(action, state) -> [state, effects]` functions for testable, replayable, undoable UI logic.
40
+ - **Smaller install** ~177 KB gzipped all included (Ink 6 pulls 16MB into node_modules)
41
+ - **Pure TypeScript, zero native deps** — no WASM, no build steps — works on Alpine, CI, Docker, everywhere
42
+ - **Incremental rendering** — per-node dirty tracking, [~100x faster interactive updates](tests/perf/render.bench.ts)
43
+ - **Responsive layout** `useContentRect()` returns actual dimensions synchronously during render
44
+ - **Dynamic scrollback** renders (and re-renders!) into the terminal's scroll history, not just alternate screen
45
+ - **Scrollable containers** `overflow="scroll"` with automatic measurement and clipping
46
+ - **Theme system** — 38 palettes, semantic design/color tokens (`$primary`, `$error`), auto-detects terminal colors
47
+ - **30+ components** — TextInput, TextArea, SelectList, VirtualList, Table, Tabs, CommandPalette, ModalDialog, Toast, and more
48
+ - **Focus system** — scoped focus, arrow-key directional nav, click-to-focus
49
+ - **Extremely composable** — use as just a renderer (`render`), add a runtime (`run`), or build full apps (`createApp`). Mix with any React state library (useState, Zustand, Jotai, Redux). Swap terminal backends (real TTY, headless, xterm.js emulator) for testing. Embed silvery components in existing CLIs. Use the layout engine standalone. Render to terminal, or (experimental) Canvas, or DOM
50
+ - **Most complete terminal protocol support** — 100+ escape sequences, all auto-negotiated: 12 OSC (hyperlinks, clipboard, palette, text sizing, semantic prompts, notifications), 35+ CSI (cursor, mouse modes, paste, focus, sync output, device queries), 50+ SGR (6 underline styles, underline colors, truecolor, 256-color), full Kitty keyboard (5 flags), full SGR mouse (any-event, drag, wheel)
120
51
 
121
52
  ## Packages
122
53
 
123
- | Package | Description |
124
- | ------------------------------------ | ----------------------------------------- |
125
- | [`silvery`](packages/) | Umbrella -- re-exports `@silvery/react` |
126
- | [`@silvery/react`](packages/react) | React reconciler, hooks, renderer |
127
- | [`@silvery/term`](packages/term) | Terminal rendering pipeline, ANSI styling |
128
- | [`@silvery/ui`](packages/ui) | Component library (30+ components) |
129
- | [`@silvery/theme`](packages/theme) | Theming with 38 palettes |
130
- | [`@silvery/tea`](packages/tea) | TEA state machine store |
131
- | [`@silvery/compat`](packages/compat) | Ink/Chalk compatibility layers |
132
- | [`@silvery/test`](packages/test) | Testing utilities and locators |
133
-
134
- ## Compatibility
135
-
136
- `silvery/ink` and `silvery/chalk` provide compatibility layers for existing React terminal apps. The core API (`Box`, `Text`, `useInput`, `render`) is intentionally familiar -- most existing code works with minimal changes. See the [migration guide](docs/guide/migration.md) for details.
137
-
138
- ## When to Use Silvery
139
-
140
- Silvery is designed for **complex interactive TUIs** — dashboards, editors, kanban boards, chat interfaces. If you need scrollable containers, mouse support, spatial focus, or components that adapt to their size, Silvery provides these out of the box.
141
-
142
- For simple one-shot CLI prompts or spinners, mature alternatives with larger plugin ecosystems may be a better fit today.
54
+ | Package | Description |
55
+ | --------------- | ------------------------------------------------------------------------------------------ |
56
+ | `silvery` | Components, hooks, renderer — the one package you need |
57
+ | `@silvery/test` | Testing utilities and locators |
58
+ | `@silvery/ink` | Ink compatibility layer |
59
+ | `@silvery/tea` | Optional [TEA](https://guide.elm-lang.org/architecture/) state management for complex apps |
143
60
 
144
61
  ## Ecosystem
145
62
 
146
- | Project | What |
147
- | ------------------------------------------ | -------------------------------------------------------------- |
148
- | [Termless](https://termless.dev) | Headless terminal testing -- like Playwright for terminal apps |
149
- | [Flexily](https://beorn.github.io/flexily) | Pure JS flexbox layout engine (Yoga-compatible, zero WASM) |
150
- | [Loggily](https://beorn.github.io/loggily) | Debug + structured logging + tracing |
151
-
152
- See the [roadmap](https://silvery.dev/roadmap) for what's next.
153
-
154
- ## Performance
63
+ | Project | What |
64
+ | ------------------------------------------ | ------------------------------------------------------------- |
65
+ | [Termless](https://termless.dev) | Headless terminal testing like Playwright for terminal apps |
66
+ | [Flexily](https://beorn.github.io/flexily) | Pure JS flexbox layout engine (Yoga-compatible, zero WASM) |
67
+ | [Loggily](https://beorn.github.io/loggily) | Debug + structured logging + tracing |
155
68
 
156
- _Apple M1 Max, Bun 1.3.9. Reproduce: `bun run bench:compare`_
69
+ ## Coming
157
70
 
158
- | Scenario | Silvery | Ink 5 |
159
- | --------------------------------------- | ------- | ------- |
160
- | Cold render (1 component) | 165 us | 271 us |
161
- | Cold render (1000 components) | 463 ms | 541 ms |
162
- | Typical interactive update (1000 nodes) | 169 us | 20.7 ms |
163
- | Layout (50-node kanban) | 57 us | 88 us |
71
+ - **Renderers** Canvas 2D, Web DOM (experimental today, production later)
72
+ - **Frameworks** Svelte, Solid.js, Vue adapters
73
+ - **@silvery/tea** Structured state management with commands, keybindings, effects-as-data
164
74
 
165
- **Why the difference?** Interactive updates (cursor move, scroll, toggle) typically change one or two nodes. Silvery's per-node dirty tracking updates only those nodes — 169 us for a 1000-node tree. Traditional full-tree renderers re-render the entire React tree and run complete layout on every state change — 20.7 ms. For the updates that dominate interactive use, Silvery is ~100x faster.
166
-
167
- Full re-renders where the entire tree changes are comparable or faster in full-tree renderers (simpler string concatenation vs Silvery's 5-phase pipeline). That trade-off is inherent to supporting responsive layout, and full re-renders are rare in interactive apps.
168
-
169
- ## Documentation
170
-
171
- Full docs at [silvery.dev](https://silvery.dev) -- getting started guide, API reference, component catalog, and migration guide.
172
-
173
- ## Development
174
-
175
- ```bash
176
- bun install
177
- bun test
178
- bun run lint
179
- ```
75
+ **Runtimes:** Bun >= 1.0 and Node.js >= 18. CLI (`silvery` command) requires Bun.
180
76
 
181
77
  ## License
182
78
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "silvery",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "React terminal UI renderer for complex interactive apps — layout-aware rendering, flexbox, scrolling, and incremental updates",
5
5
  "keywords": [
6
6
  "ansi",
@@ -38,8 +38,8 @@
38
38
  ],
39
39
  "files": [
40
40
  "src",
41
- "bin",
42
- "examples"
41
+ "dist",
42
+ "bin"
43
43
  ],
44
44
  "type": "module",
45
45
  "main": "src/index.ts",
@@ -49,20 +49,72 @@
49
49
  "types": "./src/index.ts",
50
50
  "import": "./src/index.ts"
51
51
  },
52
+ "./runtime": {
53
+ "types": "./src/runtime.ts",
54
+ "import": "./src/runtime.ts"
55
+ },
56
+ "./theme": {
57
+ "types": "./src/theme.ts",
58
+ "import": "./src/theme.ts"
59
+ },
60
+ "./ui": {
61
+ "types": "./src/ui.ts",
62
+ "import": "./src/ui.ts"
63
+ },
64
+ "./ui/cli": {
65
+ "types": "./src/ui/cli.ts",
66
+ "import": "./src/ui/cli.ts"
67
+ },
68
+ "./ui/react": {
69
+ "types": "./src/ui/react.ts",
70
+ "import": "./src/ui/react.ts"
71
+ },
72
+ "./ui/progress": {
73
+ "types": "./src/ui/progress.ts",
74
+ "import": "./src/ui/progress.ts"
75
+ },
76
+ "./ui/wrappers": {
77
+ "types": "./src/ui/wrappers.ts",
78
+ "import": "./src/ui/wrappers.ts"
79
+ },
80
+ "./ui/ansi": {
81
+ "types": "./src/ui/ansi.ts",
82
+ "import": "./src/ui/ansi.ts"
83
+ },
84
+ "./ui/display": {
85
+ "types": "./src/ui/display.ts",
86
+ "import": "./src/ui/display.ts"
87
+ },
88
+ "./ui/input": {
89
+ "types": "./src/ui/input.ts",
90
+ "import": "./src/ui/input.ts"
91
+ },
92
+ "./ui/animation": {
93
+ "types": "./src/ui/animation.ts",
94
+ "import": "./src/ui/animation.ts"
95
+ },
96
+ "./ui/image": {
97
+ "types": "./src/ui/image.ts",
98
+ "import": "./src/ui/image.ts"
99
+ },
100
+ "./ui/utils": {
101
+ "types": "./src/ui/utils.ts",
102
+ "import": "./src/ui/utils.ts"
103
+ },
52
104
  "./ink": {
53
- "types": "./packages/compat/src/ink.ts",
54
- "import": "./packages/compat/src/ink.ts"
105
+ "types": "./packages/ink/src/ink.ts",
106
+ "import": "./packages/ink/src/ink.ts"
55
107
  },
56
108
  "./chalk": {
57
- "types": "./packages/compat/src/chalk.ts",
58
- "import": "./packages/compat/src/chalk.ts"
109
+ "types": "./packages/ink/src/chalk.ts",
110
+ "import": "./packages/ink/src/chalk.ts"
59
111
  }
60
112
  },
61
113
  "publishConfig": {
62
114
  "access": "public"
63
115
  },
64
116
  "scripts": {
65
- "build": "bun build src/index.ts --outdir dist --target node",
117
+ "build": "bun run scripts/build.ts",
66
118
  "test": "bunx --bun vitest run",
67
119
  "test:fast": "bunx --bun vitest run --reporter=dot",
68
120
  "typecheck": "tsc --noEmit",
@@ -76,9 +128,9 @@
76
128
  "release": "changeset publish",
77
129
  "theme": "bun packages/theme/src/cli.ts",
78
130
  "demo": "bun examples/cli.ts",
79
- "compat": "bun packages/compat/scripts/compat-check.ts",
80
- "compat:ink": "bun packages/compat/scripts/compat-check.ts ink",
81
- "compat:chalk": "bun packages/compat/scripts/compat-check.ts chalk"
131
+ "compat": "bun packages/ink/scripts/compat-check.ts",
132
+ "compat:ink": "bun packages/ink/scripts/compat-check.ts ink",
133
+ "compat:chalk": "bun packages/ink/scripts/compat-check.ts chalk"
82
134
  },
83
135
  "dependencies": {
84
136
  "chalk": "^5.6.2",
@@ -101,7 +153,7 @@
101
153
  "playwright": "^1.58.2",
102
154
  "react": "^19.0.0",
103
155
  "typescript": "^5.5.0",
104
- "vimonkey": "github:beorn/vimonkey",
156
+ "vimonkey": "^0.2.0",
105
157
  "vitepress": "^1.5.0",
106
158
  "vitepress-plugin-llms": "^1.0.0",
107
159
  "vitest": "^4.0.18",
package/src/index.ts CHANGED
@@ -3,4 +3,70 @@
3
3
 
4
4
  export const VERSION = "0.0.1"
5
5
 
6
- export * from "@silvery/react"
6
+ // Re-export everything from @silvery/ag-react — local `render` below shadows the re-exported one
7
+ export * from "@silvery/ag-react"
8
+
9
+ import type { ReactElement } from "react"
10
+ import { render as reactRender, type RenderOptions, type TermDef } from "@silvery/ag-react"
11
+ import type { Term } from "@silvery/ag-react"
12
+
13
+ /**
14
+ * Render a React element to the terminal.
15
+ *
16
+ * Zero-ceremony entry point — auto-detects the terminal and starts an
17
+ * interactive app when stdin is a TTY. No need to create a Term first.
18
+ *
19
+ * @example Hello World (2 lines)
20
+ * ```tsx
21
+ * import { render, Text } from "silvery"
22
+ * await render(<Text>Hello!</Text>).run()
23
+ * ```
24
+ *
25
+ * @example Interactive counter
26
+ * ```tsx
27
+ * import { useState } from "react"
28
+ * import { render, Box, Text, useInput } from "silvery"
29
+ *
30
+ * function Counter() {
31
+ * const [count, setCount] = useState(0)
32
+ * useInput((input) => {
33
+ * if (input === "j") setCount((c) => c + 1)
34
+ * })
35
+ * return (
36
+ * <Box borderStyle="round" padding={1}>
37
+ * <Text>Count: {count}</Text>
38
+ * </Box>
39
+ * )
40
+ * }
41
+ *
42
+ * await render(<Counter />).run()
43
+ * ```
44
+ *
45
+ * @example Static render (explicit)
46
+ * ```tsx
47
+ * import { render, Text } from "silvery"
48
+ * await render(<Text>Report</Text>, { width: 120 })
49
+ * ```
50
+ *
51
+ * When called without a Term or TermDef:
52
+ * - **TTY detected** → interactive mode (stdin + stdout auto-wired)
53
+ * - **No TTY** → static mode (renders once and returns)
54
+ *
55
+ * Pass a Term or TermDef explicitly to override auto-detection.
56
+ */
57
+ export function render(
58
+ element: ReactElement,
59
+ termOrDef?: Term | TermDef,
60
+ options?: RenderOptions,
61
+ ): ReturnType<typeof reactRender> {
62
+ // When no term/def is provided and we're in a TTY, auto-wire stdin/stdout
63
+ // so the app runs interactively (useInput works, app stays alive until exit).
64
+ if (!termOrDef && process.stdin?.isTTY && process.stdout?.isTTY) {
65
+ const ttyDef: TermDef = {
66
+ stdin: process.stdin,
67
+ stdout: process.stdout,
68
+ }
69
+ return reactRender(element, ttyDef, options)
70
+ }
71
+ return reactRender(element, termOrDef, options)
72
+ }
package/src/runtime.ts ADDED
@@ -0,0 +1,4 @@
1
+ // silvery/runtime — re-exports from @silvery/ag-term/runtime
2
+ // run(), useInput, useExit, createRuntime, and related runtime APIs
3
+
4
+ export * from "@silvery/ag-term/runtime"
package/src/theme.ts ADDED
@@ -0,0 +1,4 @@
1
+ // silvery/theme — re-exports from @silvery/theme
2
+ // Theme tokens, palettes, ThemeProvider, useTheme, color utilities
3
+
4
+ export * from "@silvery/theme"
@@ -0,0 +1,2 @@
1
+ // silvery/ui/animation — animation utilities and hooks
2
+ export * from "@silvery/ag-react/ui/animation"
package/src/ui/ansi.ts ADDED
@@ -0,0 +1,2 @@
1
+ // silvery/ui/ansi — ANSI rendering utilities
2
+ export * from "@silvery/ag-react/ui/ansi"
package/src/ui/cli.ts ADDED
@@ -0,0 +1,2 @@
1
+ // silvery/ui/cli — CLI progress indicators (no React)
2
+ export * from "@silvery/ag-react/ui/cli"
@@ -0,0 +1,2 @@
1
+ // silvery/ui/display — display components
2
+ export * from "@silvery/ag-react/ui/display"
@@ -0,0 +1,2 @@
1
+ // silvery/ui/image — image display (kitty, sixel)
2
+ export * from "@silvery/ag-react/ui/image"
@@ -0,0 +1,2 @@
1
+ // silvery/ui/input — input components
2
+ export * from "@silvery/ag-react/ui/input"
@@ -0,0 +1,2 @@
1
+ // silvery/ui/progress — task/progress wrappers
2
+ export * from "@silvery/ag-react/ui/progress"
@@ -0,0 +1,2 @@
1
+ // silvery/ui/react — React progress components
2
+ export * from "@silvery/ag-react/ui/react"
@@ -0,0 +1,2 @@
1
+ // silvery/ui/utils — UI utilities
2
+ export * from "@silvery/ag-react/ui/utils"
@@ -0,0 +1,2 @@
1
+ // silvery/ui/wrappers — task wrappers (fluent task API)
2
+ export * from "@silvery/ag-react/ui/wrappers"
package/src/ui.ts ADDED
@@ -0,0 +1,4 @@
1
+ // silvery/ui — re-exports from @silvery/ag-react/ui
2
+ // Component library (progress, CLI, wrappers)
3
+
4
+ export * from "@silvery/ag-react/ui"
@@ -1,75 +0,0 @@
1
- # Silvery Examples & Showcases
2
-
3
- ## Directory Structure
4
-
5
- | Directory | What |
6
- | -------------- | ---------------------------------------------------------- |
7
- | `interactive/` | Full apps — run with `bun examples/interactive/<name>.tsx` |
8
- | `inline/` | Inline mode examples (no alt screen) |
9
- | `kitty/` | Kitty protocol demos |
10
- | `layout/` | Layout engine examples |
11
- | `runtime/` | Runtime layer demos (run, createApp, createStore) |
12
- | `playground/` | Quick prototyping |
13
- | `web/` | Browser renderers (DOM, Canvas2D) |
14
- | `screenshots/` | Reference screenshots for visual regression |
15
-
16
- ## Making a Great Showcase
17
-
18
- ### Design Principles
19
-
20
- 1. **Show, don't tell.** A showcase should demonstrate Silvery features through working UI, not walls of text. Intro text is fine — but collapse it once the demo starts.
21
-
22
- 2. **Auto-size to content.** `ScrollbackView`/`ScrollbackList` auto-size to their content — no manual height management. The output phase caps output at terminal height independently. Content that exceeds terminal height causes natural terminal scrolling.
23
-
24
- 3. **Single status bar.** Keep the status bar to one line. Include: context bar, elapsed time, cost, and key hints. Remove anything that doesn't help the user interact.
25
-
26
- 4. **Conditional headers.** Show feature bullets before the demo starts (when there's space). Collapse to a one-liner once content fills the screen.
27
-
28
- 5. **Respect terminal width.** Boxes with borders at 120 cols should leave room for the border characters. Test at 80 and 120 cols.
29
-
30
- 6. **Streaming feels real.** For coding agent demos: thinking spinner (1-2s) → word-by-word text reveal → tool call spinner → output. Use `setInterval` at 50ms with 8-12% fraction increments.
31
-
32
- ### Scrollback Pattern
33
-
34
- Use `ScrollbackList` (or `ScrollbackView`) — they handle terminal height, footer pinning, and overflow automatically:
35
-
36
- ```tsx
37
- function App() {
38
- return (
39
- <ScrollbackList
40
- items={items}
41
- keyExtractor={(item) => item.id}
42
- isFrozen={(item) => item.done}
43
- markers={true}
44
- footer={<StatusBar />}
45
- >
46
- {(item) => <ItemView item={item} />}
47
- </ScrollbackList>
48
- )
49
- }
50
-
51
- await render(<App />, term, { mode: "inline" })
52
- ```
53
-
54
- `ScrollbackView` auto-sizes to its content — no manual height management. The output phase independently caps output at terminal height (via `inlineFullRender()`), so content that exceeds the terminal causes natural scrolling. The footer stays pinned at the bottom of the content.
55
-
56
- ### Theme Tokens
57
-
58
- Use semantic `$token` colors instead of hardcoded values:
59
-
60
- | Token | Use for |
61
- | ---------- | ------------------------------------- |
62
- | `$primary` | Active elements, progress bars, links |
63
- | `$success` | Completed items, checkmarks |
64
- | `$warning` | Caution, compaction |
65
- | `$error` | Failures, diff removals |
66
- | `$muted` | Secondary info, timestamps |
67
- | `$border` | Default border color |
68
-
69
- ### Testing Showcases
70
-
71
- 1. **Visual check**: Run in TTY and step through all states
72
- 2. **Resize**: Verify layout adapts to terminal resize
73
- 3. **Scrollback**: After frozen items, scroll up — verify colors/borders preserved
74
- 4. **Width**: Test at 80 and 120 columns
75
- 5. **Fast mode**: `--fast` flag should skip all animation for quick validation
@@ -1,60 +0,0 @@
1
- import React from "react"
2
- import { Box, Text, Strong, Muted, ThemeProvider, getThemeByName, type Theme } from "../src/index.js"
3
-
4
- export interface ExampleMeta {
5
- name: string
6
- description: string
7
- /** API features showcased, e.g. ["VirtualList", "useContentRect()"] */
8
- features?: string[]
9
- /** Curated demo — shown in CLI viewer (`bun examples`) and web showcase */
10
- demo?: boolean
11
- }
12
-
13
- interface Props {
14
- meta: ExampleMeta
15
- /** Short controls legend, e.g. "j/k navigate q quit" */
16
- controls?: string
17
- /** Override theme (from viewer). Falls back to SILVERY_THEME env var. */
18
- theme?: Theme
19
- children: React.ReactNode
20
- }
21
-
22
- /**
23
- * Compact header shown when examples run standalone.
24
- * Wraps children in ThemeProvider for consistent theming.
25
- */
26
- export function ExampleBanner({ meta, controls, theme, children }: Props) {
27
- const resolvedTheme = theme ?? getThemeByName(process.env.SILVERY_THEME)
28
-
29
- return (
30
- <ThemeProvider theme={resolvedTheme}>
31
- <Box flexDirection="column" flexGrow={1}>
32
- {/* One-line header: dimmed to not compete with example UI */}
33
- <Box paddingX={1} gap={1}>
34
- <Text dim color="$warning">
35
- {"▸ silvery"}
36
- </Text>
37
- <Strong>{meta.name}</Strong>
38
- <Muted>— {meta.description}</Muted>
39
- </Box>
40
- {meta.features && meta.features.length > 0 && (
41
- <Box paddingX={1}>
42
- <Muted>
43
- {" "}
44
- {meta.features.join(" · ")}
45
- </Muted>
46
- </Box>
47
- )}
48
- {controls && (
49
- <Box paddingX={1}>
50
- <Muted>
51
- {" "}
52
- {controls}
53
- </Muted>
54
- </Box>
55
- )}
56
- {children}
57
- </Box>
58
- </ThemeProvider>
59
- )
60
- }