tsquare 0.3.0 → 0.3.2

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 ADDED
@@ -0,0 +1,91 @@
1
+ # Changelog
2
+
3
+ ## 0.3.2
4
+
5
+ - **`grow` on Select**, like Input: `select "State" grow` fills the rest of a row. Sonnet wrote it in 5 of 9 eval runs, its only remaining mistake; with it, every stored Sonnet text output is valid.
6
+ - **Docs:** a new [Embedding](docs/embedding.md) page for Notion, GitHub and your own site, and the Radio page shows radios side by side (`stack row` + `radio`).
7
+ - **Tests and CI:** a test suite (`npm test`), run on Node 20, 22 and 24 for every pull request, plus a check that eval scores, docs and the skill match what's committed.
8
+ - `CHANGELOG.md` is now included in the npm package.
9
+
10
+ ## 0.3.1
11
+
12
+ ### Older wireframes are upgraded in one step
13
+
14
+ - All support for older syntax now lives in one module (`upgradeWireframe`, `upgradeSpec`), run before parsing. The parser, catalog and renderer only know the current language.
15
+ - **Links made before 0.3.0 render again** when a comment follows an element on the same line (`screen phone "Home" # main`): the comment moves onto its own line. Text you write or paste still gets the error, so new comments go on their own line.
16
+ - `decodeWireframe` returns the upgraded text, so a decoded link always means what it did when it was made.
17
+
18
+ ### Better suggestions for unknown icons
19
+
20
+ - Common guesses that aren't Lucide names now suggest what was meant: `compose` → `square-pen`, `call` → `phone`, `person` → `user`, `bag` → `shopping-bag`, `notification` → `bell`, `close` → `x`.
21
+ - Among equally close matches, the usual directions come first: `chevron` suggests `chevron-right, chevron-left, chevron-down, chevron-up`.
22
+ - On a list item, `trailingIcon=chevron` (or `toggle`, `avatar`…) says to write `trailing=chevron`, since that's a kind, not an icon.
23
+
24
+ ## 0.3.0
25
+
26
+ ### Renamed: `icon` is now `leadingIcon` on Button and ListItem
27
+
28
+ Buttons and list items take an icon on either side, so the prop names now say which side:
29
+
30
+ | Before | Now |
31
+ |---|---|
32
+ | `button "Download" icon=download` | `button "Download" leadingIcon=download` |
33
+ | (not possible) | `button "Next" trailingIcon=chevron-right` |
34
+ | `listitem "Inbox" leading=icon icon=inbox` | `listitem "Inbox" leadingIcon=inbox` |
35
+ | `listitem "Delete" trailing=icon trailingIcon=trash-2` | `listitem "Delete" trailingIcon=trash-2` |
36
+
37
+ - **Nothing breaks.** The old `icon=` spelling still works on both components, so existing files and shared render links render the same. `tsquare fmt` rewrites it to `leadingIcon=`. The prompt, the skill and the docs only teach the new names.
38
+ - **Buttons can have a trailing icon**, for Next, external links or dropdowns. Both sides work together.
39
+ - `leading` and `trailing` on ListItem stay as before. They pick what fills each side (`avatar`, `image`, `checkbox`, `chevron`, `badge`, `toggle`, …). An icon or text value implies its kind, so `leading=icon` and `trailing=icon` are optional.
40
+ - `icon` stays as it is on TabBar items (`{label=Home icon=house}`) and on the Icon component.
41
+
42
+ ### Changed: comments go on their own line
43
+
44
+ A line that starts with `#` is a comment. Anywhere else, `#` is ordinary text, so order numbers, tags and hex colors need no quotes:
45
+
46
+ ```tsquare
47
+ # the orders page
48
+ screen desktop "Orders"
49
+ table columns=[Order, Customer] data=[[#1001, Ana Torres], [#1002, Ben Lee]]
50
+ ```
51
+
52
+ - **Why:** comments used to start at any `#` after a space. That cut off `[#1001, Ana Torres]` and broke the table, which happened to Sonnet in 3 of 4 eval runs. Across 160 eval outputs, models never wrote a comment.
53
+ - **Upgrading:** a comment after an element on the same line (`screen phone "Home" # main`) is now an error that tells you to move it to its own line. Nothing is dropped silently.
54
+
55
+ ### Clearer errors for props on the wrong component
56
+
57
+ When a word or prop belongs to a different component, the error now says which one:
58
+
59
+ ```
60
+ Input: fullWidth is a Button prop, not an Input one; an Input already fills its width (width=… sets a fixed one), so remove it
61
+ Card has no prop "border": border is a Stack prop, not a Card one
62
+ ```
63
+
64
+ A prop or list wrapped onto its own line says where it belongs, instead of "unknown component":
65
+
66
+ ```
67
+ trailing=…: props go on the same line as their component; move it to the end of line 15
68
+ a list must stay on one line; join this to line 6
69
+ ```
70
+
71
+ ### Playground
72
+
73
+ - A CodeMirror editor: highlighting, autocomplete for components, options, props, Lucide icons and accent colors, and problems underlined on their line.
74
+ - Light and dark themes.
75
+ - Zoom and pan, zoom to a single screen, full screen and a resizable split.
76
+ - Copy link: share link, SVG/PNG image links, Markdown and HTML snippets.
77
+ - An examples gallery with thumbnails.
78
+
79
+ ### Library
80
+
81
+ - `encodeWireframe` / `decodeWireframe` for share and render links.
82
+ - `boardLayout()` returns the board size and each screen's position.
83
+
84
+ ### List items: a value implies its kind
85
+
86
+ - `trailingIcon=trash-2` without `trailing=icon` used to render nothing, silently. Now the value implies the kind.
87
+ - A value that contradicts an explicit kind is an error that names the fix, for example: `leadingIcon only shows with leading=icon (this item has leading=avatar); remove one of them`.
88
+
89
+ ### Examples
90
+
91
+ - New: `sign-in`, `dashboard`, `checkout` and `states`.
package/README.md CHANGED
@@ -50,7 +50,7 @@ A wireframe can be embedded anywhere an image works (Notion, GitHub, docs sites)
50
50
  ![Login](https://tsquare.dev/svg/zNYwxDsIwEAT7vGJ…)
51
51
  ```
52
52
 
53
- The playground's **Copy link** menu makes these. The text is compressed into the URL, not encrypted, so anyone with the link can read it; see [privacy](https://tsquare.dev/privacy). `encodeWireframe` and `decodeWireframe` in the library produce and read the same links.
53
+ The playground's **Copy link** menu makes these; [Embedding](docs/embedding.md) has the steps for Notion, GitHub and your own site. The text is compressed into the URL, not encrypted, so anyone with the link can read it; see [privacy](https://tsquare.dev/privacy). `encodeWireframe` and `decodeWireframe` in the library produce and read the same links.
54
54
 
55
55
  ## The language
56
56
 
package/dist/catalog.d.ts CHANGED
@@ -341,6 +341,7 @@ export declare const componentDefinitions: {
341
341
  label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
342
342
  value: z.ZodOptional<z.ZodNullable<z.ZodString>>;
343
343
  placeholder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
344
+ grow: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
344
345
  width: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
345
346
  }, z.core.$strip>;
346
347
  slots: never[];
@@ -864,6 +865,7 @@ export declare const catalog: import("@json-render/core").Catalog<{
864
865
  label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
865
866
  value: z.ZodOptional<z.ZodNullable<z.ZodString>>;
866
867
  placeholder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
868
+ grow: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
867
869
  width: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
868
870
  }, z.core.$strip>;
869
871
  slots: never[];
@@ -1035,21 +1037,3 @@ export declare function listItemEnds(p: {
1035
1037
  leading: string | undefined;
1036
1038
  trailing: string | undefined;
1037
1039
  };
1038
- /**
1039
- * Props renamed in 0.3.0, still accepted under their old names so existing
1040
- * wireframes and render URLs keep working. The prompt, docs and autocomplete
1041
- * only teach the new names, and `tsquare fmt` rewrites the old ones.
1042
- */
1043
- export declare const RENAMED_PROPS: Record<string, Record<string, string>>;
1044
- /** Rewrites renamed props to their current names, in place. Safe to call more than once. */
1045
- export declare function upgradeRenamedProps(spec: {
1046
- elements: Record<string, {
1047
- type: string;
1048
- props?: Record<string, unknown>;
1049
- }>;
1050
- }): {
1051
- elements: Record<string, {
1052
- type: string;
1053
- props?: Record<string, unknown>;
1054
- }>;
1055
- };
@@ -139,8 +139,25 @@ function iconNode(name) {
139
139
  return table[pascal(name)];
140
140
  }
141
141
  var isIcon = (name) => iconNode(name) !== void 0;
142
- function suggestIcons(name, limit = 3) {
142
+ var GUESSES = {
143
+ call: ["phone"],
144
+ compose: ["square-pen", "pencil"],
145
+ person: ["user"],
146
+ bag: ["shopping-bag"],
147
+ notification: ["bell"],
148
+ notifications: ["bell"],
149
+ profile: ["user", "circle-user"],
150
+ envelope: ["mail"],
151
+ close: ["x"]
152
+ };
153
+ var DIRECTIONS = ["right", "left", "down", "up"];
154
+ var directionRank = (n) => {
155
+ const i = DIRECTIONS.indexOf(n.split("-").pop());
156
+ return i < 0 ? DIRECTIONS.length : i;
157
+ };
158
+ function suggestIcons(name, limit = 4) {
143
159
  const q = name.toLowerCase().replace(/[_ ]/g, "-");
160
+ const guessed = GUESSES[q] ?? [];
144
161
  const words = q.split("-").filter((w) => w.length > 2);
145
162
  const maxEdits = Math.max(1, Math.floor(q.length / 3));
146
163
  const scored = [];
@@ -152,7 +169,8 @@ function suggestIcons(name, limit = 3) {
152
169
  const score = Math.min(wordScore, d <= maxEdits ? 3 * d / q.length : Infinity);
153
170
  if (score < Infinity) scored.push([n, score]);
154
171
  }
155
- return scored.sort((a, b) => a[1] - b[1] || a[0].localeCompare(b[0])).slice(0, limit).map(([n]) => n);
172
+ const close = scored.sort((a, b) => a[1] - b[1] || directionRank(a[0]) - directionRank(b[0]) || a[0].localeCompare(b[0])).map(([n]) => n);
173
+ return [.../* @__PURE__ */ new Set([...guessed, ...close])].slice(0, limit);
156
174
  }
157
175
  function unknownIconMessage(name) {
158
176
  const close = suggestIcons(name);
@@ -415,6 +433,7 @@ var componentDefinitions = {
415
433
  label: o(z.string()),
416
434
  value: o(z.string()),
417
435
  placeholder: o(z.string()),
436
+ grow: o(z.boolean()).describe("Fill the remaining space in a row"),
418
437
  width: o(z.union([z.number(), z.string()])).describe("Fixed width, e.g. 320. Default fills the space.")
419
438
  }),
420
439
  slots: [],
@@ -514,23 +533,6 @@ function listItemEnds(p) {
514
533
  trailing: p.trailing ?? (p.trailingIcon ? "icon" : p.trailingText ? "text" : void 0)
515
534
  };
516
535
  }
517
- var RENAMED_PROPS = {
518
- Button: { icon: "leadingIcon" },
519
- ListItem: { icon: "leadingIcon" }
520
- };
521
- function upgradeRenamedProps(spec) {
522
- for (const el of Object.values(spec.elements)) {
523
- const renames = RENAMED_PROPS[el.type];
524
- if (!renames || !el.props) continue;
525
- for (const [from, to] of Object.entries(renames)) {
526
- if (from in el.props) {
527
- if (!(to in el.props)) el.props[to] = el.props[from];
528
- delete el.props[from];
529
- }
530
- }
531
- }
532
- return spec;
533
- }
534
536
 
535
537
  // src/components.tsx
536
538
  import React, { Children, cloneElement, isValidElement } from "react";
@@ -1052,7 +1054,7 @@ function Input({ element, dir }) {
1052
1054
  }
1053
1055
  function Select({ element, dir }) {
1054
1056
  const p = element.props;
1055
- return /* @__PURE__ */ jsxs(Box, { style: { flexDirection: "column", gap: 6, ...fieldWidth(p.width, dir) }, children: [
1057
+ return /* @__PURE__ */ jsxs(Box, { style: { flexDirection: "column", gap: 6, ...fieldWidth(p.width, dir), ...p.grow ? { flexGrow: 1 } : {} }, children: [
1056
1058
  /* @__PURE__ */ jsx(FieldLabel, { text: p.label }),
1057
1059
  /* @__PURE__ */ jsxs(Field, { children: [
1058
1060
  /* @__PURE__ */ jsx(Box, { style: { flexGrow: 1, color: p.value ? theme.ink : theme.muted }, children: p.value ?? p.placeholder ?? "Select\u2026" }),
@@ -1629,7 +1631,7 @@ function parseWireframeText(source) {
1629
1631
  if (t.kind !== "sym" && (c.isSym("=", 1) || c.isSym(":", 1))) {
1630
1632
  c.next();
1631
1633
  c.next();
1632
- const key = RENAMED_PROPS[info.name]?.[t.value] ?? t.value;
1634
+ const key = t.value;
1633
1635
  if (!(key in info.props)) {
1634
1636
  const elsewhere = belongsElsewhere(info.name, key, true);
1635
1637
  issues.push({ line: lineNo, message: `${info.name} has no prop "${key}"` + (elsewhere ? `: ${elsewhere}` : "") + ` (its props: ${Object.keys(info.props).join(", ")})` });
@@ -1687,6 +1689,122 @@ function parseWireframeText(source) {
1687
1689
  return { spec: root ? { root, elements } : null, issues, lines };
1688
1690
  }
1689
1691
 
1692
+ // src/upgrade.ts
1693
+ var RENAMED_PROPS = {
1694
+ Button: { icon: "leadingIcon" },
1695
+ ListItem: { icon: "leadingIcon" }
1696
+ };
1697
+ var RENAMED_BY_WORD = new Map(Object.entries(RENAMED_PROPS).map(([type, renames]) => [type.toLowerCase(), renames]));
1698
+ function upgradeWireframe(text, options = {}) {
1699
+ const lines = text.replace(/\r\n?/g, "\n").split("\n");
1700
+ const out = [];
1701
+ for (const line of lines) {
1702
+ const trimmed = line.trim();
1703
+ const component = trimmed.match(/^([A-Za-z][\w-]*)/)?.[1];
1704
+ if (!component || trimmed.startsWith("```")) {
1705
+ out.push(line);
1706
+ continue;
1707
+ }
1708
+ const tokens = scanLine(line, component.length + (line.length - line.trimStart().length));
1709
+ let upgraded = line;
1710
+ let comment = "";
1711
+ if (options.fromLink) {
1712
+ const hash = tokens.find((t) => t.kind === "word" && t.depth === 0 && upgraded[t.start] === "#" && !t.afterEquals);
1713
+ if (hash && !fillsMainText(component, tokens, hash)) {
1714
+ comment = upgraded.slice(hash.start).trimEnd();
1715
+ upgraded = upgraded.slice(0, hash.start).trimEnd();
1716
+ }
1717
+ }
1718
+ const renames = RENAMED_BY_WORD.get(component.toLowerCase().replace(/[-_]/g, ""));
1719
+ if (renames) {
1720
+ for (const t of [...tokens].reverse()) {
1721
+ if (t.kind === "key" && t.depth === 0 && t.start < upgraded.length && renames[t.text]) {
1722
+ upgraded = upgraded.slice(0, t.start) + renames[t.text] + upgraded.slice(t.start + t.text.length);
1723
+ }
1724
+ }
1725
+ }
1726
+ if (comment) out.push(line.slice(0, line.length - line.trimStart().length) + comment);
1727
+ out.push(upgraded);
1728
+ }
1729
+ return out.join("\n");
1730
+ }
1731
+ function upgradeSpec(spec) {
1732
+ for (const el of Object.values(spec.elements)) {
1733
+ const renames = RENAMED_PROPS[el.type];
1734
+ if (!renames || !el.props) continue;
1735
+ for (const [from, to] of Object.entries(renames)) {
1736
+ if (from in el.props) {
1737
+ if (!(to in el.props)) el.props[to] = el.props[from];
1738
+ delete el.props[from];
1739
+ }
1740
+ }
1741
+ }
1742
+ return spec;
1743
+ }
1744
+ var PLAIN_WORD_MAIN_TEXT = ["avatar", "icon"];
1745
+ function fillsMainText(component, tokens, hash) {
1746
+ if (!PLAIN_WORD_MAIN_TEXT.includes(component.toLowerCase())) return false;
1747
+ for (const t of tokens) {
1748
+ if (t === hash) return true;
1749
+ if (t.depth > 0 || t.afterEquals || t.kind === "key") continue;
1750
+ return false;
1751
+ }
1752
+ return false;
1753
+ }
1754
+ function scanLine(line, from) {
1755
+ const tokens = [];
1756
+ let depth = 0;
1757
+ let afterEquals = false;
1758
+ let i = from;
1759
+ while (i < line.length) {
1760
+ const c = line[i];
1761
+ if (c === " " || c === " ") {
1762
+ i++;
1763
+ continue;
1764
+ }
1765
+ if (c === '"' || c === "'") {
1766
+ let j2 = i + 1;
1767
+ while (j2 < line.length && line[j2] !== c) j2 += line[j2] === "\\" ? 2 : 1;
1768
+ tokens.push({ kind: "string", text: line.slice(i, j2 + 1), start: i, depth, afterEquals });
1769
+ afterEquals = false;
1770
+ i = j2 + 1;
1771
+ continue;
1772
+ }
1773
+ if (c === "[" || c === "{") {
1774
+ depth++;
1775
+ afterEquals = false;
1776
+ i++;
1777
+ continue;
1778
+ }
1779
+ if (c === "]" || c === "}") {
1780
+ depth = Math.max(0, depth - 1);
1781
+ afterEquals = false;
1782
+ i++;
1783
+ continue;
1784
+ }
1785
+ if (c === ",") {
1786
+ afterEquals = false;
1787
+ i++;
1788
+ continue;
1789
+ }
1790
+ if (c === "=" || c === ":") {
1791
+ afterEquals = true;
1792
+ i++;
1793
+ continue;
1794
+ }
1795
+ let j = i;
1796
+ while (j < line.length && !` ,=[]{}":`.includes(line[j])) j++;
1797
+ const text = line.slice(i, j);
1798
+ let k = j;
1799
+ while (line[k] === " " || line[k] === " ") k++;
1800
+ const isKey = !afterEquals && (line[k] === "=" || line[k] === ":");
1801
+ tokens.push({ kind: isKey ? "key" : "word", text, start: i, depth, afterEquals });
1802
+ afterEquals = false;
1803
+ i = j;
1804
+ }
1805
+ return tokens;
1806
+ }
1807
+
1690
1808
  // src/render.ts
