tsquare 0.4.0 → 0.4.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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.2
4
+
5
+ - **Toast corners:** `position` also takes `topLeft`, `topRight`, `bottomLeft` and `bottomRight`, besides `top` and `bottom` (centered, the default). Desktop apps often show toasts in a corner.
6
+
7
+ ## 0.4.1
8
+
9
+ ```tsquare
10
+ image map height=200 "Pickup" # a map placeholder: roads and a pin, instead of the X
11
+ accordion "How long does shipping take?" open
12
+ text "Orders arrive in 3–5 business days."
13
+ accordion "Can I return an item?" # closed: only the title row
14
+ toast "Message sent" action="Undo" icon=check # pinned to the bottom (above a tab bar), or position=top
15
+ ```
16
+
17
+ - **Toast** is an overlay like Modal and Drawer: a direct child of a Screen, listed last.
18
+ - **Measured:** three new requests (a ride map, an FAQ, a "saved" message), two runs per model. All 12 outputs were valid and used the new pieces, except one Haiku run that drew a plain image labeled "Map". The prompt grew by about 195 tokens.
19
+ - **Breadcrumbs** don't need a component: `text "Home / Settings / Profile" sm muted`. The Text docs page now shows this.
20
+
3
21
  ## 0.4.0
4
22
 
5
23
  ### New components
package/dist/catalog.d.ts CHANGED
@@ -134,6 +134,18 @@ export declare const componentDefinitions: {
134
134
  variant: string;
135
135
  };
136
136
  };
137
+ Accordion: {
138
+ props: z.ZodObject<{
139
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
140
+ open: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
141
+ }, z.core.$strip>;
142
+ slots: string[];
143
+ description: string;
144
+ example: {
145
+ title: string;
146
+ open: boolean;
147
+ };
148
+ };
137
149
  Divider: {
138
150
  props: z.ZodObject<{
139
151
  vertical: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
@@ -214,6 +226,10 @@ export declare const componentDefinitions: {
214
226
  width: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
215
227
  label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
216
228
  rounded: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
229
+ kind: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
230
+ map: "map";
231
+ photo: "photo";
232
+ }>>>;
217
233
  }, z.core.$strip>;
218
234
  slots: never[];
219
235
  description: string;
@@ -590,6 +606,27 @@ export declare const componentDefinitions: {
590
606
  size: number;
591
607
  };
592
608
  };
609
+ Toast: {
610
+ props: z.ZodObject<{
611
+ text: z.ZodOptional<z.ZodNullable<z.ZodString>>;
612
+ action: z.ZodOptional<z.ZodNullable<z.ZodString>>;
613
+ icon: z.ZodOptional<z.ZodNullable<z.ZodString>>;
614
+ position: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
615
+ bottom: "bottom";
616
+ bottomLeft: "bottomLeft";
617
+ bottomRight: "bottomRight";
618
+ top: "top";
619
+ topLeft: "topLeft";
620
+ topRight: "topRight";
621
+ }>>>;
622
+ }, z.core.$strip>;
623
+ slots: never[];
624
+ description: string;
625
+ example: {
626
+ text: string;
627
+ action: string;
628
+ };
629
+ };
593
630
  };
