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.
- package/README.md +41 -145
- package/dist/chalk.js +3 -0
- package/dist/chalk.js.map +11 -0
- package/dist/index.js +340 -0
- package/dist/index.js.map +282 -0
- package/dist/ink.js +129 -0
- package/dist/ink.js.map +140 -0
- package/dist/runtime.js +394 -0
- package/dist/runtime.js.map +286 -0
- package/dist/theme.js +343 -0
- package/dist/theme.js.map +286 -0
- package/dist/ui/animation.js +3 -0
- package/dist/ui/animation.js.map +15 -0
- package/dist/ui/ansi.js +3 -0
- package/dist/ui/ansi.js.map +10 -0
- package/dist/ui/cli.js +8 -0
- package/dist/ui/cli.js.map +14 -0
- package/dist/ui/display.js +4 -0
- package/dist/ui/display.js.map +10 -0
- package/dist/ui/image.js +4 -0
- package/dist/ui/image.js.map +15 -0
- package/dist/ui/input.js +3 -0
- package/dist/ui/input.js.map +11 -0
- package/dist/ui/progress.js +8 -0
- package/dist/ui/progress.js.map +20 -0
- package/dist/ui/react.js +3 -0
- package/dist/ui/react.js.map +15 -0
- package/dist/ui/utils.js +3 -0
- package/dist/ui/utils.js.map +10 -0
- package/dist/ui/wrappers.js +14 -0
- package/dist/ui/wrappers.js.map +19 -0
- package/dist/ui.js +17 -0
- package/dist/ui.js.map +20 -0
- package/package.json +67 -15
- package/src/index.ts +67 -1
- package/src/runtime.ts +4 -0
- package/src/theme.ts +4 -0
- package/src/ui/animation.ts +2 -0
- package/src/ui/ansi.ts +2 -0
- package/src/ui/cli.ts +2 -0
- package/src/ui/display.ts +2 -0
- package/src/ui/image.ts +2 -0
- package/src/ui/input.ts +2 -0
- package/src/ui/progress.ts +2 -0
- package/src/ui/react.ts +2 -0
- package/src/ui/utils.ts +2 -0
- package/src/ui/wrappers.ts +2 -0
- package/src/ui.ts +4 -0
- package/examples/CLAUDE.md +0 -75
- package/examples/_banner.tsx +0 -60
- package/examples/cli.ts +0 -228
- package/examples/index.md +0 -101
- package/examples/inline/inline-nontty.tsx +0 -98
- package/examples/inline/inline-progress.tsx +0 -79
- package/examples/inline/inline-simple.tsx +0 -63
- package/examples/inline/scrollback.tsx +0 -185
- package/examples/interactive/_input-debug.tsx +0 -110
- package/examples/interactive/_stdin-test.ts +0 -71
- package/examples/interactive/_textarea-bare.tsx +0 -45
- package/examples/interactive/aichat/components.tsx +0 -468
- package/examples/interactive/aichat/index.tsx +0 -207
- package/examples/interactive/aichat/script.ts +0 -460
- package/examples/interactive/aichat/state.ts +0 -326
- package/examples/interactive/aichat/types.ts +0 -19
- package/examples/interactive/app-todo.tsx +0 -198
- package/examples/interactive/async-data.tsx +0 -208
- package/examples/interactive/cli-wizard.tsx +0 -332
- package/examples/interactive/clipboard.tsx +0 -183
- package/examples/interactive/components.tsx +0 -463
- package/examples/interactive/data-explorer.tsx +0 -506
- package/examples/interactive/dev-tools.tsx +0 -379
- package/examples/interactive/explorer.tsx +0 -747
- package/examples/interactive/gallery.tsx +0 -652
- package/examples/interactive/inline-bench.tsx +0 -136
- package/examples/interactive/kanban.tsx +0 -267
- package/examples/interactive/layout-ref.tsx +0 -185
- package/examples/interactive/outline.tsx +0 -171
- package/examples/interactive/paste-demo.tsx +0 -198
- package/examples/interactive/scroll.tsx +0 -77
- package/examples/interactive/search-filter.tsx +0 -240
- package/examples/interactive/task-list.tsx +0 -279
- package/examples/interactive/terminal.tsx +0 -798
- package/examples/interactive/textarea.tsx +0 -103
- package/examples/interactive/theme.tsx +0 -336
- package/examples/interactive/transform.tsx +0 -256
- package/examples/interactive/virtual-10k.tsx +0 -413
- package/examples/kitty/canvas.tsx +0 -519
- package/examples/kitty/generate-samples.ts +0 -236
- package/examples/kitty/image-component.tsx +0 -273
- package/examples/kitty/images.tsx +0 -604
- package/examples/kitty/input.tsx +0 -371
- package/examples/kitty/keys.tsx +0 -378
- package/examples/kitty/paint.tsx +0 -1017
- package/examples/layout/dashboard.tsx +0 -551
- package/examples/layout/live-resize.tsx +0 -290
- package/examples/layout/overflow.tsx +0 -51
- package/examples/playground/README.md +0 -69
- package/examples/playground/build.ts +0 -61
- package/examples/playground/index.html +0 -420
- package/examples/playground/playground-app.tsx +0 -416
- package/examples/runtime/elm-counter.tsx +0 -206
- package/examples/runtime/hello-runtime.tsx +0 -73
- package/examples/runtime/pipe-composition.tsx +0 -184
- package/examples/runtime/run-counter.tsx +0 -78
- package/examples/runtime/runtime-counter.tsx +0 -197
- package/examples/screenshots/generate.tsx +0 -563
- package/examples/scrollback-perf.tsx +0 -230
- package/examples/viewer.tsx +0 -654
- package/examples/web/build.ts +0 -365
- package/examples/web/canvas-app.tsx +0 -80
- package/examples/web/canvas.html +0 -89
- package/examples/web/dom-app.tsx +0 -81
- package/examples/web/dom.html +0 -113
- package/examples/web/showcase-app.tsx +0 -107
- package/examples/web/showcase.html +0 -34
- package/examples/web/showcases/index.tsx +0 -56
- package/examples/web/viewer-app.tsx +0 -555
- package/examples/web/viewer.html +0 -30
- package/examples/web/xterm-app.tsx +0 -105
- package/examples/web/xterm.html +0 -118
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Live Resize Demo
|
|
3
|
-
*
|
|
4
|
-
* THE showcase demo for silvery's unique capability: components that know their size.
|
|
5
|
-
*
|
|
6
|
-
* Demonstrates:
|
|
7
|
-
* - useContentRect() providing real-time width/height during render
|
|
8
|
-
* - Multi-column layout that reflows from 1 to 2 to 3 columns based on width
|
|
9
|
-
* - Responsive breakpoints with visual feedback
|
|
10
|
-
* - Content that adapts its presentation based on available space
|
|
11
|
-
* - No useEffect, no layout thrashing — dimensions are synchronous
|
|
12
|
-
*
|
|
13
|
-
* Usage: bun run examples/live-resize/index.tsx
|
|
14
|
-
*
|
|
15
|
-
* Try resizing your terminal to see the layout reflow in real-time!
|
|
16
|
-
*
|
|
17
|
-
* Controls:
|
|
18
|
-
* Esc/q or Ctrl+C - Quit
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import React from "react"
|
|
22
|
-
import { Box, Text, H1, H3, Kbd, Muted, Small, useContentRect } from "../../src/index.js"
|
|
23
|
-
import { run, useInput, type Key } from "@silvery/term/runtime"
|
|
24
|
-
import { useCallback } from "react"
|
|
25
|
-
import { ExampleBanner, type ExampleMeta } from "../_banner.js"
|
|
26
|
-
|
|
27
|
-
export const meta: ExampleMeta = {
|
|
28
|
-
name: "Live Resize",
|
|
29
|
-
description: "Responsive multi-column grid that reflows based on terminal width",
|
|
30
|
-
features: ["useContentRect()", "responsive breakpoints", "Box flexDirection"],
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ============================================================================
|
|
34
|
-
// Types
|
|
35
|
-
// ============================================================================
|
|
36
|
-
|
|
37
|
-
interface CardData {
|
|
38
|
-
title: string
|
|
39
|
-
icon: string
|
|
40
|
-
value: string
|
|
41
|
-
detail: string
|
|
42
|
-
color: string
|
|
43
|
-
sparkline: string
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ============================================================================
|
|
47
|
-
// Data
|
|
48
|
-
// ============================================================================
|
|
49
|
-
|
|
50
|
-
const CARDS: CardData[] = [
|
|
51
|
-
{
|
|
52
|
-
title: "CPU Usage",
|
|
53
|
-
icon: "\u{1f4bb}",
|
|
54
|
-
value: "42%",
|
|
55
|
-
detail: "4 cores, 2.4 GHz base",
|
|
56
|
-
color: "green",
|
|
57
|
-
sparkline: "\u2582\u2583\u2585\u2587\u2586\u2584\u2583\u2585\u2587\u2588\u2586\u2584\u2583\u2582\u2583\u2585",
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
title: "Memory",
|
|
61
|
-
icon: "\u{1f9e0}",
|
|
62
|
-
value: "8.2 GB",
|
|
63
|
-
detail: "of 16 GB (51% used)",
|
|
64
|
-
color: "cyan",
|
|
65
|
-
sparkline: "\u2584\u2584\u2585\u2585\u2585\u2586\u2586\u2586\u2585\u2585\u2586\u2586\u2587\u2587\u2586\u2586",
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
title: "Disk I/O",
|
|
69
|
-
icon: "\u{1f4be}",
|
|
70
|
-
value: "234 MB/s",
|
|
71
|
-
detail: "Read: 180 MB/s Write: 54 MB/s",
|
|
72
|
-
color: "yellow",
|
|
73
|
-
sparkline: "\u2581\u2582\u2583\u2587\u2588\u2587\u2584\u2582\u2581\u2582\u2585\u2587\u2586\u2583\u2582\u2581",
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
title: "Network",
|
|
77
|
-
icon: "\u{1f310}",
|
|
78
|
-
value: "1.2 Gb/s",
|
|
79
|
-
detail: "In: 800 Mb/s Out: 400 Mb/s",
|
|
80
|
-
color: "magenta",
|
|
81
|
-
sparkline: "\u2583\u2584\u2585\u2586\u2587\u2586\u2585\u2584\u2585\u2586\u2587\u2588\u2587\u2586\u2585\u2584",
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
title: "Processes",
|
|
85
|
-
icon: "\u{2699}\u{fe0f}",
|
|
86
|
-
value: "247",
|
|
87
|
-
detail: "12 running, 235 sleeping",
|
|
88
|
-
color: "blue",
|
|
89
|
-
sparkline: "\u2585\u2585\u2585\u2586\u2585\u2585\u2585\u2585\u2586\u2585\u2585\u2585\u2586\u2585\u2585\u2585",
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
title: "Temperature",
|
|
93
|
-
icon: "\u{1f321}\u{fe0f}",
|
|
94
|
-
value: "62 C",
|
|
95
|
-
detail: "Max: 85 C (safe range)",
|
|
96
|
-
color: "red",
|
|
97
|
-
sparkline: "\u2583\u2583\u2584\u2584\u2585\u2585\u2586\u2586\u2585\u2585\u2584\u2584\u2583\u2584\u2585\u2585",
|
|
98
|
-
},
|
|
99
|
-
]
|
|
100
|
-
|
|
101
|
-
// ============================================================================
|
|
102
|
-
// Components
|
|
103
|
-
// ============================================================================
|
|
104
|
-
|
|
105
|
-
function MetricCard({ card, compact }: { card: CardData; compact: boolean }): JSX.Element {
|
|
106
|
-
if (compact) {
|
|
107
|
-
// Minimal: single-line card for narrow terminals
|
|
108
|
-
return (
|
|
109
|
-
<Box borderStyle="round" borderColor={card.color} paddingX={1} flexDirection="row" justifyContent="space-between">
|
|
110
|
-
<H1 color={card.color}>{card.title}</H1>
|
|
111
|
-
<H3>{card.value}</H3>
|
|
112
|
-
</Box>
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Full card with sparkline and details
|
|
117
|
-
return (
|
|
118
|
-
<Box borderStyle="round" borderColor={card.color} paddingX={1} flexDirection="column" flexGrow={1}>
|
|
119
|
-
<Box justifyContent="space-between">
|
|
120
|
-
<H1 color={card.color}>{card.title}</H1>
|
|
121
|
-
<H1 color={card.color}>{card.value}</H1>
|
|
122
|
-
</Box>
|
|
123
|
-
<Text color={card.color}>{card.sparkline}</Text>
|
|
124
|
-
<Small>{card.detail}</Small>
|
|
125
|
-
</Box>
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function BreakpointIndicator({ width, columns }: { width: number; columns: number }): JSX.Element {
|
|
130
|
-
const breakpoints = [
|
|
131
|
-
{ threshold: 0, cols: 1, label: "< 60" },
|
|
132
|
-
{ threshold: 60, cols: 2, label: "60-99" },
|
|
133
|
-
{ threshold: 100, cols: 3, label: "100+" },
|
|
134
|
-
]
|
|
135
|
-
|
|
136
|
-
return (
|
|
137
|
-
<Box gap={2} paddingX={1}>
|
|
138
|
-
{breakpoints.map((bp) => {
|
|
139
|
-
const isActive = bp.cols === columns
|
|
140
|
-
return (
|
|
141
|
-
<Box key={bp.cols} gap={1}>
|
|
142
|
-
<Text color={isActive ? "green" : "gray"} bold={isActive}>
|
|
143
|
-
{isActive ? "\u25cf" : "\u25cb"}
|
|
144
|
-
</Text>
|
|
145
|
-
<Text color={isActive ? "white" : "gray"} bold={isActive}>
|
|
146
|
-
{bp.cols} col{bp.cols > 1 ? "s" : " "} ({bp.label})
|
|
147
|
-
</Text>
|
|
148
|
-
</Box>
|
|
149
|
-
)
|
|
150
|
-
})}
|
|
151
|
-
</Box>
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function GridLayout({
|
|
156
|
-
cards,
|
|
157
|
-
columns,
|
|
158
|
-
compact,
|
|
159
|
-
}: {
|
|
160
|
-
cards: CardData[]
|
|
161
|
-
columns: number
|
|
162
|
-
compact: boolean
|
|
163
|
-
}): JSX.Element {
|
|
164
|
-
if (columns === 1) {
|
|
165
|
-
return (
|
|
166
|
-
<Box flexDirection="column" gap={compact ? 0 : 1} flexGrow={1}>
|
|
167
|
-
{cards.map((card) => (
|
|
168
|
-
<MetricCard key={card.title} card={card} compact={compact} />
|
|
169
|
-
))}
|
|
170
|
-
</Box>
|
|
171
|
-
)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Build rows of N columns
|
|
175
|
-
const rows: CardData[][] = []
|
|
176
|
-
for (let i = 0; i < cards.length; i += columns) {
|
|
177
|
-
rows.push(cards.slice(i, i + columns))
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return (
|
|
181
|
-
<Box flexDirection="column" gap={1} flexGrow={1}>
|
|
182
|
-
{rows.map((row, rowIndex) => (
|
|
183
|
-
<Box key={rowIndex} flexDirection="row" gap={1}>
|
|
184
|
-
{row.map((card) => (
|
|
185
|
-
<Box key={card.title} flexGrow={1} flexBasis={0}>
|
|
186
|
-
<MetricCard card={card} compact={false} />
|
|
187
|
-
</Box>
|
|
188
|
-
))}
|
|
189
|
-
{/* Fill remaining slots for even spacing */}
|
|
190
|
-
{row.length < columns &&
|
|
191
|
-
Array.from({ length: columns - row.length }, (_, i) => (
|
|
192
|
-
<Box key={`spacer-${i}`} flexGrow={1} flexBasis={0} />
|
|
193
|
-
))}
|
|
194
|
-
</Box>
|
|
195
|
-
))}
|
|
196
|
-
</Box>
|
|
197
|
-
)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function CodeSnippet({ width }: { width: number }): JSX.Element {
|
|
201
|
-
const showSnippet = width >= 60
|
|
202
|
-
|
|
203
|
-
if (!showSnippet) {
|
|
204
|
-
return (
|
|
205
|
-
<Box paddingX={1}>
|
|
206
|
-
<Text dim italic>
|
|
207
|
-
(Widen terminal to see the code that powers this)
|
|
208
|
-
</Text>
|
|
209
|
-
</Box>
|
|
210
|
-
)
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return (
|
|
214
|
-
<Box flexDirection="column" borderStyle="single" borderColor="$border" paddingX={1}>
|
|
215
|
-
<H1 color="yellow">How it works:</H1>
|
|
216
|
-
<Text color="gray">
|
|
217
|
-
{" "}
|
|
218
|
-
<Text color="magenta">const</Text> {"{"} width {"}"} = <Text color="cyan">useContentRect</Text>()
|
|
219
|
-
</Text>
|
|
220
|
-
<Text color="gray">
|
|
221
|
-
{" "}
|
|
222
|
-
<Text color="magenta">const</Text> columns = width {">"} 100 ? <Text color="green">3</Text> : width {">"} 60 ?{" "}
|
|
223
|
-
<Text color="green">2</Text> : <Text color="green">1</Text>
|
|
224
|
-
</Text>
|
|
225
|
-
<Text dim italic>
|
|
226
|
-
{" "}// No useEffect, no layout thrashing. Synchronous.
|
|
227
|
-
</Text>
|
|
228
|
-
</Box>
|
|
229
|
-
)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// ============================================================================
|
|
233
|
-
// Main App
|
|
234
|
-
// ============================================================================
|
|
235
|
-
|
|
236
|
-
function LiveResize(): JSX.Element {
|
|
237
|
-
const { width, height } = useContentRect()
|
|
238
|
-
|
|
239
|
-
// Responsive breakpoints
|
|
240
|
-
const columns = width >= 100 ? 3 : width >= 60 ? 2 : 1
|
|
241
|
-
const compact = height < 20 || width < 40
|
|
242
|
-
|
|
243
|
-
useInput(
|
|
244
|
-
useCallback((input: string, key: Key) => {
|
|
245
|
-
if (input === "q" || key.escape || (key.ctrl && input === "c")) {
|
|
246
|
-
return "exit"
|
|
247
|
-
}
|
|
248
|
-
}, []),
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
return (
|
|
252
|
-
<Box flexDirection="column" width="100%" height="100%" padding={1}>
|
|
253
|
-
{/* Breakpoint indicator */}
|
|
254
|
-
<BreakpointIndicator width={width} columns={columns} />
|
|
255
|
-
|
|
256
|
-
{/* Main grid */}
|
|
257
|
-
<Box flexGrow={1} flexDirection="column" marginTop={1}>
|
|
258
|
-
<GridLayout cards={CARDS} columns={columns} compact={compact} />
|
|
259
|
-
</Box>
|
|
260
|
-
|
|
261
|
-
{/* Code snippet showing how it works */}
|
|
262
|
-
{!compact && <CodeSnippet width={width} />}
|
|
263
|
-
|
|
264
|
-
{/* Footer */}
|
|
265
|
-
<Box justifyContent="space-between" paddingX={1}>
|
|
266
|
-
<Muted>Resize your terminal to see the layout reflow</Muted>
|
|
267
|
-
<Muted>
|
|
268
|
-
<Kbd>Esc/q</Kbd> quit
|
|
269
|
-
</Muted>
|
|
270
|
-
</Box>
|
|
271
|
-
</Box>
|
|
272
|
-
)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// ============================================================================
|
|
276
|
-
// Main
|
|
277
|
-
// ============================================================================
|
|
278
|
-
|
|
279
|
-
async function main() {
|
|
280
|
-
const handle = await run(
|
|
281
|
-
<ExampleBanner meta={meta} controls="Resize terminal to see reflow Esc/q quit">
|
|
282
|
-
<LiveResize />
|
|
283
|
-
</ExampleBanner>,
|
|
284
|
-
)
|
|
285
|
-
await handle.waitUntilExit()
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (import.meta.main) {
|
|
289
|
-
main().catch(console.error)
|
|
290
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
import { render, Box, Text, useApp, useInput, createTerm } from "../../src/index.js"
|
|
3
|
-
import { ExampleBanner, type ExampleMeta } from "../_banner.js"
|
|
4
|
-
|
|
5
|
-
export const meta: ExampleMeta = {
|
|
6
|
-
name: "Overflow",
|
|
7
|
-
description: 'overflow="hidden" content clipping demonstration',
|
|
8
|
-
features: ['overflow="hidden"', "Box height"],
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function OverflowApp() {
|
|
12
|
-
const { exit } = useApp()
|
|
13
|
-
useInput((input, key) => {
|
|
14
|
-
if (input === "q" || key.escape) exit()
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<Box flexDirection="column" padding={1}>
|
|
19
|
-
<Text color="yellow">Title</Text>
|
|
20
|
-
|
|
21
|
-
<Box borderStyle="single" borderColor="$primary" height={5} overflow="hidden">
|
|
22
|
-
<Box flexDirection="column" flexGrow={1}>
|
|
23
|
-
<Text>Line 1</Text>
|
|
24
|
-
<Text>Line 2</Text>
|
|
25
|
-
<Text>Line 3</Text>
|
|
26
|
-
<Text>Line 4</Text>
|
|
27
|
-
<Text>Line 5</Text>
|
|
28
|
-
<Text>Line 6 - should NOT appear</Text>
|
|
29
|
-
<Text>Line 7 - should NOT appear</Text>
|
|
30
|
-
</Box>
|
|
31
|
-
</Box>
|
|
32
|
-
|
|
33
|
-
<Text color="$success">This should NOT be corrupted</Text>
|
|
34
|
-
</Box>
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function main() {
|
|
39
|
-
using term = createTerm()
|
|
40
|
-
const { waitUntilExit } = await render(
|
|
41
|
-
<ExampleBanner meta={meta} controls="Esc/q quit">
|
|
42
|
-
<OverflowApp />
|
|
43
|
-
</ExampleBanner>,
|
|
44
|
-
term,
|
|
45
|
-
)
|
|
46
|
-
await waitUntilExit()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (import.meta.main) {
|
|
50
|
-
main().catch(console.error)
|
|
51
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# Silvery Canvas Playground
|
|
2
|
-
|
|
3
|
-
Interactive browser demo of Silvery's Canvas 2D adapter. Renders React components to an HTML5 `<canvas>` element using the same layout engine and rendering pipeline as the terminal adapter.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Build the playground bundle
|
|
9
|
-
cd vendor/silvery
|
|
10
|
-
bun run examples/playground/build.ts
|
|
11
|
-
|
|
12
|
-
# Open in browser
|
|
13
|
-
open examples/playground/index.html
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
No dev server required -- just open `index.html` directly in any modern browser.
|
|
17
|
-
|
|
18
|
-
## What It Shows
|
|
19
|
-
|
|
20
|
-
The playground includes seven preset examples accessible via buttons or number keys (1-7):
|
|
21
|
-
|
|
22
|
-
| # | Preset | Demonstrates |
|
|
23
|
-
| --- | ---------- | --------------------------------------------------------- |
|
|
24
|
-
| 1 | Hello | Basic Box + Text, `useContentRect()` size display |
|
|
25
|
-
| 2 | Text | Bold, italic, underline styles (single/double/curly/etc.) |
|
|
26
|
-
| 3 | Colors | Named ANSI colors, hex, RGB, background fills |
|
|
27
|
-
| 4 | Flexbox | Row/column layouts, `flexGrow`, `gap`, nested panels |
|
|
28
|
-
| 5 | Borders | single, double, round, bold border styles |
|
|
29
|
-
| 6 | Dashboard | Multi-panel system monitor layout |
|
|
30
|
-
| 7 | Responsive | Layout adapts between horizontal/vertical based on width |
|
|
31
|
-
|
|
32
|
-
Resize the browser window to see layouts recompute. The canvas size is shown in the bottom-right corner.
|
|
33
|
-
|
|
34
|
-
## Architecture
|
|
35
|
-
|
|
36
|
-
The playground uses the same rendering pipeline as Silvery's terminal mode:
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
React JSX
|
|
40
|
-
| React reconciler builds SilveryNode tree
|
|
41
|
-
v
|
|
42
|
-
Flexily layout engine (pure JS flexbox)
|
|
43
|
-
| Computes { x, y, width, height } for every node
|
|
44
|
-
v
|
|
45
|
-
Canvas adapter (CanvasRenderBuffer)
|
|
46
|
-
| drawText(), fillRect(), drawChar() to OffscreenCanvas
|
|
47
|
-
v
|
|
48
|
-
Visible <canvas> element
|
|
49
|
-
| ctx.drawImage(offscreenCanvas, 0, 0)
|
|
50
|
-
v
|
|
51
|
-
Browser display
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Key files:
|
|
55
|
-
|
|
56
|
-
- `src/adapters/canvas-adapter.ts` -- Canvas `RenderAdapter` implementation
|
|
57
|
-
- `src/canvas/index.ts` -- `renderToCanvas()` entry point and React integration
|
|
58
|
-
- `src/render-adapter.ts` -- The `RenderAdapter` interface shared by all targets
|
|
59
|
-
|
|
60
|
-
## Building a Full Playground (Live JSX Editing)
|
|
61
|
-
|
|
62
|
-
A static HTML page cannot bundle a JSX transpiler. For a full live-editing experience with Monaco editor, see `docs/playground-design.md`. The architecture uses:
|
|
63
|
-
|
|
64
|
-
- **Vite** for dev server and HMR
|
|
65
|
-
- **Monaco Editor** for JSX editing with TypeScript intellisense
|
|
66
|
-
- **Sucrase** (in-browser) for JSX transpilation
|
|
67
|
-
- **silvery/canvas** for rendering the user's components
|
|
68
|
-
|
|
69
|
-
Deployment targets: GitHub Pages (static export), StackBlitz (zero-install), or self-hosted.
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Build the Canvas Playground
|
|
4
|
-
*
|
|
5
|
-
* Bundles the playground React app for browser usage.
|
|
6
|
-
* Run: bun run examples/playground/build.ts
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { mkdir } from "node:fs/promises"
|
|
10
|
-
import { join, dirname } from "node:path"
|
|
11
|
-
|
|
12
|
-
const __dirname = dirname(new URL(import.meta.url).pathname)
|
|
13
|
-
const distDir = join(__dirname, "dist")
|
|
14
|
-
|
|
15
|
-
// Ensure dist directory exists
|
|
16
|
-
await mkdir(distDir, { recursive: true })
|
|
17
|
-
|
|
18
|
-
// Browser-safe defines for Node.js globals.
|
|
19
|
-
// loggily and @silvery/ansi access process.env at module init,
|
|
20
|
-
// which throws ReferenceError in browsers where `process` is undefined.
|
|
21
|
-
const browserDefines: Record<string, string> = {
|
|
22
|
-
"process.env.NODE_ENV": '"production"',
|
|
23
|
-
"process.env.LOG_LEVEL": "undefined",
|
|
24
|
-
"process.env.TRACE": "undefined",
|
|
25
|
-
"process.env.TRACE_FORMAT": "undefined",
|
|
26
|
-
"process.env.DEBUG": "undefined",
|
|
27
|
-
"process.env.NO_COLOR": "undefined",
|
|
28
|
-
"process.env.FORCE_COLOR": "undefined",
|
|
29
|
-
"process.env.TERM": "undefined",
|
|
30
|
-
"process.env.TERM_PROGRAM": "undefined",
|
|
31
|
-
"process.env.COLORTERM": "undefined",
|
|
32
|
-
"process.env.CI": "undefined",
|
|
33
|
-
"process.env.GITHUB_ACTIONS": "undefined",
|
|
34
|
-
"process.env.KITTY_WINDOW_ID": "undefined",
|
|
35
|
-
"process.env.WT_SESSION": "undefined",
|
|
36
|
-
"process.env.LANG": "undefined",
|
|
37
|
-
"process.env.LC_ALL": "undefined",
|
|
38
|
-
"process.env.LC_CTYPE": "undefined",
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const result = await Bun.build({
|
|
42
|
-
entrypoints: [join(__dirname, "playground-app.tsx")],
|
|
43
|
-
outdir: distDir,
|
|
44
|
-
target: "browser",
|
|
45
|
-
format: "esm",
|
|
46
|
-
minify: false,
|
|
47
|
-
sourcemap: "external",
|
|
48
|
-
define: browserDefines,
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
if (!result.success) {
|
|
52
|
-
console.error("Playground build failed:")
|
|
53
|
-
for (const log of result.logs) {
|
|
54
|
-
console.error(log)
|
|
55
|
-
}
|
|
56
|
-
process.exit(1)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
console.log("Built examples/playground/dist/playground-app.js")
|
|
60
|
-
console.log("\nOpen in browser:")
|
|
61
|
-
console.log(" examples/playground/index.html")
|