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,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
- }