tsquare 0.1.0 → 0.2.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.
@@ -1,35 +1,44 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ WireframeError,
4
+ compileWireframe,
5
+ formatIssues,
6
+ printWireframeText,
7
+ renderWireframe,
8
+ wireframePrompt
9
+ } from "./chunks/chunk-L6GTIZQH.js";
10
+
11
+ // src/cli.ts
2
12
  import { readFile, writeFile } from "node:fs/promises";
3
13
  import path from "node:path";
4
- import { compileWireframe, formatIssues, renderWireframe, WireframeError } from "./compile";
5
- import { printWireframeText } from "./print";
6
- import { wireframePrompt } from "./prompt";
7
-
8
- const USAGE = `tsquare — render wireframe text to SVG or PNG
14
+ var USAGE = `tsquare \u2014 render wireframe text to SVG or PNG
9
15
 
10
16
  tsquare render <file.tsq|-> [-o out.svg|out.png] [--scale 2]
11
17
  tsquare check <file.tsq|-> report problems by line number
12
18
  tsquare fmt <file.tsq|-> [-w] print in canonical form (-w rewrites the file; drops comments)
13
19
  tsquare prompt print the system prompt for models
20
+ tsquare playground [--port 4321] open the playground: live editor, component reference, prompt
14
21
 
