tsquare 0.1.0 → 0.3.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 (50) hide show
  1. package/README.md +34 -8
  2. package/assets/mark.png +0 -0
  3. package/bin/tsquare.js +2 -6
  4. package/dist/catalog.d.ts +1055 -0
  5. package/dist/chunks/chunk-46MGJLGB.js +2126 -0
  6. package/{src/cli.ts → dist/cli.js} +28 -24
  7. package/dist/colors.d.ts +29 -0
  8. package/dist/compile.d.ts +24 -0
  9. package/dist/components.d.ts +83 -0
  10. package/dist/icons.d.ts +12 -0
  11. package/dist/index.d.ts +10 -0
  12. package/dist/index.js +54 -0
  13. package/dist/layout.d.ts +66 -0
  14. package/dist/playground/index.d.ts +38 -0
  15. package/dist/playground/index.html +325 -0
  16. package/dist/playground/index.js +466 -0
  17. package/dist/playground/language-data.d.ts +28 -0
  18. package/dist/playground/playground.js +47 -0
  19. package/dist/playground/reference.d.ts +11 -0
  20. package/dist/print.d.ts +2 -0
  21. package/dist/prompt-example.d.ts +3 -0
  22. package/dist/prompt.d.ts +22 -0
  23. package/dist/render.d.ts +40 -0
  24. package/dist/schema.d.ts +33 -0
  25. package/dist/share.d.ts +4 -0
  26. package/dist/suggest.d.ts +5 -0
  27. package/dist/text.d.ts +50 -0
  28. package/examples/checkout.tsq +15 -0
  29. package/examples/dashboard.tsq +27 -0
  30. package/examples/notes-mobile.tsq +84 -0
  31. package/examples/notes-web.tsq +54 -0
  32. package/examples/sign-in.tsq +14 -0
  33. package/examples/states.tsq +16 -0
  34. package/package.json +31 -7
  35. package/skill/tsquare/SKILL.md +5 -4
  36. package/skill/tsquare/reference.md +7 -6
  37. package/src/catalog.ts +0 -329
  38. package/src/colors.ts +0 -82
  39. package/src/compile.ts +0 -64
  40. package/src/components.tsx +0 -1057
  41. package/src/icons.ts +0 -54
  42. package/src/index.ts +0 -11
  43. package/src/layout.ts +0 -63
  44. package/src/print.ts +0 -73
  45. package/src/prompt-example.ts +0 -43
  46. package/src/prompt.ts +0 -121
  47. package/src/render.ts +0 -178
  48. package/src/schema.ts +0 -43
  49. package/src/suggest.ts +0 -34
  50. package/src/text.ts +0 -333
