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,420 +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 Playground</title>
7
- <style>
8
- :root {
9
- --bg-primary: #1a1a2e;
10
- --bg-secondary: #16213e;
11
- --bg-tertiary: #0f0f1a;
12
- --text-primary: #eee;
13
- --text-secondary: #808080;
14
- --accent-cyan: #4ec9b0;
15
- --accent-blue: #9cdcfe;
16
- --accent-yellow: #dcdcaa;
17
- --border: #333;
18
- }
19
-
20
- * {
21
- box-sizing: border-box;
22
- margin: 0;
23
- padding: 0;
24
- }
25
-
26
- body {
27
- background: var(--bg-primary);
28
- color: var(--text-primary);
29
- font-family:
30
- system-ui,
31
- -apple-system,
32
- "Segoe UI",
33
- sans-serif;
34
- min-height: 100vh;
35
- display: flex;
36
- flex-direction: column;
37
- }
38
-
39
- /* Header */
40
- header {
41
- padding: 16px 24px;
42
- border-bottom: 1px solid var(--border);
43
- display: flex;
44
- align-items: center;
45
- gap: 16px;
46
- flex-wrap: wrap;
47
- }
48
-
49
- header h1 {
50
- color: var(--accent-cyan);
51
- font-size: 1.4rem;
52
- font-weight: 600;
53
- white-space: nowrap;
54
- }
55
-
56
- header .subtitle {
57
- color: var(--text-secondary);
58
- font-size: 0.9rem;
59
- }
60
-
61
- /* Preset buttons */
62
- .preset-bar {
63
- display: flex;
64
- gap: 8px;
65
- padding: 12px 24px;
66
- border-bottom: 1px solid var(--border);
67
- background: var(--bg-secondary);
68
- flex-wrap: wrap;
69
- align-items: center;
70
- }
71
-
72
- .preset-bar label {
73
- color: var(--text-secondary);
74
- font-size: 0.85rem;
75
- margin-right: 4px;
76
- }
77
-
78
- .preset-btn {
79
- background: var(--bg-tertiary);
80
- color: var(--text-primary);
81
- border: 1px solid var(--border);
82
- border-radius: 4px;
83
- padding: 6px 14px;
84
- font-size: 0.85rem;
85
- cursor: pointer;
86
- transition: all 0.15s;
87
- font-family: inherit;
88
- }
89
-
90
- .preset-btn:hover {
91
- border-color: var(--accent-cyan);
92
- color: var(--accent-cyan);
93
- }
94
-
95
- .preset-btn.active {
96
- background: var(--accent-cyan);
97
- color: var(--bg-primary);
98
- border-color: var(--accent-cyan);
99
- font-weight: 600;
100
- }
101
-
102
- /* Main area */
103
- .main {
104
- display: flex;
105
- flex: 1;
106
- min-height: 0;
107
- }
108
-
109
- /* Canvas container */
110
- .canvas-container {
111
- flex: 1;
112
- position: relative;
113
- min-height: 400px;
114
- display: flex;
115
- align-items: stretch;
116
- }
117
-
118
- #canvas {
119
- display: block;
120
- width: 100%;
121
- height: 100%;
122
- }
123
-
124
- .canvas-size-label {
125
- position: absolute;
126
- bottom: 8px;
127
- right: 8px;
128
- background: rgba(0, 0, 0, 0.7);
129
- color: var(--text-secondary);
130
- font-size: 0.75rem;
131
- padding: 2px 8px;
132
- border-radius: 3px;
133
- font-family: monospace;
134
- pointer-events: none;
135
- }
136
-
137
- /* Sidebar */
138
- .sidebar {
139
- width: 320px;
140
- border-left: 1px solid var(--border);
141
- background: var(--bg-secondary);
142
- overflow-y: auto;
143
- padding: 20px;
144
- flex-shrink: 0;
145
- }
146
-
147
- .sidebar h3 {
148
- color: var(--accent-blue);
149
- font-size: 1rem;
150
- margin-bottom: 12px;
151
- }
152
-
153
- .sidebar p {
154
- color: var(--text-secondary);
155
- font-size: 0.85rem;
156
- line-height: 1.6;
157
- margin-bottom: 16px;
158
- }
159
-
160
- .sidebar code {
161
- background: var(--bg-tertiary);
162
- padding: 1px 5px;
163
- border-radius: 3px;
164
- font-family: "SF Mono", "Fira Code", "JetBrains Mono", monospace;
165
- font-size: 0.8rem;
166
- }
167
-
168
- .code-block {
169
- background: var(--bg-tertiary);
170
- border: 1px solid var(--border);
171
- border-radius: 4px;
172
- padding: 12px;
173
- font-family: "SF Mono", "Fira Code", "JetBrains Mono", monospace;
174
- font-size: 0.78rem;
175
- line-height: 1.5;
176
- overflow-x: auto;
177
- white-space: pre;
178
- color: var(--text-primary);
179
- margin-bottom: 16px;
180
- }
181
-
182
- .code-block .keyword {
183
- color: #c586c0;
184
- }
185
- .code-block .component {
186
- color: #4ec9b0;
187
- }
188
- .code-block .string {
189
- color: #ce9178;
190
- }
191
- .code-block .prop {
192
- color: #9cdcfe;
193
- }
194
- .code-block .comment {
195
- color: #6a9955;
196
- }
197
-
198
- .section {
199
- margin-bottom: 24px;
200
- padding-bottom: 20px;
201
- border-bottom: 1px solid var(--border);
202
- }
203
-
204
- .section:last-child {
205
- border-bottom: none;
206
- }
207
-
208
- .feature-list {
209
- list-style: none;
210
- padding: 0;
211
- }
212
-
213
- .feature-list li {
214
- color: var(--text-secondary);
215
- font-size: 0.85rem;
216
- padding: 4px 0;
217
- padding-left: 16px;
218
- position: relative;
219
- }
220
-
221
- .feature-list li::before {
222
- content: "\2022";
223
- color: var(--accent-cyan);
224
- position: absolute;
225
- left: 0;
226
- }
227
-
228
- /* Loading state */
229
- .loading {
230
- display: flex;
231
- align-items: center;
232
- justify-content: center;
233
- height: 100%;
234
- color: var(--text-secondary);
235
- font-size: 1.1rem;
236
- }
237
-
238
- .loading::after {
239
- content: "";
240
- display: inline-block;
241
- width: 20px;
242
- height: 20px;
243
- border: 2px solid var(--border);
244
- border-top-color: var(--accent-cyan);
245
- border-radius: 50%;
246
- margin-left: 12px;
247
- animation: spin 0.8s linear infinite;
248
- }
249
-
250
- @keyframes spin {
251
- to {
252
- transform: rotate(360deg);
253
- }
254
- }
255
-
256
- /* Responsive */
257
- @media (max-width: 900px) {
258
- .main {
259
- flex-direction: column;
260
- }
261
- .sidebar {
262
- width: 100%;
263
- border-left: none;
264
- border-top: 1px solid var(--border);
265
- max-height: 40vh;
266
- }
267
- }
268
- </style>
269
- </head>
270
- <body>
271
- <header>
272
- <h1>silvery Canvas Playground</h1>
273
- <span class="subtitle">React components rendered to HTML5 Canvas via the silvery rendering pipeline</span>
274
- </header>
275
-
276
- <div class="preset-bar">
277
- <label>Examples:</label>
278
- <button class="preset-btn active" data-preset="hello">Hello World</button>
279
- <button class="preset-btn" data-preset="text">Text Styles</button>
280
- <button class="preset-btn" data-preset="colors">Colors</button>
281
- <button class="preset-btn" data-preset="flexbox">Flexbox</button>
282
- <button class="preset-btn" data-preset="borders">Borders</button>
283
- <button class="preset-btn" data-preset="dashboard">Dashboard</button>
284
- <button class="preset-btn" data-preset="responsive">Responsive</button>
285
- </div>
286
-
287
- <div class="main">
288
- <div class="canvas-container">
289
- <canvas id="canvas"></canvas>
290
- <div class="canvas-size-label" id="size-label"></div>
291
- </div>
292
-
293
- <div class="sidebar">
294
- <div class="section">
295
- <h3>How It Works</h3>
296
- <p>
297
- silvery is a React renderer that computes layout
298
- <strong>before</strong> content rendering. Components know their size during render via
299
- <code>useContentRect()</code>, not after.
300
- </p>
301
- <ul class="feature-list">
302
- <li>React reconciler builds an <code>InkxNode</code> tree</li>
303
- <li>Flexx (pure JS flexbox) computes layout synchronously</li>
304
- <li>Content renders to an OffscreenCanvas buffer</li>
305
- <li>Buffer is drawn to the visible canvas element</li>
306
- <li><code>requestAnimationFrame</code> drives re-renders on state changes</li>
307
- </ul>
308
- </div>
309
-
310
- <div class="section">
311
- <h3>Canvas Adapter API</h3>
312
- <div class="code-block">
313
- <span class="keyword">import</span> { <span class="component">renderToCanvas</span>,
314
- <span class="component">Box</span>, <span class="component">Text</span>,
315
- <span class="component">useContentRect</span> }
316
- <span class="keyword">from</span>
317
- <span class="string">'silvery/canvas'</span>;
318
-
319
- <span class="keyword">function</span>
320
- <span class="component">App</span>() { <span class="keyword">const</span> { width, height } =
321
- <span class="component">useContentRect</span>(); <span class="keyword">return</span> ( &lt;<span
322
- class="component"
323
- >Box</span
324
- >
325
- <span class="prop">borderStyle</span>=<span class="string">"single"</span>&gt; &lt;<span class="component"
326
- >Text</span
327
- >&gt; {width}px x {height}px &lt;/<span class="component">Text</span>&gt; &lt;/<span class="component"
328
- >Box</span
329
- >&gt; ); }
330
-
331
- <span class="keyword">const</span> canvas = document.getElementById(<span class="string">'canvas'</span>);
332
- <span class="component">renderToCanvas</span>(&lt;<span class="component">App</span>
333
- /&gt;, canvas);
334
- </div>
335
- </div>
336
-
337
- <div class="section">
338
- <h3>Render Targets</h3>
339
- <p>
340
- silvery supports multiple render targets through its
341
- <code>RenderAdapter</code> interface:
342
- </p>
343
- <ul class="feature-list">
344
- <li><strong>Terminal</strong> &mdash; ANSI character grid (production)</li>
345
- <li><strong>Canvas 2D</strong> &mdash; pixel buffer (this demo)</li>
346
- <li><strong>DOM</strong> &mdash; accessible HTML elements</li>
347
- <li><strong>WebGL</strong> &mdash; GPU-accelerated (planned)</li>
348
- </ul>
349
- </div>
350
-
351
- <div class="section">
352
- <h3>Build &amp; Run</h3>
353
- <div class="code-block">
354
- <span class="comment"># Build the playground bundle</span>
355
- cd vendor/beorn-silvery bun run examples/playground/build.ts
356
-
357
- <span class="comment"># Open in browser</span>
358
- open examples/playground/index.html
359
- </div>
360
- <p>
361
- For a full live-editing playground with Monaco editor and hot reload, see
362
- <code>docs/playground-design.md</code>.
363
- </p>
364
- </div>
365
- </div>
366
- </div>
367
-
368
- <script>
369
- // Preset button handling
370
- const buttons = document.querySelectorAll(".preset-btn")
371
- const sizeLabel = document.getElementById("size-label")
372
- const canvas = document.getElementById("canvas")
373
- let currentPreset = "hello"
374
-
375
- function setPreset(preset) {
376
- currentPreset = preset
377
- buttons.forEach((btn) => {
378
- btn.classList.toggle("active", btn.dataset.preset === preset)
379
- })
380
- // Send message to the app (loaded via module script below)
381
- window.postMessage({ type: "set-preset", preset }, "*")
382
- }
383
-
384
- buttons.forEach((btn) => {
385
- btn.addEventListener("click", () => setPreset(btn.dataset.preset))
386
- })
387
-
388
- // Update size label
389
- function updateSizeLabel() {
390
- if (canvas && sizeLabel) {
391
- sizeLabel.textContent = canvas.width + " x " + canvas.height
392
- }
393
- }
394
-
395
- const resizeObserver = new ResizeObserver(() => {
396
- requestAnimationFrame(updateSizeLabel)
397
- })
398
- if (canvas) resizeObserver.observe(canvas)
399
- updateSizeLabel()
400
-
401
- // Keyboard shortcuts for switching presets
402
- const presetKeys = {
403
- 1: "hello",
404
- 2: "text",
405
- 3: "colors",
406
- 4: "flexbox",
407
- 5: "borders",
408
- 6: "dashboard",
409
- 7: "responsive",
410
- }
411
- document.addEventListener("keydown", (e) => {
412
- if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") return
413
- const preset = presetKeys[e.key]
414
- if (preset) setPreset(preset)
415
- })
416
- </script>
417
-
418
- <script type="module" src="./dist/playground-app.js"></script>
419
- </body>
420
- </html>