594
631
  export declare const catalog: import("@json-render/core").Catalog<{
595
632
  spec: import("@json-render/core").SchemaType<"object", {
@@ -754,6 +791,18 @@ export declare const catalog: import("@json-render/core").Catalog<{
754
791
  variant: string;
755
792
  };
756
793
  };
794
+ Accordion: {
795
+ props: z.ZodObject<{
796
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
797
+ open: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
798
+ }, z.core.$strip>;
799
+ slots: string[];
800
+ description: string;
801
+ example: {
802
+ title: string;
803
+ open: boolean;
804
+ };
805
+ };
757
806
  Divider: {
758
807
  props: z.ZodObject<{
759
808
  vertical: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
@@ -834,6 +883,10 @@ export declare const catalog: import("@json-render/core").Catalog<{
834
883
  width: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
835
884
  label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
836
885
  rounded: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
886
+ kind: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
887
+ map: "map";
888
+ photo: "photo";
889
+ }>>>;
837
890
  }, z.core.$strip>;
838
891
  slots: never[];
839
892
  description: string;
@@ -1210,6 +1263,27 @@ export declare const catalog: import("@json-render/core").Catalog<{
1210
1263
  size: number;
1211
1264
  };
1212
1265
  };
1266
+ Toast: {
1267
+ props: z.ZodObject<{
1268
+ text: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1269
+ action: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1270
+ icon: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1271
+ position: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
1272
+ bottom: "bottom";
1273
+ bottomLeft: "bottomLeft";
1274
+ bottomRight: "bottomRight";
1275
+ top: "top";
1276
+ topLeft: "topLeft";
1277
+ topRight: "topRight";
1278
+ }>>>;
1279
+ }, z.core.$strip>;
1280
+ slots: never[];
1281
+ description: string;
1282
+ example: {
1283
+ text: string;
1284
+ action: string;
1285
+ };
1286
+ };
1213
1287
  };
1214
1288
  }>;
1215
1289
  export type ComponentName = keyof typeof componentDefinitions;
@@ -207,7 +207,7 @@ var wireframeSchema = defineSchema(
207
207
  "The root element MUST be a Board. A Board's children are Screens (and optional Notes placed beside them).",
208
208
  "Every Screen MUST be a direct child of the Board. Each Screen is one view of the product; use several Screens to show several views or states.",
209
209
  "Screens lay out their children top to bottom. Use Stack (direction row/column) and Grid inside a Screen to arrange content.",
210
- "Modal and Drawer are overlays. They MUST be direct children of a Screen, and SHOULD be listed last in the Screen's children so they draw on top.",
210
+ "Modal, Drawer and Toast are overlays. They MUST be direct children of a Screen, and SHOULD be listed last in the Screen's children so they draw on top.",
211
211
  "NavBar goes first in a Screen's children and TabBar goes last (before any overlay).",
212
212
  "This is a low-fidelity wireframe: prefer placeholders (Image boxes, Text with lines) over invented copy, unless the copy matters to the design.",
213
213
  'Every element MUST include a "children" array. Leaf elements use an empty array: "children": [].',
@@ -310,6 +310,15 @@ var componentDefinitions = {
310
310
  description: "Bordered container with optional title. Children stack vertically.",
311
311
  example: { title: "Revenue", variant: "outline" }
312
312
  },
313
+ Accordion: {
314
+ props: z.object({
315
+ title: o(z.string()),
316
+ open: o(z.boolean()).describe("Show the children. Default closed: only the title row")
317
+ }),
318
+ slots: ["default"],
319
+ description: "Collapsible section: a title row with a chevron, children below when open. Stack several for an FAQ.",
320
+ example: { title: "Shipping", open: true }
321
+ },
313
322
  Divider: {
314
323
  props: z.object({ vertical: o(z.boolean()) }),
315
324
  slots: [],
@@ -363,10 +372,11 @@ var componentDefinitions = {
363
372
  height: o(z.number()),
364
373
  width: o(z.union([z.number(), z.string()])).describe("Default fills the container width"),
365
374
  label: o(z.string()),
366
- rounded: o(z.boolean())
375
+ rounded: o(z.boolean()),
376
+ kind: o(z.enum(["photo", "map"])).describe("map draws roads and a pin instead of the X")
367
377
  }),
368
378
  slots: [],
369
- description: "Image placeholder: a box with an X through it.",
379
+ description: "Image placeholder: a box with an X through it, or a map.",
370
380
  example: { height: 180, label: "Hero photo" }
371
381
  },
372
382
  Chart: {
@@ -593,6 +603,17 @@ var componentDefinitions = {
593
603
  slots: ["default"],
594
604
  description: "Panel sliding in from an edge: side nav (left), filters/details (right), or bottom sheet. Must be a direct child of a Screen, listed last.",
595
605
  example: { side: "left", title: "Menu", size: 300 }
606
+ },
607
+ Toast: {
608
+ props: z.object({
609
+ text: o(z.string()),
610
+ action: o(z.string()).describe('A text button, e.g. "Undo"'),
611
+ icon: o(iconName),
612
+ position: o(z.enum(["bottom", "top", "topLeft", "topRight", "bottomLeft", "bottomRight"])).describe("Default bottom (centered), above a TabBar")
613
+ }),
614
+ slots: [],
615
+ description: "Short message over the screen, e.g. after saving. Must be a direct child of a Screen, listed last.",
616
+ example: { text: "Message sent", action: "Undo" }
596
617
  }
597
618
  };
