better-design 0.43.1 → 0.43.2
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 +110 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -332,7 +332,7 @@ var init_constants = __esm({
|
|
|
332
332
|
For every UI build, run these steps in order, and never present a page that skipped step 2 or 4:
|
|
333
333
|
1. Design system: \`find-design-system\`, then \`get-design-system-kit\`. For a product with a live site, \`extract-from-url\` gives its tokens; an app screen takes only the accent and font from them.
|
|
334
334
|
2. Principles: \`get-ui-principle\` before styling, \`get-ux-principle\` before behavior.
|
|
335
|
-
3. Build.
|
|
335
|
+
3. Build. A product screen sits inside its app shell, with realistic sample data and an app-scale title.
|
|
336
336
|
4. Review: \`get-review-rules\`, \`check-comprehension\` and \`inspect-spacing\`. Fix every critical and serious finding.
|
|
337
337
|
|
|
338
338
|
Always use the Better Design MCP tools for any frontend, UI, UX, interaction, flow, form, navigation, dialog, error, onboarding, component, styling, article widget, calculator, estimator, converter, generator, quiz, checklist, React Native, Expo, Three.js, Shopify, or sales work, without me having to explicitly ask. For a calculator or article widget, load \`get-widget-guide\`; for React Native or Expo, load task guidance with \`get-react-native-guide\`; for Three.js or React Three Fiber, use \`get-three-js-guide\`; for Shopify Liquid or theme work, use \`get-shopify-guide\`; for sales conversations or assets, use \`get-sales-principle\`. Load visual guidance with \`get-ui-principle\`, load flow guidance with \`get-ux-principle\`, and self-review with \`get-review-rules\` and \`check-comprehension\`. Run \`inspect-spacing\` on rendered DOM evidence. For dashboards, deploy the exact source to a public HTTPS preview; \`finalize-dashboard-review\` must return VALID after Better Design captures it at 390, 1440, and 1728. Start UI builds with \`find-design-system\`. For a custom brand, call \`create-design-system\`, poll \`get-design-system-status\`, and run its React install command in coding hosts; in inline canvases, call \`get-design-system-kit\` with target \`html\` and the generated projectId. For a catalog system, call \`get-design-system-kit\` with target \`react\` in coding hosts or \`html\` in inline canvases. For a Shopify store, load \`get-shopify-guide\`, then call \`get-design-system-kit\` with target \`shopify\`.`;
|
|
@@ -1075,7 +1075,7 @@ var MCP_IMPLEMENTATION_VERSION;
|
|
|
1075
1075
|
var init_version = __esm({
|
|
1076
1076
|
"../shared/src/mcp/version.ts"() {
|
|
1077
1077
|
"use strict";
|
|
1078
|
-
MCP_IMPLEMENTATION_VERSION = "0.43.
|
|
1078
|
+
MCP_IMPLEMENTATION_VERSION = "0.43.2";
|
|
1079
1079
|
}
|
|
1080
1080
|
});
|
|
1081
1081
|
|
|
@@ -19264,6 +19264,43 @@ CI runs the same check with \`npx better-design review --base main\`.
|
|
|
19264
19264
|
}
|
|
19265
19265
|
});
|
|
19266
19266
|
|
|
19267
|
+
// ../shared/src/motion-tokens.ts
|
|
19268
|
+
var MOTION_PROFILES;
|
|
19269
|
+
var init_motion_tokens = __esm({
|
|
19270
|
+
"../shared/src/motion-tokens.ts"() {
|
|
19271
|
+
"use strict";
|
|
19272
|
+
MOTION_PROFILES = {
|
|
19273
|
+
/** Dense tools, terminals, industrial and brutalist systems. */
|
|
19274
|
+
crisp: {
|
|
19275
|
+
"--transition-duration-fast": "120ms",
|
|
19276
|
+
"--transition-duration-base": "160ms",
|
|
19277
|
+
"--transition-duration-slow": "220ms",
|
|
19278
|
+
"--ease-out": "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
|
|
19279
|
+
"--ease-in-out": "cubic-bezier(0.455, 0.03, 0.515, 0.955)",
|
|
19280
|
+
"--ease-in": "cubic-bezier(0.55, 0.085, 0.68, 0.53)"
|
|
19281
|
+
},
|
|
19282
|
+
/** Product UI, and the default for a generated system. */
|
|
19283
|
+
standard: {
|
|
19284
|
+
"--transition-duration-fast": "150ms",
|
|
19285
|
+
"--transition-duration-base": "200ms",
|
|
19286
|
+
"--transition-duration-slow": "280ms",
|
|
19287
|
+
"--ease-out": "cubic-bezier(0.165, 0.84, 0.44, 1)",
|
|
19288
|
+
"--ease-in-out": "cubic-bezier(0.645, 0.045, 0.355, 1)",
|
|
19289
|
+
"--ease-in": "cubic-bezier(0.55, 0.055, 0.675, 0.19)"
|
|
19290
|
+
},
|
|
19291
|
+
/** Luxury, glass and editorial systems. */
|
|
19292
|
+
soft: {
|
|
19293
|
+
"--transition-duration-fast": "180ms",
|
|
19294
|
+
"--transition-duration-base": "240ms",
|
|
19295
|
+
"--transition-duration-slow": "300ms",
|
|
19296
|
+
"--ease-out": "cubic-bezier(0.19, 1, 0.22, 1)",
|
|
19297
|
+
"--ease-in-out": "cubic-bezier(0.77, 0, 0.175, 1)",
|
|
19298
|
+
"--ease-in": "cubic-bezier(0.895, 0.03, 0.685, 0.22)"
|
|
19299
|
+
}
|
|
19300
|
+
};
|
|
19301
|
+
}
|
|
19302
|
+
});
|
|
19303
|
+
|
|
19267
19304
|
// ../shared/src/mcp/html-kit.ts
|
|
19268
19305
|
function fence(lang, body) {
|
|
19269
19306
|
return "```" + lang + "\n" + body + "\n```";
|
|
@@ -19383,10 +19420,11 @@ function buildDsHtmlKit({ name, tokensCss, viewUrl, source, componentKit }) {
|
|
|
19383
19420
|
Full live showcase: ${viewUrl}` : ""
|
|
19384
19421
|
].join("\n");
|
|
19385
19422
|
}
|
|
19386
|
-
var MINIMUM_TEXT_CONTRAST, HOVER_POLICY, INLINE_TEXT_COLOR_POLICY, HOST_ISOLATION_CSS_TEMPLATE, BESPOKE_ASSET_POLICY, INLINE_COMPOSITION_POLICY, INLINE_HIERARCHY_POLICY, DS_KIT_CSS, DS_KIT_EXAMPLE_HTML, HTML_KIT_CONDUCT_MD;
|
|
19423
|
+
var MINIMUM_TEXT_CONTRAST, HOVER_POLICY, INLINE_TEXT_COLOR_POLICY, HOST_ISOLATION_CSS_TEMPLATE, BESPOKE_ASSET_POLICY, INLINE_COMPOSITION_POLICY, INLINE_HIERARCHY_POLICY, KIT_FAST, KIT_EASE, KIT_TIMING, DS_KIT_CSS, DS_KIT_EXAMPLE_HTML, HTML_KIT_CONDUCT_MD;
|
|
19387
19424
|
var init_html_kit = __esm({
|
|
19388
19425
|
"../shared/src/mcp/html-kit.ts"() {
|
|
19389
19426
|
"use strict";
|
|
19427
|
+
init_motion_tokens();
|
|
19390
19428
|
MINIMUM_TEXT_CONTRAST = "4.5:1";
|
|
19391
19429
|
HOVER_POLICY = "Put every :hover rule inside @media (hover: hover) and (pointer: fine); never move controls or override selected/pressed styling. For selectable controls, unselected hover stays neutral: only strengthen the neutral surface or edge, never changes text to the primary color, and never borrows the primary/accent fill. Do not add app-specific :hover rules to ds-* controls; use the kit states verbatim.";
|
|
19392
19430
|
INLINE_TEXT_COLOR_POLICY = [
|
|
@@ -19427,6 +19465,8 @@ APP_ROOT :where(h1,h2,h3,h4,h5,h6,p,strong,label,legend,li,dt,dd,small):not(.ds-
|
|
|
19427
19465
|
"Labels are a last resort: remove captions that restate an obvious value, do not label the same fact twice, and keep no more than two supporting metadata items around the primary entity.",
|
|
19428
19466
|
"Bold is selective hierarchy, not decoration: emphasize the primary display, section anchors, and the active value; keep labels, metadata, table headers, and helper copy quieter."
|
|
19429
19467
|
].join(" ");
|
|
19468
|
+
({ "--transition-duration-fast": KIT_FAST, "--ease-out": KIT_EASE } = MOTION_PROFILES.standard);
|
|
19469
|
+
KIT_TIMING = `var(--transition-duration-fast,${KIT_FAST}) var(--ease-out,${KIT_EASE})`;
|
|
19430
19470
|
DS_KIT_CSS = `*,*::before,*::after{box-sizing:border-box}
|
|
19431
19471
|
:where(.ds){font-family:var(--font-sans,ui-sans-serif,system-ui,sans-serif);color:var(--foreground,oklch(0.2 0 0));background:var(--background,oklch(1 0 0));line-height:1.5}
|
|
19432
19472
|
.ds :where(h1,h2,h3,h4,h5,h6,p,strong,label,legend,li,dt,dd,small){color:inherit}
|
|
@@ -19444,7 +19484,7 @@ APP_ROOT :where(h1,h2,h3,h4,h5,h6,p,strong,label,legend,li,dt,dd,small):not(.ds-
|
|
|
19444
19484
|
.ds-muted{color:var(--muted-foreground,oklch(0.55 0 0))}
|
|
19445
19485
|
|
|
19446
19486
|
/* Button */
|
|
19447
|
-
.ds-btn{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;font:inherit;font-size:.875rem;font-weight:500;line-height:1;padding:.6rem 1rem;border-radius:var(--radius-md,calc(var(--radius,0.5rem) - 2px));border:1px solid transparent;cursor:pointer;touch-action:manipulation;transition:opacity
|
|
19487
|
+
.ds-btn{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;font:inherit;font-size:.875rem;font-weight:500;line-height:1;padding:.6rem 1rem;border-radius:var(--radius-md,calc(var(--radius,0.5rem) - 2px));border:1px solid transparent;cursor:pointer;touch-action:manipulation;transition:opacity ${KIT_TIMING},background ${KIT_TIMING};text-decoration:none}
|
|
19448
19488
|
.ds-btn:focus-visible{outline:2px solid var(--ring,var(--primary,oklch(0.45 0.15 264)));outline-offset:2px}
|
|
19449
19489
|
.ds-btn:disabled{background:var(--muted,oklch(0.94 0 0));color:var(--foreground,oklch(0.2 0 0));border-color:var(--border,oklch(0.85 0 0));opacity:1;cursor:not-allowed}
|
|
19450
19490
|
.ds-btn-primary{background:var(--primary,oklch(0.45 0.15 264));color:var(--primary-foreground,oklch(1 0 0));box-shadow:var(--shadow-sm,var(--shadow-xs,none))}
|
|
@@ -19457,7 +19497,7 @@ APP_ROOT :where(h1,h2,h3,h4,h5,h6,p,strong,label,legend,li,dt,dd,small):not(.ds-
|
|
|
19457
19497
|
|
|
19458
19498
|
/* Form fields */
|
|
19459
19499
|
.ds-label{display:block;font-size:.8125rem;font-weight:500;margin-bottom:.4rem;color:var(--foreground,oklch(0.2 0 0))}
|
|
19460
|
-
.ds-input,.ds-textarea,.ds-select{width:100%;font:inherit;font-size:.875rem;padding:.55rem .75rem;border-radius:var(--radius-lg,var(--radius,0.5rem));border:1px solid var(--border,oklch(0.85 0 0));background:var(--input,var(--background,oklch(1 0 0)));color:var(--foreground,oklch(0.2 0 0));box-shadow:var(--shadow-inset,var(--shadow-2xs,none));transition:border-color
|
|
19500
|
+
.ds-input,.ds-textarea,.ds-select{width:100%;font:inherit;font-size:.875rem;padding:.55rem .75rem;border-radius:var(--radius-lg,var(--radius,0.5rem));border:1px solid var(--border,oklch(0.85 0 0));background:var(--input,var(--background,oklch(1 0 0)));color:var(--foreground,oklch(0.2 0 0));box-shadow:var(--shadow-inset,var(--shadow-2xs,none));transition:border-color ${KIT_TIMING},box-shadow ${KIT_TIMING}}
|
|
19461
19501
|
.ds-input::placeholder,.ds-textarea::placeholder{color:var(--muted-foreground,oklch(0.6 0 0))}
|
|
19462
19502
|
.ds-input:focus,.ds-textarea:focus,.ds-select:focus{outline:none;border-color:var(--ring,var(--primary,oklch(0.45 0.15 264)));box-shadow:var(--shadow-inset,0 0 0 0 transparent),0 0 0 2px color-mix(in oklab,var(--ring,var(--primary,oklch(0.45 0.15 264))) 35%,transparent)}
|
|
19463
19503
|
.ds-textarea{min-height:5rem;resize:vertical}
|
|
@@ -19496,9 +19536,9 @@ APP_ROOT :where(h1,h2,h3,h4,h5,h6,p,strong,label,legend,li,dt,dd,small):not(.ds-
|
|
|
19496
19536
|
|
|
19497
19537
|
/* Switch */
|
|
19498
19538
|
.ds-switch{display:inline-flex;align-items:center;gap:.6rem;font-size:.875rem;cursor:pointer}
|
|
19499
|
-
.ds-switch-track{width:2.25rem;height:1.25rem;border-radius:999px;background:var(--muted,oklch(0.9 0 0));position:relative;transition:background
|
|
19539
|
+
.ds-switch-track{width:2.25rem;height:1.25rem;border-radius:999px;background:var(--muted,oklch(0.9 0 0));position:relative;transition:background ${KIT_TIMING};flex:none}
|
|
19500
19540
|
.ds-switch-track.on{background:var(--primary,oklch(0.45 0.15 264))}
|
|
19501
|
-
.ds-switch-thumb{position:absolute;top:2px;left:2px;width:calc(1.25rem - 4px);height:calc(1.25rem - 4px);border-radius:999px;background:var(--background,oklch(1 0 0));box-shadow:var(--shadow-sm,0 1px 2px oklch(0 0 0 / 0.2));transition:transform
|
|
19541
|
+
.ds-switch-thumb{position:absolute;top:2px;left:2px;width:calc(1.25rem - 4px);height:calc(1.25rem - 4px);border-radius:999px;background:var(--background,oklch(1 0 0));box-shadow:var(--shadow-sm,0 1px 2px oklch(0 0 0 / 0.2));transition:transform ${KIT_TIMING}}
|
|
19502
19542
|
.ds-switch-track.on .ds-switch-thumb{transform:translateX(1rem)}
|
|
19503
19543
|
|
|
19504
19544
|
/* Tabs */
|
|
@@ -19516,7 +19556,7 @@ APP_ROOT :where(h1,h2,h3,h4,h5,h6,p,strong,label,legend,li,dt,dd,small):not(.ds-
|
|
|
19516
19556
|
.ds-table th{font-weight:600;color:var(--muted-foreground,oklch(0.5 0 0));font-size:.6875rem;text-transform:uppercase;letter-spacing:.04em}
|
|
19517
19557
|
|
|
19518
19558
|
/* Chip / keyboard key */
|
|
19519
|
-
.ds-chip{display:inline-flex;align-items:center;gap:.35rem;font:inherit;font-size:.8125rem;padding:.3rem .7rem;border-radius:999px;border:1px solid var(--border,oklch(0.88 0 0));background:var(--background,oklch(1 0 0));color:var(--foreground,oklch(0.2 0 0));cursor:pointer;touch-action:manipulation;transition:background-color
|
|
19559
|
+
.ds-chip{display:inline-flex;align-items:center;gap:.35rem;font:inherit;font-size:.8125rem;padding:.3rem .7rem;border-radius:999px;border:1px solid var(--border,oklch(0.88 0 0));background:var(--background,oklch(1 0 0));color:var(--foreground,oklch(0.2 0 0));cursor:pointer;touch-action:manipulation;transition:background-color ${KIT_TIMING},border-color ${KIT_TIMING},color ${KIT_TIMING}}
|
|
19520
19560
|
.ds-chip:focus-visible{outline:2px solid var(--ring,var(--primary,oklch(0.45 0.15 264)));outline-offset:2px}
|
|
19521
19561
|
.ds .ds-chip[aria-pressed="true"],.ds .ds-chip[aria-pressed="true"]:hover{background:var(--primary,oklch(0.45 0.15 264));color:var(--primary-foreground,oklch(1 0 0));border-color:var(--primary,oklch(0.45 0.15 264))}
|
|
19522
19562
|
.ds-kbd{font-family:var(--font-mono,ui-monospace,monospace);font-size:.75rem;padding:.1rem .4rem;border-radius:var(--radius-sm,4px);border:1px solid var(--border,oklch(0.85 0 0));background:var(--muted,oklch(0.97 0 0));color:var(--foreground,oklch(0.2 0 0))}
|
|
@@ -19969,6 +20009,7 @@ function dtcgType(name, value) {
|
|
|
19969
20009
|
if (n.includes("radius") || n.includes("spacing") || n.includes("size")) return "dimension";
|
|
19970
20010
|
if (n.includes("font") || n.includes("family")) return "fontFamily";
|
|
19971
20011
|
if (n.includes("duration") || n.includes("delay")) return "duration";
|
|
20012
|
+
if (/^cubic-bezier\(/i.test(value.trim())) return "cubicBezier";
|
|
19972
20013
|
if (toOklch(value) !== null) return "color";
|
|
19973
20014
|
if (/^-?[\d.]+(px|rem|em|%|vh|vw|ch)$/.test(value.trim())) return "dimension";
|
|
19974
20015
|
if (/^-?[\d.]+m?s$/.test(value.trim())) return "duration";
|
|
@@ -19989,6 +20030,15 @@ function toDuration(value) {
|
|
|
19989
20030
|
if (!Number.isFinite(n)) return null;
|
|
19990
20031
|
return { value: n, unit: m[2]?.toLowerCase() ?? "ms" };
|
|
19991
20032
|
}
|
|
20033
|
+
function toCubicBezier(value) {
|
|
20034
|
+
const m = /^cubic-bezier\(([^)]*)\)$/i.exec(value.trim());
|
|
20035
|
+
if (!m) return null;
|
|
20036
|
+
const points = m[1].split(",").map((part) => part.trim() === "" ? Number.NaN : Number(part));
|
|
20037
|
+
if (points.length !== 4 || !points.every(Number.isFinite)) return null;
|
|
20038
|
+
const [x1, y1, x2, y2] = points;
|
|
20039
|
+
if (x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) return null;
|
|
20040
|
+
return [x1, y1, x2, y2];
|
|
20041
|
+
}
|
|
19992
20042
|
function toColor(value) {
|
|
19993
20043
|
const oklch = toOklch(value);
|
|
19994
20044
|
if (oklch === null) return null;
|
|
@@ -20062,6 +20112,8 @@ function typedValue(type, value) {
|
|
|
20062
20112
|
return toDimension(v);
|
|
20063
20113
|
case "duration":
|
|
20064
20114
|
return toDuration(v);
|
|
20115
|
+
case "cubicBezier":
|
|
20116
|
+
return toCubicBezier(v);
|
|
20065
20117
|
case "shadow":
|
|
20066
20118
|
return toShadow(v);
|
|
20067
20119
|
case "fontFamily":
|
|
@@ -26346,9 +26398,9 @@ var init_build_loop = __esm({
|
|
|
26346
26398
|
"use strict";
|
|
26347
26399
|
BUILD_LOOP = [
|
|
26348
26400
|
"Required, in this order, before you present any UI:",
|
|
26349
|
-
"1. Design system: install the system you take with `get-design-system-kit`. For a marketing page of a product with a live site, its `extract-from-url` tokens are this step. For a product screen, take only its accent colour and font from `extract-from-url`, never its backgrounds, borders or input styles: those come from the kit.",
|
|
26401
|
+
"1. Design system: install the system you take with `get-design-system-kit`, choosing one whose colour scheme matches the brand's own site: a light site gets a light system. When `extract-from-url` returns both themes, the site follows the visitor's system setting, so ship both and switch them with `prefers-color-scheme`. For a marketing page of a product with a live site, its `extract-from-url` tokens are this step. For a product screen, take only its accent colour and font from `extract-from-url`, never its backgrounds, borders or input styles: those come from the kit.",
|
|
26350
26402
|
"2. Principles: call `get-ui-principle` before styling and `get-ux-principle` before behavior.",
|
|
26351
|
-
"3. Build with those tokens and principles.",
|
|
26403
|
+
"3. Build with those tokens and principles. A product screen sits inside the product's app shell: navigation with the product's real sections, the current one active, and the workspace or project it belongs to. Show the summary, the records and the selected record's detail together, not behind tabs. Fill it with realistic sample data, including the metrics, IDs and fields a working user would see: sample data in a product screen is not an invented claim. Only claims about the company itself, such as customers, results and endorsements, must stay real. Keep the page title at app scale, the size of a section heading, not a marketing display. Keep each table row on one line: drop or truncate columns instead of wrapping cells.",
|
|
26352
26404
|
"4. Review: call `get-review-rules`, then `check-comprehension` with the page's real copy, and `inspect-spacing` on the rendered page. Fix every critical and serious finding.",
|
|
26353
26405
|
"Do not present a page that skipped step 2 or step 4. If a step fails, say which one."
|
|
26354
26406
|
].join("\n");
|
|
@@ -26629,7 +26681,7 @@ ${formatCatalogMatches(results)}`;
|
|
|
26629
26681
|
return `${heading} The catalog search failed, so call \`find-design-system\` with the app description, show the user the top matches, and ask which to start from or whether to go fully custom.`;
|
|
26630
26682
|
}
|
|
26631
26683
|
};
|
|
26632
|
-
const MATCH_INSTRUCTIONS = `Keep any already selected design system and its assigned icons; do not reopen that choice unless requested. If none is selected, present these as options, keeping each name as its clickable markdown link (no scores) so they can open the preview. Explain each option's expected buyer fit and trade-offs using known project context; do not claim conversion effects without evidence. Recommend by buyer, never by the product's material or subject (a steel tool does not need a steel-dark UI). For ecommerce and DTC product pages, default to light, warm, photography-led systems unless the brand already owns a dark identity. Ask which to start their brand from, or to go fully custom, then pass the chosen \`id\` as \`baseDesignSystemId\` to create-design-system, or pass \`userConfirmedCustom: true\` if they choose fully custom. If no person is there to answer, as in an unattended run, take the top match yourself, say which one you took and why, and call \`get-design-system-kit\` with its \`id\`.`;
|
|
26684
|
+
const MATCH_INSTRUCTIONS = `Keep any already selected design system and its assigned icons; do not reopen that choice unless requested. If none is selected, present these as options, keeping each name as its clickable markdown link (no scores) so they can open the preview. Explain each option's expected buyer fit and trade-offs using known project context; do not claim conversion effects without evidence. Recommend by buyer, never by the product's material or subject (a steel tool does not need a steel-dark UI). For ecommerce and DTC product pages, default to light, warm, photography-led systems unless the brand already owns a dark identity. Ask which to start their brand from, or to go fully custom, then pass the chosen \`id\` as \`baseDesignSystemId\` to create-design-system, or pass \`userConfirmedCustom: true\` if they choose fully custom. If no person is there to answer, as in an unattended run, take the top match whose colour scheme fits the brand yourself (a dark or two-theme site needs a system with a dark mode), say which one you took and why, and call \`get-design-system-kit\` with its \`id\`.`;
|
|
26633
26685
|
const makeSearchHandler = (toolName, includeBuildRules = false) => async ({ query }) => {
|
|
26634
26686
|
const start = Date.now();
|
|
26635
26687
|
const finish = (response, topResults, isError = false, errorMessage) => {
|
|
@@ -28757,15 +28809,24 @@ function authHeaders2(authContext) {
|
|
|
28757
28809
|
const bearer = authContext.kind === "api-key" ? authContext.apiKey : authContext.accessToken;
|
|
28758
28810
|
return { Authorization: `Bearer ${bearer}` };
|
|
28759
28811
|
}
|
|
28812
|
+
function nextStepsFor(themeMode) {
|
|
28813
|
+
return themeMode === "dual" ? `${DUAL_THEME_NOTE}
|
|
28814
|
+
|
|
28815
|
+
${NEXT_STEPS}` : NEXT_STEPS;
|
|
28816
|
+
}
|
|
28760
28817
|
function withLoopFirst(summary2) {
|
|
28761
|
-
|
|
28818
|
+
const steps = nextStepsFor(summary2.themeMode);
|
|
28819
|
+
return Object.assign({ buildInstructions: steps }, summary2, { buildInstructions: steps });
|
|
28762
28820
|
}
|
|
28763
28821
|
function fitSheet(text4, maxTokens) {
|
|
28764
28822
|
if (!maxTokens) return text4;
|
|
28765
28823
|
return truncateToTokens(text4, Math.max(0, maxTokens - NOTE_TOKENS));
|
|
28766
28824
|
}
|
|
28767
|
-
function withNextSteps(text4) {
|
|
28768
|
-
|
|
28825
|
+
function withNextSteps(text4, themeMode) {
|
|
28826
|
+
const lead = themeMode === "dual" ? `${DUAL_THEME_NOTE}
|
|
28827
|
+
|
|
28828
|
+
` : "";
|
|
28829
|
+
return `${lead}${BUILD_LOOP}
|
|
28769
28830
|
|
|
28770
28831
|
${text4}
|
|
28771
28832
|
|
|
@@ -28821,7 +28882,7 @@ Common vendor names (Shopify's --color-button family) are mapped into empty sema
|
|
|
28821
28882
|
const result = extractFromSources(all, name);
|
|
28822
28883
|
const text4 = fitSheet(formatExtraction(result), maxTokens);
|
|
28823
28884
|
return {
|
|
28824
|
-
content: [{ type: "text", text: withNextSteps(text4) }],
|
|
28885
|
+
content: [{ type: "text", text: withNextSteps(text4, result.themeMode) }],
|
|
28825
28886
|
structuredContent: withLoopFirst(summarizeExtraction(result))
|
|
28826
28887
|
};
|
|
28827
28888
|
})
|
|
@@ -28870,13 +28931,13 @@ ${BUILD_LOOP}`
|
|
|
28870
28931
|
${body.markdown}` : body.markdown;
|
|
28871
28932
|
const text4 = fitSheet(raw, maxTokens);
|
|
28872
28933
|
return {
|
|
28873
|
-
content: [{ type: "text", text: withNextSteps(text4) }],
|
|
28934
|
+
content: [{ type: "text", text: withNextSteps(text4, body.summary?.themeMode) }],
|
|
28874
28935
|
...body.summary && { structuredContent: withLoopFirst(body.summary) }
|
|
28875
28936
|
};
|
|
28876
28937
|
})
|
|
28877
28938
|
);
|
|
28878
28939
|
}
|
|
28879
|
-
var REFERENCE_NOTE, NEXT_STEPS, NOTE_TOKENS;
|
|
28940
|
+
var REFERENCE_NOTE, NEXT_STEPS, DUAL_THEME_NOTE, NOTE_TOKENS;
|
|
28880
28941
|
var init_extract_tools = __esm({
|
|
28881
28942
|
"../shared/src/mcp/tools/extract-tools.ts"() {
|
|
28882
28943
|
"use strict";
|
|
@@ -28894,6 +28955,7 @@ var init_extract_tools = __esm({
|
|
|
28894
28955
|
NEXT_STEPS = `${BUILD_LOOP}
|
|
28895
28956
|
|
|
28896
28957
|
${REFERENCE_NOTE}`;
|
|
28958
|
+
DUAL_THEME_NOTE = "This site has a light and a dark theme and follows the visitor's system setting. Build both, switched with `prefers-color-scheme`, and choose a design system that has a dark mode.";
|
|
28897
28959
|
NOTE_TOKENS = Math.ceil(NEXT_STEPS.length / 4) + 1;
|
|
28898
28960
|
}
|
|
28899
28961
|
});
|
|
@@ -39483,15 +39545,38 @@ function attribute2(attributes2, name) {
|
|
|
39483
39545
|
const match = new RegExp(`(?:^|\\s)${name}\\s{0,2}=\\s{0,2}(?:"([^"]*)"|'([^']*)'|([^\\s"']+))`, "i").exec(attributes2);
|
|
39484
39546
|
return match ? match[1] ?? match[2] ?? match[3] ?? "" : "";
|
|
39485
39547
|
}
|
|
39548
|
+
function wiringReport(targets, refs) {
|
|
39549
|
+
const broken = /* @__PURE__ */ new Map();
|
|
39550
|
+
for (const { control, id } of refs) {
|
|
39551
|
+
const target = targets.get(id);
|
|
39552
|
+
const ok = target !== void 0 && (control.needs === "element" || (control.needs === "popover" ? target.popover : target.tag === "dialog"));
|
|
39553
|
+
if (ok) continue;
|
|
39554
|
+
const ids = broken.get(control.attribute) ?? [];
|
|
39555
|
+
ids.push(id);
|
|
39556
|
+
broken.set(control.attribute, ids);
|
|
39557
|
+
}
|
|
39558
|
+
return [...broken].map(([name, ids]) => ({
|
|
39559
|
+
name,
|
|
39560
|
+
reason: `${ids.length} control(s) point at a missing or wrong target (${ids.slice(0, 3).map((id) => JSON.stringify(id.slice(0, 40))).join(", ")}), so clicking them does nothing`
|
|
39561
|
+
}));
|
|
39562
|
+
}
|
|
39486
39563
|
function partsReport(body, css = "") {
|
|
39487
39564
|
const rules = classRules(css);
|
|
39488
39565
|
const padded = (names) => names.some((name) => rules.padded.has(name));
|
|
39489
39566
|
const bare = /* @__PURE__ */ new Map();
|
|
39490
39567
|
let loose = 0;
|
|
39491
39568
|
let card = null;
|
|
39569
|
+
const targets = /* @__PURE__ */ new Map();
|
|
39570
|
+
const refs = [];
|
|
39492
39571
|
for (const match of body.matchAll(OPENING_TAG)) {
|
|
39493
39572
|
const tag = match[1].toLowerCase();
|
|
39494
39573
|
const classes = attribute2(match[2], "class").split(/\s+/);
|
|
39574
|
+
const id = attribute2(match[2], "id");
|
|
39575
|
+
if (id) targets.set(id, { tag, popover: /(?:^|\s)popover(?=[\s=/]|$)/i.test(match[2]) });
|
|
39576
|
+
for (const control of CONTROL_TARGETS) {
|
|
39577
|
+
const target = attribute2(match[2], control.attribute);
|
|
39578
|
+
if (target) refs.push({ control, id: target });
|
|
39579
|
+
}
|
|
39495
39580
|
if (card) {
|
|
39496
39581
|
const between2 = body.slice(card.end, match.index);
|
|
39497
39582
|
const empty = between2.trim().startsWith("</");
|
|
@@ -39508,6 +39593,7 @@ function partsReport(body, css = "") {
|
|
|
39508
39593
|
name,
|
|
39509
39594
|
reason: `${count} on the page carry no ds-* class and no class the page styles, so they render as browser defaults`
|
|
39510
39595
|
}));
|
|
39596
|
+
report.push(...wiringReport(targets, refs));
|
|
39511
39597
|
if (loose) {
|
|
39512
39598
|
report.push({
|
|
39513
39599
|
name: "ds-card",
|
|
@@ -39587,7 +39673,7 @@ async function renderPrototypeWithModel(input) {
|
|
|
39587
39673
|
const page = pageFromReply({ frame, designSystem: input.designSystem, text: reply2.text, author: provider.model, omitted: prompt.omitted, warnings });
|
|
39588
39674
|
return { ...page, durationMs, inputTokens: reply2.inputTokens, outputTokens: reply2.outputTokens };
|
|
39589
39675
|
}
|
|
39590
|
-
var unplacedSchema, UNSAFE_BLOCKS, UNSAFE_TAGS, ATTRIBUTE_IN_TAG, COLOUR_PROPERTY, ENTITIES, NATIVE_INPUTS, CONTROLS, CARD_PARTS, OPENING_TAG, MODEL_PAGE_CSP, CONTEXT_CUT_RATIO;
|
|
39676
|
+
var unplacedSchema, UNSAFE_BLOCKS, UNSAFE_TAGS, ATTRIBUTE_IN_TAG, COLOUR_PROPERTY, ENTITIES, NATIVE_INPUTS, CONTROLS, CARD_PARTS, OPENING_TAG, CONTROL_TARGETS, MODEL_PAGE_CSP, CONTEXT_CUT_RATIO;
|
|
39591
39677
|
var init_figma_model = __esm({
|
|
39592
39678
|
"../shared/src/mcp/figma-model.ts"() {
|
|
39593
39679
|
"use strict";
|
|
@@ -39610,6 +39696,12 @@ var init_figma_model = __esm({
|
|
|
39610
39696
|
CONTROLS = /* @__PURE__ */ new Set(["button", "select", "textarea", "input"]);
|
|
39611
39697
|
CARD_PARTS = ["ds-card-header", "ds-card-body", "ds-card-footer"];
|
|
39612
39698
|
OPENING_TAG = /<([a-zA-Z][\w-]*)(?![\w-])([^<>]*)>/g;
|
|
39699
|
+
CONTROL_TARGETS = [
|
|
39700
|
+
{ attribute: "popovertarget", needs: "popover" },
|
|
39701
|
+
{ attribute: "data-dialog-open", needs: "dialog" },
|
|
39702
|
+
{ attribute: "data-toggle", needs: "element" },
|
|
39703
|
+
{ attribute: "data-toggle-open", needs: "element" }
|
|
39704
|
+
];
|
|
39613
39705
|
MODEL_PAGE_CSP = `default-src 'none'; script-src '${PROTOTYPE_RUNTIME_HASH}'; style-src 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src data:; form-action 'none'; base-uri 'none'`;
|
|
39614
39706
|
CONTEXT_CUT_RATIO = 0.5;
|
|
39615
39707
|
}
|