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.
- package/README.md +34 -8
- package/assets/mark.png +0 -0
- package/bin/tsquare.js +2 -6
- package/dist/catalog.d.ts +1055 -0
- package/dist/chunks/chunk-46MGJLGB.js +2126 -0
- package/{src/cli.ts → dist/cli.js} +28 -24
- package/dist/colors.d.ts +29 -0
- package/dist/compile.d.ts +24 -0
- package/dist/components.d.ts +83 -0
- package/dist/icons.d.ts +12 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +54 -0
- package/dist/layout.d.ts +66 -0
- package/dist/playground/index.d.ts +38 -0
- package/dist/playground/index.html +325 -0
- package/dist/playground/index.js +466 -0
- package/dist/playground/language-data.d.ts +28 -0
- package/dist/playground/playground.js +47 -0
- package/dist/playground/reference.d.ts +11 -0
- package/dist/print.d.ts +2 -0
- package/dist/prompt-example.d.ts +3 -0
- package/dist/prompt.d.ts +22 -0
- package/dist/render.d.ts +40 -0
- package/dist/schema.d.ts +33 -0
- package/dist/share.d.ts +4 -0
- package/dist/suggest.d.ts +5 -0
- package/dist/text.d.ts +50 -0
- package/examples/checkout.tsq +15 -0
- package/examples/dashboard.tsq +27 -0
- package/examples/notes-mobile.tsq +84 -0
- package/examples/notes-web.tsq +54 -0
- package/examples/sign-in.tsq +14 -0
- package/examples/states.tsq +16 -0
- package/package.json +31 -7
- package/skill/tsquare/SKILL.md +5 -4
- package/skill/tsquare/reference.md +7 -6
- package/src/catalog.ts +0 -329
- package/src/colors.ts +0 -82
- package/src/compile.ts +0 -64
- package/src/components.tsx +0 -1057
- package/src/icons.ts +0 -54
- package/src/index.ts +0 -11
- package/src/layout.ts +0 -63
- package/src/print.ts +0 -73
- package/src/prompt-example.ts +0 -43
- package/src/prompt.ts +0 -121
- package/src/render.ts +0 -178
- package/src/schema.ts +0 -43
- package/src/suggest.ts +0 -34
- package/src/text.ts +0 -333
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Examples for the playground's Components tab and the generated component
|
|
3
|
+
* docs. Each one is a complete wireframe so it can be opened in the editor
|
|
4
|
+
* as-is. Every catalog component needs one, in a group, and each must compile:
|
|
5
|
+
* `npm run build` fails otherwise, and the playground refuses to start.
|
|
6
|
+
*/
|
|
7
|
+
export declare const GROUPS: {
|
|
8
|
+
name: string;
|
|
9
|
+
components: string[];
|
|
10
|
+
}[];
|
|
11
|
+
export declare const EXAMPLES: Record<string, string>;
|
package/dist/print.d.ts
ADDED
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface PropDoc {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
required: boolean;
|
|
5
|
+
/** This prop is filled by a quoted string, e.g. the label of `button "Save"`. */
|
|
6
|
+
main: boolean;
|
|
7
|
+
description?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Every component with its description and props, straight from the catalog. */
|
|
10
|
+
export declare function componentDocs(): {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
props: PropDoc[];
|
|
14
|
+
}[];
|
|
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.";
|
|
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
|
+
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
|
+
/** The system prompt for writing wireframe text. */
|
|
20
|
+
export declare function wireframePrompt(): string;
|
|
21
|
+
/** Follow-up message asking a model to fix the problems in its last reply. */
|
|
22
|
+
export declare function repairPrompt(issues: string): string;
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Spec } from "@json-render/core";
|
|
2
|
+
export declare class SpecError extends Error {
|
|
3
|
+
issues: string[];
|
|
4
|
+
constructor(issues: string[]);
|
|
5
|
+
}
|
|
6
|
+
/** Catalog validation plus the structural rules the renderer depends on. */
|
|
7
|
+
export declare function checkSpec(spec: Spec): string[];
|
|
8
|
+
export interface BoardItem {
|
|
9
|
+
type: "Screen" | "Note";
|
|
10
|
+
/** Screen name or note text, for labels in tools like the playground. */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Box on the board in px, including the screen's name label above its frame. */
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Where everything sits on the board: the canvas size (Satori needs it up
|
|
20
|
+
* front) and each screen's and note's box, laid out in rows exactly as the
|
|
21
|
+
* Board component does.
|
|
22
|
+
*/
|
|
23
|
+
export declare function boardLayout(spec: Spec): {
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
items: BoardItem[];
|
|
27
|
+
};
|
|
28
|
+
/** The canvas size for a board. */
|
|
29
|
+
export declare function boardSize(spec: Spec): {
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
};
|
|
33
|
+
export interface RenderWireframeOptions {
|
|
34
|
+
/** Skip validation (e.g. while a spec is still streaming in). */
|
|
35
|
+
skipValidation?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare function renderWireframeSvg(spec: Spec, opts?: RenderWireframeOptions): Promise<string>;
|
|
38
|
+
export declare function renderWireframePng(spec: Spec, opts?: RenderWireframeOptions & {
|
|
39
|
+
scale?: number;
|
|
40
|
+
}): Promise<Buffer<ArrayBufferLike>>;
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wireframe spec schema. Same flat element shape as the other json-render
|
|
3
|
+
* renderers (root + elements map), with wireframe-specific rules for the
|
|
4
|
+
* generated LLM prompt.
|
|
5
|
+
*/
|
|
6
|
+
export declare const wireframeSchema: import("@json-render/core").Schema<{
|
|
7
|
+
spec: import("@json-render/core").SchemaType<"object", {
|
|
8
|
+
root: import("@json-render/core").SchemaType<"string", unknown>;
|
|
9
|
+
elements: import("@json-render/core").SchemaType<"record", import("@json-render/core").SchemaType<"object", {
|
|
10
|
+
type: import("@json-render/core").SchemaType<"ref", string>;
|
|
11
|
+
props: import("@json-render/core").SchemaType<"propsOf", string>;
|
|
12
|
+
children: import("@json-render/core").SchemaType<"array", import("@json-render/core").SchemaType<"string", unknown>>;
|
|
13
|
+
visible: {
|
|
14
|
+
optional: true;
|
|
15
|
+
kind: "any";
|
|
16
|
+
inner?: unknown;
|
|
17
|
+
};
|
|
18
|
+
repeat: {
|
|
19
|
+
optional: true;
|
|
20
|
+
kind: "any";
|
|
21
|
+
inner?: unknown;
|
|
22
|
+
};
|
|
23
|
+
}>>;
|
|
24
|
+
}>;
|
|
25
|
+
catalog: import("@json-render/core").SchemaType<"object", {
|
|
26
|
+
components: import("@json-render/core").SchemaType<"map", {
|
|
27
|
+
props: import("@json-render/core").SchemaType<"zod", unknown>;
|
|
28
|
+
slots: import("@json-render/core").SchemaType<"array", import("@json-render/core").SchemaType<"string", unknown>>;
|
|
29
|
+
description: import("@json-render/core").SchemaType<"string", unknown>;
|
|
30
|
+
example: import("@json-render/core").SchemaType<"any", unknown>;
|
|
31
|
+
}>;
|
|
32
|
+
}>;
|
|
33
|
+
}>;
|
package/dist/share.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Largest decoded text accepted, in bytes. Plenty for a board; stops a tiny URL from inflating into megabytes. */
|
|
2
|
+
export declare const MAX_SHARED_TEXT = 64000;
|
|
3
|
+
export declare function encodeWireframe(text: string): string;
|
|
4
|
+
export declare function decodeWireframe(data: string): string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Edit distance counting a swap of neighbouring letters as one edit ("serach" → search). */
|
|
2
|
+
export declare function editDistance(a: string, b: string): any;
|
|
3
|
+
/** Up to `limit` candidates within a small edit distance of `word`, closest first. */
|
|
4
|
+
export declare function closeMatches(word: string, candidates: string[], limit?: number): string[];
|
|
5
|
+
export declare function unknownComponentMessage(name: string, components: string[]): string;
|
package/dist/text.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text syntax for wireframes. Compiles to the same flat json-render spec.
|
|
3
|
+
*
|
|
4
|
+
* board "Notes app"
|
|
5
|
+
* screen phone "Sign in" padding=28
|
|
6
|
+
* heading "Welcome back"
|
|
7
|
+
* input "Email" placeholder="you@example.com"
|
|
8
|
+
* stack row justify=between
|
|
9
|
+
* checkbox "Remember me" checked
|
|
10
|
+
* button "Forgot?" ghost sm
|
|
11
|
+
* button "Sign in" primary lg fullWidth
|
|
12
|
+
*
|
|
13
|
+
* Line = component name, then arguments, children indented below.
|
|
14
|
+
* Arguments:
|
|
15
|
+
* "quoted" → the component's main text prop (label, title, text…)
|
|
16
|
+
* bare word → an enum value (phone, primary, row, sm…) or a boolean prop set to true (checked)
|
|
17
|
+
* key=value → any prop; value is "string", number, true/false, word, [list] or {key=value …}
|
|
18
|
+
* list items are separated by commas only, so [Ana Torres, Admin] is two items;
|
|
19
|
+
* quote an item that contains a comma: ["$1,200", "Smith, J"]
|
|
20
|
+
* # comment → only on a line of its own; anywhere else # is text ([#1001, …], accent=#1a73e8)
|
|
21
|
+
*/
|
|
22
|
+
import type { Spec } from "@json-render/core";
|
|
23
|
+
/** Which prop a quoted string fills, per component. */
|
|
24
|
+
export declare const PRIMARY_PROP: Record<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* For an error message: where a prop name or bare word that this component doesn't take
|
|
27
|
+
* belongs instead, e.g. fullWidth on an input → "fullWidth is a Button prop". Empty if nowhere.
|
|
28
|
+
*/
|
|
29
|
+
export declare function belongsElsewhere(component: string, word: string, asProp: boolean): string;
|
|
30
|
+
/** What a component accepts as bare words, for docs: option values (with their prop), ambiguous values, and boolean props. */
|
|
31
|
+
export declare function bareWords(component: string): {
|
|
32
|
+
options: {
|
|
33
|
+
value: string;
|
|
34
|
+
prop: string;
|
|
35
|
+
}[];
|
|
36
|
+
ambiguous: string[];
|
|
37
|
+
booleans: string[];
|
|
38
|
+
wordPrimary: boolean;
|
|
39
|
+
};
|
|
40
|
+
export interface TextIssue {
|
|
41
|
+
line: number;
|
|
42
|
+
message: string;
|
|
43
|
+
}
|
|
44
|
+
export interface ParseResult {
|
|
45
|
+
spec: Spec | null;
|
|
46
|
+
issues: TextIssue[];
|
|
47
|
+
/** element id → source line, for mapping validation errors back */
|
|
48
|
+
lines: Record<string, number>;
|
|
49
|
+
}
|
|
50
|
+
export declare function parseWireframeText(source: string): ParseResult;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
board row "Checkout" accent=green
|
|
2
|
+
screen phone "Cart"
|
|
3
|
+
navbar "Cart" leading=back
|
|
4
|
+
list
|
|
5
|
+
listitem "Oat latte" subtitle="Large, extra shot" leading=image trailing=text trailingText="$5.40"
|
|
6
|
+
listitem "Croissant" subtitle="Warm" leading=image trailing=text trailingText="$3.20"
|
|
7
|
+
spacer
|
|
8
|
+
button primary lg "Checkout · $8.60" fullWidth
|
|
9
|
+
screen phone "Pay"
|
|
10
|
+
navbar "Cart" leading=back
|
|
11
|
+
text lines=4
|
|
12
|
+
drawer bottom "Payment" size=300
|
|
13
|
+
select "Card" value="Visa ending 6411"
|
|
14
|
+
toggle "Save card" on
|
|
15
|
+
button primary lg "Pay $8.60" fullWidth
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
board "Analytics" accent=blue
|
|
2
|
+
screen desktop "Dashboard" padding=0 height=640
|
|
3
|
+
stack row grow
|
|
4
|
+
stack width=220 border=right fill padding=16 gap=6
|
|
5
|
+
text "Acme" bold
|
|
6
|
+
list dividers=false
|
|
7
|
+
listitem "Overview" leadingIcon=layout-grid
|
|
8
|
+
listitem "Customers" leadingIcon=users
|
|
9
|
+
listitem "Orders" leadingIcon=receipt
|
|
10
|
+
listitem "Settings" leadingIcon=settings
|
|
11
|
+
stack grow padding=24 gap=20
|
|
12
|
+
stack row justify=between align=center
|
|
13
|
+
heading "Overview" level=2
|
|
14
|
+
stack row gap=8
|
|
15
|
+
input search placeholder="Search" width=220
|
|
16
|
+
button primary "Export" leadingIcon=download
|
|
17
|
+
avatar "DM"
|
|
18
|
+
grid columns=4 gap=12
|
|
19
|
+
card "Revenue"
|
|
20
|
+
heading "$48.2k" level=2
|
|
21
|
+
card "Orders"
|
|
22
|
+
heading "1,284" level=2
|
|
23
|
+
card "Customers"
|
|
24
|
+
heading "932" level=2
|
|
25
|
+
card "Refunds"
|
|
26
|
+
heading "12" level=2
|
|
27
|
+
table columns=[Order, Customer, Status, Total] data=[["#1042", Ana Torres, Paid, "$120"], ["#1041", Ben Cho, Pending, "$80"], ["#1040", Priya Nair, Paid, "$45"]]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
board row "Notes app — mobile"
|
|
2
|
+
screen phone "1 · Sign in" padding=28 gap=18
|
|
3
|
+
spacer size=36
|
|
4
|
+
image width=64 height=64 rounded
|
|
5
|
+
heading "Welcome back" level=1
|
|
6
|
+
text "Sign in to sync your notes." muted
|
|
7
|
+
input email "Email" placeholder="you@example.com"
|
|
8
|
+
input password "Password" value=hunter22
|
|
9
|
+
stack row justify=between
|
|
10
|
+
checkbox "Remember me" checked
|
|
11
|
+
button ghost sm "Forgot?"
|
|
12
|
+
button primary lg "Sign in" fullWidth
|
|
13
|
+
stack row gap=12 align=center
|
|
14
|
+
stack grow
|
|
15
|
+
divider
|
|
16
|
+
text sm "or" muted
|
|
17
|
+
stack grow
|
|
18
|
+
divider
|
|
19
|
+
button secondary lg "Continue with Google" fullWidth leadingIcon=globe
|
|
20
|
+
spacer
|
|
21
|
+
text sm "New here? Create an account" align=center muted
|
|
22
|
+
screen phone "2 · Notes"
|
|
23
|
+
navbar "Notes" leading=menu actions=[bell, more-vertical]
|
|
24
|
+
input search placeholder="Search notes"
|
|
25
|
+
tabs items=[All, Pinned, Shared] active=0
|
|
26
|
+
list
|
|
27
|
+
listitem "Grocery list" subtitle="Eggs, oat milk, basil…" leadingIcon=shopping-cart trailing=text trailingText="9:12"
|
|
28
|
+
listitem "Trip ideas" subtitle="Lisbon in October?" leadingIcon=map trailing=badge trailingText="2"
|
|
29
|
+
listitem leading=image trailing=chevron
|
|
30
|
+
listitem leading=image trailing=chevron
|
|
31
|
+
listitem leading=image trailing=chevron
|
|
32
|
+
stack row justify=end grow align=end
|
|
33
|
+
button primary "New note" leadingIcon=plus
|
|
34
|
+
tabbar items=[{label=Notes icon=notebook-pen}, {label=Search icon=search}, {label=Shared icon=users}, {label=Profile icon=user}] active=0
|
|
35
|
+
screen phone "3 · Side menu (drawer)"
|
|
36
|
+
navbar "Notes" leading=menu actions=[bell, more-vertical]
|
|
37
|
+
list
|
|
38
|
+
listitem leading=image
|
|
39
|
+
listitem leading=image
|
|
40
|
+
listitem leading=image
|
|
41
|
+
listitem leading=image
|
|
42
|
+
listitem leading=image
|
|
43
|
+
listitem leading=image
|
|
44
|
+
drawer left size=290
|
|
45
|
+
stack row gap=12
|
|
46
|
+
avatar "DM" size=44
|
|
47
|
+
stack gap=2
|
|
48
|
+
text "Dana Moore" bold
|
|
49
|
+
text sm "dana@example.com" muted
|
|
50
|
+
divider
|
|
51
|
+
list dividers=false
|
|
52
|
+
listitem "All notes" leadingIcon=notebook-pen trailing=text trailingText="24"
|
|
53
|
+
listitem "Folders" leadingIcon=folder trailing=chevron
|
|
54
|
+
listitem "Archive" leadingIcon=archive
|
|
55
|
+
listitem "Settings" leadingIcon=settings
|
|
56
|
+
spacer
|
|
57
|
+
button secondary "Sign out" leadingIcon=log-out fullWidth
|
|
58
|
+
screen phone "4 · Discard draft (modal)"
|
|
59
|
+
navbar "New note" leading=close actions=[image, check] align=center
|
|
60
|
+
heading "Untitled" level=2
|
|
61
|
+
text lines=7
|
|
62
|
+
stack row gap=8
|
|
63
|
+
badge outline "work"
|
|
64
|
+
badge outline "ideas"
|
|
65
|
+
modal "Discard this draft?"
|
|
66
|
+
text "You'll lose what you've written so far." muted
|
|
67
|
+
stack row justify=end gap=8
|
|
68
|
+
button ghost "Keep editing"
|
|
69
|
+
button primary "Discard"
|
|
70
|
+
screen phone "5 · Note options (bottom sheet)"
|
|
71
|
+
navbar leading=back actions=[share, more-horizontal]
|
|
72
|
+
image "Cover image" height=180
|
|
73
|
+
heading "Trip ideas" level=1
|
|
74
|
+
text lines=5
|
|
75
|
+
drawer bottom "Note options" size=400
|
|
76
|
+
toggle "Pin to top" on
|
|
77
|
+
toggle "Share with link" on=false
|
|
78
|
+
select "Folder" value=Travel
|
|
79
|
+
stack gap=10
|
|
80
|
+
text sm "Reminder" bold
|
|
81
|
+
stack row gap=20
|
|
82
|
+
radio "None" checked
|
|
83
|
+
radio "Tomorrow"
|
|
84
|
+
radio "Pick date"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
board "Notes app — web"
|
|
2
|
+
screen desktop "Workspace overview" padding=0 gap=0
|
|
3
|
+
stack row grow gap=0 align=stretch
|
|
4
|
+
stack width=240 padding=20 gap=6 border=right fill
|
|
5
|
+
stack row gap=10
|
|
6
|
+
image width=32 height=32 rounded
|
|
7
|
+
text "Notebook" bold
|
|
8
|
+
spacer size=12
|
|
9
|
+
list dividers=false
|
|
10
|
+
listitem "Overview" leadingIcon=layout-dashboard
|
|
11
|
+
listitem "All notes" leadingIcon=notebook-pen trailing=text trailingText="128"
|
|
12
|
+
listitem "Shared" leadingIcon=users
|
|
13
|
+
listitem "Settings" leadingIcon=settings
|
|
14
|
+
spacer
|
|
15
|
+
stack row gap=10
|
|
16
|
+
avatar "DM" size=32
|
|
17
|
+
text sm "Dana Moore"
|
|
18
|
+
stack grow padding=32 gap=24
|
|
19
|
+
stack row gap=12
|
|
20
|
+
heading "Overview" level=1
|
|
21
|
+
spacer
|
|
22
|
+
stack width=260
|
|
23
|
+
input search placeholder=Search
|
|
24
|
+
button "New note" leadingIcon=plus
|
|
25
|
+
grid columns=3 gap=20
|
|
26
|
+
card "Notes this week"
|
|
27
|
+
heading "42" level=1
|
|
28
|
+
text lines=1
|
|
29
|
+
card "Shared with you"
|
|
30
|
+
heading "7" level=1
|
|
31
|
+
text lines=1
|
|
32
|
+
card filled "Storage"
|
|
33
|
+
heading "1.2 GB" level=1
|
|
34
|
+
text lines=1
|
|
35
|
+
tabs items=[Recent, Pinned, Archived] active=0
|
|
36
|
+
table columns=[Title, Folder, Owner, Updated] data=[["Grocery list", Personal, Dana, "9:12 AM"], ["Trip ideas", Travel, Dana, Yesterday], ["Q4 planning", Work, Sam, Mon], ["Reading list", Personal, Dana, "Sep 20"]]
|
|
37
|
+
screen desktop "Filters (right drawer)" padding=32 gap=24
|
|
38
|
+
stack row gap=12
|
|
39
|
+
heading "All notes" level=1
|
|
40
|
+
spacer
|
|
41
|
+
button secondary "Filters" leadingIcon=sliders-horizontal
|
|
42
|
+
table columns=[Title, Folder, Owner, Updated] rows=9
|
|
43
|
+
drawer right "Filters" size=340
|
|
44
|
+
select "Folder" placeholder="Any folder"
|
|
45
|
+
select "Owner" value=Me
|
|
46
|
+
divider
|
|
47
|
+
checkbox "Pinned only" checked
|
|
48
|
+
checkbox "Has attachments"
|
|
49
|
+
checkbox "Shared with me"
|
|
50
|
+
spacer
|
|
51
|
+
stack row justify=end gap=8
|
|
52
|
+
button ghost "Reset"
|
|
53
|
+
button "Apply filters"
|
|
54
|
+
note "Filters open in a right drawer so the table keeps its width. Open question: persist filters per workspace?" color=yellow
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
board "Sign in"
|
|
2
|
+
screen phone "Sign in" padding=28 gap=16
|
|
3
|
+
spacer size=24
|
|
4
|
+
heading "Welcome back" level=1
|
|
5
|
+
text "Sign in to sync your notes." muted
|
|
6
|
+
input email "Email" placeholder="you@example.com"
|
|
7
|
+
input password "Password"
|
|
8
|
+
stack row justify=between
|
|
9
|
+
checkbox "Remember me" checked
|
|
10
|
+
button ghost sm "Forgot?"
|
|
11
|
+
button primary lg "Sign in" fullWidth
|
|
12
|
+
spacer
|
|
13
|
+
text sm "New here? Create an account" align=center muted
|
|
14
|
+
note "Show the error state when the password is wrong" width=200
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
board row "Empty and error states"
|
|
2
|
+
screen phone "No notes yet"
|
|
3
|
+
navbar "Notes" actions=[plus]
|
|
4
|
+
stack grow justify=center align=center gap=12 padding=24
|
|
5
|
+
image "Illustration" height=140 width=200
|
|
6
|
+
heading "No notes yet" level=2
|
|
7
|
+
text "Your notes will show up here." muted align=center
|
|
8
|
+
button primary "New note" leadingIcon=plus
|
|
9
|
+
screen phone "Couldn't load"
|
|
10
|
+
navbar "Notes" actions=[plus]
|
|
11
|
+
stack grow justify=center align=center gap=12 padding=24
|
|
12
|
+
icon triangle-alert size=40
|
|
13
|
+
heading "Something went wrong" level=2
|
|
14
|
+
text "Check your connection and try again." muted align=center
|
|
15
|
+
button secondary "Try again" leadingIcon=refresh-cw
|
|
16
|
+
badge "Offline" tone=warning
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsquare",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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",
|
|
@@ -23,13 +23,26 @@
|
|
|
23
23
|
"url": "https://github.com/tsquare-js/tsquare/issues"
|
|
24
24
|
},
|
|
25
25
|
"type": "module",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./playground": {
|
|
32
|
+
"types": "./dist/playground/index.d.ts",
|
|
33
|
+
"import": "./dist/playground/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
26
37
|
"bin": {
|
|
27
38
|
"tsquare": "bin/tsquare.js"
|
|
28
39
|
},
|
|
29
40
|
"files": [
|
|
30
41
|
"bin/",
|
|
31
|
-
"
|
|
42
|
+
"dist/",
|
|
32
43
|
"skill/tsquare/",
|
|
44
|
+
"examples/*.tsq",
|
|
45
|
+
"assets/mark.png",
|
|
33
46
|
"LICENSE",
|
|
34
47
|
"README.md"
|
|
35
48
|
],
|
|
@@ -41,28 +54,39 @@
|
|
|
41
54
|
"check": "tsx src/cli.ts check",
|
|
42
55
|
"fmt": "tsx src/cli.ts fmt",
|
|
43
56
|
"prompt": "tsx src/cli.ts prompt",
|
|
44
|
-
"playground": "tsx
|
|
57
|
+
"playground": "tsx src/cli.ts playground",
|
|
45
58
|
"skill": "tsx skill/build.ts",
|
|
46
59
|
"docs": "tsx docs/build.ts",
|
|
47
60
|
"typecheck": "tsc",
|
|
48
61
|
"examples": "for f in examples/*.tsq; do tsx src/cli.ts render $f -o ${f%.tsq}.png --scale 2; done",
|
|
49
62
|
"eval:prompts": "tsx eval/prompts.ts",
|
|
50
|
-
"eval:score": "tsx eval/score.ts"
|
|
63
|
+
"eval:score": "tsx eval/score.ts",
|
|
64
|
+
"build": "tsx scripts/build.ts",
|
|
65
|
+
"prepare": "npm run build"
|
|
51
66
|
},
|
|
52
67
|
"dependencies": {
|
|
53
68
|
"@fontsource/inter": "5.3.0",
|
|
54
69
|
"@json-render/core": "^0.21.0",
|
|
55
70
|
"@json-render/image": "^0.21.0",
|
|
56
71
|
"@resvg/resvg-js": "^2.6.2",
|
|
72
|
+
"@types/node": ">=20",
|
|
73
|
+
"@types/react": "^19.3.0",
|
|
57
74
|
"lucide": "1.48.0",
|
|
58
75
|
"react": "^19.3.0",
|
|
59
|
-
"tsx": "^4.23.15",
|
|
60
76
|
"zod": "^4.6.5"
|
|
61
77
|
},
|
|
62
78
|
"devDependencies": {
|
|
63
|
-
"@
|
|
64
|
-
"@
|
|
79
|
+
"@codemirror/autocomplete": "^6.20.3",
|
|
80
|
+
"@codemirror/commands": "^6.11.1",
|
|
81
|
+
"@codemirror/language": "^6.12.4",
|
|
82
|
+
"@codemirror/lint": "^6.9.7",
|
|
83
|
+
"@codemirror/search": "^6.7.2",
|
|
84
|
+
"@codemirror/state": "^6.7.6",
|
|
85
|
+
"@codemirror/view": "^6.43.13",
|
|
86
|
+
"@lezer/highlight": "^1.2.5",
|
|
87
|
+
"esbuild": "^0.28.2",
|
|
65
88
|
"gpt-tokenizer": "^4.0.0",
|
|
89
|
+
"tsx": "^4.23.15",
|
|
66
90
|
"typescript": "^7.0.2"
|
|
67
91
|
}
|
|
68
92
|
}
|
package/skill/tsquare/SKILL.md
CHANGED
|
@@ -13,13 +13,13 @@ tsquare is a small text language for UI wireframe boards. You write a `.tsq` fil
|
|
|
13
13
|
2. **Check:** `tsquare check <name>.tsq`. It lists problems by line, with the valid options or a "did you mean". Fix every one and check again until it says the file is valid.
|
|
14
14
|
3. **Render:** `tsquare render <name>.tsq -o <name>.png --scale 2` (use `-o <name>.svg` for docs that take SVG).
|
|
15
15
|
4. **Look at the PNG** before showing it. The checker can't see layout, so check for:
|
|
16
|
-
- content cut off at the bottom of a screen (screens have a fixed height; nothing warns about clipping). Remove content, split it into another screen, or
|
|
16
|
+
- content cut off at the bottom of a screen (screens have a fixed height; nothing warns about clipping). Remove content, split it into another screen, or give the screen a larger `height` (it works on any device).
|
|
17
17
|
- cramped rows, or text squeezed into narrow columns.
|
|
18
18
|
- anything the request asked for that's missing.
|
|
19
19
|
Fix the `.tsq` and render again.
|
|
20
20
|
5. **Deliver** the image path and the `.tsq` source, so the user can edit it later.
|
|
21
21
|
|
|
22
|
-
If `tsquare` isn't
|
|
22
|
+
If `tsquare` isn't installed, run it through npx (`npx tsquare check <name>.tsq`); it needs Node 20 or newer. If that fails, stop and ask the user how they want it installed.
|
|
23
23
|
|
|
24
24
|
## Keep in mind
|
|
25
25
|
|
|
@@ -33,9 +33,10 @@ One element per line. Indent children two spaces under their parent. The first l
|
|
|
33
33
|
A line is the component name in lowercase, followed by arguments separated by spaces:
|
|
34
34
|
- "a quoted string" sets the component's main text prop (marked "main text" in reference.md)
|
|
35
35
|
- a bare word that is one of the component's option values sets that option: phone, desktop, primary, ghost, row, sm, left, bottom, password, …
|
|
36
|
-
- a bare prop name sets
|
|
36
|
+
- 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
|
|
37
37
|
- key=value sets any prop. Values: "string", number, true/false, bare word, [list, of, values], {key=value key=value}
|
|
38
|
-
|
|
38
|
+
|
|
39
|
+
Comments: 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.
|
|
39
40
|
|
|
40
41
|
Lists: 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].
|
|
41
42
|
|
|
@@ -15,8 +15,8 @@ Root canvas (artboard). Holds Screens side by side, plus optional Notes. Must be
|
|
|
15
15
|
One view of the product in a device frame. Children stack vertically.
|
|
16
16
|
- name: string (main text) — Label shown above the screen
|
|
17
17
|
- device: "phone" | "tablet" | "desktop" | "custom"
|
|
18
|
-
- width: number —
|
|
19
|
-
- height: number —
|
|
18
|
+
- width: number — Overrides the device width
|
|
19
|
+
- height: number — Overrides the device height, e.g. height=1400 for a long scrolling page
|
|
20
20
|
- chrome: boolean — Phone status bar / desktop browser bar. Default true for phone and desktop.
|
|
21
21
|
- padding: number
|
|
22
22
|
- gap: number
|
|
@@ -106,7 +106,8 @@ Button. Primary is filled, secondary is outlined, ghost is text only.
|
|
|
106
106
|
- label: string (main text)
|
|
107
107
|
- variant: "primary" | "secondary" | "ghost"
|
|
108
108
|
- size: "sm" | "md" | "lg"
|
|
109
|
-
-
|
|
109
|
+
- leadingIcon: string — Lucide icon before the label, e.g. chevron-left for Back
|
|
110
|
+
- trailingIcon: string — Lucide icon after the label, e.g. chevron-right for Next
|
|
110
111
|
- fullWidth: boolean
|
|
111
112
|
|
|
112
113
|
### Input
|
|
@@ -170,10 +171,10 @@ Row in a List. Omit title for a placeholder bar.
|
|
|
170
171
|
- title: string (main text)
|
|
171
172
|
- subtitle: string
|
|
172
173
|
- leading: "none" | "icon" | "avatar" | "image" | "checkbox"
|
|
173
|
-
-
|
|
174
|
+
- leadingIcon: string — Lucide icon on the left; setting it implies leading=icon
|
|
174
175
|
- trailing: "none" | "chevron" | "toggle" | "text" | "badge" | "icon"
|
|
175
|
-
- trailingText: string
|
|
176
|
-
- trailingIcon: string — Lucide icon
|
|
176
|
+
- trailingText: string — Text for trailing=text or badge; setting it implies trailing=text
|
|
177
|
+
- trailingIcon: string — Lucide icon on the right; setting it implies trailing=icon
|
|
177
178
|
|
|
178
179
|
### Table
|
|
179
180
|
Table with headers. Rows are placeholder bars unless `data` is given.
|