@@ -0,0 +1,325 @@
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">
6
+ <title>tsquare Playground</title>
7
+ <link rel="icon" type="image/png" href="/mark.png">
8
+ <script>
9
+ // Apply the saved or system theme before first paint, so there's no flash.
10
+ try { document.documentElement.dataset.theme = localStorage.getItem("wf.theme") || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"); } catch {}
11
+ </script>
12
+ <style>
13
+ :root, :root[data-theme="light"] {
14
+ --bg: #f4f5f7;
15
+ --panel: #ffffff;
16
+ --ink: #1f2328;
17
+ --text: #3d434b;
18
+ --muted: #6f7782;
19
+ --line: #dfe2e6;
20
+ --line-strong: #c7cbd1;
21
+ --fill: #eef0f3;
22
+ --stage: #e6e8eb;
23
+ --primary: #1f6fd6; /* tsquare blue, deep enough for white text (4.9:1) */
24
+ --on-primary: #ffffff;
25
+ --accent-soft: rgba(59, 153, 251, 0.16);
26
+ --error: #c2362b;
27
+ --error-bg: #fcefed;
28
+ --ok: #2f7d4f;
29
+ --shadow: 0 12px 32px -12px rgba(15, 23, 42, 0.28);
30
+ --editor-bg: #ffffff;
31
+ --editor-ink: #1f2328;
32
+ /* translucent: CodeMirror draws selections behind the text, so an opaque current-line color would hide them */
33
+ --editor-active: rgba(31, 111, 214, 0.045);
34
+ --editor-selection: rgba(31, 111, 214, 0.26);
35
+ --syn-component: #0b3d91;
36
+ --syn-string: #17754a;
37
+ --syn-prop: #6d28d9;
38
+ --syn-number: #b45309;
39
+ --syn-word: #0e7490;
40
+ --syn-color: #be185d;
41
+ --syn-comment: #8b95a5;
42
+ --syn-punct: #5b6472;
43
+ --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
44
+ --sans: Inter, ui-sans-serif, -apple-system, "Segoe UI", sans-serif;
45
+ color-scheme: light;
46
+ }
47
+ :root[data-theme="dark"] {
48
+ --bg: #0e131a;
49
+ --panel: #151c25;
50
+ --ink: #e6ebf2;
51
+ --text: #c3cad4;
52
+ --muted: #8b95a5;
53
+ --line: #253041;
54
+ --line-strong: #344155;
55
+ --fill: #1c2532;
56
+ --stage: #0a0e13;
57
+ --primary: #2f7fe6;
58
+ --on-primary: #ffffff;
59
+ --accent-soft: rgba(59, 153, 251, 0.22);
60
+ --error: #ff7b70;
61
+ --error-bg: #3a1d1c;
62
+ --ok: #5cc98a;
63
+ --shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.7);
64
+ --editor-bg: #111821;
65
+ --editor-ink: #e6ebf2;
66
+ --editor-active: rgba(255, 255, 255, 0.035);
67
+ --editor-selection: rgba(80, 150, 255, 0.36);
68
+ --syn-component: #8ab4ff;
69
+ --syn-string: #7ee2a8;
70
+ --syn-prop: #c4a7ff;
71
+ --syn-number: #f5b971;
72
+ --syn-word: #67d4e8;
73
+ --syn-color: #f49ac1;
74
+ --syn-comment: #6f7a8a;
75
+ --syn-punct: #9aa4b2;
76
+ color-scheme: dark;
77
+ }
78
+ * { box-sizing: border-box; }
79
+ html, body { height: 100%; margin: 0; }
80
+ body { background: var(--bg); color: var(--ink); font: 14px/1.45 var(--sans); display: flex; flex-direction: column; }
81
+ button, select { font: inherit; color: inherit; }
82
+ [data-icon] { display: inline-flex; }
83
+ [data-icon] svg { width: 16px; height: 16px; }
84
+
85
+ header { display: flex; align-items: center; gap: 20px; padding: 0 12px 0 16px; height: 52px; background: var(--panel); border-bottom: 1px solid var(--line); flex-shrink: 0; }
86
+ header h1 { font-size: 15px; font-weight: 650; margin: 0; letter-spacing: -0.01em; display: flex; align-items: center; gap: 8px; }
87
+ header h1 img { width: 26px; height: 26px; border-radius: 6px; }
88
+ header .brand { display: flex; align-items: center; gap: 8px; color: inherit; text-decoration: none; border-radius: 6px; }
89
+ header .brand:hover { opacity: .8; }
90
+ header .spacer { flex: 1; }
91
+ nav { display: flex; gap: 4px; }
92
+ nav button { border: 0; background: none; padding: 6px 12px; border-radius: 6px; color: var(--muted); cursor: pointer; font-weight: 500; }
93
+ nav button:hover { color: var(--ink); background: var(--fill); }
94
+ nav button[aria-selected="true"] { color: var(--ink); background: var(--fill); }
95
+
96
+ .btn { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line-strong); background: var(--panel); padding: 5px 10px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; white-space: nowrap; }
97
+ .btn:hover { border-color: var(--muted); }
98
+ .btn:disabled { opacity: .45; cursor: default; }
99
+ .btn.primary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
100
+ .btn.icon-only { padding: 5px 7px; }
101
+ .btn.quiet { border-color: transparent; background: none; }
102
+ .btn.quiet:hover { background: var(--fill); }
103
+
104
+ main { flex: 1; min-height: 0; display: flex; }
105
+ .view { flex: 1; min-width: 0; display: none; }
106
+ .view.active { display: flex; }
107
+
108
+ /* ── Editor view ── */
109
+ .pane { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
110
+ .editor-pane { width: var(--split, 42%); min-width: 280px; background: var(--editor-bg); }
111
+ .splitter { width: 6px; margin: 0 -3px; cursor: col-resize; position: relative; z-index: 2; flex-shrink: 0; }
112
+ .splitter::after { content: ""; position: absolute; inset: 0 2px; background: var(--line); transition: background .15s; }
113
+ .splitter:hover::after, .splitter.dragging::after { background: var(--primary); }
114
+ .toolbar { display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-bottom: 1px solid var(--line); flex-shrink: 0; background: var(--panel); min-height: 47px; min-width: 0; }
115
+ .toolbar .grow { flex: 1; }
116
+ .doc-title { font-weight: 600; font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
117
+ .code { flex: 1; min-height: 0; overflow: hidden; }
118
+ .issues { border-top: 1px solid var(--line); max-height: 30%; overflow: auto; flex-shrink: 0; background: var(--panel); }
119
+ .issues:empty { display: none; }
120
+ .issue { display: flex; gap: 10px; padding: 7px 12px; border-bottom: 1px solid var(--line); cursor: pointer; font-size: 13px; width: 100%; text-align: left; background: none; border-left: 0; border-right: 0; border-top: 0; }
121
+ .issue:hover { background: var(--error-bg); }
122
+ .issue .ln { color: var(--error); font: 600 12px/20px var(--mono); flex-shrink: 0; min-width: 52px; }
123
+
124
+ .preview-pane { flex: 1; container-type: inline-size; }
125
+ /* Narrow preview: long button labels collapse to their icons (SVG/PNG keep their text) */
126
+ @container (max-width: 620px) { .label-wide { display: none; } .btn:has(.label-wide) { padding: 5px 7px; } }
127
+ @container (max-width: 470px) { .zoom-level { display: none; } }
128
+ .status { font-size: 13px; color: var(--muted); display: flex; align-items: center; gap: 6px; white-space: nowrap; }
129
+ .status .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
130
+ .status.ok .dot { background: var(--ok); }
131
+ .status.bad { color: var(--error); }
132
+ .status.bad .dot { background: var(--error); }
133
+ .zoom-level { min-width: 52px; justify-content: center; font-variant-numeric: tabular-nums; }
134
+ .screens { display: flex; gap: 4px; padding: 6px 10px; border-bottom: 1px solid var(--line); background: var(--panel); overflow-x: auto; scrollbar-width: none; flex-shrink: 0; }
135
+ .screens::-webkit-scrollbar { display: none; }
136
+ .screens:empty { display: none; }
137
+ .screens button { border: 1px solid var(--line); background: none; border-radius: 999px; padding: 3px 10px; font-size: 12.5px; color: var(--text); cursor: pointer; white-space: nowrap; }
138
+ .screens button:hover { border-color: var(--line-strong); }
139
+ .screens button[aria-pressed="true"] { background: var(--accent-soft); border-color: transparent; color: var(--ink); font-weight: 600; }
140
+ .stage { flex: 1; min-height: 0; overflow: hidden; position: relative; background: var(--stage); cursor: grab; touch-action: none; }
141
+ .stage.panning { cursor: grabbing; }
142
+ .stage .canvas { position: absolute; left: 0; top: 0; transform-origin: 0 0; }
143
+ .stage .canvas img { display: block; user-select: none; -webkit-user-drag: none; transition: opacity .15s; }
144
+ .stage.stale .canvas img { opacity: .35; }
145
+ .stage .empty { position: absolute; inset: 0; display: grid; place-items: center; color: var(--muted); }
146
+ .stage.animate .canvas { transition: transform .25s ease; }
147
+ .preview-pane:fullscreen { background: var(--bg); }
148
+
149
+ /* ── Menu (share links) ── */
150
+ .menu-wrap { position: relative; }
151
+ .menu { position: absolute; right: 0; top: calc(100% + 6px); min-width: 250px; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow); padding: 6px; z-index: 20; display: none; }
152
+ .menu.open { display: block; }
153
+ .menu button { display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left; border: 0; background: none; padding: 8px 10px; border-radius: 7px; cursor: pointer; }
154
+ .menu button:hover, .menu button:focus-visible { background: var(--fill); outline: none; }
155
+ .menu button small { display: block; color: var(--muted); font-size: 12px; }
156
+ .menu hr { border: 0; border-top: 1px solid var(--line); margin: 6px 4px; }
157
+
158
+ /* ── Examples gallery ── */
159
+ dialog { border: 1px solid var(--line); border-radius: 14px; padding: 0; background: var(--panel); color: var(--ink); width: min(920px, calc(100vw - 32px)); max-height: calc(100vh - 64px); box-shadow: var(--shadow); }
160
+ dialog::backdrop { background: rgba(10, 14, 20, .45); }
161
+ .dialog-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--panel); }
162
+ .dialog-head h2 { margin: 0; font-size: 16px; }
163
+ .gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; padding: 16px; }
164
+ .card { border: 1px solid var(--line); border-radius: 10px; background: var(--panel); cursor: pointer; padding: 0; text-align: left; overflow: hidden; display: flex; flex-direction: column; }
165
+ .card:hover, .card:focus-visible { border-color: var(--primary); outline: none; box-shadow: 0 0 0 3px var(--accent-soft); }
166
+ .card .thumb { height: 140px; background: var(--stage); display: flex; align-items: center; justify-content: center; padding: 8px; overflow: hidden; }
167
+ .card .thumb img { max-width: 100%; max-height: 124px; object-fit: contain; }
168
+ .card .thumb.blank { color: var(--muted); font-size: 13px; }
169
+ .card .name { padding: 9px 12px; font-weight: 600; font-size: 13px; }
170
+
171
+ /* ── Components view ── */
172
+ #components-view { min-height: 0; }
173
+ .toc { width: 200px; flex-shrink: 0; overflow: auto; padding: 16px 8px 32px 16px; border-right: 1px solid var(--line); background: var(--panel); }
174
+ .toc h3 { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 16px 0 4px; font-weight: 600; }
175
+ .toc h3:first-child { margin-top: 0; }
176
+ .toc a { display: block; padding: 3px 8px; margin-left: -8px; border-radius: 5px; color: var(--text); text-decoration: none; }
177
+ .toc a:hover { background: var(--fill); color: var(--ink); }
178
+ .docs { flex: 1; overflow: auto; padding: 24px 32px 80px; }
179
+ .docs-intro { max-width: 760px; color: var(--text); margin-bottom: 24px; }
180
+ .docs-intro code, .comp code, .prompt-help code { font: 12.5px var(--mono); background: var(--fill); padding: 1px 5px; border-radius: 4px; }
181
+ .group-title { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 40px 0 12px; font-weight: 600; }
182
+ .comp { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; margin-bottom: 20px; overflow: hidden; }
183
+ .comp-head { display: flex; align-items: baseline; gap: 12px; padding: 16px 20px 0; }
184
+ .comp-head h2 { margin: 0; font-size: 18px; font-weight: 650; }
185
+ .comp-head h2 code { background: none; padding: 0; font-size: 16px; color: var(--muted); font-weight: 500; }
186
+ .comp-head .grow { flex: 1; }
187
+ .comp > p { margin: 4px 20px 12px; color: var(--text); }
188
+ .comp-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); border-top: 1px solid var(--line); }
189
+ .comp-body > div { min-width: 0; }
190
+ .shot { background: var(--stage); display: flex; align-items: center; justify-content: center; padding: 12px; border-right: 1px solid var(--line); }
191
+ .shot img { max-width: 100%; max-height: 420px; }
192
+ .props { width: 100%; border-collapse: collapse; font-size: 13px; }
193
+ .props th { text-align: left; font-weight: 600; color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .05em; padding: 10px 14px 6px; }
194
+ .props td { padding: 7px 14px; border-top: 1px solid var(--line); vertical-align: top; }
195
+ .props td:first-child { font: 600 12.5px/20px var(--mono); white-space: nowrap; }
196
+ .props .type { font: 12px/18px var(--mono); color: var(--text); word-break: break-word; }
197
+ .props .desc { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
198
+ .tag { display: inline-block; font: 600 10px/1 var(--sans); text-transform: uppercase; letter-spacing: .04em; padding: 3px 5px; border-radius: 4px; background: var(--fill); color: var(--text); margin-left: 6px; vertical-align: 2px; }
199
+ pre.src { margin: 0; padding: 12px 14px; border-top: 1px solid var(--line); background: var(--panel); font: 12px/18px var(--mono); overflow: auto; max-height: 260px; color: var(--text); }
200
+
201
+ /* ── Prompt view ── */
202
+ #prompt-view { flex-direction: column; min-height: 0; }
203
+ .prompt-help { padding: 16px 24px; background: var(--panel); border-bottom: 1px solid var(--line); display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
204
+ .prompt-help ol { margin: 0; padding-left: 20px; color: var(--text); }
205
+ .prompt-help .grow { flex: 1; }
206
+ #prompt-text { flex: 1; margin: 0; overflow: auto; padding: 20px 24px 60px; font: 12.5px/19px var(--mono); white-space: pre-wrap; color: var(--text); max-width: 980px; }
207
+
208
+ /* ── Toast ── */
209
+ .toast { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%) translateY(20px); background: var(--ink); color: var(--panel); padding: 8px 8px 8px 14px; border-radius: 10px; font-size: 13px; opacity: 0; transition: .2s; pointer-events: none; display: flex; align-items: center; gap: 12px; z-index: 50; box-shadow: var(--shadow); }
210
+ .toast.show { opacity: 1; transform: translateX(-50%); pointer-events: auto; }
211
+ .toast button { border: 0; background: rgba(127, 127, 127, .25); color: inherit; padding: 4px 10px; border-radius: 6px; cursor: pointer; font-weight: 600; font-size: 12.5px; }
212
+ .toast button:empty { display: none; }
213
+ .toast:not(:has(button:not(:empty))) { padding-right: 14px; }
214
+
215
+ @media (max-width: 860px) {
216
+ #editor-view.active { flex-direction: column; }
217
+ .editor-pane { width: auto; min-width: 0; height: 48%; border-bottom: 1px solid var(--line); }
218
+ .splitter { display: none; }
219
+ .toc { display: none; }
220
+ .docs { padding: 16px 16px 60px; }
221
+ .comp-body { grid-template-columns: 1fr; }
222
+ .shot { border-right: 0; border-bottom: 1px solid var(--line); }
223
+ header { gap: 10px; }
224
+ }
225
+ @media (max-width: 480px) {
226
+ header { gap: 6px; padding: 0 8px 0 12px; }
227
+ header h1 { font-size: 0; } /* keep the logo mark, drop the word */
228
+ nav button { padding: 6px 8px; }
229
+ }
230
+ </style>
231
+ </head>
232
+ <body>
233
+ <header>
234
+ <h1><a class="brand" id="brand" href="https://tsquare.dev"><img src="/mark.png" alt="">tsquare</a></h1>
235
+ <nav role="tablist">
236
+ <button role="tab" data-view="editor" aria-selected="true">Editor</button>
237
+ <button role="tab" data-view="components" aria-selected="false">Components</button>
238
+ <button role="tab" data-view="prompt" aria-selected="false">Prompt</button>
239
+ </nav>
240
+ <span class="spacer"></span>
241
+ <button class="btn quiet icon-only" id="theme" type="button" aria-label="Switch to dark mode" title="Switch theme"><span data-icon="moon"></span></button>
242
+ </header>
243
+
244
+ <main>
245
+ <section id="editor-view" class="view active">
246
+ <div class="pane editor-pane">
247
+ <div class="toolbar">
248
+ <button class="btn" id="open-examples" type="button"><span data-icon="layout-grid"></span>Examples</button>
249
+ <span class="doc-title" id="doc-title" title="The board's title"></span>
250
+ <span class="grow"></span>
251
+ <span class="status" id="status"><span class="dot"></span><span id="status-text">Loading…</span></span>
252
+ </div>
253
+ <div class="code" id="code"></div>
254
+ <div class="issues" id="issues"></div>
255
+ </div>
256
+ <div class="splitter" id="splitter" role="separator" aria-orientation="vertical" aria-label="Resize editor" tabindex="0"></div>
257
+ <div class="pane preview-pane" id="preview-pane">
258
+ <div class="toolbar">
259
+ <button class="btn icon-only" id="zoom-out" type="button" aria-label="Zoom out" title="Zoom out"><span data-icon="zoom-out"></span></button>
260
+ <button class="btn zoom-level" id="zoom-fit" type="button" title="Fit to the window">100%</button>
261
+ <button class="btn icon-only" id="zoom-in" type="button" aria-label="Zoom in" title="Zoom in"><span data-icon="zoom-in"></span></button>
262
+ <span class="grow"></span>
263
+ <div class="menu-wrap">
264
+ <button class="btn" id="share" type="button" aria-haspopup="menu" aria-expanded="false" title="Copy a link" disabled><span data-icon="link"></span><span class="label-wide">Copy link</span></button>
265
+ <div class="menu" id="share-menu" role="menu">
266
+ <button role="menuitem" data-copy="share"><span data-icon="share-2"></span><span>Share link<small>Opens this wireframe in the playground</small></span></button>
267
+ <hr>
268
+ <button role="menuitem" data-copy="svg"><span data-icon="image"></span><span>Image link (SVG)<small>Embed anywhere an image works</small></span></button>
269
+ <button role="menuitem" data-copy="png"><span data-icon="image"></span><span>Image link (PNG)<small>For tools that don't show SVG</small></span></button>
270
+ <button role="menuitem" data-copy="markdown"><span data-icon="file-text"></span><span>Markdown<small>For READMEs and docs</small></span></button>
271
+ <button role="menuitem" data-copy="html"><span data-icon="code"></span><span>HTML<small>An &lt;img&gt; tag</small></span></button>
272
+ </div>
273
+ </div>
274
+ <button class="btn" id="copy-svg" type="button" disabled title="Copy SVG"><span data-icon="copy"></span><span class="label-wide">Copy SVG</span></button>
275
+ <button class="btn" id="dl-svg" type="button" disabled title="Download SVG"><span data-icon="download"></span>SVG</button>
276
+ <button class="btn" id="dl-png" type="button" disabled title="Download PNG"><span data-icon="download"></span>PNG</button>
277
+ <button class="btn icon-only" id="fullscreen" type="button" aria-label="Full screen" title="Full screen"><span data-icon="maximize"></span></button>
278
+ </div>
279
+ <div class="screens" id="screens" aria-label="Zoom to a screen"></div>
280
+ <div class="stage" id="stage"><div class="canvas" id="canvas"></div><div class="empty" id="empty">Nothing rendered yet</div></div>
281
+ </div>
282
+ </section>
283
+
284
+ <section id="components-view" class="view">
285
+ <div class="toc" id="toc"></div>
286
+ <div class="docs" id="docs">
287
+ <div class="docs-intro">
288
+ Every component, its props, and an example you can open in the editor. One element per line, children indented two spaces.
289
+ A <code>"quoted string"</code> fills the prop marked <span class="tag">main</span>. Enum values and booleans can be written bare
290
+ (<code>button primary lg</code>, <code>checkbox checked</code>, <code>toggle off</code>); anything else is <code>key=value</code>.
291
+ Lists use commas, and items can contain spaces: <code>[All notes, Pinned]</code>. Quote an item that contains a comma:
292
+ <code>["$1,200", "Smith, J"]</code>. Objects are <code>{key=value key=value}</code>.
293
+ </div>
294
+ <div id="docs-body">Loading…</div>
295
+ </div>
296
+ </section>
297
+
298
+ <section id="prompt-view" class="view">
299
+ <div class="prompt-help">
300
+ <ol>
301
+ <li>Copy this system prompt into Claude (or another model).</li>
302
+ <li>Ask for a wireframe, e.g. “a settings screen for a notes app”.</li>
303
+ <li>Paste the reply into the <strong>Editor</strong>. Code fences are fine; errors show by line and can be pasted back to the model.</li>
304
+ </ol>
305
+ <span class="grow"></span>
306
+ <button class="btn primary" id="copy-prompt" type="button"><span data-icon="copy"></span>Copy prompt</button>
307
+ </div>
308
+ <pre id="prompt-text">Loading…</pre>
309
+ </section>
310
+ </main>
311
+
312
+ <dialog id="examples-dialog" aria-labelledby="examples-title">
313
+ <div class="dialog-head">
314
+ <h2 id="examples-title">Examples</h2>
315
+ <span class="grow" style="flex:1"></span>
316
+ <button class="btn quiet icon-only" type="button" id="close-examples" aria-label="Close"><span data-icon="x"></span></button>
317
+ </div>
318
+ <div class="gallery" id="gallery"></div>
319
+ </dialog>
320
+
321
+ <div class="toast" id="toast" role="status"><span id="toast-text"></span><button type="button" id="toast-action"></button></div>
322
+
323
+ <script type="module" src="/playground.js"></script>
324
+ </body>
325
+ </html>