tsquare 0.7.1 → 0.9.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.
- package/CHANGELOG.md +33 -0
- package/README.md +3 -3
- package/dist/chunks/chunk-GTWUMF6W.js +65 -0
- package/dist/chunks/{chunk-ZLVSCEWO.js → chunk-W3OU5IS6.js} +20 -8
- package/dist/cli.js +1 -1
- package/dist/icons.d.ts +14 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -1
- package/dist/playground/index.js +4 -60
- package/dist/playground/playground.js +18 -18
- package/dist/playground/viewer.d.ts +44 -0
- package/dist/playground/viewer.js +172 -0
- package/dist/prompt.d.ts +1 -1
- package/package.json +15 -4
- package/skill/tsquare/SKILL.md +4 -1
- package/skill/tsquare/reference.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
The language and the renderer haven't changed: every wireframe renders exactly as in 0.8.0, and render URLs give the same images.
|
|
6
|
+
|
|
7
|
+
### For editors and your own pages
|
|
8
|
+
|
|
9
|
+
- **`tsquare/viewer`:** the playground's zoomable board (zoom, pan, pinch, a chip per screen, dimmed while the text has problems) as a browser module with no dependencies, for showing boards in your own page. Give `createViewer` the elements, then `show()` the SVG from `renderWireframeSvg` with the size and screens from `boardLayout`. It sets the styles its zoom math depends on itself, so a page only gives the stage a size (and can style the `panning`, `animate` and `stale` classes). The playground uses it, and so will the VS Code extension's preview.
|
|
10
|
+
- **`languageData()`** is exported from `tsquare`: every component with its props, option words and description, plus icon names and accents. It's what the playground's highlighting and autocomplete use, now available to editors.
|
|
11
|
+
- **`require("tsquare")` works** in Node versions that can load ES modules from CommonJS (20.19+, 22.12+): every export now has a `default` condition next to `import`. The package is still ESM only.
|
|
12
|
+
|
|
13
|
+
### Playground
|
|
14
|
+
|
|
15
|
+
- **Fix: pinch zoom and two-finger pan on touch screens.** Touch screens report each finger's movement separately, and each one was handled as a zoom around a different point, so the board drifted out from under the fingers while pinching, and moving two fingers together zoomed out and back in and moved the board the wrong way. Now each move pans by how far the point between the fingers moved, then zooms around it, so the part of the board between your fingers stays there. Trackpad pinch and mouse dragging were already right.
|
|
16
|
+
- **Tests for the playground,** which had none: every endpoint through a real HTTP server (render, PNG, language, prompt, examples, reference, the page), the page's markup against its script and against what tsquare.dev's build expects, and the viewer in jsdom (fitting, zooming to a screen, wheel and buttons, dragging, pinching and two-finger panning).
|
|
17
|
+
|
|
18
|
+
## 0.8.0
|
|
19
|
+
|
|
20
|
+
### Brand logos
|
|
21
|
+
|
|
22
|
+
```tsquare
|
|
23
|
+
button secondary "Continue with Google" leadingIcon=brand-google fullWidth
|
|
24
|
+
listitem "GitHub" subtitle="Connected" leadingIcon=brand-github trailing=toggle
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- **Seven brand logos**, usable anywhere an icon is: `brand-apple`, `brand-facebook`, `brand-github`, `brand-google`, `brand-notion`, `brand-stripe`, `brand-x`. They're for sign-in buttons and integrations, and are drawn in the wireframe's gray at the same visual size as Lucide icons.
|
|
28
|
+
- **The `brand-` prefix keeps them apart from Lucide:** `apple` is still Lucide's fruit and `x` its close icon. A brand name without the prefix is an error that suggests it (`github` → `brand-github`, `twitter` → `brand-x`), and an unknown `brand-…` name lists the logos there are.
|
|
29
|
+
- The logos come from [Simple Icons](https://simpleicons.org) 16.34.0 (CC0), a new dependency pinned exactly like Lucide, since a release can change or remove logos. Simple Icons has no Slack, LinkedIn or Microsoft logo, so tsquare doesn't either.
|
|
30
|
+
- **Prompt:** rule 11 names the logos and the `apple`/`x` trap. The skill and the docs (Icons page, Icon component) are regenerated.
|
|
31
|
+
|
|
32
|
+
### Flow wording
|
|
33
|
+
|
|
34
|
+
- **"Board level" is gone.** The Flow description, prompt rule 10, the docs and the error for a flow written inside a screen now say what to do: write each flow on its own line after the screens, indented like a screen (not inside one). The error used to read "flow lines go at the board level, after the screens (indented like a screen)". A clarification only: what's valid hasn't changed.
|
|
35
|
+
|
|
3
36
|
## 0.7.1
|
|
4
37
|
|
|
5
38
|
- **`share_wireframe` returns only the links asked for.** A new `links` option takes the kinds from the playground's Copy link menu, with the same names: `share`, `svg`, `png`, `markdown`, `html`. The default is `["svg", "share"]`, an image link and the link to edit it; before, every call returned four links. Each link carries the whole wireframe (about 1,200 characters for a three-screen board), and the model has to write out every link it passes on, exactly, so fewer links means less to write and fewer chances to break one. A wireframe too long for an image link gets its share link instead, with a note saying why. New exports: `LINK_KINDS`, `DEFAULT_LINKS`, `LinkKind`.
|
package/README.md
CHANGED
|
@@ -95,7 +95,7 @@ Sticky notes beside the wireframe have their own colors, since they're annotatio
|
|
|
95
95
|
|---|---|
|
|
96
96
|
| Canvas | board, screen (`phone` 390×844, `tablet` 820×1180, `desktop` 1280×800, `custom`), note |
|
|
97
97
|
| Layout | stack, grid, card, divider, spacer |
|
|
98
|
-
| Content | heading, text (`lines=3` draws placeholder lines), image (X-box placeholder), icon (any [Lucide](https://lucide.dev/icons) name
|
|
98
|
+
| Content | heading, text (`lines=3` draws placeholder lines), image (X-box placeholder), icon (any [Lucide](https://lucide.dev/icons) name or a brand logo such as `brand-google`, see [Icons](docs/icons.md)), avatar, badge |
|
|
99
99
|
| Controls | button, input (`multiline=4` for a textarea), checkbox, radio, toggle, select |
|
|
100
100
|
| Navigation & data | navbar, tabbar, tabs, list, listitem, table |
|
|
101
101
|
| Overlays | modal, drawer (`left`, `right`, `bottom` sheet) |
|
|
@@ -123,7 +123,7 @@ const { issues } = compileWireframe(source); // problems by line, empt
|
|
|
123
123
|
const png = await renderWireframe(source, { format: "png", scale: 2 });
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
`tsquare/playground` exports the playground's request handler, for hosting it inside your own Node server. See [Using it with AI](docs/ai.md) for the prompt and the repair loop.
|
|
126
|
+
`tsquare/playground` exports the playground's request handler, for hosting it inside your own Node server. `tsquare/viewer` is the playground's zoomable board (zoom, pan, pinch, a chip per screen) for your own page: pass it the elements, then `show()` the SVG from `renderWireframeSvg` with the size and screens from `boardLayout`. It sets the styles its zoom math needs itself; give the stage a size, and optionally style the classes it toggles on the stage: `panning` (while dragging), `animate` (while fitting or zooming to a screen, e.g. `.animate > * { transition: transform .25s }`) and `stale` (from `setStale`, e.g. `.stale img { opacity: .35 }`). `languageData()` lists every component, prop, option word, icon and accent, for editor highlighting and autocomplete. See [Using it with AI](docs/ai.md) for the prompt and the repair loop.
|
|
127
127
|
|
|
128
128
|
## Using it with Claude (skill)
|
|
129
129
|
|
|
@@ -158,4 +158,4 @@ How it was measured, the tasks, every model output, and the scripts: [`eval/`](e
|
|
|
158
158
|
|
|
159
159
|
## License
|
|
160
160
|
|
|
161
|
-
MIT. The Inter font comes from the `@fontsource/inter` dependency, under the SIL Open Font License 1.1. Icons are from [Lucide](https://lucide.dev), under the ISC license.
|
|
161
|
+
MIT. The Inter font comes from the `@fontsource/inter` dependency, under the SIL Open Font License 1.1. Icons are from [Lucide](https://lucide.dev), under the ISC license. Brand logos are from [Simple Icons](https://simpleicons.org) (CC0); the logos themselves are trademarks of their owners.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ACCENTS,
|
|
3
|
+
PRIMARY_PROP,
|
|
4
|
+
UNIVERSAL_PROPS,
|
|
5
|
+
bareWords,
|
|
6
|
+
componentDefinitions,
|
|
7
|
+
iconNames,
|
|
8
|
+
takesUniversalProps
|
|
9
|
+
} from "./chunk-W3OU5IS6.js";
|
|
10
|
+
|
|
11
|
+
// src/playground/language-data.ts
|
|
12
|
+
function unwrap(t) {
|
|
13
|
+
let cur = t;
|
|
14
|
+
while (cur && ["optional", "nullable", "default"].includes(cur.def?.type)) cur = cur.def.innerType;
|
|
15
|
+
return cur;
|
|
16
|
+
}
|
|
17
|
+
function describe(name, schema) {
|
|
18
|
+
const s = unwrap(schema);
|
|
19
|
+
const description = schema.description ?? s?.description;
|
|
20
|
+
const iconish = /Lucide icon|Icon names/i.test(description ?? "");
|
|
21
|
+
switch (s?.def?.type) {
|
|
22
|
+
case "enum":
|
|
23
|
+
return { name, kind: "enum", values: s.options, description };
|
|
24
|
+
case "boolean":
|
|
25
|
+
return { name, kind: "boolean", values: ["true", "false"], description };
|
|
26
|
+
case "number":
|
|
27
|
+
return { name, kind: "number", description };
|
|
28
|
+
case "array": {
|
|
29
|
+
const el = unwrap(s.def.element);
|
|
30
|
+
const objects = el?.def?.type === "union" ? el.def.options.map(unwrap) : [el];
|
|
31
|
+
const hasIcon = objects.some((x) => x?.def?.type === "object" && "icon" in x.shape);
|
|
32
|
+
return { name, kind: "list", icon: iconish || hasIcon, description };
|
|
33
|
+
}
|
|
34
|
+
case "object":
|
|
35
|
+
return { name, kind: "object", description };
|
|
36
|
+
default:
|
|
37
|
+
return { name, kind: "string", icon: iconish || void 0, accent: name === "accent" || void 0, description };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
var cache = null;
|
|
41
|
+
function languageData() {
|
|
42
|
+
if (cache) return cache;
|
|
43
|
+
const components = Object.entries(componentDefinitions).map(([type, def]) => {
|
|
44
|
+
const shape = def.props.shape;
|
|
45
|
+
const props = Object.entries(shape).map(([name, schema]) => describe(name, schema));
|
|
46
|
+
if (takesUniversalProps(type)) for (const [name, schema] of Object.entries(UNIVERSAL_PROPS)) props.push(describe(name, schema));
|
|
47
|
+
if (type === "Icon") props.find((p) => p.name === "name").icon = true;
|
|
48
|
+
const words = bareWords(type);
|
|
49
|
+
const off = words.booleans.flatMap((b) => b === "on" ? ["off"] : b === "checked" ? ["unchecked"] : [`no-${b}`]);
|
|
50
|
+
return {
|
|
51
|
+
name: type.toLowerCase(),
|
|
52
|
+
description: def.description,
|
|
53
|
+
main: PRIMARY_PROP[type],
|
|
54
|
+
children: def.slots.length > 0,
|
|
55
|
+
props,
|
|
56
|
+
words: [...words.options.map((o) => o.value), ...words.booleans, ...off]
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
cache = { components, icons: [...new Set(iconNames)].sort(), accents: Object.keys(ACCENTS) };
|
|
60
|
+
return cache;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
languageData
|
|
65
|
+
};
|
|
@@ -131,14 +131,20 @@ function paletteFor(accent) {
|
|
|
131
131
|
|
|
132
132
|
// src/icons.ts
|
|
133
133
|
import { icons } from "lucide";
|
|
134
|
+
import { siApple, siFacebook, siGithub, siGoogle, siNotion, siStripe, siX } from "simple-icons";
|
|
135
|
+
var BRAND_ICONS = Object.fromEntries(
|
|
136
|
+
[siApple, siFacebook, siGithub, siGoogle, siNotion, siStripe, siX].map((i) => [`brand-${i.slug}`, { title: i.title, path: i.path }])
|
|
137
|
+
);
|
|
138
|
+
var brandIconNames = Object.keys(BRAND_ICONS).sort();
|
|
139
|
+
var brandPath = (name) => BRAND_ICONS[name.toLowerCase()]?.path;
|
|
134
140
|
var pascal = (s) => s.replace(/(^|[-_ ])(\w)/g, (_, __, c) => c.toUpperCase());
|
|
135
141
|
var kebab = (s) => s.replace(/([a-z])([A-Z0-9])/g, "$1-$2").replace(/([0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
|
|
136
142
|
var table = icons;
|
|
137
|
-
var iconNames = Object.keys(table).map(kebab);
|
|
143
|
+
var iconNames = [...Object.keys(table).map(kebab), ...brandIconNames];
|
|
138
144
|
function iconNode(name) {
|
|
139
145
|
return table[pascal(name)];
|
|
140
146
|
}
|
|
141
|
-
var isIcon = (name) => iconNode(name) !== void 0;
|
|
147
|
+
var isIcon = (name) => iconNode(name) !== void 0 || brandPath(name) !== void 0;
|
|
142
148
|
var GUESSES = {
|
|
143
149
|
call: ["phone"],
|
|
144
150
|
compose: ["square-pen", "pencil"],
|
|
@@ -148,7 +154,8 @@ var GUESSES = {
|
|
|
148
154
|
notifications: ["bell"],
|
|
149
155
|
profile: ["user", "circle-user"],
|
|
150
156
|
envelope: ["mail"],
|
|
151
|
-
close: ["x"]
|
|
157
|
+
close: ["x"],
|
|
158
|
+
twitter: ["brand-x"]
|
|
152
159
|
};
|
|
153
160
|
var DIRECTIONS = ["right", "left", "down", "up"];
|
|
154
161
|
var directionRank = (n) => {
|
|
@@ -157,7 +164,7 @@ var directionRank = (n) => {
|
|
|
157
164
|
};
|
|
158
165
|
function suggestIcons(name, limit = 4) {
|
|
159
166
|
const q = name.toLowerCase().replace(/[_ ]/g, "-");
|
|
160
|
-
const guessed = GUESSES[q] ?? [];
|
|
167
|
+
const guessed = [...BRAND_ICONS[`brand-${q}`] ? [`brand-${q}`] : [], ...GUESSES[q] ?? []];
|
|
161
168
|
const words = q.split("-").filter((w) => w.length > 2);
|
|
162
169
|
const maxEdits = Math.max(1, Math.floor(q.length / 3));
|
|
163
170
|
const scored = [];
|
|
@@ -173,6 +180,7 @@ function suggestIcons(name, limit = 4) {
|
|
|
173
180
|
return [.../* @__PURE__ */ new Set([...guessed, ...close])].slice(0, limit);
|
|
174
181
|
}
|
|
175
182
|
function unknownIconMessage(name) {
|
|
183
|
+
if (/^brand-/i.test(name)) return `unknown icon "${name}"; the brand logos are ${brandIconNames.join(", ")}`;
|
|
176
184
|
const close = suggestIcons(name);
|
|
177
185
|
return close.length ? `unknown icon "${name}" (did you mean ${close.join(", ")}?)` : `unknown icon "${name}" (use a Lucide name in kebab-case, e.g. search, user, settings)`;
|
|
178
186
|
}
|
|
@@ -297,7 +305,7 @@ var componentDefinitions = {
|
|
|
297
305
|
)
|
|
298
306
|
}),
|
|
299
307
|
slots: [],
|
|
300
|
-
description: 'An arrow between two elements or screens, named with #id.
|
|
308
|
+
description: 'An arrow between two elements or screens, named with #id. Write each flow on its own line after the screens, indented like a screen (not inside one): flow signin -> home "Tap Sign in".',
|
|
301
309
|
example: { from: "signin", to: "home", label: "Tap Sign in" }
|
|
302
310
|
},
|
|
303
311
|
// ── Layout ────────────────────────────────────────────────────────────
|
|
@@ -427,7 +435,7 @@ var componentDefinitions = {
|
|
|
427
435
|
size: o(z.number())
|
|
428
436
|
}),
|
|
429
437
|
slots: [],
|
|
430
|
-
description: "Line icon from Lucide.",
|
|
438
|
+
description: "Line icon from Lucide, or a brand logo (brand-google, brand-apple, \u2026).",
|
|
431
439
|
example: { name: "search", size: 20 }
|
|
432
440
|
},
|
|
433
441
|
Avatar: {
|
|
@@ -735,6 +743,9 @@ function hug(dir, aligned = false) {
|
|
|
735
743
|
return dir === "row" || aligned ? {} : { alignSelf: "flex-start" };
|
|
736
744
|
}
|
|
737
745
|
function IconGlyph({ name, size = 20, color = theme.ink, strokeWidth = 2 }) {
|
|
746
|
+
const brand = name ? brandPath(name) : void 0;
|
|
747
|
+
if (brand)
|
|
748
|
+
return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "-2 -2 28 28", fill: color, style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx("path", { d: brand }) });
|
|
738
749
|
const node = name ? iconNode(name) : null;
|
|
739
750
|
return /* @__PURE__ */ jsx(
|
|
740
751
|
"svg",
|
|
@@ -2848,7 +2859,7 @@ function checkSpec(spec) {
|
|
|
2848
2859
|
for (const [k, el] of Object.entries(spec.elements)) for (const c of el.children ?? []) parentOf.set(c, k);
|
|
2849
2860
|
for (const [key, el] of Object.entries(spec.elements)) {
|
|
2850
2861
|
if (el.type !== "Flow") continue;
|
|
2851
|
-
if (!(root.children ?? []).includes(key)) issues.push(`${key}:
|
|
2862
|
+
if (!(root.children ?? []).includes(key)) issues.push(`${key}: write each flow on its own line after the screens, indented like a screen (not inside one)`);
|
|
2852
2863
|
const p = el.props ?? {};
|
|
2853
2864
|
for (const end of ["from", "to"]) {
|
|
2854
2865
|
if (typeof p[end] !== "string") continue;
|
|
@@ -3229,7 +3240,8 @@ var RULES = `## Rules
|
|
|
3229
3240
|
7. Only use the components and props listed below. All props are optional unless marked required.
|
|
3230
3241
|
8. Wireframes are grayscale. The only UI colors: Board accent (one color for primary buttons, checked controls, toggles, active tabs and ghost buttons), Badge tone (success, warning, danger), Input error and Flow color. Only add an accent if the request asks for color or a brand.
|
|
3231
3242
|
9. Any element except Board, Screen, Note and Flow can take tooltip="text" to show a tooltip next to it. Select (with options), Input type=date, and Button, ListItem or NavBar (with menu) take open to show their list, calendar or menu over the screen.
|
|
3232
|
-
10. To show how screens connect, name elements or screens by writing #name after them (button primary "Sign in" #signin, screen phone "Home" #home), then
|
|
3243
|
+
10. To show how screens connect, name elements or screens by writing #name after them (button primary "Sign in" #signin, screen phone "Home" #home), then write each flow on its own line after the screens, indented like a screen (not inside one): flow signin -> home "Tap Sign in".
|
|
3244
|
+
11. Icons are Lucide names, plus brand logos named brand-\u2026: brand-google, brand-apple, brand-github, brand-facebook, brand-x, brand-notion, brand-stripe (button "Continue with Google" leadingIcon=brand-google). Plain apple is a fruit and x is a close icon.`;
|
|
3233
3245
|
var TEXT_FORMAT = `## Output format: wireframe text
|
|
3234
3246
|
One element per line. Indent children two spaces under their parent. The first line is the board.
|
|
3235
3247
|
|
package/dist/cli.js
CHANGED
package/dist/icons.d.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Brand logos, from Simple Icons (CC0; pinned exactly, like lucide). Filled shapes on a 24×24 grid,
|
|
3
|
+
* named brand-… so they can't collide with Lucide: `apple` is Lucide's fruit and `x` its close icon.
|
|
4
|
+
* Imported one by one so a browser bundle carries only these. Simple Icons has no Slack, LinkedIn or
|
|
5
|
+
* Microsoft logo (removed); don't draw our own.
|
|
6
|
+
*/
|
|
7
|
+
export declare const BRAND_ICONS: Record<string, {
|
|
8
|
+
title: string;
|
|
9
|
+
path: string;
|
|
10
|
+
}>;
|
|
11
|
+
export declare const brandIconNames: string[];
|
|
12
|
+
/** The SVG path of a brand-… logo, or undefined. */
|
|
13
|
+
export declare const brandPath: (name: string) => string | undefined;
|
|
14
|
+
/** Kebab-case names for every Lucide icon (including Lucide's aliases, e.g. home and house), then the brand logos. */
|
|
2
15
|
export declare const iconNames: string[];
|
|
3
16
|
/** The Lucide icon node for a kebab-case name, or undefined if there's no such icon. */
|
|
4
17
|
export declare function iconNode(name: string): any;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,3 +10,4 @@ export { parseWireframeText, type TextIssue, type ParseResult } from "./text.js"
|
|
|
10
10
|
export { catalog, componentDefinitions } from "./catalog.js";
|
|
11
11
|
export { registry } from "./components.js";
|
|
12
12
|
export { theme, DEVICES } from "./layout.js";
|
|
13
|
+
export { languageData, type LanguageData, type ComponentInfo, type PropInfo } from "./playground/language-data.js";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
languageData
|
|
3
|
+
} from "./chunks/chunk-GTWUMF6W.js";
|
|
1
4
|
import {
|
|
2
5
|
DEVICES,
|
|
3
6
|
RENAMED_PROPS,
|
|
@@ -21,7 +24,7 @@ import {
|
|
|
21
24
|
upgradeSpec,
|
|
22
25
|
upgradeWireframe,
|
|
23
26
|
wireframePrompt
|
|
24
|
-
} from "./chunks/chunk-
|
|
27
|
+
} from "./chunks/chunk-W3OU5IS6.js";
|
|
25
28
|
|
|
26
29
|
// src/share.ts
|
|
27
30
|
import { deflateRawSync, inflateRawSync } from "node:zlib";
|
|
@@ -180,6 +183,7 @@ export {
|
|
|
180
183
|
encodeWireframe,
|
|
181
184
|
formatIssues,
|
|
182
185
|
printWireframeText as formatWireframe,
|
|
186
|
+
languageData,
|
|
183
187
|
mcpTools,
|
|
184
188
|
parseWireframeText,
|
|
185
189
|
registry,
|
package/dist/playground/index.js
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
bareWords,
|
|
2
|
+
languageData
|
|
3
|
+
} from "../chunks/chunk-GTWUMF6W.js";
|
|
4
|
+
import {
|
|
6
5
|
boardLayout,
|
|
7
6
|
compileWireframe,
|
|
8
|
-
componentDefinitions,
|
|
9
7
|
componentDocs,
|
|
10
8
|
formatIssues,
|
|
11
|
-
iconNames,
|
|
12
9
|
renderWireframePng,
|
|
13
10
|
renderWireframeSvg,
|
|
14
|
-
takesUniversalProps,
|
|
15
11
|
wireframePrompt
|
|
16
|
-
} from "../chunks/chunk-
|
|
12
|
+
} from "../chunks/chunk-W3OU5IS6.js";
|
|
17
13
|
|
|
18
14
|
// src/playground/index.ts
|
|
19
15
|
import { createServer } from "node:http";
|
|
@@ -21,58 +17,6 @@ import { readFile, readdir } from "node:fs/promises";
|
|
|
21
17
|
import path from "node:path";
|
|
22
18
|
import { fileURLToPath } from "node:url";
|
|
23
19
|
|
|
24
|
-
// src/playground/language-data.ts
|
|
25
|
-
function unwrap(t) {
|
|
26
|
-
let cur = t;
|
|
27
|
-
while (cur && ["optional", "nullable", "default"].includes(cur.def?.type)) cur = cur.def.innerType;
|
|
28
|
-
return cur;
|
|
29
|
-
}
|
|
30
|
-
function describe(name, schema) {
|
|
31
|
-
const s = unwrap(schema);
|
|
32
|
-
const description = schema.description ?? s?.description;
|
|
33
|
-
const iconish = /Lucide icon|Icon names/i.test(description ?? "");
|
|
34
|
-
switch (s?.def?.type) {
|
|
35
|
-
case "enum":
|
|
36
|
-
return { name, kind: "enum", values: s.options, description };
|
|
37
|
-
case "boolean":
|
|
38
|
-
return { name, kind: "boolean", values: ["true", "false"], description };
|
|
39
|
-
case "number":
|
|
40
|
-
return { name, kind: "number", description };
|
|
41
|
-
case "array": {
|
|
42
|
-
const el = unwrap(s.def.element);
|
|
43
|
-
const objects = el?.def?.type === "union" ? el.def.options.map(unwrap) : [el];
|
|
44
|
-
const hasIcon = objects.some((x) => x?.def?.type === "object" && "icon" in x.shape);
|
|
45
|
-
return { name, kind: "list", icon: iconish || hasIcon, description };
|
|
46
|
-
}
|
|
47
|
-
case "object":
|
|
48
|
-
return { name, kind: "object", description };
|
|
49
|
-
default:
|
|
50
|
-
return { name, kind: "string", icon: iconish || void 0, accent: name === "accent" || void 0, description };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
var cache = null;
|
|
54
|
-
function languageData() {
|
|
55
|
-
if (cache) return cache;
|
|
56
|
-
const components = Object.entries(componentDefinitions).map(([type, def]) => {
|
|
57
|
-
const shape = def.props.shape;
|
|
58
|
-
const props = Object.entries(shape).map(([name, schema]) => describe(name, schema));
|
|
59
|
-
if (takesUniversalProps(type)) for (const [name, schema] of Object.entries(UNIVERSAL_PROPS)) props.push(describe(name, schema));
|
|
60
|
-
if (type === "Icon") props.find((p) => p.name === "name").icon = true;
|
|
61
|
-
const words = bareWords(type);
|
|
62
|
-
const off = words.booleans.flatMap((b) => b === "on" ? ["off"] : b === "checked" ? ["unchecked"] : [`no-${b}`]);
|
|
63
|
-
return {
|
|
64
|
-
name: type.toLowerCase(),
|
|
65
|
-
description: def.description,
|
|
66
|
-
main: PRIMARY_PROP[type],
|
|
67
|
-
children: def.slots.length > 0,
|
|
68
|
-
props,
|
|
69
|
-
words: [...words.options.map((o) => o.value), ...words.booleans, ...off]
|
|
70
|
-
};
|
|
71
|
-
});
|
|
72
|
-
cache = { components, icons: [...new Set(iconNames)].sort(), accents: Object.keys(ACCENTS) };
|
|
73
|
-
return cache;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
20
|
// src/playground/reference.ts
|
|
77
21
|
var GROUPS = [
|
|
78
22
|
{ name: "Canvas", components: ["Board", "Screen", "Note", "Flow"] },
|