better-design 0.31.1 → 0.33.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/dist/index.js +556 -169
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1232,6 +1232,7 @@ var init_tool_names = __esm({
|
|
|
1232
1232
|
"get-design-config",
|
|
1233
1233
|
"get-design-system-kit",
|
|
1234
1234
|
"get-design-system-status",
|
|
1235
|
+
"get-inspiration",
|
|
1235
1236
|
"get-jig-guide",
|
|
1236
1237
|
"get-motion-guide",
|
|
1237
1238
|
"get-react-native-guide",
|
|
@@ -1269,6 +1270,7 @@ var init_tool_names = __esm({
|
|
|
1269
1270
|
"get-design-config",
|
|
1270
1271
|
"get-design-system-kit",
|
|
1271
1272
|
"get-design-system-status",
|
|
1273
|
+
"get-inspiration",
|
|
1272
1274
|
"get-jig-guide",
|
|
1273
1275
|
"get-motion-guide",
|
|
1274
1276
|
"get-react-native-guide",
|
|
@@ -1374,6 +1376,7 @@ Better Design provides design systems, design principles and UI review checks fo
|
|
|
1374
1376
|
|
|
1375
1377
|
- find-design-system searches the catalog. preview-design-system shows one system. get-design-system-kit returns its install command or CSS kit.
|
|
1376
1378
|
- create-design-system generates a custom system. get-design-system-status reports its progress.
|
|
1379
|
+
- get-inspiration returns real App Store screenshot sets from shipped apps as images.
|
|
1377
1380
|
- get-ui-principle, get-ux-principle and the platform guides return design principles and best practices on a topic.
|
|
1378
1381
|
- get-review-rules, check-comprehension, inspect-spacing and review-dashboard-structure check finished interfaces.
|
|
1379
1382
|
- find-icon-library, find-icons and install-icons return icon component files.`;
|
|
@@ -1396,7 +1399,8 @@ Better Design provides design systems, design principles and UI review checks fo
|
|
|
1396
1399
|
"install-icons": "Fetches the named icons from the public Iconify API and returns one React component file per icon, plus an index.ts barrel, as text for the caller to save. It writes no files. Takes a `libraryId` and icon names from find-icons.",
|
|
1397
1400
|
"detect-icon-library": "Reads the contents of the `.library.json` file that an earlier icon install left in a project and returns the icon library the project uses and the icons it has installed, or reports that none is recorded.",
|
|
1398
1401
|
"find-design-system": "Searches Better Design's catalog of design systems by industry, personality or a description of the interface, and returns ranked matches with a preview link for each. Use it when the user wants a design system or a visual direction for an app, site, page, dashboard or presentation.",
|
|
1399
|
-
"get-design-system-kit": "Returns the build kit for a catalog design system or a generated project. `target` picks the format: `react` returns one shadcn install command for every component and the tokens, `html` returns framework-free token and component CSS for inline canvases, `react-native` and `swiftui` return mobile kits, `shopify` returns Liquid theme files,
|
|
1402
|
+
"get-design-system-kit": "Returns the build kit for a catalog design system or a generated project. `target` picks the format: `react` returns one shadcn install command for every component and the tokens, `html` returns framework-free token and component CSS for inline canvases, `react-native` and `swiftui` return mobile kits, `shopify` returns Liquid theme files, `dtcg` returns a W3C Design Tokens document, and `store-screenshots` returns slide files and a render script for App Store and Google Play screenshots. `component` narrows the React install to one component.",
|
|
1403
|
+
"get-inspiration": 'Returns real reference designs from Better Design\'s library as images. `type` picks the collection: today `app-store-screenshots`, the App Store screenshot sets of shipped apps, each with its title and category. `category`, such as "Productivity", narrows the sets, and `limit` sets how many come back, 1 to 6.',
|
|
1400
1404
|
"create-design-system": "Starts generating a custom design system from a description of the product and its look, and returns the project id and project URL at once. Generation runs in the background and typically takes 1 to 3 minutes. get-design-system-status reports progress and returns the install command when it finishes. `baseDesignSystemId` starts from a catalog system, and `brandCss` builds from an existing brand token sheet, such as the output of extract-design-system. Each call uses one credit from the user's Better Design account. Anyone with the registry or preview URL can view the generated source and previews.",
|
|
1401
1405
|
"get-design-system-status": "Reports the progress of a design system started with create-design-system. While generation runs it returns the current stage. When generation finishes it returns the live preview URL and the `npx shadcn add` install command.",
|
|
1402
1406
|
"preview-design-system": "Returns a visual preview of a catalog design system: its palette, typography, buttons, card, input and badge, with a link to the full live showcase. Takes an id from find-design-system.",
|
|
@@ -1891,6 +1895,7 @@ function buildKitOutputSchema(enableShopifyKit = true) {
|
|
|
1891
1895
|
componentExampleHtml: z5.string().min(1).optional().describe("HTML target only. Starter markup that demonstrates the required .ds wrapper and ds-* component classes."),
|
|
1892
1896
|
tokensJson: z5.string().min(1).optional().describe("DTCG target only. The design tokens as a W3C Design Tokens (DTCG) document. Write it to tokens.json; every token tool reads this format, including Style Dictionary, Tokens Studio, Terrazzo, and the Figma plugins that create variables."),
|
|
1893
1897
|
nativeFiles: z5.array(z5.object({ path: z5.string().min(1), content: z5.string().min(1) })).optional().describe("React Native and SwiftUI targets only. Write each file verbatim at its path: this system's stored theme and material, and the primitives that read it."),
|
|
1898
|
+
screenshotFiles: z5.array(z5.object({ path: z5.string().min(1), content: z5.string().min(1) })).optional().describe("Store screenshots target only. Write each file verbatim at its path, then follow buildInstructions to capture screens, write the slides and render the PNG files."),
|
|
1894
1899
|
unsupportedTokens: z5.array(z5.object({
|
|
1895
1900
|
name: z5.string().min(1),
|
|
1896
1901
|
value: z5.string().min(1),
|
|
@@ -10867,6 +10872,184 @@ ${FAQ_CARD_ITEM}
|
|
|
10867
10872
|
}
|
|
10868
10873
|
});
|
|
10869
10874
|
|
|
10875
|
+
// ../shared/src/mcp/store-screenshots.ts
|
|
10876
|
+
function renderScript() {
|
|
10877
|
+
const sizes = JSON.stringify(STORE_SCREENSHOT_SIZES);
|
|
10878
|
+
const feature = JSON.stringify(PLAY_FEATURE_GRAPHIC);
|
|
10879
|
+
return `// Renders store screenshots from slides.json at the exact store sizes. [Better Design]
|
|
10880
|
+
// Run once per change: node store-screenshots/render.mjs
|
|
10881
|
+
import { chromium } from "playwright";
|
|
10882
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
10883
|
+
import { dirname, join } from "node:path";
|
|
10884
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10885
|
+
|
|
10886
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
10887
|
+
const SIZES = ${sizes};
|
|
10888
|
+
const FEATURE = ${feature};
|
|
10889
|
+
const RANGE = ${JSON.stringify(STORE_SLIDE_RANGE)};
|
|
10890
|
+
const deck = JSON.parse(readFileSync(join(here, "slides.json"), "utf8"));
|
|
10891
|
+
|
|
10892
|
+
if (deck.slides.length < RANGE.min || deck.slides.length > RANGE.max) {
|
|
10893
|
+
throw new Error(\`slides.json holds \${deck.slides.length} slides. Use \${RANGE.min} to \${RANGE.max}: Google Play shows at most \${RANGE.max}.\`);
|
|
10894
|
+
}
|
|
10895
|
+
|
|
10896
|
+
// App Store Connect rejects a PNG with an alpha channel, so check the header, not the look.
|
|
10897
|
+
function check(file, size) {
|
|
10898
|
+
const png = readFileSync(file);
|
|
10899
|
+
const width = png.readUInt32BE(16), height = png.readUInt32BE(20), colorType = png[25];
|
|
10900
|
+
if (width !== size.width || height !== size.height) throw new Error(\`\${file} is \${width} x \${height}, not \${size.width} x \${size.height}.\`);
|
|
10901
|
+
if (colorType !== 2) throw new Error(\`\${file} has a transparency layer. Give every slide an opaque background.\`);
|
|
10902
|
+
}
|
|
10903
|
+
|
|
10904
|
+
async function shoot(browser, size, data, file) {
|
|
10905
|
+
const page = await browser.newPage({ viewport: { width: size.width, height: size.height }, deviceScaleFactor: 1 });
|
|
10906
|
+
await page.addInitScript((slide) => { window.__SLIDE__ = slide; }, data);
|
|
10907
|
+
await page.goto(pathToFileURL(join(here, "slide.html")).href);
|
|
10908
|
+
await page.evaluate(async () => {
|
|
10909
|
+
await document.fonts.ready;
|
|
10910
|
+
await Promise.all([...document.images].map((img) => img.decode().catch(() => { throw new Error("Missing capture: " + img.src); })));
|
|
10911
|
+
});
|
|
10912
|
+
await page.screenshot({ path: file, type: "png" });
|
|
10913
|
+
await page.close();
|
|
10914
|
+
check(file, size);
|
|
10915
|
+
}
|
|
10916
|
+
|
|
10917
|
+
const browser = await chromium.launch();
|
|
10918
|
+
try {
|
|
10919
|
+
for (const size of SIZES) {
|
|
10920
|
+
mkdirSync(join(here, "out", size.id), { recursive: true });
|
|
10921
|
+
for (const [i, slide] of deck.slides.entries()) {
|
|
10922
|
+
const file = join(here, "out", size.id, String(i + 1).padStart(2, "0") + ".png");
|
|
10923
|
+
await shoot(browser, size, { kind: "slide", app: deck.app, slide, frame: size.frame }, file);
|
|
10924
|
+
console.log("wrote", file);
|
|
10925
|
+
}
|
|
10926
|
+
}
|
|
10927
|
+
mkdirSync(join(here, "out", FEATURE.id), { recursive: true });
|
|
10928
|
+
const feature = join(here, "out", FEATURE.id, "feature-graphic.png");
|
|
10929
|
+
await shoot(browser, FEATURE, { kind: "feature", app: deck.app, slide: deck.featureGraphic, frame: "none" }, feature);
|
|
10930
|
+
console.log("wrote", feature);
|
|
10931
|
+
} finally {
|
|
10932
|
+
await browser.close();
|
|
10933
|
+
}
|
|
10934
|
+
`;
|
|
10935
|
+
}
|
|
10936
|
+
function buildStoreScreenshotFiles(tokensCss) {
|
|
10937
|
+
return [
|
|
10938
|
+
{ path: "store-screenshots/theme.css", content: tokensCss },
|
|
10939
|
+
{ path: "store-screenshots/slides.json", content: `${JSON.stringify(EXAMPLE_SLIDES, null, 2)}
|
|
10940
|
+
` },
|
|
10941
|
+
{ path: "store-screenshots/slide.html", content: SLIDE_HTML },
|
|
10942
|
+
{ path: "store-screenshots/render.mjs", content: renderScript() }
|
|
10943
|
+
];
|
|
10944
|
+
}
|
|
10945
|
+
function fencedFile(file) {
|
|
10946
|
+
const longest = Math.max(0, ...[...file.content.matchAll(/`+/g)].map((run) => run[0].length));
|
|
10947
|
+
const fence3 = "`".repeat(Math.max(3, longest + 1));
|
|
10948
|
+
return `## ${file.path}
|
|
10949
|
+
|
|
10950
|
+
${fence3}
|
|
10951
|
+
${file.content.replace(/\n$/, "")}
|
|
10952
|
+
${fence3}`;
|
|
10953
|
+
}
|
|
10954
|
+
function storeScreenshotText(name, files) {
|
|
10955
|
+
return [storeScreenshotInstructions(name), ...files.map(fencedFile)].join("\n\n");
|
|
10956
|
+
}
|
|
10957
|
+
function storeScreenshotInstructions(name) {
|
|
10958
|
+
const sizes = [...STORE_SCREENSHOT_SIZES, PLAY_FEATURE_GRAPHIC].map((size) => `- ${size.label}: ${size.width} x ${size.height}, in \`store-screenshots/out/${size.id}/\``).join("\n");
|
|
10959
|
+
return [
|
|
10960
|
+
`# Store screenshots in ${name}`,
|
|
10961
|
+
"",
|
|
10962
|
+
"1. Write each file in `screenshotFiles` verbatim at its path.",
|
|
10963
|
+
`2. Capture ${STORE_SLIDE_RANGE.min} to ${STORE_SLIDE_RANGE.max} app screens into \`store-screenshots/captures/\`. iOS Simulator: \`xcrun simctl io booted screenshot store-screenshots/captures/01.png\`. Android emulator: \`adb exec-out screencap -p > store-screenshots/captures/01.png\`.`,
|
|
10964
|
+
'3. Edit `slides.json`: one outcome per slide, a headline a person understands in one second, and no claims such as "Best", "#1" or "Free", which Google Play rejects. Check every headline with Better Design\'s `check-comprehension` tool before you render.',
|
|
10965
|
+
"4. Install Playwright once as a dev dependency with the project's package manager, then its browser: `npx playwright install chromium`.",
|
|
10966
|
+
"5. Google Play asks the feature graphic to avoid pure white, black and dark gray. When this system's primary color is one of those, set `--feature-background` and `--feature-foreground` to a brand color pair at the end of `theme.css`.",
|
|
10967
|
+
"6. Run `node store-screenshots/render.mjs`. It stops on any file with the wrong size or a transparency layer.",
|
|
10968
|
+
"",
|
|
10969
|
+
"It writes:",
|
|
10970
|
+
sizes,
|
|
10971
|
+
"",
|
|
10972
|
+
"Google Play screenshots carry no drawn phone, because Google asks for no device imagery. Show the user every PNG before they upload it."
|
|
10973
|
+
].join("\n");
|
|
10974
|
+
}
|
|
10975
|
+
var STORE_SCREENSHOT_SIZES, PLAY_FEATURE_GRAPHIC, STORE_SLIDE_RANGE, EXAMPLE_SLIDES, SLIDE_HTML;
|
|
10976
|
+
var init_store_screenshots = __esm({
|
|
10977
|
+
"../shared/src/mcp/store-screenshots.ts"() {
|
|
10978
|
+
"use strict";
|
|
10979
|
+
STORE_SCREENSHOT_SIZES = [
|
|
10980
|
+
{ id: "iphone-6-9", label: "iPhone 6.9 inch", width: 1320, height: 2868, frame: "phone" },
|
|
10981
|
+
{ id: "play-phone", label: "Google Play phone", width: 1080, height: 1920, frame: "none" }
|
|
10982
|
+
];
|
|
10983
|
+
PLAY_FEATURE_GRAPHIC = {
|
|
10984
|
+
id: "play-feature-graphic",
|
|
10985
|
+
label: "Google Play feature graphic",
|
|
10986
|
+
width: 1024,
|
|
10987
|
+
height: 500,
|
|
10988
|
+
frame: "none"
|
|
10989
|
+
};
|
|
10990
|
+
STORE_SLIDE_RANGE = { min: 3, max: 8 };
|
|
10991
|
+
EXAMPLE_SLIDES = {
|
|
10992
|
+
app: "Your app",
|
|
10993
|
+
slides: [
|
|
10994
|
+
{ headline: "Say the first outcome in five words", subhead: "", capture: "captures/01.png" },
|
|
10995
|
+
{ headline: "Show the second outcome next", subhead: "", capture: "captures/02.png" },
|
|
10996
|
+
{ headline: "End on the reason to stay", subhead: "", capture: "captures/03.png" }
|
|
10997
|
+
],
|
|
10998
|
+
featureGraphic: { headline: "Your app, in one line" }
|
|
10999
|
+
};
|
|
11000
|
+
SLIDE_HTML = `<!doctype html>
|
|
11001
|
+
<html lang="en">
|
|
11002
|
+
<head>
|
|
11003
|
+
<meta charset="utf-8">
|
|
11004
|
+
<link rel="stylesheet" href="theme.css">
|
|
11005
|
+
<style>
|
|
11006
|
+
html, body { margin: 0; width: 100vw; height: 100vh; overflow: hidden; }
|
|
11007
|
+
body { background: var(--background); color: var(--foreground); font-family: var(--font-sans, system-ui, sans-serif);
|
|
11008
|
+
display: flex; flex-direction: column; align-items: center; box-sizing: border-box; padding: 7vh 8vw 0; gap: 3vh; }
|
|
11009
|
+
h1 { margin: 0; font-size: 7.5vw; line-height: 1.12; font-weight: 700; text-align: center; text-wrap: balance; letter-spacing: -0.02em; }
|
|
11010
|
+
p { margin: 0; font-size: 4vw; line-height: 1.35; text-align: center; color: var(--muted-foreground); text-wrap: balance; }
|
|
11011
|
+
.shot { flex: 1; min-height: 0; width: 100%; display: flex; justify-content: center; align-items: flex-start; }
|
|
11012
|
+
.shot img { display: block; height: 100%; max-width: 100%; object-fit: cover; object-position: top; }
|
|
11013
|
+
.phone { height: 104%; aspect-ratio: 9 / 19.5; box-sizing: border-box; padding: 1.6vw; border-radius: 11vw 11vw 0 0;
|
|
11014
|
+
background: var(--foreground); overflow: hidden; }
|
|
11015
|
+
.phone img { width: 100%; height: 100%; border-radius: 9.4vw 9.4vw 0 0; }
|
|
11016
|
+
.bare img { border-radius: calc(var(--radius, 0.75rem) * 3) calc(var(--radius, 0.75rem) * 3) 0 0; }
|
|
11017
|
+
body.feature { background: var(--feature-background, var(--primary)); color: var(--feature-foreground, var(--primary-foreground)); justify-content: center; align-items: flex-start; padding: 0 10vw; gap: 4vh; }
|
|
11018
|
+
body.feature h1 { font-size: 7vw; text-align: left; }
|
|
11019
|
+
body.feature .app { font-size: 3.4vw; font-weight: 600; opacity: 0.85; }
|
|
11020
|
+
</style>
|
|
11021
|
+
</head>
|
|
11022
|
+
<body>
|
|
11023
|
+
<script>
|
|
11024
|
+
// The render script sets window.__SLIDE__ before this page loads.
|
|
11025
|
+
const { kind, app, slide, frame } = window.__SLIDE__;
|
|
11026
|
+
const add = (tag, className, text) => {
|
|
11027
|
+
const el = document.createElement(tag);
|
|
11028
|
+
if (className) el.className = className;
|
|
11029
|
+
if (text) el.textContent = text;
|
|
11030
|
+
return document.body.appendChild(el);
|
|
11031
|
+
};
|
|
11032
|
+
if (kind === "feature") {
|
|
11033
|
+
document.body.classList.add("feature");
|
|
11034
|
+
add("div", "app", app);
|
|
11035
|
+
add("h1", "", slide.headline);
|
|
11036
|
+
} else {
|
|
11037
|
+
add("h1", "", slide.headline);
|
|
11038
|
+
if (slide.subhead) add("p", "", slide.subhead);
|
|
11039
|
+
const shot = add("div", "shot " + (frame === "phone" ? "" : "bare"));
|
|
11040
|
+
const holder = frame === "phone" ? shot.appendChild(document.createElement("div")) : shot;
|
|
11041
|
+
if (frame === "phone") holder.className = "phone";
|
|
11042
|
+
const img = holder.appendChild(document.createElement("img"));
|
|
11043
|
+
img.src = slide.capture;
|
|
11044
|
+
img.alt = "";
|
|
11045
|
+
}
|
|
11046
|
+
</script>
|
|
11047
|
+
</body>
|
|
11048
|
+
</html>
|
|
11049
|
+
`;
|
|
11050
|
+
}
|
|
11051
|
+
});
|
|
11052
|
+
|
|
10870
11053
|
// ../shared/src/registry-css.ts
|
|
10871
11054
|
function parseCssVars(css) {
|
|
10872
11055
|
const out = { theme: {}, light: {}, dark: {} };
|
|
@@ -13892,36 +14075,38 @@ ${fence3}` }],
|
|
|
13892
14075
|
)
|
|
13893
14076
|
}, icons);
|
|
13894
14077
|
});
|
|
14078
|
+
const loadTokenSource = async ({ designSystemId, projectId }) => {
|
|
14079
|
+
if (projectId) {
|
|
14080
|
+
const result = await fetchProjectTokensCss(projectId, apiFetch, authContext);
|
|
14081
|
+
return {
|
|
14082
|
+
tokensCss: result?.tokensCss,
|
|
14083
|
+
name: result?.name ?? `Project ${projectId.slice(0, 8)}`,
|
|
14084
|
+
viewUrl: `${APP_BASE_URL}/projects/${projectId}`,
|
|
14085
|
+
icons: result?.iconLibrary
|
|
14086
|
+
};
|
|
14087
|
+
}
|
|
14088
|
+
if (!designSystemId) return null;
|
|
14089
|
+
const ds = await provider.getDesignSystemById(designSystemId, { includeComponents: false, includeHtml: false });
|
|
14090
|
+
return {
|
|
14091
|
+
tokensCss: await fetchCatalogTokensCss(designSystemId),
|
|
14092
|
+
name: ds?.metadata.title ?? designSystemId,
|
|
14093
|
+
viewUrl: `${APP_BASE_URL}/design-systems/${designSystemId}`,
|
|
14094
|
+
icons: ds ? await catalogKitIcons(ds.metadata, provider) : void 0
|
|
14095
|
+
};
|
|
14096
|
+
};
|
|
13895
14097
|
const getDtcgKit = async ({
|
|
13896
14098
|
designSystemId,
|
|
13897
14099
|
projectId,
|
|
13898
14100
|
context
|
|
13899
14101
|
}) => tracked("get-design-system-kit", { target: "dtcg", designSystemId, projectId, context }, async () => {
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
let viewUrl;
|
|
13903
|
-
let icons;
|
|
13904
|
-
if (projectId) {
|
|
13905
|
-
viewUrl = `${APP_BASE_URL}/projects/${projectId}`;
|
|
13906
|
-
const result = await fetchProjectTokensCss(projectId, apiFetch, authContext);
|
|
13907
|
-
tokensCss = result?.tokensCss;
|
|
13908
|
-
icons = result?.iconLibrary;
|
|
13909
|
-
name = result?.name ?? `Project ${projectId.slice(0, 8)}`;
|
|
13910
|
-
} else if (designSystemId) {
|
|
13911
|
-
viewUrl = `${APP_BASE_URL}/design-systems/${designSystemId}`;
|
|
13912
|
-
const ds = await provider.getDesignSystemById(designSystemId, {
|
|
13913
|
-
includeComponents: false,
|
|
13914
|
-
includeHtml: false
|
|
13915
|
-
});
|
|
13916
|
-
tokensCss = await fetchCatalogTokensCss(designSystemId);
|
|
13917
|
-
name = ds?.metadata.title ?? designSystemId;
|
|
13918
|
-
icons = ds ? await catalogKitIcons(ds.metadata, provider) : void 0;
|
|
13919
|
-
} else {
|
|
14102
|
+
const source = await loadTokenSource({ designSystemId, projectId });
|
|
14103
|
+
if (!source) {
|
|
13920
14104
|
return {
|
|
13921
14105
|
content: [{ type: "text", text: "For target dtcg, pass either a catalog designSystemId (from find-design-system) or a projectId (from create-design-system)." }],
|
|
13922
14106
|
structuredContent: { status: "error", name: "Missing design system", tokensCss: "" }
|
|
13923
14107
|
};
|
|
13924
14108
|
}
|
|
14109
|
+
const { tokensCss, name, viewUrl, icons } = source;
|
|
13925
14110
|
if (!tokensCss) {
|
|
13926
14111
|
return {
|
|
13927
14112
|
content: [{ type: "text", text: `Could not load tokens for "${projectId ?? designSystemId}". A generated project may still be building. Check ${viewUrl}.` }],
|
|
@@ -13947,6 +14132,33 @@ ${fence3}` }],
|
|
|
13947
14132
|
structuredContent: { status: "completed", name, viewUrl, tokensCss, tokensJson }
|
|
13948
14133
|
}, icons);
|
|
13949
14134
|
});
|
|
14135
|
+
const getStoreScreenshots = async ({
|
|
14136
|
+
designSystemId,
|
|
14137
|
+
projectId,
|
|
14138
|
+
context
|
|
14139
|
+
}) => tracked("get-design-system-kit", { target: "store-screenshots", designSystemId, projectId, context }, async () => {
|
|
14140
|
+
const source = await loadTokenSource({ designSystemId, projectId });
|
|
14141
|
+
if (!source?.tokensCss) {
|
|
14142
|
+
const reason = source ? `Could not load tokens for "${projectId ?? designSystemId}". A generated project may still be building. Check ${source.viewUrl}.` : "For target store-screenshots, pass either a catalog designSystemId (from find-design-system) or a projectId (from create-design-system).";
|
|
14143
|
+
return {
|
|
14144
|
+
content: [{ type: "text", text: reason }],
|
|
14145
|
+
structuredContent: { status: "error", name: source?.name ?? "Missing design system", tokensCss: "" }
|
|
14146
|
+
};
|
|
14147
|
+
}
|
|
14148
|
+
const files = buildStoreScreenshotFiles(source.tokensCss);
|
|
14149
|
+
const instructions = storeScreenshotInstructions(source.name);
|
|
14150
|
+
return {
|
|
14151
|
+
content: [{ type: "text", text: storeScreenshotText(source.name, files) }],
|
|
14152
|
+
structuredContent: {
|
|
14153
|
+
status: "completed",
|
|
14154
|
+
name: source.name,
|
|
14155
|
+
viewUrl: source.viewUrl,
|
|
14156
|
+
tokensCss: source.tokensCss,
|
|
14157
|
+
buildInstructions: instructions,
|
|
14158
|
+
screenshotFiles: files
|
|
14159
|
+
}
|
|
14160
|
+
};
|
|
14161
|
+
});
|
|
13950
14162
|
const fetchProjectMobilePrimitives = async (projectId) => {
|
|
13951
14163
|
try {
|
|
13952
14164
|
const res = await apiFetch(`${APP_BASE_URL}/api/projects/${projectId}/mobile/all.json`, { headers: authHeaders(authContext) });
|
|
@@ -14162,9 +14374,10 @@ ${BUILD_TARGET_RULE}`,
|
|
|
14162
14374
|
"html",
|
|
14163
14375
|
...enableShopifyKit ? ["shopify"] : [],
|
|
14164
14376
|
"dtcg",
|
|
14377
|
+
"store-screenshots",
|
|
14165
14378
|
...enableMobileKits ? ["react-native", "swiftui"] : []
|
|
14166
14379
|
]
|
|
14167
|
-
).describe(`Required build target. Use "react" for any host with filesystem + terminal; use "html" only for inline chat canvases/artifacts.${enableShopifyKit ? ' Use "shopify" for Shopify stores and themes.' : ""} Use "dtcg" to hand the tokens to a design tool: it returns a W3C Design Tokens document for Style Dictionary, Tokens Studio, Terrazzo, or a Figma plugin that creates variables. ${enableMobileKits ? ` Use "react-native" for a React Native or Expo app and "swiftui" for an iOS app: each returns this system's stored mobile kit, its own material on native primitives, plus the handoff to the matching guide.` : ""}`),
|
|
14380
|
+
).describe(`Required build target. Use "react" for any host with filesystem + terminal; use "html" only for inline chat canvases/artifacts.${enableShopifyKit ? ' Use "shopify" for Shopify stores and themes.' : ""} Use "dtcg" to hand the tokens to a design tool: it returns a W3C Design Tokens document for Style Dictionary, Tokens Studio, Terrazzo, or a Figma plugin that creates variables. Use "store-screenshots" for App Store and Google Play screenshots in this system: it returns slide files and a render script that writes PNG files at the store sizes. ${enableMobileKits ? ` Use "react-native" for a React Native or Expo app and "swiftui" for an iOS app: each returns this system's stored mobile kit, its own material on native primitives, plus the handoff to the matching guide.` : ""}`),
|
|
14168
14381
|
designSystemId: z14.string().min(1).optional().describe(`Catalog design system ID from find-design-system. Required for target react; for every other target pass this or projectId.`),
|
|
14169
14382
|
projectId: z14.string().min(1).optional().describe(`Generated project ID from create-design-system / get-design-system-status. Supported with every target but react.`),
|
|
14170
14383
|
component: z14.union([z14.string(), z14.array(z14.string())]).optional().describe("React target only. Pass only when the user asks to inspect specific component source code. Omit to scaffold the entire design system."),
|
|
@@ -14180,7 +14393,7 @@ ${BUILD_TARGET_RULE}`,
|
|
|
14180
14393
|
// interactive preview path. [BD-240]
|
|
14181
14394
|
},
|
|
14182
14395
|
async ({ target, designSystemId, projectId, component, library, context }) => {
|
|
14183
|
-
if (target !== "react" && target !== "html" && target !== "shopify" && target !== "dtcg" && target !== "react-native" && target !== "swiftui") {
|
|
14396
|
+
if (target !== "react" && target !== "html" && target !== "shopify" && target !== "dtcg" && target !== "store-screenshots" && target !== "react-native" && target !== "swiftui") {
|
|
14184
14397
|
return {
|
|
14185
14398
|
content: [{ type: "text", text: `Unknown target "${target}".` }],
|
|
14186
14399
|
structuredContent: { status: "error", name: "Unknown target", tokensCss: "" }
|
|
@@ -14192,6 +14405,9 @@ ${BUILD_TARGET_RULE}`,
|
|
|
14192
14405
|
if (target === "dtcg") {
|
|
14193
14406
|
return withRedesignContract(await getDtcgKit({ designSystemId, projectId, context }));
|
|
14194
14407
|
}
|
|
14408
|
+
if (target === "store-screenshots") {
|
|
14409
|
+
return getStoreScreenshots({ designSystemId, projectId, context });
|
|
14410
|
+
}
|
|
14195
14411
|
if (target === "react-native" || target === "swiftui") {
|
|
14196
14412
|
if (!enableMobileKits) {
|
|
14197
14413
|
return {
|
|
@@ -14687,6 +14903,7 @@ var init_design_system_tools = __esm({
|
|
|
14687
14903
|
init_shopify_kit();
|
|
14688
14904
|
init_redesign_contract();
|
|
14689
14905
|
init_selected_icon_handoff();
|
|
14906
|
+
init_store_screenshots();
|
|
14690
14907
|
init_shopify_primitives();
|
|
14691
14908
|
init_preview_html();
|
|
14692
14909
|
init_shared();
|
|
@@ -22772,7 +22989,7 @@ function columnToSchema(column, factory) {
|
|
|
22772
22989
|
}
|
|
22773
22990
|
return schema;
|
|
22774
22991
|
}
|
|
22775
|
-
function numberColumnToSchema(column,
|
|
22992
|
+
function numberColumnToSchema(column, z37, coerce) {
|
|
22776
22993
|
let unsigned = column.getSQLType().includes("unsigned");
|
|
22777
22994
|
let min;
|
|
22778
22995
|
let max;
|
|
@@ -22840,20 +23057,20 @@ function numberColumnToSchema(column, z35, coerce) {
|
|
|
22840
23057
|
min = Number.MIN_SAFE_INTEGER;
|
|
22841
23058
|
max = Number.MAX_SAFE_INTEGER;
|
|
22842
23059
|
}
|
|
22843
|
-
let schema = coerce === true || coerce?.number ?
|
|
23060
|
+
let schema = coerce === true || coerce?.number ? z37.coerce.number() : z37.number();
|
|
22844
23061
|
schema = schema.min(min).max(max);
|
|
22845
23062
|
return integer2 ? schema.int() : schema;
|
|
22846
23063
|
}
|
|
22847
|
-
function bigintColumnToSchema(column,
|
|
23064
|
+
function bigintColumnToSchema(column, z37, coerce) {
|
|
22848
23065
|
const unsigned = column.getSQLType().includes("unsigned");
|
|
22849
23066
|
const min = unsigned ? 0n : CONSTANTS.INT64_MIN;
|
|
22850
23067
|
const max = unsigned ? CONSTANTS.INT64_UNSIGNED_MAX : CONSTANTS.INT64_MAX;
|
|
22851
|
-
const schema = coerce === true || coerce?.bigint ?
|
|
23068
|
+
const schema = coerce === true || coerce?.bigint ? z37.coerce.bigint() : z37.bigint();
|
|
22852
23069
|
return schema.min(min).max(max);
|
|
22853
23070
|
}
|
|
22854
|
-
function stringColumnToSchema(column,
|
|
23071
|
+
function stringColumnToSchema(column, z37, coerce) {
|
|
22855
23072
|
if (isColumnType(column, ["PgUUID"])) {
|
|
22856
|
-
return
|
|
23073
|
+
return z37.string().uuid();
|
|
22857
23074
|
}
|
|
22858
23075
|
let max;
|
|
22859
23076
|
let regex;
|
|
@@ -22885,7 +23102,7 @@ function stringColumnToSchema(column, z35, coerce) {
|
|
|
22885
23102
|
regex = /^[01]+$/;
|
|
22886
23103
|
max = column.dimensions;
|
|
22887
23104
|
}
|
|
22888
|
-
let schema = coerce === true || coerce?.string ?
|
|
23105
|
+
let schema = coerce === true || coerce?.string ? z37.coerce.string() : z37.string();
|
|
22889
23106
|
schema = regex ? schema.regex(regex) : schema;
|
|
22890
23107
|
return max && fixed ? schema.length(max) : max ? schema.max(max) : schema;
|
|
22891
23108
|
}
|
|
@@ -22983,7 +23200,7 @@ var init_drizzle_zod = __esm({
|
|
|
22983
23200
|
|
|
22984
23201
|
// ../shared/src/schema.ts
|
|
22985
23202
|
import { z as z16 } from "zod";
|
|
22986
|
-
var vector2, messageRoleEnum, messageTypeEnum, designSystemStatusEnum, onboardingPathValues, onboardingPathSchema, onboardingPathSqlValues, mcpIssueCategoryValues, mcpIssueStatusValues, mcpIssueImprovementStatusValues, mcpIssueImprovementVerdictValues, mcpIssueImprovementStatusSchema, mcpIssueImprovementVerdictSchema, mcpIssueImprovementStatusSqlValues, mcpIssueImprovementVerdictSqlValues, designConfigSchema, projectMemoryDataSchema, projects, messages, projectMemory, fragments, usage, scrapeBlock, resourceAccess, user, signupAlertDeliveries, billingAlertDeliveries, TEAM_RULES_MAX_BYTES, teamRules, billingSubscription, githubInstallationClaim, githubInstallation, featureAccess, session, account, verification, jwks, oauthClient, oauthRefreshToken, oauthAccessToken, oauthConsent, designSystems, designSystemComponents, foundationalDocs, apiKeys, emailCampaignSends, emailCampaignSteps, projectEvents, iconLibraries, dsSearchQueries, mcpToolCalls, mcpClientConnections, evalVerdicts, generationInputs, mcpIssues, mcpIssueImprovements, projectsRelations, projectMemoryRelations, mcpIssuesRelations, mcpIssueImprovementsRelations, messagesRelations, fragmentsRelations, designSystemsRelations, designSystemComponentsRelations, insertProjectSchema, selectProjectSchema, insertMessageSchema, selectMessageSchema, insertFragmentSchema, selectFragmentSchema, insertProjectMemorySchema, selectProjectMemorySchema, insertDesignSystemSchema, selectDesignSystemSchema, insertDesignSystemComponentSchema, selectDesignSystemComponentSchema, insertFoundationalDocSchema, selectFoundationalDocSchema, insertIconLibrarySchema, selectIconLibrarySchema, insertApiKeySchema, selectApiKeySchema, insertUserSchema, selectUserSchema, insertSignupAlertDeliverySchema, selectSignupAlertDeliverySchema, insertEmailCampaignSendSchema, selectEmailCampaignSendSchema, insertEmailCampaignStepSchema, selectEmailCampaignStepSchema, insertMcpIssueImprovementSchema, selectMcpIssueImprovementSchema, pluginAuthNonces;
|
|
23203
|
+
var vector2, messageRoleEnum, messageTypeEnum, designSystemStatusEnum, onboardingPathValues, onboardingPathSchema, onboardingPathSqlValues, mcpIssueCategoryValues, mcpIssueStatusValues, mcpIssueImprovementStatusValues, mcpIssueImprovementVerdictValues, mcpIssueImprovementStatusSchema, mcpIssueImprovementVerdictSchema, mcpIssueImprovementStatusSqlValues, mcpIssueImprovementVerdictSqlValues, designConfigSchema, projectMemoryDataSchema, projects, messages, projectMemory, fragments, usage, scrapeBlock, resourceAccess, user, signupAlertDeliveries, billingAlertDeliveries, billingEmailDeliveries, TEAM_RULES_MAX_BYTES, teamRules, billingSubscription, githubInstallationClaim, githubInstallation, featureAccess, session, account, verification, jwks, oauthClient, oauthRefreshToken, oauthAccessToken, oauthConsent, designSystems, designSystemComponents, foundationalDocs, apiKeys, emailCampaignSends, emailCampaignSteps, projectEvents, iconLibraries, dsSearchQueries, mcpToolCalls, mcpClientConnections, evalVerdicts, generationInputs, mcpIssues, mcpIssueImprovements, projectsRelations, projectMemoryRelations, mcpIssuesRelations, mcpIssueImprovementsRelations, messagesRelations, fragmentsRelations, designSystemsRelations, designSystemComponentsRelations, insertProjectSchema, selectProjectSchema, insertMessageSchema, selectMessageSchema, insertFragmentSchema, selectFragmentSchema, insertProjectMemorySchema, selectProjectMemorySchema, insertDesignSystemSchema, selectDesignSystemSchema, insertDesignSystemComponentSchema, selectDesignSystemComponentSchema, insertFoundationalDocSchema, selectFoundationalDocSchema, insertIconLibrarySchema, selectIconLibrarySchema, insertApiKeySchema, selectApiKeySchema, insertUserSchema, selectUserSchema, insertSignupAlertDeliverySchema, selectSignupAlertDeliverySchema, insertEmailCampaignSendSchema, selectEmailCampaignSendSchema, insertEmailCampaignStepSchema, selectEmailCampaignStepSchema, insertMcpIssueImprovementSchema, selectMcpIssueImprovementSchema, pluginAuthNonces;
|
|
22987
23204
|
var init_schema2 = __esm({
|
|
22988
23205
|
"../shared/src/schema.ts"() {
|
|
22989
23206
|
"use strict";
|
|
@@ -23314,6 +23531,40 @@ var init_schema2 = __esm({
|
|
|
23314
23531
|
)
|
|
23315
23532
|
]
|
|
23316
23533
|
);
|
|
23534
|
+
billingEmailDeliveries = pgTable(
|
|
23535
|
+
"billing_email_deliveries",
|
|
23536
|
+
{
|
|
23537
|
+
id: text("id").primaryKey(),
|
|
23538
|
+
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
23539
|
+
toEmail: text("to_email").notNull(),
|
|
23540
|
+
/** The Stripe subscription the email is about, so the daily cap can count per subscription. */
|
|
23541
|
+
stripeSubscriptionId: text("stripe_subscription_id").notNull(),
|
|
23542
|
+
email: jsonb("email").$type().notNull(),
|
|
23543
|
+
occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
|
|
23544
|
+
status: text("status").$type().default("pending").notNull(),
|
|
23545
|
+
attemptCount: integer("attempt_count").default(0).notNull(),
|
|
23546
|
+
nextAttemptAt: timestamp("next_attempt_at", { withTimezone: true }).defaultNow().notNull(),
|
|
23547
|
+
leaseUntil: timestamp("lease_until", { withTimezone: true }),
|
|
23548
|
+
leaseToken: uuid("lease_token"),
|
|
23549
|
+
deliveredAt: timestamp("delivered_at", { withTimezone: true }),
|
|
23550
|
+
resendEmailId: text("resend_email_id"),
|
|
23551
|
+
lastError: text("last_error"),
|
|
23552
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
|
23553
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
|
|
23554
|
+
},
|
|
23555
|
+
(t) => [
|
|
23556
|
+
index("billing_email_deliveries_due_idx").on(t.status, t.nextAttemptAt, t.leaseUntil),
|
|
23557
|
+
index("billing_email_deliveries_subscription_idx").on(t.stripeSubscriptionId, t.createdAt),
|
|
23558
|
+
check(
|
|
23559
|
+
"billing_email_deliveries_status_valid",
|
|
23560
|
+
sql`${t.status} in ('pending', 'processing', 'delivered', 'failed')`
|
|
23561
|
+
),
|
|
23562
|
+
check(
|
|
23563
|
+
"billing_email_deliveries_processing_lease_valid",
|
|
23564
|
+
sql`${t.status} <> 'processing' or (${t.leaseUntil} is not null and ${t.leaseToken} is not null)`
|
|
23565
|
+
)
|
|
23566
|
+
]
|
|
23567
|
+
);
|
|
23317
23568
|
TEAM_RULES_MAX_BYTES = 32 * 1024;
|
|
23318
23569
|
teamRules = pgTable(
|
|
23319
23570
|
"team_rules",
|
|
@@ -28887,8 +29138,131 @@ var init_feature_map_tools = __esm({
|
|
|
28887
29138
|
}
|
|
28888
29139
|
});
|
|
28889
29140
|
|
|
29141
|
+
// ../shared/src/mcp/inspiration.ts
|
|
29142
|
+
import { z as z28 } from "zod";
|
|
29143
|
+
function describeSet(set) {
|
|
29144
|
+
const heading = `## ${set.title}${set.category ? ` (${set.category})` : ""}`;
|
|
29145
|
+
if (!set.notes) return heading;
|
|
29146
|
+
const written = /* @__PURE__ */ new Date(`${set.notes.writtenAt}T00:00:00Z`);
|
|
29147
|
+
const date2 = Number.isNaN(written.getTime()) ? set.notes.writtenAt : NOTE_DATE.format(written);
|
|
29148
|
+
return [
|
|
29149
|
+
heading,
|
|
29150
|
+
`Headlines: ${set.notes.headline}`,
|
|
29151
|
+
`Slide order: ${set.notes.order}`,
|
|
29152
|
+
`Framing: ${set.notes.framing}`,
|
|
29153
|
+
`Color: ${set.notes.color}`,
|
|
29154
|
+
`Better Design's notes, written by a vision model from these screenshots on ${date2}. The listing may have changed since.`
|
|
29155
|
+
].join("\n");
|
|
29156
|
+
}
|
|
29157
|
+
var INSPIRATION_TYPES, MAX_INSPIRATION_LIMIT, noteSchema, inspirationNotesFileSchema, indexFileSchema, indexEntrySchema, inspirationIndexSchema, NOTE_DATE;
|
|
29158
|
+
var init_inspiration = __esm({
|
|
29159
|
+
"../shared/src/mcp/inspiration.ts"() {
|
|
29160
|
+
"use strict";
|
|
29161
|
+
INSPIRATION_TYPES = ["app-store-screenshots"];
|
|
29162
|
+
MAX_INSPIRATION_LIMIT = 6;
|
|
29163
|
+
noteSchema = z28.object({
|
|
29164
|
+
slug: z28.string(),
|
|
29165
|
+
/** The day the note was written, as YYYY-MM-DD. A listing can change after it. */
|
|
29166
|
+
writtenAt: z28.string(),
|
|
29167
|
+
headline: z28.string(),
|
|
29168
|
+
order: z28.string(),
|
|
29169
|
+
framing: z28.string(),
|
|
29170
|
+
color: z28.string()
|
|
29171
|
+
});
|
|
29172
|
+
inspirationNotesFileSchema = z28.object({
|
|
29173
|
+
model: z28.string(),
|
|
29174
|
+
notes: z28.array(noteSchema)
|
|
29175
|
+
});
|
|
29176
|
+
indexFileSchema = z28.object({
|
|
29177
|
+
key: z28.string(),
|
|
29178
|
+
type: z28.string(),
|
|
29179
|
+
width: z28.number(),
|
|
29180
|
+
height: z28.number()
|
|
29181
|
+
});
|
|
29182
|
+
indexEntrySchema = z28.object({
|
|
29183
|
+
title: z28.string(),
|
|
29184
|
+
slug: z28.string(),
|
|
29185
|
+
category: z28.string().nullish(),
|
|
29186
|
+
files: z28.array(indexFileSchema)
|
|
29187
|
+
});
|
|
29188
|
+
inspirationIndexSchema = z28.object({
|
|
29189
|
+
entries: z28.array(indexEntrySchema)
|
|
29190
|
+
});
|
|
29191
|
+
NOTE_DATE = new Intl.DateTimeFormat("en-GB", { day: "numeric", month: "long", year: "numeric", timeZone: "UTC" });
|
|
29192
|
+
}
|
|
29193
|
+
});
|
|
29194
|
+
|
|
29195
|
+
// ../shared/src/mcp/tools/inspiration-tools.ts
|
|
29196
|
+
import { z as z29 } from "zod";
|
|
29197
|
+
function describeSets(result) {
|
|
29198
|
+
const lines = result.sets.map((set, i) => {
|
|
29199
|
+
const category = set.category ? `, ${set.category}` : "";
|
|
29200
|
+
return `${i + 1}. ${set.title}${category}: ${set.images.length} screenshot${set.images.length === 1 ? "" : "s"} below`;
|
|
29201
|
+
});
|
|
29202
|
+
return [
|
|
29203
|
+
`# App Store screenshot sets`,
|
|
29204
|
+
"",
|
|
29205
|
+
...lines,
|
|
29206
|
+
"",
|
|
29207
|
+
"Each image follows the set it belongs to. Read each set for its headline style, slide order, device framing and color, then propose two or three directions for the user's own screenshots and let the user pick one."
|
|
29208
|
+
].join("\n");
|
|
29209
|
+
}
|
|
29210
|
+
function noMatch(result, category) {
|
|
29211
|
+
const asked = category ? `No App Store screenshot sets match the category "${category}".` : "The library holds no App Store screenshot sets yet.";
|
|
29212
|
+
return `${asked} Categories in the library: ${result.categories.join(", ")}.`;
|
|
29213
|
+
}
|
|
29214
|
+
function registerInspirationTools({ server, tracked }, readInspiration2) {
|
|
29215
|
+
server.registerTool(
|
|
29216
|
+
"get-inspiration",
|
|
29217
|
+
{
|
|
29218
|
+
title: "Get Inspiration",
|
|
29219
|
+
description: `Send real reference designs from Better Design's library as images, so you can study how shipped products solved the same design problem before you design the user's own.
|
|
29220
|
+
|
|
29221
|
+
Use it before designing App Store or Google Play screenshots: pass \`type: "app-store-screenshots"\`, and a \`category\` such as "Health & Fitness" when the user's app has one. Each set arrives as its title and category, followed by its first screenshots.
|
|
29222
|
+
|
|
29223
|
+
Then propose two or three directions drawn from what the sets do (headline style, slide order, device framing, color), and let the user choose before you build anything.`,
|
|
29224
|
+
inputSchema: z29.object({
|
|
29225
|
+
type: z29.enum(INSPIRATION_TYPES).describe('The kind of reference to send. Today: "app-store-screenshots".'),
|
|
29226
|
+
category: z29.string().trim().min(1).max(60).optional().describe('Optional App Store category, for example "Productivity" or "Health & Fitness". Ignores case.'),
|
|
29227
|
+
limit: z29.number().int().min(1).max(MAX_INSPIRATION_LIMIT).optional().describe(`How many sets to send, 1 to ${MAX_INSPIRATION_LIMIT}. Defaults to 3.`),
|
|
29228
|
+
context: CONTEXT_ARG
|
|
29229
|
+
}),
|
|
29230
|
+
annotations: READ_ONLY_TOOL_ANNOTATIONS
|
|
29231
|
+
},
|
|
29232
|
+
async ({ type, category, limit, context }) => tracked("get-inspiration", { type, category: category ?? null, limit: limit ?? null, context }, async () => {
|
|
29233
|
+
if (!readInspiration2) return { content: [{ type: "text", text: NOT_AVAILABLE }] };
|
|
29234
|
+
const result = await readInspiration2({ type, category, limit });
|
|
29235
|
+
if (result.sets.length === 0) {
|
|
29236
|
+
return { content: [{ type: "text", text: noMatch(result, category) }] };
|
|
29237
|
+
}
|
|
29238
|
+
return {
|
|
29239
|
+
content: [
|
|
29240
|
+
{ type: "text", text: describeSets(result) },
|
|
29241
|
+
...result.sets.flatMap((set) => [
|
|
29242
|
+
{ type: "text", text: describeSet(set) },
|
|
29243
|
+
...set.images.map((image) => ({
|
|
29244
|
+
type: "image",
|
|
29245
|
+
data: image.data,
|
|
29246
|
+
mimeType: image.mimeType
|
|
29247
|
+
}))
|
|
29248
|
+
])
|
|
29249
|
+
]
|
|
29250
|
+
};
|
|
29251
|
+
})
|
|
29252
|
+
);
|
|
29253
|
+
}
|
|
29254
|
+
var NOT_AVAILABLE;
|
|
29255
|
+
var init_inspiration_tools = __esm({
|
|
29256
|
+
"../shared/src/mcp/tools/inspiration-tools.ts"() {
|
|
29257
|
+
"use strict";
|
|
29258
|
+
init_shared();
|
|
29259
|
+
init_inspiration();
|
|
29260
|
+
NOT_AVAILABLE = "The inspiration library is not available from this server. Try again with the hosted Better Design MCP server or the latest `better-design` npm package.";
|
|
29261
|
+
}
|
|
29262
|
+
});
|
|
29263
|
+
|
|
28890
29264
|
// ../shared/src/mcp/tools.ts
|
|
28891
|
-
function registerAllTools(server, provider, onToolUsage, authContext, options = {}, apiFetch = globalThis.fetch, userId, captureDashboardBrowser2) {
|
|
29265
|
+
function registerAllTools(server, provider, onToolUsage, authContext, options = {}, apiFetch = globalThis.fetch, userId, captureDashboardBrowser2, readInspiration2) {
|
|
28892
29266
|
const { track, tracked } = createTracking(server, onToolUsage);
|
|
28893
29267
|
const deps = {
|
|
28894
29268
|
server,
|
|
@@ -28914,6 +29288,7 @@ function registerAllTools(server, provider, onToolUsage, authContext, options =
|
|
|
28914
29288
|
registerInspectionTools(deps);
|
|
28915
29289
|
registerFeatureMapTools(deps);
|
|
28916
29290
|
registerTeamRulesTools(deps);
|
|
29291
|
+
registerInspirationTools(deps, readInspiration2);
|
|
28917
29292
|
registerPreviewResource(deps);
|
|
28918
29293
|
registerInspectionResource(deps);
|
|
28919
29294
|
}
|
|
@@ -28937,6 +29312,7 @@ var init_tools = __esm({
|
|
|
28937
29312
|
init_widget_tools();
|
|
28938
29313
|
init_feature_map_tools();
|
|
28939
29314
|
init_team_rules_tools();
|
|
29315
|
+
init_inspiration_tools();
|
|
28940
29316
|
init_shared();
|
|
28941
29317
|
}
|
|
28942
29318
|
});
|
|
@@ -28946,7 +29322,7 @@ var MCP_IMPLEMENTATION_VERSION;
|
|
|
28946
29322
|
var init_version2 = __esm({
|
|
28947
29323
|
"../shared/src/mcp/version.ts"() {
|
|
28948
29324
|
"use strict";
|
|
28949
|
-
MCP_IMPLEMENTATION_VERSION = "0.
|
|
29325
|
+
MCP_IMPLEMENTATION_VERSION = "0.33.0";
|
|
28950
29326
|
}
|
|
28951
29327
|
});
|
|
28952
29328
|
|
|
@@ -29011,7 +29387,8 @@ function createMcpServer(options) {
|
|
|
29011
29387
|
apiFetch = globalThis.fetch,
|
|
29012
29388
|
userId,
|
|
29013
29389
|
captureDashboardBrowser: captureDashboardBrowser2,
|
|
29014
|
-
directoryListing = false
|
|
29390
|
+
directoryListing = false,
|
|
29391
|
+
readInspiration: readInspiration2
|
|
29015
29392
|
} = options;
|
|
29016
29393
|
const server = new McpServer(
|
|
29017
29394
|
{ name, version: version2 },
|
|
@@ -29038,7 +29415,8 @@ function createMcpServer(options) {
|
|
|
29038
29415
|
},
|
|
29039
29416
|
apiFetch,
|
|
29040
29417
|
userId,
|
|
29041
|
-
captureDashboardBrowser2
|
|
29418
|
+
captureDashboardBrowser2,
|
|
29419
|
+
readInspiration2
|
|
29042
29420
|
);
|
|
29043
29421
|
return server;
|
|
29044
29422
|
}
|
|
@@ -29078,6 +29456,7 @@ __export(api_client_exports, {
|
|
|
29078
29456
|
getSessionGuidance: () => getSessionGuidance,
|
|
29079
29457
|
getTeamRules: () => getTeamRules,
|
|
29080
29458
|
listPrinciples: () => listPrinciples,
|
|
29459
|
+
readInspiration: () => readInspiration,
|
|
29081
29460
|
searchDesignSystems: () => searchDesignSystems,
|
|
29082
29461
|
searchIconLibraries: () => searchIconLibraries,
|
|
29083
29462
|
searchIcons: () => searchIcons,
|
|
@@ -29115,6 +29494,12 @@ function captureDashboardBrowser(input) {
|
|
|
29115
29494
|
timeoutMs: 9e4
|
|
29116
29495
|
});
|
|
29117
29496
|
}
|
|
29497
|
+
function readInspiration(input) {
|
|
29498
|
+
const params = new URLSearchParams({ type: input.type });
|
|
29499
|
+
if (input.category) params.set("category", input.category);
|
|
29500
|
+
if (input.limit !== void 0) params.set("limit", String(input.limit));
|
|
29501
|
+
return request(`/v1/inspiration?${params}`, { timeoutMs: 3e4 });
|
|
29502
|
+
}
|
|
29118
29503
|
async function searchDesignSystems(query, limit = 5) {
|
|
29119
29504
|
return request(
|
|
29120
29505
|
`/v1/design-systems/search?q=${encodeURIComponent(query)}&limit=${limit}`
|
|
@@ -31091,7 +31476,7 @@ var init_engine = __esm({
|
|
|
31091
31476
|
});
|
|
31092
31477
|
|
|
31093
31478
|
// ../shared/src/lint/config.ts
|
|
31094
|
-
import { z as
|
|
31479
|
+
import { z as z30 } from "zod";
|
|
31095
31480
|
function mergeReviewConfig(override) {
|
|
31096
31481
|
if (!override) return DEFAULT_REVIEW_CONFIG;
|
|
31097
31482
|
return {
|
|
@@ -31149,47 +31534,47 @@ var init_config = __esm({
|
|
|
31149
31534
|
semanticTokens: { enabled: true }
|
|
31150
31535
|
}
|
|
31151
31536
|
};
|
|
31152
|
-
reviewFlowStepSchema =
|
|
31153
|
-
|
|
31154
|
-
|
|
31155
|
-
|
|
31156
|
-
|
|
31537
|
+
reviewFlowStepSchema = z30.discriminatedUnion("action", [
|
|
31538
|
+
z30.object({ action: z30.literal("click"), selector: z30.string() }),
|
|
31539
|
+
z30.object({ action: z30.literal("type"), selector: z30.string(), value: z30.string() }),
|
|
31540
|
+
z30.object({ action: z30.literal("press"), key: z30.string() }),
|
|
31541
|
+
z30.object({ action: z30.literal("wait"), milliseconds: z30.number().finite().nonnegative() })
|
|
31157
31542
|
]);
|
|
31158
|
-
visualReviewConfigSchema =
|
|
31159
|
-
enabled:
|
|
31160
|
-
model:
|
|
31161
|
-
enabled:
|
|
31162
|
-
maxScreenshots:
|
|
31543
|
+
visualReviewConfigSchema = z30.object({
|
|
31544
|
+
enabled: z30.boolean().optional(),
|
|
31545
|
+
model: z30.object({
|
|
31546
|
+
enabled: z30.boolean().optional(),
|
|
31547
|
+
maxScreenshots: z30.number().int().nonnegative().optional()
|
|
31163
31548
|
}).optional(),
|
|
31164
|
-
routes:
|
|
31165
|
-
viewports:
|
|
31166
|
-
flows:
|
|
31167
|
-
name:
|
|
31168
|
-
persona:
|
|
31169
|
-
path:
|
|
31170
|
-
steps:
|
|
31549
|
+
routes: z30.array(z30.string()).optional(),
|
|
31550
|
+
viewports: z30.array(z30.enum(["desktop", "mobile", "reflow"])).optional(),
|
|
31551
|
+
flows: z30.array(z30.object({
|
|
31552
|
+
name: z30.string(),
|
|
31553
|
+
persona: z30.string(),
|
|
31554
|
+
path: z30.string().optional(),
|
|
31555
|
+
steps: z30.array(reviewFlowStepSchema)
|
|
31171
31556
|
})).optional(),
|
|
31172
|
-
componentGallery:
|
|
31173
|
-
enabled:
|
|
31174
|
-
componentSelector:
|
|
31175
|
-
canvasSelector:
|
|
31176
|
-
labelSelector:
|
|
31177
|
-
maxComponents:
|
|
31557
|
+
componentGallery: z30.object({
|
|
31558
|
+
enabled: z30.boolean().optional(),
|
|
31559
|
+
componentSelector: z30.string().optional(),
|
|
31560
|
+
canvasSelector: z30.string().optional(),
|
|
31561
|
+
labelSelector: z30.string().optional(),
|
|
31562
|
+
maxComponents: z30.number().int().nonnegative().optional()
|
|
31178
31563
|
}).optional()
|
|
31179
31564
|
});
|
|
31180
|
-
reviewConfigOverrideSchema =
|
|
31181
|
-
designSystem:
|
|
31565
|
+
reviewConfigOverrideSchema = z30.object({
|
|
31566
|
+
designSystem: z30.string().optional(),
|
|
31182
31567
|
visual: visualReviewConfigSchema.optional(),
|
|
31183
|
-
rules:
|
|
31184
|
-
elements:
|
|
31185
|
-
tokens:
|
|
31186
|
-
wcag:
|
|
31187
|
-
enabled:
|
|
31188
|
-
severity:
|
|
31568
|
+
rules: z30.object({
|
|
31569
|
+
elements: z30.record(z30.string(), z30.object({ use: z30.string(), from: z30.string() })).optional(),
|
|
31570
|
+
tokens: z30.record(z30.string(), z30.string()).optional(),
|
|
31571
|
+
wcag: z30.object({
|
|
31572
|
+
enabled: z30.boolean().optional(),
|
|
31573
|
+
severity: z30.enum(["all", "critical+serious"]).optional()
|
|
31189
31574
|
}).optional(),
|
|
31190
|
-
slop:
|
|
31191
|
-
copy:
|
|
31192
|
-
semanticTokens:
|
|
31575
|
+
slop: z30.object({ enabled: z30.boolean().optional() }).optional(),
|
|
31576
|
+
copy: z30.object({ enabled: z30.boolean().optional() }).optional(),
|
|
31577
|
+
semanticTokens: z30.object({ enabled: z30.boolean().optional() }).optional()
|
|
31193
31578
|
}).optional()
|
|
31194
31579
|
});
|
|
31195
31580
|
}
|
|
@@ -31565,7 +31950,7 @@ __export(review_tool_exports, {
|
|
|
31565
31950
|
isReviewableSourcePath: () => isReviewableSourcePath,
|
|
31566
31951
|
registerReviewTool: () => registerReviewTool
|
|
31567
31952
|
});
|
|
31568
|
-
import { z as
|
|
31953
|
+
import { z as z31 } from "zod";
|
|
31569
31954
|
function isReviewableSourcePath(path6) {
|
|
31570
31955
|
return /\.(tsx|jsx|ts|js|css)$/i.test(path6);
|
|
31571
31956
|
}
|
|
@@ -31589,11 +31974,11 @@ get-review-rules gives you the judgment rules to apply yourself. This tool is th
|
|
|
31589
31974
|
This source reviewer cannot judge rendered hierarchy or semantic color roles. After it passes, inspect the rendered dashboard and confirm title \u2192 metrics \u2192 records \u2192 interpretation, distinct action and status colors, and that a stepper begins only after a task starts.
|
|
31590
31975
|
|
|
31591
31976
|
Fix every critical and serious finding, then call it again on the fixed source. Findings feed the same loop as a human code review: read, fix, re-check.`,
|
|
31592
|
-
inputSchema:
|
|
31593
|
-
files:
|
|
31594
|
-
|
|
31595
|
-
path:
|
|
31596
|
-
content:
|
|
31977
|
+
inputSchema: z31.object({
|
|
31978
|
+
files: z31.array(
|
|
31979
|
+
z31.object({
|
|
31980
|
+
path: z31.string().trim().min(1).max(400).describe("Repo-relative file path, e.g. components/signup-form.tsx."),
|
|
31981
|
+
content: z31.string().max(MAX_FILE_CHARS).describe("The file's full source.")
|
|
31597
31982
|
})
|
|
31598
31983
|
).min(1).max(MAX_FILES).describe("The .tsx, .jsx, .ts, .js, and .css files to review, as written."),
|
|
31599
31984
|
context: CONTEXT_ARG
|
|
@@ -31654,7 +32039,7 @@ review-ui-code({ files: [{ path: "components/signup-form.tsx", content: "..." },
|
|
|
31654
32039
|
});
|
|
31655
32040
|
|
|
31656
32041
|
// ../shared/src/mcp/figma-prototype.ts
|
|
31657
|
-
import { z as
|
|
32042
|
+
import { z as z32 } from "zod";
|
|
31658
32043
|
function escapeHtml(value) {
|
|
31659
32044
|
return value.replace(/[&<>"]/g, (char2) => ESCAPES.get(char2) ?? char2);
|
|
31660
32045
|
}
|
|
@@ -31688,63 +32073,63 @@ var colorSchema, paintSchema, sizingSchema, baseSchema, prototypeNodeSchema, pro
|
|
|
31688
32073
|
var init_figma_prototype = __esm({
|
|
31689
32074
|
"../shared/src/mcp/figma-prototype.ts"() {
|
|
31690
32075
|
"use strict";
|
|
31691
|
-
colorSchema =
|
|
31692
|
-
paintSchema =
|
|
31693
|
-
sizingSchema =
|
|
31694
|
-
baseSchema =
|
|
31695
|
-
name:
|
|
31696
|
-
width:
|
|
31697
|
-
height:
|
|
31698
|
-
x:
|
|
31699
|
-
y:
|
|
31700
|
-
sizing:
|
|
31701
|
-
absolute:
|
|
31702
|
-
opacity:
|
|
32076
|
+
colorSchema = z32.object({ r: z32.number(), g: z32.number(), b: z32.number(), a: z32.number() });
|
|
32077
|
+
paintSchema = z32.object({ variable: z32.string().optional(), color: colorSchema.optional() });
|
|
32078
|
+
sizingSchema = z32.enum(["FIXED", "HUG", "FILL"]);
|
|
32079
|
+
baseSchema = z32.object({
|
|
32080
|
+
name: z32.string(),
|
|
32081
|
+
width: z32.number(),
|
|
32082
|
+
height: z32.number(),
|
|
32083
|
+
x: z32.number(),
|
|
32084
|
+
y: z32.number(),
|
|
32085
|
+
sizing: z32.object({ horizontal: sizingSchema, vertical: sizingSchema }).optional(),
|
|
32086
|
+
absolute: z32.boolean().optional(),
|
|
32087
|
+
opacity: z32.number().optional()
|
|
31703
32088
|
});
|
|
31704
|
-
prototypeNodeSchema =
|
|
32089
|
+
prototypeNodeSchema = z32.lazy(() => z32.discriminatedUnion("kind", [
|
|
31705
32090
|
baseSchema.extend({
|
|
31706
|
-
kind:
|
|
31707
|
-
auditKey:
|
|
31708
|
-
systemId:
|
|
31709
|
-
texts:
|
|
31710
|
-
mainTexts:
|
|
32091
|
+
kind: z32.literal("instance"),
|
|
32092
|
+
auditKey: z32.string(),
|
|
32093
|
+
systemId: z32.string(),
|
|
32094
|
+
texts: z32.array(z32.string()),
|
|
32095
|
+
mainTexts: z32.array(z32.string())
|
|
31711
32096
|
}),
|
|
31712
32097
|
baseSchema.extend({
|
|
31713
|
-
kind:
|
|
31714
|
-
layout:
|
|
31715
|
-
direction:
|
|
31716
|
-
gap:
|
|
31717
|
-
padding:
|
|
31718
|
-
primary:
|
|
31719
|
-
counter:
|
|
31720
|
-
wrap:
|
|
32098
|
+
kind: z32.literal("frame"),
|
|
32099
|
+
layout: z32.object({
|
|
32100
|
+
direction: z32.enum(["HORIZONTAL", "VERTICAL"]),
|
|
32101
|
+
gap: z32.number(),
|
|
32102
|
+
padding: z32.tuple([z32.number(), z32.number(), z32.number(), z32.number()]),
|
|
32103
|
+
primary: z32.enum(["MIN", "CENTER", "MAX", "SPACE_BETWEEN"]),
|
|
32104
|
+
counter: z32.enum(["MIN", "CENTER", "MAX", "BASELINE"]),
|
|
32105
|
+
wrap: z32.boolean().optional()
|
|
31721
32106
|
}).optional(),
|
|
31722
32107
|
fill: paintSchema.optional(),
|
|
31723
|
-
stroke:
|
|
31724
|
-
radius:
|
|
31725
|
-
clips:
|
|
31726
|
-
children:
|
|
32108
|
+
stroke: z32.object({ paint: paintSchema, weight: z32.number() }).optional(),
|
|
32109
|
+
radius: z32.number().optional(),
|
|
32110
|
+
clips: z32.boolean().optional(),
|
|
32111
|
+
children: z32.array(prototypeNodeSchema)
|
|
31727
32112
|
}),
|
|
31728
32113
|
baseSchema.extend({
|
|
31729
|
-
kind:
|
|
31730
|
-
characters:
|
|
31731
|
-
fontSize:
|
|
31732
|
-
fontWeight:
|
|
31733
|
-
lineHeight:
|
|
31734
|
-
letterSpacing:
|
|
31735
|
-
fontFamily:
|
|
31736
|
-
align:
|
|
32114
|
+
kind: z32.literal("text"),
|
|
32115
|
+
characters: z32.string(),
|
|
32116
|
+
fontSize: z32.number(),
|
|
32117
|
+
fontWeight: z32.number(),
|
|
32118
|
+
lineHeight: z32.number().optional(),
|
|
32119
|
+
letterSpacing: z32.number().optional(),
|
|
32120
|
+
fontFamily: z32.string().optional(),
|
|
32121
|
+
align: z32.enum(["LEFT", "CENTER", "RIGHT", "JUSTIFIED"]),
|
|
31737
32122
|
fill: paintSchema.optional()
|
|
31738
32123
|
}),
|
|
31739
32124
|
baseSchema.extend({
|
|
31740
|
-
kind:
|
|
31741
|
-
svg:
|
|
31742
|
-
reason:
|
|
32125
|
+
kind: z32.literal("shape"),
|
|
32126
|
+
svg: z32.string(),
|
|
32127
|
+
reason: z32.string()
|
|
31743
32128
|
})
|
|
31744
32129
|
]));
|
|
31745
|
-
prototypeRequestSchema =
|
|
31746
|
-
type:
|
|
31747
|
-
id:
|
|
32130
|
+
prototypeRequestSchema = z32.object({
|
|
32131
|
+
type: z32.literal("better-design-figma-prototype"),
|
|
32132
|
+
id: z32.string(),
|
|
31748
32133
|
frame: prototypeNodeSchema
|
|
31749
32134
|
});
|
|
31750
32135
|
ESCAPES = /* @__PURE__ */ new Map([["&", "&"], ["<", "<"], [">", ">"], ['"', """]]);
|
|
@@ -31930,7 +32315,7 @@ var init_figma_model_prompt = __esm({
|
|
|
31930
32315
|
});
|
|
31931
32316
|
|
|
31932
32317
|
// ../shared/src/mcp/figma-model.ts
|
|
31933
|
-
import { z as
|
|
32318
|
+
import { z as z33 } from "zod";
|
|
31934
32319
|
function fenced(text3, language) {
|
|
31935
32320
|
const match = text3.match(new RegExp("```" + language + "[^\\n]*\\n([\\s\\S]*?)```", "i"));
|
|
31936
32321
|
return match ? match[1] : null;
|
|
@@ -32136,8 +32521,8 @@ var init_figma_model = __esm({
|
|
|
32136
32521
|
init_figma_prototype();
|
|
32137
32522
|
init_html_kit();
|
|
32138
32523
|
init_preview_html();
|
|
32139
|
-
unplacedSchema =
|
|
32140
|
-
unplaced:
|
|
32524
|
+
unplacedSchema = z33.object({
|
|
32525
|
+
unplaced: z33.array(z33.object({ name: z33.string(), reason: z33.string() }))
|
|
32141
32526
|
});
|
|
32142
32527
|
UNSAFE_BLOCKS = /<(script|iframe|object|embed|noscript)\b[\s\S]*?(<\/\1[^>]*>|$)/gi;
|
|
32143
32528
|
UNSAFE_TAGS = /<\/?(meta|link|base|form|style)\b[^>]*(>|$)/gi;
|
|
@@ -32238,7 +32623,7 @@ var init_figma_model_handoff = __esm({
|
|
|
32238
32623
|
});
|
|
32239
32624
|
|
|
32240
32625
|
// ../shared/src/mcp/figma-model-providers.ts
|
|
32241
|
-
import { z as
|
|
32626
|
+
import { z as z34 } from "zod";
|
|
32242
32627
|
function serverLabel(url) {
|
|
32243
32628
|
try {
|
|
32244
32629
|
const parsed = new URL(url);
|
|
@@ -32331,19 +32716,19 @@ var init_figma_model_providers = __esm({
|
|
|
32331
32716
|
DEFAULT_LOCAL_MODEL = "gemma4:latest";
|
|
32332
32717
|
DEFAULT_LOCAL_BASE_URL = "http://localhost:11434/v1";
|
|
32333
32718
|
MAX_OUTPUT_TOKENS = 16e3;
|
|
32334
|
-
chatReplySchema =
|
|
32335
|
-
choices:
|
|
32336
|
-
message:
|
|
32337
|
-
finish_reason:
|
|
32719
|
+
chatReplySchema = z34.object({
|
|
32720
|
+
choices: z34.array(z34.object({
|
|
32721
|
+
message: z34.object({ content: z34.string().nullable().optional() }),
|
|
32722
|
+
finish_reason: z34.string().nullable().optional()
|
|
32338
32723
|
})).min(1),
|
|
32339
|
-
usage:
|
|
32724
|
+
usage: z34.object({ prompt_tokens: z34.number(), completion_tokens: z34.number() }).optional()
|
|
32340
32725
|
});
|
|
32341
|
-
errorCodeSchema =
|
|
32726
|
+
errorCodeSchema = z34.object({ code: z34.string() });
|
|
32342
32727
|
}
|
|
32343
32728
|
});
|
|
32344
32729
|
|
|
32345
32730
|
// ../shared/src/mcp/figma-relay.ts
|
|
32346
|
-
import { z as
|
|
32731
|
+
import { z as z35 } from "zod";
|
|
32347
32732
|
function noPluginMessage() {
|
|
32348
32733
|
return "No Figma plugin is connected. Open the Figma file, run Plugins, Development, Better Design, and leave the panel open.";
|
|
32349
32734
|
}
|
|
@@ -32357,35 +32742,35 @@ var init_figma_relay = __esm({
|
|
|
32357
32742
|
"use strict";
|
|
32358
32743
|
init_figma_prototype();
|
|
32359
32744
|
FIGMA_RELAY_PORT = 4397;
|
|
32360
|
-
relayRequestSchema =
|
|
32361
|
-
id:
|
|
32745
|
+
relayRequestSchema = z35.object({
|
|
32746
|
+
id: z35.string(),
|
|
32362
32747
|
/** A frame name as it reads in Figma. Omitted means the current selection. */
|
|
32363
|
-
frame:
|
|
32748
|
+
frame: z35.string().optional(),
|
|
32364
32749
|
/**
|
|
32365
32750
|
* `model` asks for the serialized frame and its PNG instead of a rendered
|
|
32366
32751
|
* page, so a model can rebuild a frame that holds no library component.
|
|
32367
32752
|
* Omitted means the library path, unchanged. [BD-866]
|
|
32368
32753
|
*/
|
|
32369
|
-
mode:
|
|
32754
|
+
mode: z35.enum(["library", "model"]).optional()
|
|
32370
32755
|
});
|
|
32371
|
-
relayResultSchema =
|
|
32372
|
-
id:
|
|
32373
|
-
html:
|
|
32374
|
-
error:
|
|
32375
|
-
components:
|
|
32376
|
-
report:
|
|
32377
|
-
frame:
|
|
32756
|
+
relayResultSchema = z35.object({
|
|
32757
|
+
id: z35.string(),
|
|
32758
|
+
html: z35.string().optional(),
|
|
32759
|
+
error: z35.string().optional(),
|
|
32760
|
+
components: z35.number().optional(),
|
|
32761
|
+
report: z35.array(z35.object({ name: z35.string(), reason: z35.string() })).default([]),
|
|
32762
|
+
frame: z35.string().optional(),
|
|
32378
32763
|
/** The frames the plugin found, so a missing name can say what is there. */
|
|
32379
|
-
frames:
|
|
32764
|
+
frames: z35.array(z35.string()).optional(),
|
|
32380
32765
|
/** Model mode: the serialized frame, for the model to rebuild. */
|
|
32381
32766
|
tree: prototypeNodeSchema.optional(),
|
|
32382
32767
|
/** Model mode: the frame's PNG export, base64. */
|
|
32383
|
-
png:
|
|
32768
|
+
png: z35.string().optional()
|
|
32384
32769
|
});
|
|
32385
|
-
relayHelloSchema =
|
|
32386
|
-
file:
|
|
32770
|
+
relayHelloSchema = z35.object({
|
|
32771
|
+
file: z35.string(),
|
|
32387
32772
|
/** The design system the file holds, when the plugin knows it. */
|
|
32388
|
-
system:
|
|
32773
|
+
system: z35.string().optional()
|
|
32389
32774
|
});
|
|
32390
32775
|
POLL_HOLD_MS = 25e3;
|
|
32391
32776
|
REQUEST_TIMEOUT_MS = 3e5;
|
|
@@ -32553,7 +32938,7 @@ __export(figma_prototype_tool_exports, {
|
|
|
32553
32938
|
});
|
|
32554
32939
|
import { mkdir as mkdir2, readFile, stat, writeFile as writeFile2 } from "fs/promises";
|
|
32555
32940
|
import { dirname as dirname3, resolve as resolve2 } from "path";
|
|
32556
|
-
import { z as
|
|
32941
|
+
import { z as z36 } from "zod";
|
|
32557
32942
|
async function ensureRelay() {
|
|
32558
32943
|
if (relay) return relay;
|
|
32559
32944
|
starting ??= startFigmaRelay().then((started) => {
|
|
@@ -32613,30 +32998,30 @@ function registerFigmaPrototypeTool(server) {
|
|
|
32613
32998
|
"yourself: the tool returns a prompt file and the frame's image, you write the answer to a file, and",
|
|
32614
32999
|
"you call this tool again with frameId and answerPath so it checks the page and reports what is missing."
|
|
32615
33000
|
].join(" "),
|
|
32616
|
-
inputSchema:
|
|
32617
|
-
frame:
|
|
32618
|
-
outputPath:
|
|
32619
|
-
mode:
|
|
33001
|
+
inputSchema: z36.object({
|
|
33002
|
+
frame: z36.string().optional().describe("The frame's name as it reads in Figma. Omit to use the current selection."),
|
|
33003
|
+
outputPath: z36.string().optional().describe("Where to write the HTML. Relative paths resolve against the working directory."),
|
|
33004
|
+
mode: z36.enum(["library", "model"]).optional().describe(
|
|
32620
33005
|
"library (the default) maps components from a Better Design library. model asks a model to rebuild a frame from any file."
|
|
32621
33006
|
),
|
|
32622
|
-
system:
|
|
32623
|
-
provider:
|
|
33007
|
+
system: z36.string().optional().describe("Model mode: the catalog design system to build with, as find-design-system names it."),
|
|
33008
|
+
provider: z36.enum(["agent", "openai-compatible"]).optional().describe(
|
|
32624
33009
|
"Model mode: agent (the default) hands the frame to you to rebuild; openai-compatible calls a local server, Ollama by default."
|
|
32625
33010
|
),
|
|
32626
|
-
model:
|
|
32627
|
-
frameId:
|
|
32628
|
-
answerPath:
|
|
33011
|
+
model: z36.string().optional().describe(`Model mode with openai-compatible: the local model id. Defaults to ${DEFAULT_LOCAL_MODEL}.`),
|
|
33012
|
+
frameId: z36.string().optional().describe("Model mode, second call: the frameId the first call returned."),
|
|
33013
|
+
answerPath: z36.string().optional().describe("Model mode, second call: the file holding your answer, as the prompt file asks.")
|
|
32629
33014
|
}),
|
|
32630
|
-
outputSchema:
|
|
32631
|
-
status:
|
|
32632
|
-
message:
|
|
32633
|
-
frameId:
|
|
32634
|
-
promptPath:
|
|
32635
|
-
imagePath:
|
|
32636
|
-
path:
|
|
32637
|
-
components:
|
|
32638
|
-
report:
|
|
32639
|
-
model:
|
|
33015
|
+
outputSchema: z36.object({
|
|
33016
|
+
status: z36.enum(["completed", "failed", "needs-answer"]),
|
|
33017
|
+
message: z36.string(),
|
|
33018
|
+
frameId: z36.string().optional(),
|
|
33019
|
+
promptPath: z36.string().optional(),
|
|
33020
|
+
imagePath: z36.string().optional(),
|
|
33021
|
+
path: z36.string().optional(),
|
|
33022
|
+
components: z36.number().optional(),
|
|
33023
|
+
report: z36.array(z36.object({ name: z36.string(), reason: z36.string() })).optional(),
|
|
33024
|
+
model: z36.string().optional()
|
|
32640
33025
|
}),
|
|
32641
33026
|
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
32642
33027
|
},
|
|
@@ -32858,6 +33243,7 @@ async function startServer() {
|
|
|
32858
33243
|
const { createHttpDataProvider: createHttpDataProvider2 } = await Promise.resolve().then(() => (init_http_data_provider(), http_data_provider_exports));
|
|
32859
33244
|
const {
|
|
32860
33245
|
captureDashboardBrowser: captureDashboardBrowser2,
|
|
33246
|
+
readInspiration: readInspiration2,
|
|
32861
33247
|
trackToolUsage: trackToolUsage2,
|
|
32862
33248
|
trackMcpInstalled: trackMcpInstalled2
|
|
32863
33249
|
} = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
@@ -32883,7 +33269,8 @@ async function startServer() {
|
|
|
32883
33269
|
name: "DesignSystems",
|
|
32884
33270
|
version: MCP_IMPLEMENTATION_VERSION2,
|
|
32885
33271
|
onToolUsage,
|
|
32886
|
-
captureDashboardBrowser: captureDashboardBrowser2
|
|
33272
|
+
captureDashboardBrowser: captureDashboardBrowser2,
|
|
33273
|
+
readInspiration: readInspiration2
|
|
32887
33274
|
});
|
|
32888
33275
|
registerReviewTool2(server, onToolUsage);
|
|
32889
33276
|
registerFigmaPrototypeTool2(server);
|