Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quadrant-charts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sketchi/cli": minor
---

Add native quadrant charts: `sketchi create` builds `{ "type": "quadrant", "spec": { "title", "xAxis", "yAxis", "quadrants", "points" } }` documents offline, and `sketchi generate --type quadrant` generates them. Each axis carries a title and its low and high end labels, the four quadrants are named by corner (`topLeft`, `topRight`, `bottomLeft`, `bottomRight`), and each point's `x` and `y` run from 0 to 1. The chart draws a square plot of four tinted quadrants, every dot at its exact position, and every label clear of the others, on a leader line where the plot is crowded.
3 changes: 2 additions & 1 deletion .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,8 @@
"packages/diagram/core/src/types/venn.ts",
"packages/diagram/core/src/types/journey.ts",
"packages/diagram/core/src/types/sankey.ts",
"packages/diagram/core/src/types/wireframe.ts"
"packages/diagram/core/src/types/wireframe.ts",
"packages/diagram/core/src/types/quadrant.ts"
],
"rules": {
"eslint/no-restricted-imports": [
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ scene representations.

## What Sketchi Does

- Creates hosted flowcharts, mindmaps, sequence diagrams, timelines, state machines, Gantt charts, ER diagrams, UML class diagrams, org charts, Venn diagrams, user journey maps, Sankey diagrams, UI wireframes, and bar, line, and pie charts from natural-language prompts.
- Creates hosted flowcharts, mindmaps, sequence diagrams, timelines, state machines, Gantt charts, ER diagrams, UML class diagrams, org charts, Venn diagrams, user journey maps, Sankey diagrams, UI wireframes, quadrant charts, and bar, line, and pie charts from natural-language prompts.
- Produces validated, editable Excalidraw scenes instead of opaque images.
- Exposes Code Mode through MCP and versioned HTTP artifact endpoints.
- Keeps layout, bindings, wrapping, and scene conversion deterministic in code.
Expand Down Expand Up @@ -68,7 +68,7 @@ Explicit `--type` and file `--dest` values preset their wizard questions;
interactive generation remains PNG-only, so other formats and `--dest -` also
require `--prompt`.
Without `--type`, the model selects a supported native type. Explicit flowchart,
mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, or wireframe selection is authoritative; architecture and
mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, wireframe, or quadrant selection is authoritative; architecture and
swimlane requests fail clearly instead of being coerced.
The default file is `<generated-id>.png` in the current directory. Use
`--format excalidraw` or `--format scene` for another artifact, and `--dest`
Expand Down Expand Up @@ -149,7 +149,7 @@ model candidate → typed diagram IR → deterministic scene → Excalidraw arti
| `packages/svg-excalidraw` | Native SVG-to-Excalidraw conversion and library serialization |

The first high-reliability target is decision-heavy flowchart generation.
`flowchart`, `mindmap`, `sequence`, `timeline`, `chart`, `state-machine`, `gantt`, `er`, `class`, `org-chart`, `venn`, `journey`, `sankey`, and `wireframe` are explicit diagram families; unsupported families do
`flowchart`, `mindmap`, `sequence`, `timeline`, `chart`, `state-machine`, `gantt`, `er`, `class`, `org-chart`, `venn`, `journey`, `sankey`, `wireframe`, and `quadrant` are explicit diagram families; unsupported families do
not silently fall back to a generic template. See the full
[architecture guide](docs/architecture.md) and the
[MCP-first generation boundary](docs/mcp-first-generation.md).
Expand Down Expand Up @@ -343,3 +343,5 @@ Native journey maps: use `buildJourneyMap` (Code Mode) or `generate --type journ
Native Sankey diagrams: use `buildSankey` (Code Mode) or `generate --type sankey` (CLI). Supply `nodes: [{ id, label }]` (ids without colons) and `links: [{ source, target, value, label? }]` with every stated amount (`value` > 0), plus an optional `unit` formatted as charts format it (`$k` gives $310k; a word unit such as `visits` reads on node totals). Links run one way: no cycles, self-links or repeated pairs, and no invented balancing node. The renderer places each node one column past the longest run of links into it (a sink fed by several nodes aligns with the last column), draws each node as a bar as tall as the larger of its inflow and outflow with its label and total, and each link as a band as wide as its value in its source's color with its value written; a band under 3 px draws at 3 px and keeps its exact value. It owns every coordinate.

Native wireframes: use `buildWireframe` (Code Mode) or `generate --type wireframe` (CLI). Supply `screens`, each with a `name`, a `device` (`desktop`, `tablet` or `mobile`, which sets the frame's size) and `children`: a tree of components in reading order. Containers hold children: `header`, `footer`, `sidebar` (`side: "left" | "right"`), `main`, `section`, `card` and `modal` (with a `title`), `row`, `column` and `grid` (`columns: n`); `align` spreads, centers or ends a row's children. Widgets draw from their own fields: `heading`, `text` (or `lines` of placeholder copy), `link`, `badge` and `button` show `text`; `input`, `textarea`, `select` and `search` take a `label`, a `placeholder` or the `value` they show; `checkbox`, `radio` and `toggle` take a `label`; `tabs`, `nav`, `breadcrumbs` and `list` take `items`; and `avatar`, `icon`, `image`, `chart`, `table` (`columns` of headers) and `divider` draw placeholders. State: `variant: "primary"` on the main action, `checked: true` on what is on, and `active` on the selected tab or nav item; `target` on a button or link names the screen it opens, drawn as an arrow to that screen's frame. The renderer draws each screen as an Excalidraw frame, its header across the top, footer along the bottom, sidebars down their sides and modals over a dimmed backdrop, with every component a group it places: rows wrap and grids drop columns before anything overflows, and text wraps at whole words; it owns every coordinate.

Native quadrant charts: use `buildQuadrantChart` (Code Mode) or `generate --type quadrant` (CLI). Supply `xAxis` and `yAxis` as `{ title, low, high }` (the end labels, x left to right and y bottom to top, as the source words them), `quadrants` as `{ topLeft, topRight, bottomLeft, bottomRight }`, and up to 30 `points: [{ label, x, y, group? }]` with `x` and `y` from 0 (the low end) to 1 (the high end); a score out of 100 is score / 100. `group` colors points and adds a legend. The renderer draws a square plot of four tinted quadrants with their names in their corners, axis arrows carrying the end labels, and each point as a dot at its exact position with its label beside it, or on a leader line where the plot is crowded; it owns every coordinate.
10 changes: 6 additions & 4 deletions apps/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<p align="center">
<strong>Typed, deterministic diagrams from your terminal.</strong><br />
Create, inspect, revise, and export local flowcharts, mindmaps, sequence diagrams, timelines, charts, state machines, Gantt charts, ER diagrams, class diagrams, org charts, Venn diagrams, journey maps, Sankey diagrams, and wireframes—or turn one prompt into validated, editable Excalidraw.
Create, inspect, revise, and export local flowcharts, mindmaps, sequence diagrams, timelines, charts, state machines, Gantt charts, ER diagrams, class diagrams, org charts, Venn diagrams, journey maps, Sankey diagrams, wireframes, and quadrant charts—or turn one prompt into validated, editable Excalidraw.
</p>

<p align="center">
Expand Down Expand Up @@ -53,7 +53,7 @@ sketchi generate --prompt "Map release approval with pass and revise branches"

With no `--prompt`, Sketchi opens a short wizard only when both standard input
and standard output are human terminals, output is text, and CI is absent. It
asks for prompt text, flowchart (the default), mind map, sequence diagram, timeline, chart, state machine, Gantt chart, ER diagram, class diagram, org chart, Venn diagram, journey map, Sankey diagram, or wireframe, and where to save the
asks for prompt text, flowchart (the default), mind map, sequence diagram, timeline, chart, state machine, Gantt chart, ER diagram, class diagram, org chart, Venn diagram, journey map, Sankey diagram, wireframe, or quadrant chart, and where to save the
PNG. The choices are the current directory, a `diagrams/` folder under exactly
the directory where Sketchi was run, or a custom path with the same semantics as
`--dest`. Sketchi does not inspect Git, and it creates `diagrams/` only after
Expand All @@ -64,7 +64,7 @@ that wizard question. Explicit `--format png` may proceed; another format or
Scripts, pipes, redirected streams, CI, and `--output json` never prompt or
block. They must pass `--prompt`; omitting it retains the deterministic usage
error. Passing `--prompt` is always direct and noninteractive.
`--type` accepts only flowchart, mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, or wireframe and is authoritative.
`--type` accepts only flowchart, mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, wireframe, or quadrant and is authoritative.
Without it, the generation model selects one of those from the request; a
request for any other kind of diagram returns a typed unsupported-type error
instead of being coerced into a supported one.
Expand Down Expand Up @@ -103,7 +103,7 @@ The endpoint defaults to
`https://playground.sketchi.app/api/v1/canvases/create`; use `--endpoint URL` or
`SKETCHI_CANVAS_ENDPOINT` only for preview and local testing. The existing
`create` command remains the separate, strictly offline command for accepted
flowchart, mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, and wireframe documents.
flowchart, mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, wireframe, and quadrant documents.

## Create and work with a diagram offline

Expand Down Expand Up @@ -340,3 +340,5 @@ Native journey maps: use `buildJourneyMap` (Code Mode) or `generate --type journ
Native Sankey diagrams: use `buildSankey` (Code Mode) or `generate --type sankey` (CLI). Supply `nodes: [{ id, label }]` (ids without colons) and `links: [{ source, target, value, label? }]` with every stated amount (`value` > 0), plus an optional `unit` formatted as charts format it (`$k` gives $310k; a word unit such as `visits` reads on node totals). Links run one way: no cycles, self-links or repeated pairs, and no invented balancing node. The renderer places each node one column past the longest run of links into it (a sink fed by several nodes aligns with the last column), draws each node as a bar as tall as the larger of its inflow and outflow with its label and total, and each link as a band as wide as its value in its source's color with its value written; a band under 3 px draws at 3 px and keeps its exact value. It owns every coordinate.

Native wireframes: use `buildWireframe` (Code Mode) or `generate --type wireframe` (CLI). Supply `screens`, each with a `name`, a `device` (`desktop`, `tablet` or `mobile`, which sets the frame's size) and `children`: a tree of components in reading order. Containers hold children: `header`, `footer`, `sidebar` (`side: "left" | "right"`), `main`, `section`, `card` and `modal` (with a `title`), `row`, `column` and `grid` (`columns: n`); `align` spreads, centers or ends a row's children. Widgets draw from their own fields: `heading`, `text` (or `lines` of placeholder copy), `link`, `badge` and `button` show `text`; `input`, `textarea`, `select` and `search` take a `label`, a `placeholder` or the `value` they show; `checkbox`, `radio` and `toggle` take a `label`; `tabs`, `nav`, `breadcrumbs` and `list` take `items`; and `avatar`, `icon`, `image`, `chart`, `table` (`columns` of headers) and `divider` draw placeholders. State: `variant: "primary"` on the main action, `checked: true` on what is on, and `active` on the selected tab or nav item; `target` on a button or link names the screen it opens, drawn as an arrow to that screen's frame. The renderer draws each screen as an Excalidraw frame, its header across the top, footer along the bottom, sidebars down their sides and modals over a dimmed backdrop, with every component a group it places: rows wrap and grids drop columns before anything overflows, and text wraps at whole words; it owns every coordinate.

Native quadrant charts: use `buildQuadrantChart` (Code Mode) or `generate --type quadrant` (CLI). Supply `xAxis` and `yAxis` as `{ title, low, high }` (the end labels, x left to right and y bottom to top, as the source words them), `quadrants` as `{ topLeft, topRight, bottomLeft, bottomRight }`, and up to 30 `points: [{ label, x, y, group? }]` with `x` and `y` from 0 (the low end) to 1 (the high end); a score out of 100 is score / 100. `group` colors points and adds a legend. The renderer draws a square plot of four tinted quadrants with their names in their corners, axis arrows carrying the end labels, and each point as a dot at its exact position with its label beside it, or on a leader line where the plot is crowded; it owns every coordinate.
2 changes: 1 addition & 1 deletion apps/cli/src/__fixtures__/help/agent-docs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Semantic color patch example:
sketchi patch release-flow --json '{"operations":[{"op":"setStyle","selector":{"nodeIds":["review","approve"]},"style":{"fillColor":"#dbeafe","strokeColor":"#2563eb","textColor":"#1e3a8a"}}]}'

Explicit network commands (one credential-free HTTPS request each):
sketchi generate [--prompt TEXT] [--type flowchart|mindmap|sequence|timeline|chart|state-machine|gantt|er|class|org-chart|venn|journey|sankey|wireframe] [--model MODEL]
sketchi generate [--prompt TEXT] [--type flowchart|mindmap|sequence|timeline|chart|state-machine|gantt|er|class|org-chart|venn|journey|sankey|wireframe|quadrant] [--model MODEL]
sketchi canvas --file PATH|- [--format png|excalidraw|scene] [--dest PATH|-]
sketchi share DIAGRAM_ID [--open]
sketchi pull DIAGRAM_ID --link URL|-
Expand Down
4 changes: 2 additions & 2 deletions apps/cli/src/__fixtures__/help/generate.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Network and options:
Endpoint: https://playground.sketchi.app/api/v1/generate
The model call, schema validation, and quality gate run server-side; the finished diagram
and Excalidraw artifact are returned over plain HTTPS. Sketchi sends no credentials.
Without --type, the model selects flowchart, mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, or wireframe from the request;
Without --type, the model selects flowchart, mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, wireframe, or quadrant from the request;
a request for any other kind of diagram fails with a typed unsupported error.
--model defaults to gemini-3.1-flash-lite.
Override the endpoint for preview or local testing with SKETCHI_GENERATE_ENDPOINT
Expand Down Expand Up @@ -41,7 +41,7 @@ USAGE
FLAGS
--output text|json Result presentation format. (choices: text, json)
--prompt TEXT Diagram request text sent directly without interactive prompts.
--type TYPE Authoritative diagram type; omit for model selection. (choices: flowchart, mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, wireframe)
--type TYPE Authoritative diagram type; omit for model selection. (choices: flowchart, mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, wireframe, quadrant)
--model MODEL Server-routed generation model id; default gemini-3.1-flash-lite.
--endpoint URL Unauthenticated generate API URL; defaults to the production Sketchi endpoint.
--format png|excalidraw|scene Artifact exported after generation; default png. (choices: png, excalidraw, scene)
Expand Down
1 change: 1 addition & 0 deletions apps/cli/src/code-mode-artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export function codeModeFailure(
"invalid_journey",
"invalid_sankey",
"invalid_wireframe",
"invalid_quadrant_chart",
"quality_failed",
];
if (validationStatuses.includes(result.status))
Expand Down
1 change: 1 addition & 0 deletions apps/cli/src/generation-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const GENERATION_TYPE_CHOICES = {
journey: { label: "Journey map", hint: "best for stages, steps and how each one feels" },
sankey: { label: "Sankey diagram", hint: "best for budgets, traffic and funnels that flow" },
wireframe: { label: "Wireframe", hint: "best for low-fidelity screens, forms and app layouts" },
quadrant: { label: "Quadrant chart", hint: "best for 2×2 matrices of items scored on two axes" },
} as const satisfies Record<GenerationType, { readonly hint: string; readonly label: string }>;

/** Wizard options in registry order, one per canonical family. */
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/src/png-renderer-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,8 @@ function bannerTitle(scene: PngRenderInput["scene"]): string | undefined {
element.id === "chart:title" ||
element.id === "gantt:title" ||
element.id === "text:journey:title" ||
element.id === "sankey:title",
element.id === "sankey:title" ||
element.id === "quadrant:title",
)
)
return undefined;
Expand Down
Loading
Loading