better-design 0.11.0 → 0.12.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 +1 -2
- package/dist/index.js +80 -178
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,8 +25,7 @@ The server exposes MCP tools only; it does not register prompts.
|
|
|
25
25
|
| `find-design-system` | Start any UI build by searching the catalog for visual foundations, or browse and compare existing systems. Returns IDs and the build/review guardrails needed for the next step. |
|
|
26
26
|
| `create-design-system` | Start a custom design system generation job. |
|
|
27
27
|
| `get-design-system-status` | Poll a generated design system and return the install command when ready. |
|
|
28
|
-
| `
|
|
29
|
-
| `get-illustration-status` | Collect a drawing started by `draw-illustration`. Returns the complete svg and its layer names once ready. |
|
|
28
|
+
| `review-illustration` | Render an SVG illustration you drew, look at it, and report what is visibly wrong |
|
|
30
29
|
| `get-design-system-kit` | Return the correct implementation kit through one entry point. Use `target: "react"` in coding hosts for shadcn install commands and real components; use `target: "html"` only in inline canvases/artifacts for the framework-free HTML/CSS kit. Accepts a catalog `designSystemId`, or a generated `projectId` for the HTML target. Base UI is the default React flavor; pass `library: "radix"` when the project already uses Radix. |
|
|
31
30
|
| `preview-design-system` | Return a token-true visual preview for a catalog design system. |
|
|
32
31
|
| `get-ui-principle` | Load or search visual UI principles. |
|
package/dist/index.js
CHANGED
|
@@ -6673,6 +6673,19 @@ var init_extract_tools = __esm({
|
|
|
6673
6673
|
|
|
6674
6674
|
// ../shared/src/mcp/tools/illustration-tools.ts
|
|
6675
6675
|
import { z as z7 } from "zod";
|
|
6676
|
+
function fail(message) {
|
|
6677
|
+
return {
|
|
6678
|
+
content: [{ type: "text", text: message }],
|
|
6679
|
+
structuredContent: {
|
|
6680
|
+
faults: [message],
|
|
6681
|
+
layers: [],
|
|
6682
|
+
reviewed: false,
|
|
6683
|
+
unseen: true,
|
|
6684
|
+
reviewToken: "",
|
|
6685
|
+
message
|
|
6686
|
+
}
|
|
6687
|
+
};
|
|
6688
|
+
}
|
|
6676
6689
|
function registerIllustrationTools({ server, track, tracked, authContext }) {
|
|
6677
6690
|
const authHeaders2 = () => {
|
|
6678
6691
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -6683,215 +6696,104 @@ function registerIllustrationTools({ server, track, tracked, authContext }) {
|
|
|
6683
6696
|
return headers;
|
|
6684
6697
|
};
|
|
6685
6698
|
server.registerTool(
|
|
6686
|
-
"
|
|
6699
|
+
"review-illustration",
|
|
6687
6700
|
{
|
|
6688
|
-
title: "
|
|
6689
|
-
description: `
|
|
6701
|
+
title: "Review an Illustration",
|
|
6702
|
+
description: `Look at an SVG illustration you drew and get back what is visibly wrong with it.
|
|
6690
6703
|
|
|
6691
|
-
|
|
6704
|
+
You write SVG blind. This renders it in a real browser and judges it against this project's illustration principles, so you find out that a shape does not close, a part floats unattached, the light comes from two directions, or contents sit outside the container meant to hold them.
|
|
6692
6705
|
|
|
6693
|
-
|
|
6706
|
+
Use it whenever you have drawn an illustration, before you hand it to anyone. Drawing something worth shipping usually takes 2 or 3 rounds.
|
|
6694
6707
|
|
|
6695
6708
|
How to call it:
|
|
6696
|
-
1. \`
|
|
6697
|
-
2.
|
|
6698
|
-
3.
|
|
6709
|
+
1. Get the rules first with \`get-ui-principle({ topic: "svg-illustration" })\` and draw against them.
|
|
6710
|
+
2. \`review-illustration({ svg, intent: "an open inbox tray with letters" })\`. Say what the drawing is meant to be, in your own words.
|
|
6711
|
+
3. Fix what \`faults\` names, then call again passing \`reviewToken\` back. Reviewing the same drawing again costs nothing while that token is valid.
|
|
6712
|
+
4. Stop when \`faults\` is empty.
|
|
6699
6713
|
|
|
6700
|
-
|
|
6714
|
+
Markup carrying script, event handlers, remote references, or styles that reach outside the drawing is refused rather than cleaned, so the drawing you fix is the drawing you sent.`,
|
|
6701
6715
|
inputSchema: z7.object({
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6716
|
+
svg: z7.string().min(1).describe("The complete svg element you drew."),
|
|
6717
|
+
intent: z7.string().min(1).describe('What it is meant to be, in plain words. For example "an open inbox tray with letters".'),
|
|
6718
|
+
animated: z7.boolean().optional().describe("Set when the drawing was meant to move, so the motion checks apply."),
|
|
6719
|
+
reviewToken: z7.string().optional().describe("The token from a previous review of this drawing. Reviews it again free.")
|
|
6706
6720
|
}),
|
|
6707
|
-
outputSchema:
|
|
6721
|
+
outputSchema: REVIEW_OUTPUT_SCHEMA,
|
|
6722
|
+
annotations: { readOnlyHint: true }
|
|
6708
6723
|
},
|
|
6709
|
-
async ({
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
const
|
|
6724
|
+
async ({
|
|
6725
|
+
svg,
|
|
6726
|
+
intent,
|
|
6727
|
+
animated,
|
|
6728
|
+
reviewToken
|
|
6729
|
+
}) => tracked(
|
|
6730
|
+
"review-illustration",
|
|
6731
|
+
{ query: intent, animated, repeat: Boolean(reviewToken) },
|
|
6732
|
+
async () => {
|
|
6733
|
+
try {
|
|
6734
|
+
const res = await fetch(`${APP_BASE_URL}/api/illustrations/review`, {
|
|
6735
|
+
method: "POST",
|
|
6736
|
+
headers: authHeaders2(),
|
|
6737
|
+
signal: AbortSignal.timeout(6e4),
|
|
6738
|
+
body: JSON.stringify({ svg, intent, animated: animated ?? false, reviewToken })
|
|
6739
|
+
});
|
|
6740
|
+
if (!res.ok) {
|
|
6741
|
+
const detail = await res.text().catch(() => "");
|
|
6742
|
+
return fail(`Could not review that drawing (HTTP ${res.status}). ${detail.slice(0, 300)}`);
|
|
6743
|
+
}
|
|
6744
|
+
const body = await res.json();
|
|
6745
|
+
const faults = body.faults ?? [];
|
|
6746
|
+
const layers = body.layers ?? [];
|
|
6747
|
+
const message = !body.reviewed ? faults[0] ?? "That drawing was not reviewed." : faults.length === 0 ? "Nothing visibly wrong. The drawing is ready to use." : [
|
|
6748
|
+
`${faults.length} thing${faults.length === 1 ? "" : "s"} to fix:`,
|
|
6749
|
+
...faults.map((f) => `- ${f}`),
|
|
6750
|
+
body.unseen ? "The drawing could not be rendered, so only its structure was checked." : "",
|
|
6751
|
+
body.reviewToken ? "Fix these and call again with the same reviewToken, which costs nothing." : "Fix these and call again."
|
|
6752
|
+
].filter(Boolean).join("\n");
|
|
6720
6753
|
return {
|
|
6721
6754
|
content: [
|
|
6722
6755
|
{
|
|
6723
6756
|
type: "text",
|
|
6724
|
-
text:
|
|
6757
|
+
text: layers.length ? `${message}
|
|
6758
|
+
|
|
6759
|
+
Layers, in paint order: ${layers.join(", ")}.` : message
|
|
6725
6760
|
}
|
|
6726
6761
|
],
|
|
6727
6762
|
structuredContent: {
|
|
6728
|
-
|
|
6729
|
-
illustrationId: "",
|
|
6730
|
-
svg: "",
|
|
6731
|
-
layers: [],
|
|
6732
|
-
subject: "",
|
|
6733
|
-
message: `Could not start the drawing (HTTP ${res.status}).`
|
|
6734
|
-
}
|
|
6735
|
-
};
|
|
6736
|
-
}
|
|
6737
|
-
const body = await res.json();
|
|
6738
|
-
if (!body.id) {
|
|
6739
|
-
return {
|
|
6740
|
-
content: [{ type: "text", text: "The drawing service did not return an id." }],
|
|
6741
|
-
structuredContent: {
|
|
6742
|
-
status: "error",
|
|
6743
|
-
illustrationId: "",
|
|
6744
|
-
svg: "",
|
|
6745
|
-
layers: [],
|
|
6746
|
-
subject: "",
|
|
6747
|
-
message: "The drawing service did not return an id."
|
|
6748
|
-
}
|
|
6749
|
-
};
|
|
6750
|
-
}
|
|
6751
|
-
const next = `Drawing started. Wait about a minute, then call get-illustration-status with id ${body.id}.`;
|
|
6752
|
-
return {
|
|
6753
|
-
content: [{ type: "text", text: next }],
|
|
6754
|
-
structuredContent: {
|
|
6755
|
-
status: "drawing",
|
|
6756
|
-
illustrationId: body.id,
|
|
6757
|
-
svg: "",
|
|
6758
|
-
layers: [],
|
|
6759
|
-
subject: "",
|
|
6760
|
-
message: next
|
|
6761
|
-
}
|
|
6762
|
-
};
|
|
6763
|
-
} catch (err) {
|
|
6764
|
-
const message = `Could not reach the drawing service: ${err instanceof Error ? err.message : String(err)}`;
|
|
6765
|
-
return {
|
|
6766
|
-
content: [{ type: "text", text: message }],
|
|
6767
|
-
structuredContent: {
|
|
6768
|
-
status: "error",
|
|
6769
|
-
illustrationId: "",
|
|
6770
|
-
svg: "",
|
|
6771
|
-
layers: [],
|
|
6772
|
-
subject: "",
|
|
6773
|
-
message
|
|
6774
|
-
}
|
|
6775
|
-
};
|
|
6776
|
-
}
|
|
6777
|
-
})
|
|
6778
|
-
);
|
|
6779
|
-
server.registerTool(
|
|
6780
|
-
"get-illustration-status",
|
|
6781
|
-
{
|
|
6782
|
-
title: "Get Illustration Status",
|
|
6783
|
-
description: `Collect a drawing started by draw-illustration.
|
|
6784
|
-
|
|
6785
|
-
Call this about a minute after starting. While it is still drawing, wait and call again rather than starting a second drawing.
|
|
6786
|
-
|
|
6787
|
-
When ready, \`svg\` holds the complete element: paste it into a component as it is. \`layers\` names the groups in paint order, so you know which part to animate. For a container, the back group renders behind whatever falls in and the front group in front of it, which is what makes something look like it landed inside.`,
|
|
6788
|
-
inputSchema: z7.object({
|
|
6789
|
-
illustrationId: z7.string().min(1).describe("The id returned by draw-illustration.")
|
|
6790
|
-
}),
|
|
6791
|
-
outputSchema: DRAW_OUTPUT_SCHEMA,
|
|
6792
|
-
annotations: { readOnlyHint: true }
|
|
6793
|
-
},
|
|
6794
|
-
async ({ illustrationId }) => tracked("get-illustration-status", { illustrationId }, async () => {
|
|
6795
|
-
try {
|
|
6796
|
-
const res = await fetch(`${APP_BASE_URL}/api/illustrations/${illustrationId}`, {
|
|
6797
|
-
headers: authHeaders2(),
|
|
6798
|
-
signal: AbortSignal.timeout(15e3)
|
|
6799
|
-
});
|
|
6800
|
-
if (!res.ok) {
|
|
6801
|
-
const message = `Could not read that drawing (HTTP ${res.status}).`;
|
|
6802
|
-
return {
|
|
6803
|
-
content: [{ type: "text", text: message }],
|
|
6804
|
-
structuredContent: {
|
|
6805
|
-
status: "error",
|
|
6806
|
-
illustrationId,
|
|
6807
|
-
svg: "",
|
|
6808
|
-
layers: [],
|
|
6809
|
-
subject: "",
|
|
6810
|
-
message
|
|
6811
|
-
}
|
|
6812
|
-
};
|
|
6813
|
-
}
|
|
6814
|
-
const body = await res.json();
|
|
6815
|
-
if ((body.status === "ready" || body.status === "partial") && body.svg) {
|
|
6816
|
-
const layers = body.layers ?? [];
|
|
6817
|
-
const partial = body.status === "partial";
|
|
6818
|
-
const summary = [
|
|
6819
|
-
partial ? `Drawing done, with reservations: ${body.subject ?? "illustration"}. It ran out of correction passes, so look it over before shipping it.` : `Drawing ready: ${body.subject ?? "illustration"}.`,
|
|
6820
|
-
layers.length ? `Layers, in paint order: ${layers.join(", ")}.` : "",
|
|
6821
|
-
"Paste the svg into a component. Keep the group ids so parts stay animatable."
|
|
6822
|
-
].filter(Boolean).join(" ");
|
|
6823
|
-
return {
|
|
6824
|
-
content: [{ type: "text", text: `${summary}
|
|
6825
|
-
|
|
6826
|
-
${body.svg}` }],
|
|
6827
|
-
structuredContent: {
|
|
6828
|
-
status: partial ? "partial" : "ready",
|
|
6829
|
-
illustrationId,
|
|
6830
|
-
svg: body.svg,
|
|
6763
|
+
faults,
|
|
6831
6764
|
layers,
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
};
|
|
6836
|
-
}
|
|
6837
|
-
if (body.status === "error") {
|
|
6838
|
-
const message = body.error ?? "The drawing failed.";
|
|
6839
|
-
return {
|
|
6840
|
-
content: [{ type: "text", text: message }],
|
|
6841
|
-
structuredContent: {
|
|
6842
|
-
status: "error",
|
|
6843
|
-
illustrationId,
|
|
6844
|
-
svg: "",
|
|
6845
|
-
layers: [],
|
|
6846
|
-
subject: "",
|
|
6765
|
+
reviewed: body.reviewed ?? false,
|
|
6766
|
+
unseen: body.unseen ?? false,
|
|
6767
|
+
reviewToken: body.reviewToken ?? "",
|
|
6847
6768
|
message
|
|
6848
6769
|
}
|
|
6849
6770
|
};
|
|
6771
|
+
} catch (err) {
|
|
6772
|
+
return fail(
|
|
6773
|
+
`Could not reach the review service: ${err instanceof Error ? err.message : String(err)}`
|
|
6774
|
+
);
|
|
6850
6775
|
}
|
|
6851
|
-
const waiting = "Still drawing. Wait another thirty seconds and call this again.";
|
|
6852
|
-
return {
|
|
6853
|
-
content: [{ type: "text", text: waiting }],
|
|
6854
|
-
structuredContent: {
|
|
6855
|
-
status: "drawing",
|
|
6856
|
-
illustrationId,
|
|
6857
|
-
svg: "",
|
|
6858
|
-
layers: [],
|
|
6859
|
-
subject: "",
|
|
6860
|
-
message: waiting
|
|
6861
|
-
}
|
|
6862
|
-
};
|
|
6863
|
-
} catch (err) {
|
|
6864
|
-
const message = `Could not reach the drawing service: ${err instanceof Error ? err.message : String(err)}`;
|
|
6865
|
-
return {
|
|
6866
|
-
content: [{ type: "text", text: message }],
|
|
6867
|
-
structuredContent: {
|
|
6868
|
-
status: "error",
|
|
6869
|
-
illustrationId,
|
|
6870
|
-
svg: "",
|
|
6871
|
-
layers: [],
|
|
6872
|
-
subject: "",
|
|
6873
|
-
message
|
|
6874
|
-
}
|
|
6875
|
-
};
|
|
6876
6776
|
}
|
|
6877
|
-
|
|
6777
|
+
)
|
|
6878
6778
|
);
|
|
6879
6779
|
void track;
|
|
6880
6780
|
}
|
|
6881
|
-
var
|
|
6781
|
+
var REVIEW_OUTPUT_SCHEMA;
|
|
6882
6782
|
var init_illustration_tools = __esm({
|
|
6883
6783
|
"../shared/src/mcp/tools/illustration-tools.ts"() {
|
|
6884
6784
|
"use strict";
|
|
6885
6785
|
init_shared();
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
illustrationId: z7.string(),
|
|
6890
|
-
/** Present once ready. The complete svg element, ready to paste. */
|
|
6891
|
-
svg: z7.string(),
|
|
6786
|
+
REVIEW_OUTPUT_SCHEMA = {
|
|
6787
|
+
/** Empty when nothing is visibly wrong. */
|
|
6788
|
+
faults: z7.array(z7.string()),
|
|
6892
6789
|
/** Group ids in paint order, the parts an animation can target. */
|
|
6893
6790
|
layers: z7.array(z7.string()),
|
|
6894
|
-
|
|
6791
|
+
/** False when the markup was refused; faults says why. */
|
|
6792
|
+
reviewed: z7.boolean(),
|
|
6793
|
+
/** True when the drawing could not be rendered, so only structure was checked. */
|
|
6794
|
+
unseen: z7.boolean(),
|
|
6795
|
+
/** Pass this back to review the same drawing again without spending another credit. */
|
|
6796
|
+
reviewToken: z7.string(),
|
|
6895
6797
|
message: z7.string()
|
|
6896
6798
|
};
|
|
6897
6799
|
}
|