598
619
  var catalog = defineCatalog(wireframeSchema, {
@@ -743,7 +764,7 @@ function Screen({ element, children }) {
743
764
  const type = isValidElement(c) ? c.props.element?.type : null;
744
765
  if (type === "NavBar") top.push(c);
745
766
  else if (type === "TabBar") bottom.push(c);
746
- else if (type === "Modal" || type === "Drawer") overlays.push(c);
767
+ else if (type === "Modal" || type === "Drawer" || type === "Toast") overlays.push(c);
747
768
  else body.push(c);
748
769
  }
749
770
  return /* @__PURE__ */ jsxs(Box, { style: { flexDirection: "column", flexShrink: 0 }, children: [
@@ -782,7 +803,7 @@ function Screen({ element, children }) {
782
803
  }
783
804
  ),
784
805
  bottom,
785
- overlays
806
+ overlays.map((o2) => isValidElement(o2) ? cloneElement(o2, { aboveTabBar: bottom.length > 0 }) : o2)
786
807
  ] })
787
808
  ]
788
809
  }
@@ -885,6 +906,16 @@ function Card({ element, children, dir, stretch }) {
885
906
  }
886
907
  );
887
908
  }
909
+ function Accordion({ element, children }) {
910
+ const p = element.props;
911
+ return /* @__PURE__ */ jsxs(Box, { style: { flexDirection: "column", borderBottom: `1px solid ${theme.line}` }, children: [
912
+ /* @__PURE__ */ jsxs(Box, { style: { alignItems: "center", justifyContent: "space-between", gap: 12, padding: "12px 0" }, children: [
913
+ /* @__PURE__ */ jsx(Box, { style: { fontSize: 15, fontWeight: 600, color: theme.ink }, children: p.title ?? "" }),
914
+ /* @__PURE__ */ jsx(IconGlyph, { name: p.open ? "chevron-up" : "chevron-down", size: 18, color: theme.muted })
915
+ ] }),
916
+ p.open ? /* @__PURE__ */ jsx(Box, { style: { flexDirection: "column", gap: 10, paddingBottom: 14 }, children: withDir(children, "column") }) : null
917
+ ] });
918
+ }
888
919
  function Divider({ element, dir }) {
889
920
  const vertical = element.props.vertical ?? dir === "row";
890
921
  return vertical ? /* @__PURE__ */ jsx(Box, { style: { width: 1, alignSelf: "stretch", backgroundColor: theme.line, flexShrink: 0 } }) : /* @__PURE__ */ jsx(Box, { style: { height: 1, alignSelf: "stretch", backgroundColor: theme.line, flexShrink: 0 } });
@@ -961,6 +992,7 @@ function Bullets({ element }) {
961
992
  ] }, i);
962
993
  }) });
963
994
  }