1691
1809
  import { readFile } from "node:fs/promises";
1692
1810
  import { createRequire } from "node:module";
@@ -1711,8 +1829,19 @@ var SpecError = class extends Error {
1711
1829
  }
1712
1830
  issues;
1713
1831
  };
1832
+ function listItemKindHint(el, path) {
1833
+ const prop = path[0];
1834
+ if (el.type !== "ListItem" || path.length !== 1 || prop !== "leadingIcon" && prop !== "trailingIcon") return void 0;
1835
+ const side = prop === "leadingIcon" ? "leading" : "trailing";
1836
+ const value = String(el.props?.[prop]);
1837
+ let schema = componentDefinitions.ListItem.props.shape[side];
1838
+ while (schema && ["optional", "nullable", "default"].includes(schema.def?.type)) schema = schema.def.innerType;
1839
+ const kinds = schema?.options ?? [];
1840
+ if (value === "icon" || !kinds.includes(value)) return void 0;
1841
+ return `${value} is a ${side} kind, not an icon name: write ${side}=${value} instead of ${prop}=${value}`;
1842
+ }
1714
1843
  function checkSpec(spec) {
1715
- upgradeRenamedProps(spec);
1844
+ upgradeSpec(spec);
1716
1845
  const issues = [];
1717
1846
  const result = catalog.validate(spec);
1718
1847
  if (!result.success) {
@@ -1738,7 +1867,7 @@ function checkSpec(spec) {
1738
1867
  const parsed = def.props.safeParse(el.props ?? {});
1739
1868
  if (!parsed.success) {
1740
1869
  for (const i of parsed.error.issues) {
1741
- issues.push(`${id}.props${i.path.length ? "." + i.path.join(".") : ""}: ${i.message}`);
1870
+ issues.push(`${id}.props${i.path.length ? "." + i.path.join(".") : ""}: ${listItemKindHint(el, i.path) ?? i.message}`);
1742
1871
  }
1743
1872
  }
1744
1873
  for (const key of Object.keys(el.props ?? {})) {
@@ -1826,7 +1955,7 @@ function boardSize(spec) {
1826
1955
  return { width, height };
1827
1956
  }
1828
1957
  async function renderWireframeSvg(spec, opts = {}) {
1829
- upgradeRenamedProps(spec);
1958
+ upgradeSpec(spec);
1830
1959
  if (!opts.skipValidation) {
1831
1960
  const issues = checkSpec(spec);
1832
1961
  if (issues.length) throw new SpecError(issues);
@@ -1849,7 +1978,7 @@ async function renderWireframePng(spec, opts = {}) {
1849
1978
 
1850
1979
  // src/compile.ts
1851
1980
  function compileWireframe(source) {
1852
- const parsed = parseWireframeText(source);
1981
+ const parsed = parseWireframeText(upgradeWireframe(source));
1853
1982
  const issues = [...parsed.issues];
1854
1983
  if (parsed.spec) {
1855
1984
  for (const msg of checkSpec(parsed.spec)) {
@@ -2110,6 +2239,9 @@ export {
2110
2239
  PRIMARY_PROP,
2111
2240
  bareWords,
2112
2241
  parseWireframeText,
2242
+ RENAMED_PROPS,
2243
+ upgradeWireframe,
2244
+ upgradeSpec,
2113
2245
  checkSpec,
2114
2246
  boardLayout,
2115
2247
  boardSize,
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  printWireframeText,
7
7
  renderWireframe,
8
8
  wireframePrompt
9
- } from "./chunks/chunk-46MGJLGB.js";
9
+ } from "./chunks/chunk-X46EDSCQ.js";
10
10
 
11
11
  // src/cli.ts
12
12
  import { readFile, writeFile } from "node:fs/promises";
package/dist/icons.d.ts CHANGED
@@ -4,9 +4,8 @@ export declare const iconNames: string[];
4
4
  export declare function iconNode(name: string): any;
5
5
  export declare const isIcon: (name: string) => boolean;
6
6
  /**
7
- * Close matches for an unknown name: near spellings ("serach" → search) and names that
8
- * share a word with it ("cart" → shopping-cart, "call" → phone-call).
9
- * There are no synonyms: "notification" won't find bell.
7
+ * Close matches for an unknown name: known guesses ("compose" → square-pen), near spellings
8
+ * ("serach" → search) and names that share a word with it ("cart" → shopping-cart).
10
9
  */
11
10
  export declare function suggestIcons(name: string, limit?: number): string[];
12
11
  export declare function unknownIconMessage(name: string): string;
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export { renderWireframe, compileWireframe, formatIssues, WireframeError, type C
3
3
  export { wireframePrompt, repairPrompt } from "./prompt.js";
4
4
  export { printWireframeText as formatWireframe } from "./print.js";
5
5
  export { encodeWireframe, decodeWireframe, MAX_SHARED_TEXT } from "./share.js";
6
+ export { upgradeWireframe, upgradeSpec, RENAMED_PROPS, type UpgradeOptions } from "./upgrade.js";
6
7
  export { renderWireframeSvg, renderWireframePng, checkSpec, boardSize, boardLayout, type BoardItem, type RenderWireframeOptions } from "./render.js";
7
8
  export { parseWireframeText, type TextIssue, type ParseResult } from "./text.js";
8
9
  export { catalog, componentDefinitions } from "./catalog.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  DEVICES,
3
+ RENAMED_PROPS,
3
4
  WireframeError,
4
5
  boardLayout,
5
6
  boardSize,
@@ -16,8 +17,10 @@ import {
16
17
  renderWireframeSvg,
17
18
  repairPrompt,
18
19
  theme,
20
+ upgradeSpec,
21
+ upgradeWireframe,
19
22
  wireframePrompt
20
- } from "./chunks/chunk-46MGJLGB.js";
23
+ } from "./chunks/chunk-X46EDSCQ.js";
21
24
 
22
25
  // src/share.ts
23
26
  import { deflateRawSync, inflateRawSync } from "node:zlib";
@@ -27,11 +30,13 @@ function encodeWireframe(text) {
27
30
  }
28
31
  function decodeWireframe(data) {
29
32
  if (!data.startsWith("z")) throw new Error(`unknown encoding "${data.slice(0, 1)}" (expected a "z" prefix)`);
30
- return inflateRawSync(Buffer.from(data.slice(1), "base64url"), { maxOutputLength: MAX_SHARED_TEXT }).toString("utf8");
33
+ const text = inflateRawSync(Buffer.from(data.slice(1), "base64url"), { maxOutputLength: MAX_SHARED_TEXT }).toString("utf8");
34
+ return upgradeWireframe(text, { fromLink: true });
31
35
  }
32
36
  export {
33
37
  DEVICES,
34
38
  MAX_SHARED_TEXT,
39
+ RENAMED_PROPS,
35
40
  WireframeError,
36
41
  boardLayout,
37
42
  boardSize,
@@ -50,5 +55,7 @@ export {
50
55
  renderWireframeSvg,
51
56
  repairPrompt,
52
57
  theme,
58
+ upgradeSpec,
59
+ upgradeWireframe,
53
60
  wireframePrompt
54
61
  };
@@ -11,7 +11,7 @@ import {
11
11
  renderWireframePng,
12
12
  renderWireframeSvg,
13
13
  wireframePrompt
14
- } from "../chunks/chunk-46MGJLGB.js";
14
+ } from "../chunks/chunk-X46EDSCQ.js";
15
15
 
16
16
  // src/playground/index.ts
17
17
  import { createServer } from "node:http";
@@ -242,18 +242,26 @@ var EXAMPLES = {
242
242
  checkbox "Checked" checked
243
243
  checkbox "Unchecked"`,
244
244
  Radio: `board
245
- screen custom width=300 height=130
245
+ screen custom width=380 height=230
246
246
  radio "Selected" checked
247
247
  radio "Not selected"
248
- radio "Not selected"`,
248
+ radio "Not selected"
249
+ text "Side by side: put them in a row" sm muted
250
+ stack row gap=20
251
+ radio "Standard" checked
252
+ radio "Express"
253
+ radio "Pickup"`,
249
254
  Toggle: `board
250
255
  screen custom width=300 height=100
251
256
  toggle "On" on
252
257
  toggle "Off" off`,
253
258
  Select: `board
254
- screen custom width=320 height=170
259
+ screen custom width=380 height=260
255
260
  select "Country" value="Mexico"
256
- select "Size" placeholder="Choose one" width=160`,
261
+ select "Size" placeholder="Choose one" width=160
262
+ stack row gap=12
263
+ input "City" placeholder="Monterrey" grow
264
+ select "State" value="Nuevo Le\xF3n" grow`,
257
265
  NavBar: `board
258
266
  screen custom width=380 height=240 padding=0 gap=0
259
267
  navbar "leading=menu" leading=menu actions=[search, bell]