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
@@ -1,365 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * Build web examples
4
- *
5
- * Bundles the React apps for browser usage.
6
- * Run: bun run examples/web/build.ts
7
- */
8
-
9
- import { mkdir, cp, readdir } 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
- const docsDistDir = join(__dirname, "../../docs/public/examples/dist")
15
-
16
- // Ensure dist directories exist
17
- await mkdir(distDir, { recursive: true })
18
- await mkdir(docsDistDir, { recursive: true })
19
-
20
- // Browser-safe defines for Node.js globals.
21
- // loggily and @silvery/ansi access process.env at module init,
22
- // which throws ReferenceError in browsers where `process` is undefined.
23
- const browserDefines: Record<string, string> = {
24
- "process.env.NODE_ENV": '"production"',
25
- "process.env.LOG_LEVEL": "undefined",
26
- "process.env.TRACE": "undefined",
27
- "process.env.TRACE_FORMAT": "undefined",
28
- "process.env.DEBUG": "undefined",
29
- "process.env.NO_COLOR": "undefined",
30
- "process.env.FORCE_COLOR": "undefined",
31
- "process.env.TERM": "undefined",
32
- "process.env.TERM_PROGRAM": "undefined",
33
- "process.env.COLORTERM": "undefined",
34
- "process.env.CI": "undefined",
35
- "process.env.GITHUB_ACTIONS": "undefined",
36
- "process.env.KITTY_WINDOW_ID": "undefined",
37
- "process.env.WT_SESSION": "undefined",
38
- "process.env.LANG": "undefined",
39
- "process.env.LC_ALL": "undefined",
40
- "process.env.LC_CTYPE": "undefined",
41
- }
42
-
43
- // Browser process shim — ansi/logger access process.stdout, process.stdin,
44
- // process.stderr, and dynamic process.env[key] at module init.
45
- // The `define` map above handles static process.env.KEY references, but
46
- // process.stdout/stdin/stderr and process.env[dynamic] need a real object.
47
- const processShim = `
48
- // Polyfill Symbol.dispose for Safari and older browsers that lack
49
- // TC39 Explicit Resource Management. Bun's __using helper uses a
50
- // polyfilled __dispose, but property definitions like [Symbol.dispose]
51
- // need the global symbol to exist.
52
- Symbol.dispose ??= Symbol.for("Symbol.dispose");
53
- Symbol.asyncDispose ??= Symbol.for("Symbol.asyncDispose");
54
- if (typeof globalThis.process === "undefined") {
55
- globalThis.process = {
56
- env: { NODE_ENV: "production" },
57
- stdout: { write() {}, columns: 80, rows: 24, isTTY: false },
58
- stdin: { isTTY: false, setRawMode() {}, on() {}, resume() {} },
59
- stderr: { write() {} },
60
- emit() {},
61
- on() {},
62
- platform: "browser",
63
- };
64
- }
65
- `
66
-
67
- // Plugin to stub Node.js built-in modules that can't be resolved in browsers.
68
- // Using `external` leaves bare `import "child_process"` in the output, which
69
- // browsers can't resolve (they require relative paths). This plugin replaces
70
- // the import with an inline empty module instead.
71
- const nodeStubPlugin: import("bun").BunPlugin = {
72
- name: "node-stub",
73
- setup(build) {
74
- const stubs: Record<string, string> = {
75
- child_process: "export function spawnSync() { return { status: 1, stdout: '', stderr: '' } }",
76
- "node:process": "export default globalThis.process",
77
- "node:zlib": "export function deflateSync(buf) { return buf }",
78
- }
79
- for (const mod of Object.keys(stubs)) {
80
- build.onResolve({ filter: new RegExp(`^${mod}$`) }, (args) => ({
81
- path: args.path,
82
- namespace: "node-stub",
83
- }))
84
- build.onLoad({ filter: new RegExp(`^${mod}$`), namespace: "node-stub" }, (args) => ({
85
- contents: stubs[args.path]!,
86
- loader: "js",
87
- }))
88
- }
89
- },
90
- }
91
-
92
- // Shared build options for all browser targets.
93
- // External: packages not needed in browser builds.
94
- // yoga-wasm-web is an optional layout engine (WASM, not needed for demos).
95
- // ws is used by React DevTools connection (not needed in browser).
96
- // Note: flexture IS bundled — all renderers use it for layout via browser-renderer.ts.
97
- const sharedOptions = {
98
- outdir: distDir,
99
- target: "browser" as const,
100
- format: "esm" as const,
101
- minify: false,
102
- sourcemap: "external" as const,
103
- define: browserDefines,
104
- banner: processShim,
105
- external: ["yoga-wasm-web", "ws", "@termless/core"],
106
- plugins: [nodeStubPlugin],
107
- }
108
-
109
- // =============================================================================
110
- // Registry Generation — scan examples and write viewer-registry.ts
111
- //
112
- // Showcase metadata is auto-discovered from:
113
- // 1. Terminal example `meta` exports (ExampleMeta) — the single source of truth
114
- // 2. SHOWCASES registry keys in web/showcases/index.ts
115
- // No hardcoded metadata arrays — everything flows from the examples themselves.
116
- // =============================================================================
117
-
118
- const skipFiles = new Set([
119
- "interactive/clipboard.tsx",
120
- "interactive/_input-debug.tsx",
121
- "interactive/_textarea-bare.tsx",
122
- "runtime/hello-runtime.tsx",
123
- "inline/scrollback.tsx",
124
- "inline/inline-nontty.tsx",
125
- ])
126
-
127
- const categories = [
128
- { dir: "layout", color: "#cba6f7", label: "Layout" },
129
- { dir: "interactive", color: "#89dceb", label: "Interactive" },
130
- { dir: "runtime", color: "#a6e3a1", label: "Runtime" },
131
- { dir: "inline", color: "#fab387", label: "Inline" },
132
- ] as const
133
-
134
- interface RegistryEntry {
135
- key: string
136
- name: string
137
- description: string
138
- features: string[]
139
- category: string
140
- categoryColor: string
141
- source: string
142
- type: "showcase" | "example"
143
- }
144
-
145
- /** Extract ExampleMeta from source text via regex (avoids importing modules at build time). */
146
- function extractMeta(source: string): { name?: string; description?: string; features: string[] } {
147
- const metaMatch = source.match(/export const meta:\s*ExampleMeta\s*=\s*\{([^}]+)\}/)
148
- if (!metaMatch) return { features: [] }
149
- const metaStr = metaMatch[1]!
150
- const nameMatch = metaStr.match(/name:\s*"([^"]+)"/)
151
- const descMatch = metaStr.match(/description:\s*"([^"]+)"/)
152
- const featMatch = metaStr.match(/features:\s*\[([^\]]*)\]/)
153
- return {
154
- name: nameMatch?.[1],
155
- description: descMatch?.[1],
156
- features: featMatch?.[1]?.match(/"([^"]+)"/g)?.map((s) => s.replace(/"/g, "")) ?? [],
157
- }
158
- }
159
-
160
- /** Map from SHOWCASES registry keys to their terminal example source paths.
161
- * Flagship showcases and their legacy aliases both map to the same source. */
162
- const SHOWCASE_SOURCE_MAP: Record<string, string> = {
163
- // Flagship keys
164
- aichat: "interactive/aichat/index.tsx",
165
- gallery: "interactive/gallery.tsx",
166
- kanban: "interactive/kanban.tsx",
167
- explorer: "interactive/explorer.tsx",
168
- wizard: "interactive/cli-wizard.tsx",
169
- dashboard: "layout/dashboard.tsx",
170
- terminal: "interactive/terminal.tsx",
171
- components: "interactive/components.tsx",
172
- theme: "interactive/theme.tsx",
173
-
174
- // Legacy / additional keys
175
- "ai-chat": "interactive/aichat/index.tsx",
176
- "cli-wizard": "interactive/cli-wizard.tsx",
177
- "dev-tools": "interactive/dev-tools.tsx",
178
- "data-explorer": "interactive/data-explorer.tsx",
179
- scroll: "interactive/scroll.tsx",
180
- "search-filter": "interactive/search-filter.tsx",
181
- transform: "interactive/transform.tsx",
182
- textarea: "interactive/textarea.tsx",
183
- }
184
-
185
- async function generateRegistry(): Promise<void> {
186
- const entries: RegistryEntry[] = []
187
-
188
- // --- Showcase entries: auto-discover from SHOWCASES keys + terminal meta ---
189
- const { SHOWCASES } = await import("./showcases/index.js")
190
- for (const key of Object.keys(SHOWCASES)) {
191
- const sourcePath = SHOWCASE_SOURCE_MAP[key]
192
- let name = key
193
- let description = ""
194
- let features: string[] = []
195
-
196
- if (sourcePath) {
197
- // Read meta from the terminal example source
198
- const source = await Bun.file(join(__dirname, "..", sourcePath)).text()
199
- const meta = extractMeta(source)
200
- if (meta.name) name = meta.name
201
- if (meta.description) description = meta.description
202
- features = meta.features
203
- }
204
-
205
- entries.push({
206
- key: `showcase-${key}`,
207
- name,
208
- description,
209
- features,
210
- category: "Showcases",
211
- categoryColor: "#f9e2af",
212
- source: "",
213
- type: "showcase",
214
- })
215
- }
216
-
217
- // --- Scan example directories ---
218
- for (const cat of categories) {
219
- const dir = join(__dirname, "..", cat.dir)
220
- const files = (await readdir(dir)).filter((f) => f.endsWith(".tsx") && !skipFiles.has(`${cat.dir}/${f}`))
221
-
222
- for (const file of files.sort()) {
223
- const source = await Bun.file(join(dir, file)).text()
224
- const key = file.replace(".tsx", "")
225
- const meta = extractMeta(source)
226
-
227
- entries.push({
228
- key,
229
- name: meta.name ?? key,
230
- description: meta.description ?? "",
231
- features: meta.features,
232
- category: cat.label,
233
- categoryColor: cat.color,
234
- source,
235
- type: "example",
236
- })
237
- }
238
- }
239
-
240
- // Write registry file
241
- const registryContent = `// AUTO-GENERATED by build.ts — do not edit manually
242
-
243
- export interface ExampleEntry {
244
- key: string
245
- name: string
246
- description: string
247
- features: string[]
248
- category: string
249
- categoryColor: string
250
- source: string
251
- type: "showcase" | "example"
252
- }
253
-
254
- export const REGISTRY: ExampleEntry[] = ${JSON.stringify(entries, null, 2)}
255
- `
256
- await Bun.write(join(__dirname, "viewer-registry.ts"), registryContent)
257
- }
258
-
259
- // Generate registry before building (viewer-app imports it)
260
- await generateRegistry()
261
-
262
- // Build canvas app
263
- const canvasResult = await Bun.build({
264
- entrypoints: [join(__dirname, "canvas-app.tsx")],
265
- ...sharedOptions,
266
- })
267
-
268
- if (!canvasResult.success) {
269
- console.error("Canvas build failed:")
270
- for (const log of canvasResult.logs) {
271
- console.error(log)
272
- }
273
- process.exit(1)
274
- }
275
-
276
- // Build DOM app
277
- const domResult = await Bun.build({
278
- entrypoints: [join(__dirname, "dom-app.tsx")],
279
- ...sharedOptions,
280
- })
281
-
282
- if (!domResult.success) {
283
- console.error("DOM build failed:")
284
- for (const log of domResult.logs) {
285
- console.error(log)
286
- }
287
- process.exit(1)
288
- }
289
-
290
- // Build xterm app
291
- const xtermResult = await Bun.build({
292
- entrypoints: [join(__dirname, "xterm-app.tsx")],
293
- ...sharedOptions,
294
- })
295
-
296
- if (!xtermResult.success) {
297
- console.error("xterm build failed:")
298
- for (const log of xtermResult.logs) {
299
- console.error(log)
300
- }
301
- process.exit(1)
302
- }
303
-
304
- // Build showcase app (use-case demos for docs site)
305
- const showcaseResult = await Bun.build({
306
- entrypoints: [join(__dirname, "showcase-app.tsx")],
307
- ...sharedOptions,
308
- })
309
-
310
- if (!showcaseResult.success) {
311
- console.error("Showcase build failed:")
312
- for (const log of showcaseResult.logs) {
313
- console.error(log)
314
- }
315
- process.exit(1)
316
- }
317
-
318
- // Build viewer app (unified example browser)
319
- const viewerResult = await Bun.build({
320
- entrypoints: [join(__dirname, "viewer-app.tsx")],
321
- ...sharedOptions,
322
- })
323
-
324
- if (!viewerResult.success) {
325
- console.error("Viewer build failed:")
326
- for (const log of viewerResult.logs) {
327
- console.error(log)
328
- }
329
- process.exit(1)
330
- }
331
-
332
- // Copy built files to VitePress public dir for docs site
333
- await cp(distDir, docsDistDir, { recursive: true })
334
-
335
- // Copy showcase.html to docs public dir
336
- await cp(join(__dirname, "showcase.html"), join(__dirname, "../../docs/public/examples/showcase.html"))
337
-
338
- // Copy viewer.html to docs public dir (if it exists)
339
- try {
340
- await cp(join(__dirname, "viewer.html"), join(__dirname, "../../docs/public/examples/viewer.html"))
341
- } catch {
342
- // viewer.html may not exist yet — skip silently
343
- }
344
-
345
- // Copy xterm.css to docs public dir (needed by showcase.html in production)
346
- await mkdir(join(__dirname, "../../docs/public/examples/xterm"), { recursive: true })
347
- await cp(
348
- join(__dirname, "../../node_modules/@xterm/xterm/css/xterm.css"),
349
- join(__dirname, "../../docs/public/examples/xterm/xterm.css"),
350
- )
351
-
352
- console.log("✓ Generated examples/web/viewer-registry.ts")
353
- console.log("✓ Built examples/web/dist/canvas-app.js")
354
- console.log("✓ Built examples/web/dist/dom-app.js")
355
- console.log("✓ Built examples/web/dist/xterm-app.js")
356
- console.log("✓ Built examples/web/dist/showcase-app.js")
357
- console.log("✓ Built examples/web/dist/viewer-app.js")
358
- console.log("✓ Copied to docs/public/examples/dist/")
359
- console.log("✓ Copied showcase.html to docs/public/examples/")
360
- console.log("\nOpen in browser:")
361
- console.log(" examples/web/canvas.html")
362
- console.log(" examples/web/dom.html")
363
- console.log(" examples/web/xterm.html")
364
- console.log(" examples/web/showcase.html?demo=dashboard")
365
- console.log(" examples/web/viewer.html")
@@ -1,80 +0,0 @@
1
- /**
2
- * Canvas Adapter Demo
3
- *
4
- * Demonstrates silvery rendering React components to Canvas.
5
- */
6
-
7
- import React, { useState } from "react"
8
- import { renderToCanvas, Box, Text, useContentRect } from "../../packages/ui/src/canvas/index.js"
9
-
10
- // Component that shows its dimensions
11
- function SizeDisplay() {
12
- const { width, height } = useContentRect()
13
- return (
14
- <Text color="green">
15
- Size: {Math.round(width)}px × {Math.round(height)}px
16
- </Text>
17
- )
18
- }
19
-
20
- // Demo component with various styles
21
- function App() {
22
- return (
23
- <Box flexDirection="column" padding={1}>
24
- <Box borderStyle="single" borderColor="cyan" padding={1}>
25
- <Box flexDirection="column">
26
- <Text bold color="cyan">
27
- silvery Canvas Rendering
28
- </Text>
29
- <SizeDisplay />
30
- </Box>
31
- </Box>
32
-
33
- <Box marginTop={1} borderStyle="round" borderColor="magenta" padding={1}>
34
- <Box flexDirection="column">
35
- <Text color="magenta">Text Styles</Text>
36
- <Box flexDirection="row" gap={2}>
37
- <Text>Normal</Text>
38
- <Text bold>Bold</Text>
39
- <Text italic>Italic</Text>
40
- </Box>
41
- <Box flexDirection="row" gap={2}>
42
- <Text underline>Underline</Text>
43
- <Text strikethrough>Strike</Text>
44
- <Text underlineStyle="curly" underlineColor="red">
45
- Curly
46
- </Text>
47
- </Box>
48
- </Box>
49
- </Box>
50
-
51
- <Box marginTop={1} flexDirection="row" gap={1}>
52
- <Box backgroundColor="red" padding={1}>
53
- <Text color="white">Red</Text>
54
- </Box>
55
- <Box backgroundColor="green" padding={1}>
56
- <Text color="black">Green</Text>
57
- </Box>
58
- <Box backgroundColor="blue" padding={1}>
59
- <Text color="white">Blue</Text>
60
- </Box>
61
- </Box>
62
-
63
- <Box marginTop={1}>
64
- <Text dim>Layout by Flexx, rendered to OffscreenCanvas, drawn to visible canvas</Text>
65
- </Box>
66
- </Box>
67
- )
68
- }
69
-
70
- // Mount to canvas
71
- const canvas = document.getElementById("canvas") as HTMLCanvasElement
72
- if (canvas) {
73
- const instance = renderToCanvas(<App />, canvas, {
74
- fontSize: 14,
75
- fontFamily: "monospace",
76
- })
77
-
78
- // Expose for debugging
79
- ;(window as any).silveryInstance = instance
80
- }
@@ -1,89 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>silvery Canvas Demo</title>
7
- <style>
8
- * {
9
- box-sizing: border-box;
10
- }
11
- body {
12
- margin: 0;
13
- padding: 20px;
14
- background: #1a1a2e;
15
- color: #eee;
16
- font-family: system-ui, sans-serif;
17
- }
18
- h1 {
19
- color: #4ec9b0;
20
- margin-bottom: 10px;
21
- }
22
- p {
23
- color: #808080;
24
- margin-top: 0;
25
- }
26
- #canvas {
27
- border: 1px solid #333;
28
- display: block;
29
- }
30
- .info {
31
- margin-top: 20px;
32
- padding: 15px;
33
- background: #16213e;
34
- border-radius: 4px;
35
- }
36
- .info h3 {
37
- color: #9cdcfe;
38
- margin-top: 0;
39
- }
40
- .info ul {
41
- margin: 0;
42
- padding-left: 20px;
43
- }
44
- .info li {
45
- margin: 5px 0;
46
- }
47
- code {
48
- background: #0f0f1a;
49
- padding: 2px 6px;
50
- border-radius: 3px;
51
- font-family: monospace;
52
- }
53
- </style>
54
- </head>
55
- <body>
56
- <h1>silvery Canvas Adapter</h1>
57
- <p>React components rendered to HTML5 Canvas via silvery</p>
58
-
59
- <canvas id="canvas" width="500" height="300"></canvas>
60
-
61
- <div class="info">
62
- <h3>How it works</h3>
63
- <ul>
64
- <li>React components use <code>&lt;Box&gt;</code> and <code>&lt;Text&gt;</code> from silvery</li>
65
- <li><code>useContentRect()</code> returns pixel dimensions during render</li>
66
- <li>Layout computed by Flexture (pure JS flexbox)</li>
67
- <li>Rendered to OffscreenCanvas, then drawn to visible canvas</li>
68
- </ul>
69
- </div>
70
-
71
- <div class="info">
72
- <h3>Code</h3>
73
- <pre><code>import { renderToCanvas, Box, Text, useContentRect } from '@silvery/term/canvas';
74
-
75
- function App() {
76
- const { width, height } = useContentRect();
77
- return (
78
- &lt;Box borderStyle="single"&gt;
79
- &lt;Text&gt;Size: {width}px × {height}px&lt;/Text&gt;
80
- &lt;/Box&gt;
81
- );
82
- }
83
-
84
- renderToCanvas(&lt;App /&gt;, canvas);</code></pre>
85
- </div>
86
-
87
- <script type="module" src="./dist/canvas-app.js"></script>
88
- </body>
89
- </html>
@@ -1,81 +0,0 @@
1
- /**
2
- * DOM Adapter Demo
3
- *
4
- * Demonstrates silvery rendering React components to DOM elements.
5
- * Advantages: text selection, accessibility, CSS integration.
6
- */
7
-
8
- import React, { useState } from "react"
9
- import { renderToDOM, Box, Text, useContentRect } from "../../packages/term/src/dom/index.js"
10
-
11
- // Component that shows its dimensions
12
- function SizeDisplay() {
13
- const { width, height } = useContentRect()
14
- return (
15
- <Text color="green">
16
- Size: {Math.round(width)}px × {Math.round(height)}px
17
- </Text>
18
- )
19
- }
20
-
21
- // Demo component with various styles
22
- function App() {
23
- return (
24
- <Box flexDirection="column" padding={1}>
25
- <Box borderStyle="single" borderColor="cyan" padding={1}>
26
- <Box flexDirection="column">
27
- <Text bold color="cyan">
28
- silvery DOM Rendering
29
- </Text>
30
- <SizeDisplay />
31
- </Box>
32
- </Box>
33
-
34
- <Box marginTop={1} borderStyle="round" borderColor="magenta" padding={1}>
35
- <Box flexDirection="column">
36
- <Text color="magenta">Text Styles (try selecting!)</Text>
37
- <Box flexDirection="row" gap={2}>
38
- <Text>Normal</Text>
39
- <Text bold>Bold</Text>
40
- <Text italic>Italic</Text>
41
- </Box>
42
- <Box flexDirection="row" gap={2}>
43
- <Text underline>Underline</Text>
44
- <Text strikethrough>Strike</Text>
45
- <Text underlineStyle="wavy" underlineColor="red">
46
- Wavy
47
- </Text>
48
- </Box>
49
- </Box>
50
- </Box>
51
-
52
- <Box marginTop={1} flexDirection="row" gap={1}>
53
- <Box backgroundColor="red" padding={1}>
54
- <Text color="white">Red</Text>
55
- </Box>
56
- <Box backgroundColor="green" padding={1}>
57
- <Text color="black">Green</Text>
58
- </Box>
59
- <Box backgroundColor="blue" padding={1}>
60
- <Text color="white">Blue</Text>
61
- </Box>
62
- </Box>
63
-
64
- <Box marginTop={1}>
65
- <Text dim>Text is selectable! Screen readers work. CSS hover states available.</Text>
66
- </Box>
67
- </Box>
68
- )
69
- }
70
-
71
- // Mount to container
72
- const container = document.getElementById("app") as HTMLElement
73
- if (container) {
74
- const instance = renderToDOM(<App />, container, {
75
- fontSize: 14,
76
- fontFamily: "monospace",
77
- })
78
-
79
- // Expose for debugging
80
- ;(window as any).silveryInstance = instance
81
- }