995
+ var MAP_ROADS = ["M0 42 C30 38 60 50 100 44", "M38 0 C42 30 34 60 44 100", "M0 78 L100 70", "M72 0 L80 100", "M0 16 L40 22", "M44 60 L100 92"];
964
996
  function Image({ element, dir }) {
965
997
  const p = element.props;
966
998
  const h = p.height ?? 160;
@@ -981,7 +1013,12 @@ function Image({ element, dir }) {
981
1013
  justifyContent: "center"
982
1014
  },
983
1015
  children: [
984
- /* @__PURE__ */ jsxs(
1016
+ p.kind === "map" ? /* @__PURE__ */ jsxs("svg", { width: "100%", height: "100%", viewBox: "0 0 100 100", preserveAspectRatio: "none", style: { position: "absolute", top: 0, left: 0 }, children: [
1017
+ /* @__PURE__ */ jsx("rect", { x: "0", y: "0", width: "100", height: "100", fill: theme.fill2 }),
1018
+ /* @__PURE__ */ jsx("rect", { x: "62", y: "8", width: "26", height: "22", fill: theme.fill }),
1019
+ /* @__PURE__ */ jsx("rect", { x: "8", y: "64", width: "20", height: "26", fill: theme.fill }),
1020
+ MAP_ROADS.map((d, i) => /* @__PURE__ */ jsx("path", { d, fill: "none", stroke: theme.paper, strokeWidth: i < 2 ? 7 : 4, vectorEffect: "non-scaling-stroke" }, i))
1021
+ ] }) : /* @__PURE__ */ jsxs(
985
1022
  "svg",
986
1023
  {
987
1024
  width: "100%",
@@ -995,6 +1032,10 @@ function Image({ element, dir }) {
995
1032
  ]
996
1033
  }
997
1034
  ),
1035
+ p.kind === "map" ? /* @__PURE__ */ jsx(Box, { style: { position: "absolute", left: 0, right: 0, top: 0, bottom: 0, alignItems: "center", justifyContent: "center", paddingBottom: 28 }, children: /* @__PURE__ */ jsxs("svg", { width: "28", height: "36", viewBox: "0 0 24 31", children: [
1036
+ /* @__PURE__ */ jsx("path", { d: "M12 1C6.5 1 2 5.3 2 10.7 2 18 12 30 12 30s10-12 10-19.3C22 5.3 17.5 1 12 1z", fill: theme.ink }),
1037
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "10.5", r: "3.6", fill: theme.paper })
1038
+ ] }) }) : null,
998
1039
  p.label ? /* @__PURE__ */ jsx(
999
1040
  Box,
1000
1041
  {
@@ -1003,7 +1044,9 @@ function Image({ element, dir }) {
1003
1044
  fontSize: 12,
1004
1045
  color: theme.muted,
1005
1046
  backgroundColor: theme.fill,
1006
- borderRadius: 4
1047
+ borderRadius: 4,
1048
+ // a map keeps its pin in the middle, so the label goes in the corner
1049
+ ...p.kind === "map" ? { position: "absolute", left: 8, bottom: 8, backgroundColor: theme.paper } : {}
1007
1050
  },
1008
1051
  children: p.label
1009
1052
  }
@@ -1647,6 +1690,17 @@ function Drawer({ element, children }) {
1647
1690
  }
1648
1691
  ) });
1649
1692
  }