15
22
  Use - to read from stdin. Code fences (\`\`\`) in the input are ignored.
16
23
  `;
17
-
18
- async function readInput(file: string) {
24
+ async function readInput(file) {
19
25
  if (file !== "-") return readFile(file, "utf8");
20
- const chunks: Buffer[] = [];
21
- for await (const c of process.stdin) chunks.push(c as Buffer);
26
+ const chunks = [];
27
+ for await (const c of process.stdin) chunks.push(c);
22
28
  return Buffer.concat(chunks).toString("utf8");
23
29
  }
24
-
25
- async function main(argv: string[]) {
30
+ async function main(argv) {
26
31
  const [cmd, ...rest] = argv;
27
- const flag = (name: string) => {
32
+ const flag = (name) => {
28
33
  const i = rest.indexOf(name);
29
- return i >= 0 ? rest[i + 1] : undefined;
34
+ return i >= 0 ? rest[i + 1] : void 0;
30
35
  };
31
36
  const file = rest.find((a, i) => (a === "-" || !a.startsWith("-")) && !["-o", "--scale"].includes(rest[i - 1]));
32
-
37
+ if (cmd === "playground") {
38
+ const { startPlayground } = await import("./playground/index.js");
39
+ startPlayground(Number(flag("--port") ?? 4321));
40
+ return;
41
+ }
33
42
  if (cmd === "prompt") {
34
43
  process.stdout.write(wireframePrompt() + "\n");
35
44
  return;
@@ -39,36 +48,31 @@ async function main(argv: string[]) {
39
48
  process.exitCode = cmd && cmd !== "help" ? 1 : 0;
40
49
  return;
41
50
  }
42
-
43
51
  const source = await readInput(file);
44
-
45
52
  if (cmd === "check") {
46
53
  const { issues } = compileWireframe(source);
47
54
  if (issues.length) {
48
55
  console.error(formatIssues(issues));
49
56
  process.exitCode = 1;
50
57
  } else {
51
- console.log(`✓ ${file === "-" ? "input" : file} is valid`);
58
+ console.log(`\u2713 ${file === "-" ? "input" : file} is valid`);
52
59
  }
53
60
  return;
54
61
  }
55
-
56
62
  if (cmd === "fmt") {
57
63
  const { spec, issues } = compileWireframe(source);
58
64
  if (!spec || issues.length) throw new WireframeError(issues);
59
- const out = printWireframeText(spec);
60
- if (rest.includes("-w") && file !== "-") await writeFile(file, out);
61
- else process.stdout.write(out);
65
+ const out2 = printWireframeText(spec);
66
+ if (rest.includes("-w") && file !== "-") await writeFile(file, out2);
67
+ else process.stdout.write(out2);
62
68
  return;
63
69
  }
64
-
65
70
  const out = flag("-o") ?? (file === "-" ? "wireframe.svg" : file.replace(/\.(tsq|wf)$/, "") + ".svg");
66
71
  const png = out.endsWith(".png");
67
72
  const data = await renderWireframe(source, { format: png ? "png" : "svg", scale: Number(flag("--scale") ?? 1) });
68
73
  await writeFile(out, data);
69
- console.log(`✓ wrote ${path.relative(process.cwd(), out)}`);
74
+ console.log(`\u2713 wrote ${path.relative(process.cwd(), out)}`);
70
75
  }
71
-
72
76
  main(process.argv.slice(2)).catch((err) => {
73
77
  console.error(err instanceof WireframeError ? err.message : err);
74
78
  process.exitCode = 1;
@@ -0,0 +1,29 @@
1
+ /** Named accents. Each keeps white text above 4.5:1 contrast. */
2
+ export declare const ACCENTS: {
3
+ readonly blue: "#2563eb";
4
+ readonly indigo: "#4f46e5";
5
+ readonly violet: "#7c3aed";
6
+ readonly pink: "#db2777";
7
+ readonly red: "#dc2626";
8
+ readonly orange: "#c2410c";
9
+ readonly green: "#15803d";
10
+ readonly teal: "#0f766e";
11
+ };
12
+ /** Status tones for badges and input errors. They don't change with the accent. */
13
+ export declare const TONES: {
14
+ readonly success: "#15803d";
15
+ readonly warning: "#b45309";
16
+ readonly danger: "#dc2626";
17
+ };
18
+ export declare const isAccent: (v: string) => boolean;
19
+ export declare function accentMessage(v: string): string;
20
+ export interface Palette {
21
+ /** Fills for primary buttons, solid badges, checked controls, toggles that are on. */
22
+ accent: string;
23
+ /** Text and icons drawn on the accent fill. */
24
+ onAccent: string;
25
+ /** Accent as text or a line on white: active tabs, the active tab-bar item, ghost buttons. Dark gray without an accent. */
26
+ accentText: string;
27
+ }
28
+ /** Colors for one board. Without an accent this is exactly the grayscale theme. */
29
+ export declare function paletteFor(accent?: string | null): Palette;
@@ -0,0 +1,24 @@
1
+ import type { Spec } from "@json-render/core";
2
+ import { type TextIssue } from "./text.js";
3
+ export interface CompileResult {
4
+ /** The compiled spec, or null if the text couldn't be parsed at all. */
5
+ spec: Spec | null;
6
+ /** Every problem, tied to a source line. Empty means the wireframe is valid. */
7
+ issues: TextIssue[];
8
+ }
9
+ /**
10
+ * Parse wireframe text and validate it against the catalog, reporting every
11
+ * problem by line number so it can be shown to a person or fed back to a model.
12
+ */
13
+ export declare function compileWireframe(source: string): CompileResult;
14
+ /** One issue per line, e.g. for an error message or a model repair prompt. */
15
+ export declare function formatIssues(issues: TextIssue[]): string;
16
+ export declare class WireframeError extends Error {
17
+ issues: TextIssue[];
18
+ constructor(issues: TextIssue[]);
19
+ }
20
+ /** Compile and render wireframe text. Throws WireframeError (with line-numbered issues) if it's invalid. */
21
+ export declare function renderWireframe(source: string, opts?: {
22
+ format?: "svg" | "png";
23
+ scale?: number;
24
+ }): Promise<string | Buffer<ArrayBufferLike>>;
@@ -0,0 +1,83 @@
1
+ import React from "react";
2
+ import { type Palette } from "./colors.js";
3
+ import type { ComponentRenderProps } from "@json-render/image";
4
+ /**
5
+ * Satori renders a subset of CSS. Rules followed here:
6
+ * - every element that has more than one child sets display:flex
7
+ * - absolute positioning is relative to the direct parent (Yoga), so overlays
8
+ * are lifted out and placed by Screen rather than nested in the body
9
+ */
10
+ type Dir = "row" | "column";
11
+ type Props<P = Record<string, any>> = ComponentRenderProps<P> & {
12
+ dir?: Dir;
13
+ stretch?: boolean;
14
+ aligned?: boolean;
15
+ colors?: Palette;
16
+ };
17
+ /** A registry whose components all receive this board's palette as `colors`. */
18
+ export declare function withPalette(colors: Palette): typeof registry;
19
+ export declare function IconGlyph({ name, size, color, strokeWidth }: {
20
+ name?: string | null;
21
+ size?: number;
22
+ color?: string;
23
+ strokeWidth?: number;
24
+ }): React.JSX.Element;
25
+ declare function Board({ element, children }: Props): React.JSX.Element;
26
+ declare function Screen({ element, children }: Props): React.JSX.Element;
27
+ declare function Note({ element, dir, aligned }: Props): React.JSX.Element;
28
+ declare function Stack({ element, children, dir: parentDir, stretch }: Props): React.JSX.Element;
29
+ declare function Grid({ element, children }: Props): React.JSX.Element;
30
+ declare function Card({ element, children, dir, stretch }: Props): React.JSX.Element;
31
+ declare function Divider({ element, dir }: Props): React.JSX.Element;
32
+ declare function Spacer({ element }: Props): React.JSX.Element;
33
+ declare function Heading({ element }: Props): React.JSX.Element;
34
+ declare function Text({ element }: Props): React.JSX.Element;
35
+ declare function Image({ element, dir }: Props): React.JSX.Element;
36
+ declare function Icon({ element, dir, aligned }: Props): React.JSX.Element;
37
+ declare function Avatar({ element, dir, aligned }: Props): React.JSX.Element;
38
+ declare function Badge({ element, dir, aligned, colors }: Props): React.JSX.Element;
39
+ declare function Button({ element, dir, aligned, colors: c }: Props): React.JSX.Element;
40
+ declare function Input({ element, dir }: Props): React.JSX.Element;
41
+ declare function Select({ element, dir }: Props): React.JSX.Element;
42
+ declare function Checkbox({ element, dir, aligned, colors }: Props): React.JSX.Element;
43
+ declare function Radio({ element, dir, aligned, colors }: Props): React.JSX.Element;
44
+ declare function Toggle({ element, colors }: Props): React.JSX.Element;
45
+ declare function NavBar({ element }: Props): React.JSX.Element;
46
+ declare function TabBar({ element, colors: c }: Props): React.JSX.Element;
47
+ declare function Tabs({ element, colors: c }: Props): React.JSX.Element;
48
+ declare function List({ element, children, dir, stretch }: Props): React.JSX.Element;
49
+ declare function ListItem({ element, colors }: Props): React.JSX.Element;
50
+ declare function Table({ element }: Props): React.JSX.Element;
51
+ declare function Modal({ element, children }: Props): React.JSX.Element;
52
+ declare function Drawer({ element, children }: Props): React.JSX.Element;
53
+ export declare const registry: {
54
+ Board: typeof Board;
55
+ Screen: typeof Screen;
56
+ Note: typeof Note;
57
+ Stack: typeof Stack;
58
+ Grid: typeof Grid;
59
+ Card: typeof Card;
60
+ Divider: typeof Divider;
61
+ Spacer: typeof Spacer;
62
+ Heading: typeof Heading;
63
+ Text: typeof Text;
64
+ Image: typeof Image;
65
+ Icon: typeof Icon;
66
+ Avatar: typeof Avatar;
67
+ Badge: typeof Badge;
68
+ Button: typeof Button;
69
+ Input: typeof Input;
70
+ Checkbox: typeof Checkbox;
71
+ Radio: typeof Radio;
72
+ Toggle: typeof Toggle;
73
+ Select: typeof Select;
74
+ NavBar: typeof NavBar;
75
+ TabBar: typeof TabBar;
76
+ Tabs: typeof Tabs;
77
+ List: typeof List;
78
+ ListItem: typeof ListItem;
79
+ Table: typeof Table;
80
+ Modal: typeof Modal;
81
+ Drawer: typeof Drawer;
82
+ };
83
+ export {};
@@ -0,0 +1,12 @@
1
+ /** Kebab-case names for every Lucide icon (including Lucide's aliases, e.g. home and house). */
2
+ export declare const iconNames: string[];
3
+ /** The Lucide icon node for a kebab-case name, or undefined if there's no such icon. */
4
+ export declare function iconNode(name: string): any;
5
+ export declare const isIcon: (name: string) => boolean;
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.
10
+ */
11
+ export declare function suggestIcons(name: string, limit?: number): string[];
12
+ export declare function unknownIconMessage(name: string): string;
@@ -0,0 +1,9 @@
1
+ /// <reference types="node" preserve="true" />
2
+ export { renderWireframe, compileWireframe, formatIssues, WireframeError, type CompileResult } from "./compile.js";
3
+ export { wireframePrompt, repairPrompt } from "./prompt.js";
4
+ export { printWireframeText as formatWireframe } from "./print.js";
5
+ export { renderWireframeSvg, renderWireframePng, checkSpec, boardSize, type RenderWireframeOptions } from "./render.js";
6
+ export { parseWireframeText, type TextIssue, type ParseResult } from "./text.js";
7
+ export { catalog, componentDefinitions } from "./catalog.js";
8
+ export { registry } from "./components.js";
9
+ export { theme, DEVICES } from "./layout.js";
package/dist/index.js ADDED
@@ -0,0 +1,38 @@
1
+ import {
2
+ DEVICES,
3
+ WireframeError,
4
+ boardSize,
5
+ catalog,
6
+ checkSpec,
7
+ compileWireframe,
8
+ componentDefinitions,
9
+ formatIssues,
10
+ parseWireframeText,
11
+ printWireframeText,
12
+ registry,
13
+ renderWireframe,
14
+ renderWireframePng,
15
+ renderWireframeSvg,
16
+ repairPrompt,
17
+ theme,
18
+ wireframePrompt
19
+ } from "./chunks/chunk-L6GTIZQH.js";
20
+ export {
21
+ DEVICES,
22
+ WireframeError,
23
+ boardSize,
24
+ catalog,
25
+ checkSpec,
26
+ compileWireframe,
27
+ componentDefinitions,
28
+ formatIssues,
29
+ printWireframeText as formatWireframe,
30
+ parseWireframeText,
31
+ registry,
32
+ renderWireframe,
33
+ renderWireframePng,
34
+ renderWireframeSvg,
35
+ repairPrompt,
36
+ theme,
37
+ wireframePrompt
38
+ };
@@ -0,0 +1,66 @@
1
+ /** Sizes and colors shared by the components and the board-size calculation. */
2
+ export declare const theme: {
3
+ readonly font: "Inter";
4
+ readonly board: "#e6e8eb";
5
+ readonly paper: "#ffffff";
6
+ readonly ink: "#1f2328";
7
+ readonly text: "#3d434a";
8
+ readonly muted: "#868d96";
9
+ readonly line: "#d0d4d9";
10
+ readonly lineStrong: "#9ca3ab";
11
+ readonly fill: "#f0f2f4";
12
+ readonly fill2: "#e1e4e8";
13
+ readonly primary: "#2b3035";
14
+ readonly onPrimary: "#ffffff";
15
+ readonly scrim: "rgba(31, 35, 40, 0.38)";
16
+ readonly notes: {
17
+ readonly yellow: "#fdf1a8";
18
+ readonly blue: "#cfe6fb";
19
+ readonly pink: "#fbd3e0";
20
+ readonly green: "#d4f1d0";
21
+ };
22
+ };
23
+ export declare const DEVICES: {
24
+ readonly phone: {
25
+ readonly width: 390;
26
+ readonly height: 844;
27
+ readonly radius: 36;
28
+ readonly padding: 16;
29
+ };
30
+ readonly tablet: {
31
+ readonly width: 820;
32
+ readonly height: 1180;
33
+ readonly radius: 24;
34
+ readonly padding: 24;
35
+ };
36
+ readonly desktop: {
37
+ readonly width: 1280;
38
+ readonly height: 800;
39
+ readonly radius: 10;
40
+ readonly padding: 24;
41
+ };
42
+ readonly custom: {
43
+ readonly width: 800;
44
+ readonly height: 600;
45
+ readonly radius: 8;
46
+ readonly padding: 20;
47
+ };
48
+ };
49
+ export type Device = keyof typeof DEVICES;
50
+ /** Height of the screen name label plus the space under it. */
51
+ export declare const LABEL_H = 40;
52
+ /** Height of the board title block. */
53
+ export declare const TITLE_H = 64;
54
+ export declare const BOARD_PADDING = 56;
55
+ export declare const BOARD_GAP = 64;
56
+ export declare const NOTE_WIDTH = 220;
57
+ export declare function screenSize(props?: Record<string, any>): {
58
+ device: "custom" | "desktop" | "phone" | "tablet";
59
+ width: any;
60
+ height: any;
61
+ radius: 8 | 10 | 24 | 36;
62
+ padding: any;
63
+ chrome: any;
64
+ };
65
+ /** Rough height of a Note so the board is tall enough to hold it. */
66
+ export declare function estimateNoteHeight(text: string, width: number): number;
@@ -0,0 +1,27 @@
1
+ /// <reference types="node" preserve="true" />
2
+ /**
3
+ * The playground: an editor with live rendering, the component reference and
4
+ * the model prompt. Ships in the package so it runs locally (`tsquare
5
+ * playground`) and on the website from the same code.
6
+ *
7
+ * The page (index.html) talks to a small set of endpoints, all answered by
8
+ * `createPlaygroundHandler`:
9
+ *
10
+ * GET / the page
11
+ * GET /mark.png the logo mark (tab icon, header)
12
+ * GET /api/reference components, props and rendered examples
13
+ * GET /api/examples the example wireframes
14
+ * GET /api/prompt the model prompt
15
+ * POST /api/render wireframe text → { issues, svg? }
16
+ * POST /api/png?scale=2 wireframe text → image/png
17
+ */
18
+ import { type IncomingMessage, type ServerResponse } from "node:http";
19
+ /** Problems with the reference examples: a component without an example or group, or an example that doesn't compile. */
20
+ export declare function checkReferenceExamples(): string[];
21
+ /**
22
+ * Handles a playground request. Resolves to false when the path isn't a
23
+ * playground route, so a host server can serve its own routes around it.
24
+ */
25
+ export declare function createPlaygroundHandler(): (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
26
+ /** Runs the playground on localhost. */
27
+ export declare function startPlayground(port?: number): import("node:http").Server<typeof IncomingMessage, typeof ServerResponse>;