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,230 +0,0 @@
1
- /**
2
- * Benchmark: ScrollbackList typing performance
3
- * Measures per-keystroke cost across React reconciliation + silvery pipeline.
4
- *
5
- * Usage: bun examples/scrollback-perf.tsx
6
- *
7
- * Modes:
8
- * --simple Simple items (Box + 2 Text nodes)
9
- * --complex Complex items matching ai-chat (default)
10
- * --timers Add pulse/elapsed timers like the real demo
11
- */
12
- import React, { useState, useEffect, useRef } from "react"
13
- import { createRenderer } from "../src/testing/index.js"
14
- import { Box, Text, ScrollbackList, TextInput, Spinner } from "../src/index.js"
15
-
16
- interface Item {
17
- id: string
18
- text: string
19
- role: string
20
- frozen: boolean
21
- }
22
-
23
- const useSimple = process.argv.includes("--simple")
24
- const useTimers = process.argv.includes("--timers")
25
-
26
- /** Simple item — matches original benchmark (Box + 2-3 Text nodes) */
27
- function SimpleItem({ item, isLatest }: { item: Item; isLatest: boolean }) {
28
- return (
29
- <Box flexDirection="column" borderStyle={item.role === "assistant" ? "single" : undefined}>
30
- <Text bold color={item.role === "user" ? "green" : "blue"}>
31
- {item.role === "user" ? "❯" : "◆"} {item.role}
32
- </Text>
33
- <Text>{item.text}</Text>
34
- {isLatest && <Text dimColor>Latest item</Text>}
35
- </Box>
36
- )
37
- }
38
-
39
- /** Complex item — matches ai-chat's ExchangeItem */
40
- function ComplexItem({ item }: { item: Item }) {
41
- if (item.role === "user") {
42
- return (
43
- <Box flexDirection="column">
44
- <Text> </Text>
45
- <Box paddingX={1}>
46
- <Text>
47
- <Text bold color="blue">
48
- {"❯ "}
49
- </Text>
50
- {item.text}
51
- </Text>
52
- </Box>
53
- <Text> </Text>
54
- </Box>
55
- )
56
- }
57
- return (
58
- <Box flexDirection="column" borderStyle="round" borderColor="green" paddingX={1}>
59
- <Text>
60
- <Text bold color="green">
61
- ◆ Agent
62
- </Text>
63
- <Text color="gray" dim>
64
- {" "}
65
- 624 tokens
66
- </Text>
67
- </Text>
68
- <Text> </Text>
69
- <Text>{item.text}</Text>
70
- <Text> </Text>
71
- <Box flexDirection="column">
72
- <Text>
73
- <Text color="green">{"✓ "}</Text>
74
- <Text color="cyan" bold>
75
- Read
76
- </Text>{" "}
77
- src/auth.ts
78
- </Text>
79
- <Box
80
- borderStyle="bold"
81
- borderColor="green"
82
- borderLeft
83
- borderRight={false}
84
- borderTop={false}
85
- borderBottom={false}
86
- paddingLeft={1}
87
- >
88
- <Text>export async function login(token: string)</Text>
89
- </Box>
90
- </Box>
91
- </Box>
92
- )
93
- }
94
-
95
- function StatusBar() {
96
- return (
97
- <Box flexDirection="row" justifyContent="space-between" paddingX={1}>
98
- <Text color="gray" dim>
99
- <Text color="blue">0:00</Text>
100
- {" ⏎ send tab auto ^L clear esc quit"}
101
- </Text>
102
- <Text color="gray" dim>
103
- ctx {"█".repeat(4)}
104
- {"░".repeat(16)} 20% · $0.12
105
- </Text>
106
- </Box>
107
- )
108
- }
109
-
110
- /** Footer that owns its own inputText state (lifted down from parent). */
111
- function LiftedFooter() {
112
- const [inputText, setInputText] = useState("")
113
- return (
114
- <Box flexDirection="column">
115
- <Box borderStyle="round" paddingX={1}>
116
- <TextInput value={inputText} onChange={setInputText} prompt="> " isActive={true} />
117
- </Box>
118
- {!useSimple && <StatusBar />}
119
- </Box>
120
- )
121
- }
122
-
123
- const useLifted = process.argv.includes("--lifted")
124
-
125
- function TestApp({ itemCount }: { itemCount: number }) {
126
- const [items] = useState<Item[]>(() =>
127
- Array.from({ length: itemCount }, (_, i) => ({
128
- id: `item-${i}`,
129
- text:
130
- i % 2 === 0
131
- ? `Fix the login bug in auth.ts — expired tokens throw instead of refreshing.`
132
- : `Found it. The expiry check compares seconds (jwt.exp) to milliseconds (Date.now()). Fixing now.`,
133
- role: i % 2 === 0 ? "user" : "assistant",
134
- frozen: false,
135
- })),
136
- )
137
- // When NOT lifted: inputText lives in parent (causes full re-render)
138
- const [inputText, setInputText] = useState("")
139
-
140
- // Optional timers (like ai-chat)
141
- const [_pulse, setPulse] = useState(false)
142
- const [_elapsed, setElapsed] = useState(0)
143
- const startRef = useRef(Date.now())
144
-
145
- useEffect(() => {
146
- if (!useTimers) return
147
- const t1 = setInterval(() => setPulse((p) => !p), 800)
148
- const t2 = setInterval(() => setElapsed(Math.floor((Date.now() - startRef.current) / 1000)), 1000)
149
- return () => {
150
- clearInterval(t1)
151
- clearInterval(t2)
152
- }
153
- }, [])
154
-
155
- return (
156
- <Box flexDirection="column">
157
- <ScrollbackList
158
- items={items}
159
- keyExtractor={(item) => item.id}
160
- isFrozen={(item) => item.frozen}
161
- footer={
162
- useLifted ? (
163
- <LiftedFooter />
164
- ) : (
165
- <Box flexDirection="column">
166
- <Box borderStyle="round" paddingX={1}>
167
- <TextInput value={inputText} onChange={setInputText} prompt="> " isActive={true} />
168
- </Box>
169
- {!useSimple && <StatusBar />}
170
- </Box>
171
- )
172
- }
173
- footerHeight={useSimple ? 3 : 4}
174
- width={120}
175
- stdout={{ write: () => true }}
176
- >
177
- {(item, index) => {
178
- const isLatest = index === items.length - 1
179
- return useSimple ? <SimpleItem item={item} isLatest={isLatest} /> : <ComplexItem item={item} />
180
- }}
181
- </ScrollbackList>
182
- </Box>
183
- )
184
- }
185
-
186
- async function benchmark(itemCount: number, label: string) {
187
- const render = createRenderer({ cols: 120, rows: 40 })
188
- const app = render(<TestApp itemCount={itemCount} />)
189
-
190
- // Warm up
191
- await app.press("x")
192
- await app.press("Backspace")
193
-
194
- const chars = "the quick brown fox jumps over the lazy dog"
195
- const times: number[] = []
196
-
197
- for (const char of chars) {
198
- const t0 = performance.now()
199
- await app.press(char)
200
- times.push(performance.now() - t0)
201
- }
202
-
203
- times.sort((a, b) => a - b)
204
- const avg = times.reduce((a, b) => a + b) / times.length
205
- const p50 = times[Math.floor(times.length * 0.5)]!
206
- const p95 = times[Math.floor(times.length * 0.95)]!
207
- const max = times[times.length - 1]!
208
-
209
- // Pipeline breakdown from last frame
210
- const pipeline = (globalThis as any).__silvery_last_pipeline
211
- const pipelineStr = pipeline
212
- ? Object.entries(pipeline)
213
- .filter(([, v]) => typeof v === "number" && (v as number) > 0.05)
214
- .map(([k, v]) => `${k}=${(v as number).toFixed(1)}ms`)
215
- .join(" ")
216
- : "n/a"
217
-
218
- console.log(
219
- `${label.padEnd(20)} avg=${avg.toFixed(1)}ms p50=${p50.toFixed(1)}ms p95=${p95.toFixed(1)}ms max=${max.toFixed(1)}ms pipeline=[${pipelineStr}]`,
220
- )
221
- }
222
-
223
- console.log("=== ScrollbackList Typing Performance ===\n")
224
- await benchmark(1, "1 item")
225
- await benchmark(5, "5 items")
226
- await benchmark(10, "10 items")
227
- await benchmark(20, "20 items")
228
- await benchmark(50, "50 items")
229
- await benchmark(100, "100 items")
230
- console.log("\nDone.")