1693
+ function Toast({ element, aboveTabBar }) {
1694
+ const p = element.props;
1695
+ const position = p.position ?? "bottom";
1696
+ const top = position.startsWith("top");
1697
+ const side = position.endsWith("Left") ? "flex-start" : position.endsWith("Right") ? "flex-end" : "center";
1698
+ return /* @__PURE__ */ jsx(Box, { style: { position: "absolute", left: 16, right: 16, justifyContent: side, ...top ? { top: 16 } : { bottom: aboveTabBar ? 92 : 24 } }, children: /* @__PURE__ */ jsxs(Box, { style: { alignItems: "center", gap: 12, padding: "12px 16px", borderRadius: 12, backgroundColor: theme.ink, color: theme.onPrimary, fontSize: 14, boxShadow: "0 6px 20px rgba(0,0,0,0.22)", maxWidth: 420 }, children: [
1699
+ p.icon ? /* @__PURE__ */ jsx(IconGlyph, { name: p.icon, size: 18, color: theme.onPrimary }) : null,
1700
+ /* @__PURE__ */ jsx(Box, { style: { flexShrink: 1 }, children: p.text ?? "" }),
1701
+ p.action ? /* @__PURE__ */ jsx(Box, { style: { fontWeight: 700, marginLeft: 8, flexShrink: 0 }, children: p.action }) : null
1702
+ ] }) });
1703
+ }
1650
1704
  var registry = {
1651
1705
  Board,
1652
1706
  Screen,
@@ -1654,6 +1708,7 @@ var registry = {
1654
1708
  Stack,
1655
1709
  Grid,
1656
1710
  Card,
1711
+ Accordion,
1657
1712
  Divider,
1658
1713
  Spacer,
1659
1714
  Heading,
@@ -1681,7 +1736,8 @@ var registry = {
1681
1736
  ListItem,
1682
1737
  Table,
1683
1738
  Modal,
1684
- Drawer
1739
+ Drawer,
1740
+ Toast
1685
1741
  };
1686
1742
 
1687
1743
  // src/text.ts
@@ -1709,7 +1765,9 @@ var PRIMARY_PROP = {
1709
1765
  NavBar: "title",
1710
1766
  ListItem: "title",
1711
1767
  Modal: "title",
1712
- Drawer: "title"
1768
+ Drawer: "title",
1769
+ Accordion: "title",
1770
+ Toast: "text"
1713
1771
  };
1714
1772
  function unwrap(t) {
1715
1773
  let cur = t;
@@ -2225,7 +2283,7 @@ function checkSpec(spec) {
2225
2283
  if (el.type === "Screen" && !(root.children ?? []).includes(id)) {
2226
2284
  issues.push(`${id}: Screen must be a direct child of the Board`);
2227
2285
  }
2228
- if (el.type === "Modal" || el.type === "Drawer") {
2286
+ if (el.type === "Modal" || el.type === "Drawer" || el.type === "Toast") {
2229
2287
  const parent = Object.entries(spec.elements).find(([, p]) => p.children?.includes(id));
2230
2288
  if (parent && parent[1].type !== "Screen") {
2231
2289
  issues.push(`${id}: ${el.type} must be a direct child of a Screen (found in ${parent[1].type} "${parent[0]}")`);
@@ -2552,7 +2610,7 @@ var RULES = `## Rules
2552
2610
  2. Each Screen is one view of the product. Use several Screens to show several views or states.
2553
2611
  3. A Screen lays out its children top to bottom. Use Stack (direction row or column) and Grid to arrange content.
2554
2612
  4. NavBar is pinned to the top of its Screen and TabBar to the bottom.
2555
- 5. Modal and Drawer are overlays. They must be direct children of a Screen.
2613
+ 5. Modal, Drawer and Toast are overlays. They must be direct children of a Screen.
2556
2614
  6. This is a low-fidelity wireframe. Prefer placeholders (Image boxes, Text with lines) over invented copy unless the copy matters.
2557
2615
  7. Only use the components and props listed below. All props are optional unless marked required.
2558
2616
  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) and Input error. Only add an accent if the request asks for color or a brand.`;
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  printWireframeText,
7
7
  renderWireframe,
8
8
  wireframePrompt
9
- } from "./chunks/chunk-2IDCJVKK.js";
9
+ } from "./chunks/chunk-NREXWHAE.js";
10
10
 
11
11
  // src/cli.ts
12
12
  import { readFile, writeFile } from "node:fs/promises";
@@ -28,6 +28,7 @@ declare function Note({ element, dir, aligned }: Props): React.JSX.Element;
28
28
  declare function Stack({ element, children, dir: parentDir, stretch }: Props): React.JSX.Element;
29
29
  declare function Grid({ element, children }: Props): React.JSX.Element;
30
30
  declare function Card({ element, children, dir, stretch }: Props): React.JSX.Element;
31
+ declare function Accordion({ element, children }: Props): React.JSX.Element;
31
32
  declare function Divider({ element, dir }: Props): React.JSX.Element;
32
33
  declare function Spacer({ element }: Props): React.JSX.Element;
33
34
  declare function Heading({ element }: Props): React.JSX.Element;
@@ -63,6 +64,9 @@ declare function ListItem({ element, colors }: Props): React.JSX.Element;
63
64
  declare function Table({ element }: Props): React.JSX.Element;
64
65
  declare function Modal({ element, children }: Props): React.JSX.Element;
65
66
  declare function Drawer({ element, children }: Props): React.JSX.Element;
67
+ declare function Toast({ element, aboveTabBar }: Props & {
68
+ aboveTabBar?: boolean;
69
+ }): React.JSX.Element;
66
70
  export declare const registry: {
67
71
  Board: typeof Board;
68
72
  Screen: typeof Screen;
@@ -70,6 +74,7 @@ export declare const registry: {
70
74
  Stack: typeof Stack;
71
75
  Grid: typeof Grid;
72
76
  Card: typeof Card;
77
+ Accordion: typeof Accordion;
73
78
  Divider: typeof Divider;
74
79
  Spacer: typeof Spacer;
75
80
  Heading: typeof Heading;
@@ -98,5 +103,6 @@ export declare const registry: {
98
103
  Table: typeof Table;
99
104
  Modal: typeof Modal;
100
105
  Drawer: typeof Drawer;
106
+ Toast: typeof Toast;
101
107
  };
102
108
  export {};
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  upgradeSpec,
21
21
  upgradeWireframe,
22
22
  wireframePrompt
23
- } from "./chunks/chunk-2IDCJVKK.js";
23
+ } from "./chunks/chunk-NREXWHAE.js";
24
24
 
25
25
  // src/share.ts
26
26
  import { deflateRawSync, inflateRawSync } from "node:zlib";
@@ -11,7 +11,7 @@ import {
11
11
  renderWireframePng,
12
12
  renderWireframeSvg,
13
13
  wireframePrompt
14
- } from "../chunks/chunk-2IDCJVKK.js";
14
+ } from "../chunks/chunk-NREXWHAE.js";
15
15
 
16
16
  // src/playground/index.ts
17
17
  import { createServer } from "node:http";
@@ -73,11 +73,11 @@ function languageData() {
73
73
  // src/playground/reference.ts
74
74
  var GROUPS = [
75
75
  { name: "Canvas", components: ["Board", "Screen", "Note"] },
76
- { name: "Layout", components: ["Stack", "Grid", "Card", "Divider", "Spacer"] },
76
+ { name: "Layout", components: ["Stack", "Grid", "Card", "Accordion", "Divider", "Spacer"] },
77
77
  { name: "Content", components: ["Heading", "Text", "Bullets", "Image", "Chart", "Icon", "Avatar", "Badge"] },
78
78
  { name: "Controls", components: ["Button", "Input", "Checkbox", "Radio", "Toggle", "Select", "Slider", "Progress", "Calendar"] },
79
79
  { name: "Navigation & data", components: ["NavBar", "TabBar", "Tabs", "Pagination", "List", "ListItem", "Table"] },
80
- { name: "Overlays", components: ["Modal", "Drawer"] }
80
+ { name: "Overlays", components: ["Modal", "Drawer", "Toast"] }
81
81
  ];
82
82
  var EXAMPLES = {
83
83
  Board: `board row "Checkout flow" gap=48 accent=blue
@@ -149,6 +149,14 @@ var EXAMPLES = {
149
149
  text lines=3
150
150
  card "filled" variant=filled grow
151
151
  text lines=3`,
152
+ Accordion: `board
153
+ screen custom width=380 height=330 gap=0
154
+ heading "FAQ" level=3
155
+ accordion "How long does shipping take?" open
156
+ text "Orders arrive in 3\u20135 business days." muted
157
+ accordion "Can I return an item?"
158
+ accordion "Do you ship internationally?"
159
+ accordion "How do I track my order?"`,
152
160
  Divider: `board
153
161
  screen custom width=360 height=150
154
162
  text "Above"
@@ -172,13 +180,15 @@ var EXAMPLES = {
172
180
  heading "Level 3" level=3
173
181
  heading "Centered" level=3 align=center`,
174
182
  Text: `board
175
- screen custom width=360 height=300
183
+ screen custom width=360 height=370
176
184
  text "Body text, default size"
177
185
  text lg "Large"
178
186
  text sm "Small and muted" muted
179
187
  text "Bold" bold
180
188
  text "Placeholder lines when lines is set and there's no text:" sm muted
181
- text lines=3`,
189
+ text lines=3
190
+ text "Breadcrumbs are small muted text:" sm muted
191
+ text "Home / Settings / Profile" sm muted`,
182
192
  Bullets: `board
183
193
  screen custom width=420 height=600
184
194
  text "Bullets" bold
@@ -190,12 +200,13 @@ var EXAMPLES = {
190
200
  text "One item can change its icon, or be muted" bold
191
201
  bullets icon=check items=[Unlimited boards, Share links, {label="SSO" icon=x muted}]`,
192
202
  Image: `board
193
- screen custom width=480 height=210
203
+ screen custom width=480 height=400
194
204
  stack row gap=12
195
205
  image "label" height=120 width=160
196
206
  image height=120 width=120 rounded
197
207
  image height=120
198
- text "In a row, an image without a width is square." sm muted`,
208
+ text "In a row, an image without a width is square." sm muted
209
+ image map height=150 "Map"`,
199
210
  Chart: `board
200
211
  screen custom width=640 height=430
201
212
  stack row gap=16
@@ -371,7 +382,24 @@ var EXAMPLES = {
371
382
  drawer bottom "Share" size=280
372
383
  list dividers=false
373
384
  listitem "Copy link" leadingIcon=link
374
- listitem "Message" leadingIcon=message-circle`
385
+ listitem "Message" leadingIcon=message-circle`,
386
+ Toast: `board
387
+ screen phone "Bottom, above a tab bar"
388
+ heading "Inbox" level=2
389
+ list
390
+ listitem "Ana Torres" subtitle="Lunch on Friday?" leading=avatar
391
+ listitem "Ben Cho" subtitle="Draft attached" leading=avatar
392
+ tabbar items=[{label=Inbox icon=inbox}, {label=Settings icon=settings}]
393
+ toast "Message sent" action="Undo" icon=check
394
+ screen phone "Top"
395
+ heading "Profile" level=2
396
+ image height=160
397
+ toast "Changes saved" position=top
398
+ screen desktop "Corners: topLeft, topRight, bottomLeft, bottomRight" width=900 height=420
399
+ heading "Dashboard" level=2
400
+ image height=220
401
+ toast "topRight" position=topRight
402
+ toast "bottomLeft" action="Undo" position=bottomLeft`
375
403
  };
376
404
 
377
405
  // src/playground/index.ts
package/dist/prompt.d.ts CHANGED
@@ -13,7 +13,7 @@ export declare function componentDocs(): {
13
13
  props: PropDoc[];
14
14
  }[];
15
15
  export declare function componentReference(): string;
16
- export declare const RULES = "## Rules\n1. The top element is a Board. The Board's children are Screens, plus optional Notes beside them.\n2. Each Screen is one view of the product. Use several Screens to show several views or states.\n3. A Screen lays out its children top to bottom. Use Stack (direction row or column) and Grid to arrange content.\n4. NavBar is pinned to the top of its Screen and TabBar to the bottom.\n5. Modal and Drawer are overlays. They must be direct children of a Screen.\n6. This is a low-fidelity wireframe. Prefer placeholders (Image boxes, Text with lines) over invented copy unless the copy matters.\n7. Only use the components and props listed below. All props are optional unless marked required.\n8. 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) and Input error. Only add an accent if the request asks for color or a brand.";
16
+ export declare const RULES = "## Rules\n1. The top element is a Board. The Board's children are Screens, plus optional Notes beside them.\n2. Each Screen is one view of the product. Use several Screens to show several views or states.\n3. A Screen lays out its children top to bottom. Use Stack (direction row or column) and Grid to arrange content.\n4. NavBar is pinned to the top of its Screen and TabBar to the bottom.\n5. Modal, Drawer and Toast are overlays. They must be direct children of a Screen.\n6. This is a low-fidelity wireframe. Prefer placeholders (Image boxes, Text with lines) over invented copy unless the copy matters.\n7. Only use the components and props listed below. All props are optional unless marked required.\n8. 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) and Input error. Only add an accent if the request asks for color or a brand.";
17
17
  export declare const TEXT_FORMAT = "## Output format: wireframe text\nOne element per line. Indent children two spaces under their parent. The first line is the board.\n\nA line is the component name in lowercase, followed by arguments separated by spaces:\n- \"a quoted string\" sets the component's main text prop (marked \"main text\" below)\n- a bare word that is one of the component's option values sets that option: phone, desktop, primary, ghost, row, sm, left, bottom, password, \u2026\n- a bare prop name sets that component's yes/no prop to true, only on components that have it (see each component's props below): checked (checkbox, radio), fullWidth (button), muted (text), grow (stack, card, input, list). `off` and `unchecked` set on/checked to false\n- key=value sets any prop. Values: \"string\", number, true/false, bare word, [list, of, values], {key=value key=value}\n\nComments: a line that starts with # is a comment. A comment must be on its own line, never after an element on the same line. Anywhere else, # is ordinary text: [#1001, #1002], \"Order #12345\", accent=#1a73e8.\n\nLists: items are separated by commas, and an item can contain spaces without quotes: [All notes, Pinned, Shared]. To put a comma inside an item, quote the item: [\"$1,200\", \"Smith, J\"]. Inside quotes, write \\\" for a quote character. Numbers in a list of text are fine: [2023, 2024].\n\nIf a bare word could mean more than one prop, write it as key=value.";
18
18
  export declare const EXAMPLE_REQUEST = "Two phone screens for a recipe app: a browse screen with search, category tabs, a grid of recipe cards and a tab bar; and a recipe detail screen with its options sheet open.";
19
19
  /** The system prompt for writing wireframe text. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsquare",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Wireframes in plain text: easy for LLMs to write, fast to render as SVG or PNG.",
5
5
  "keywords": [
6
6
  "wireframe",
@@ -47,7 +47,7 @@ If a bare word could mean more than one prop, write it as key=value.
47
47
  2. Each Screen is one view of the product. Use several Screens to show several views or states.
48
48
  3. A Screen lays out its children top to bottom. Use Stack (direction row or column) and Grid to arrange content.
49
49
  4. NavBar is pinned to the top of its Screen and TabBar to the bottom.
50
- 5. Modal and Drawer are overlays. They must be direct children of a Screen.
50
+ 5. Modal, Drawer and Toast are overlays. They must be direct children of a Screen.
51
51
  6. This is a low-fidelity wireframe. Prefer placeholders (Image boxes, Text with lines) over invented copy unless the copy matters.
52
52
  7. Only use the components and props listed below. All props are optional unless marked required.
53
53
  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) and Input error. Only add an accent if the request asks for color or a brand.
@@ -55,6 +55,11 @@ Bordered container with optional title. Children stack vertically.
55
55
  - grow: boolean
56
56
  - width: number | string — Fixed width, e.g. 320. Default fills the space.
57
57
 
58
+ ### Accordion
59
+ Collapsible section: a title row with a chevron, children below when open. Stack several for an FAQ.
60
+ - title: string (main text)
61
+ - open: boolean — Show the children. Default closed: only the title row
62
+
58
63
  ### Divider
59
64
  Thin separator line.
60
65
  - vertical: boolean
@@ -85,11 +90,12 @@ Bulleted, numbered or icon list of short lines.
85
90
  - icon: string — A Lucide icon instead of dots, e.g. check for a feature list
86
91
 
87
92
  ### Image
88
- Image placeholder: a box with an X through it.
93
+ Image placeholder: a box with an X through it, or a map.
89
94
  - height: number
90
95
  - width: number | string — Default fills the container width
91
96
  - label: string (main text)
92
97
  - rounded: boolean
98
+ - kind: "photo" | "map" — map draws roads and a pin instead of the X
93
99
 
94
100
  ### Chart
95
101
  Chart placeholder: a generic line, bar, area, pie or donut shape, no data.
@@ -236,3 +242,10 @@ Panel sliding in from an edge: side nav (left), filters/details (right), or bott
236
242
  - size: number — Width for left/right, height for bottom
237
243
  - scrim: boolean — Dim the screen behind. Default true.
238
244
  - handle: boolean — Grab handle on bottom sheets. Default true for bottom.
245
+
246
+ ### Toast
247
+ Short message over the screen, e.g. after saving. Must be a direct child of a Screen, listed last.
248
+ - text: string (main text)
249
+ - action: string — A text button, e.g. "Undo"
250
+ - icon: string — Lucide icon name in kebab-case, e.g. menu, search, arrow-left, settings, bell, user
251
+ - position: "bottom" | "top" | "topLeft" | "topRight" | "bottomLeft" | "bottomRight" — Default bottom (centered), above a TabBar