@agent-native/core 0.139.0 → 0.140.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/corpus/README.md +1 -1
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +44 -1
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +265 -1
- package/corpus/templates/design/actions/create-design-system.ts +9 -10
- package/corpus/templates/design/actions/get-design-system.ts +53 -0
- package/corpus/templates/design/actions/import-design-tokens.ts +5 -71
- package/corpus/templates/design/actions/index-design-tokens.ts +61 -80
- package/corpus/templates/design/actions/update-design-system.ts +7 -10
- package/corpus/templates/design/app/components/design/EditPanel.tsx +45 -2
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +12 -0
- package/corpus/templates/design/app/components/design/edit-panel/design-system-swatches.ts +215 -0
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +10 -6
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +19 -3
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +36 -10
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +15 -0
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +19 -1
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +6 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +53 -10
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +142 -4
- package/corpus/templates/design/app/components/design/types.ts +6 -0
- package/corpus/templates/design/app/i18n-data.ts +20 -0
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +1 -1
- package/corpus/templates/design/app/pages/DesignSystems.tsx +78 -1
- package/corpus/templates/design/app/pages/design-editor/html-layer-positioning.ts +6 -1
- package/corpus/templates/design/changelog/2026-08-04-design-system-tokens-show-by-name-in-the-editor.md +6 -0
- package/corpus/templates/design/e2e/helpers.ts +17 -0
- package/corpus/templates/design/server/lib/design-system-tokens.ts +35 -0
- package/corpus/templates/design/shared/authored-color-styles.ts +401 -0
- package/corpus/templates/design/shared/resolve-tweaks.ts +10 -18
- package/dist/brand-kit/index.d.ts +1 -0
- package/dist/brand-kit/index.js +1 -0
- package/dist/brand-kit/tokens.d.ts +92 -0
- package/dist/brand-kit/tokens.js +280 -0
- package/dist/brand-kit/types.d.ts +25 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
- package/dist/provider-api/actions/provider-api.d.ts +14 -14
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +2 -1
package/corpus/README.md
CHANGED
|
@@ -73,6 +73,14 @@ interface DesignSystemData {
|
|
|
73
73
|
name: string;
|
|
74
74
|
variant: "light" | "dark" | "auto";
|
|
75
75
|
}[];
|
|
76
|
+
tokens?: {
|
|
77
|
+
name: string; // The source system's own name, e.g. "interactive-01"
|
|
78
|
+
cssVar: string; // e.g. "--cds-interactive-01"
|
|
79
|
+
value: string; // e.g. "#0F62FE" or "0.5rem"
|
|
80
|
+
type: "color" | "typography" | "spacing" | "radius" | "shadow" | "other";
|
|
81
|
+
group?: string; // Collection path, e.g. "Colors/Interactive"
|
|
82
|
+
source?: string; // e.g. "Carbon v11" or "globals.css"
|
|
83
|
+
}[];
|
|
76
84
|
imageStyle?: {
|
|
77
85
|
referenceUrls: string[];
|
|
78
86
|
styleDescription: string; // e.g., "Clean, minimal product photography"
|
|
@@ -82,6 +90,29 @@ interface DesignSystemData {
|
|
|
82
90
|
}
|
|
83
91
|
```
|
|
84
92
|
|
|
93
|
+
### The seven color roles are a summary, not the vocabulary
|
|
94
|
+
|
|
95
|
+
`colors` exists so a generated page can be themed from any brand. It is **not**
|
|
96
|
+
where a real design system goes. Carbon has `interactive-01`, Material has
|
|
97
|
+
`md-sys-color-primary-container`; mapping those onto `secondary` throws away the
|
|
98
|
+
only name the design team uses, and the Brand Kit page then shows the wrong
|
|
99
|
+
label for a token the user recognises.
|
|
100
|
+
|
|
101
|
+
So when a source names its tokens, populate **both**: `colors` for generation,
|
|
102
|
+
and `tokens` for the real vocabulary. `tokens` is capped at 500 entries, every
|
|
103
|
+
`cssVar` must be a valid custom property, and values may not contain `;`, `{`,
|
|
104
|
+
`}`, `<`, `>`, or CSS comments — `create-design-system` and
|
|
105
|
+
`update-design-system` reject the whole write and name the offending entries
|
|
106
|
+
rather than storing a partial vocabulary that would read as a smaller system.
|
|
107
|
+
|
|
108
|
+
`update-design-system` replaces the entire `data` object, so carry the existing
|
|
109
|
+
`tokens` array through any edit or those names are lost.
|
|
110
|
+
|
|
111
|
+
A kit with no stored `tokens` falls back to whatever names its `customCSS`
|
|
112
|
+
declares, which is why the production templates already display real Carbon,
|
|
113
|
+
Material, and Primer token names. Prefer real stored `tokens` when a source
|
|
114
|
+
gives them to you.
|
|
115
|
+
|
|
85
116
|
`imageStyle.styleDescription` is not just metadata — fold it into every
|
|
86
117
|
`generate-asset` prompt for a design linked to this system, alongside the
|
|
87
118
|
subject/composition/lighting direction described in the design-generation
|
|
@@ -456,7 +487,11 @@ When the user provides multiple sources, call all applicable import actions in p
|
|
|
456
487
|
3. **Cross-reference with website** — validates colors/fonts are actually deployed
|
|
457
488
|
4. **Documents supplement** — presentations may reveal brand colors not in code
|
|
458
489
|
5. **Images inform mood** — color temperature, density, visual style
|
|
459
|
-
6. **Aggregate into DesignSystemData** — merge all extracted tokens, resolve
|
|
490
|
+
6. **Aggregate into DesignSystemData** — merge all extracted tokens, resolve
|
|
491
|
+
conflicts, and carry every extracted name into `tokens`. The import actions
|
|
492
|
+
return `cssCustomProperties` keyed by the source's real variable names; those
|
|
493
|
+
keys are the vocabulary, so do not reduce them to the seven `colors` roles and
|
|
494
|
+
drop the rest.
|
|
460
495
|
7. **Call `create-design-system`** with the combined result
|
|
461
496
|
8. **Link to design** via `update-design --designSystemId`
|
|
462
497
|
|
|
@@ -509,6 +544,14 @@ When generating a design that has a linked design system, replace all default CS
|
|
|
509
544
|
}
|
|
510
545
|
```
|
|
511
546
|
|
|
547
|
+
### Reference named tokens, do not inline their values
|
|
548
|
+
|
|
549
|
+
`get-design-system` injects the kit's colour tokens and this requirement into
|
|
550
|
+
the generation context, so follow it there: write `var(--cds-link-primary,
|
|
551
|
+
#0f62fe)` rather than the colour. The fallback is required — exports and
|
|
552
|
+
kit-less designs define no custom properties, and a bare `var()` renders
|
|
553
|
+
transparent.
|
|
554
|
+
|
|
512
555
|
Also update the Google Fonts `<link>` tag to include the design system's fonts:
|
|
513
556
|
|
|
514
557
|
```html
|
|
@@ -388,6 +388,263 @@ export const editorChromeBridgeScript: string = `"use strict";
|
|
|
388
388
|
};
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
// shared/authored-color-styles.ts
|
|
392
|
+
var AUTHORED_COLOR_PROPERTIES = [
|
|
393
|
+
"color",
|
|
394
|
+
"backgroundColor",
|
|
395
|
+
"borderColor",
|
|
396
|
+
"outlineColor",
|
|
397
|
+
// Not colours themselves, but the only place a shadow's colour keeps its name.
|
|
398
|
+
"boxShadow",
|
|
399
|
+
"textShadow"
|
|
400
|
+
];
|
|
401
|
+
var SHORTHAND_FALLBACK = {
|
|
402
|
+
backgroundColor: "background",
|
|
403
|
+
borderColor: "border",
|
|
404
|
+
outlineColor: "outline"
|
|
405
|
+
};
|
|
406
|
+
var INHERITED_PROPERTIES = ["color"];
|
|
407
|
+
var RESET_KEYWORDS = /* @__PURE__ */ new Set([
|
|
408
|
+
"initial",
|
|
409
|
+
"inherit",
|
|
410
|
+
"unset",
|
|
411
|
+
"revert",
|
|
412
|
+
"revert-layer"
|
|
413
|
+
]);
|
|
414
|
+
function isAuthoredColor(value) {
|
|
415
|
+
return typeof value === "string" && value !== "" && !RESET_KEYWORDS.has(value.trim().toLowerCase());
|
|
416
|
+
}
|
|
417
|
+
var LINE_STYLE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
418
|
+
"none",
|
|
419
|
+
"hidden",
|
|
420
|
+
"solid",
|
|
421
|
+
"dashed",
|
|
422
|
+
"dotted",
|
|
423
|
+
"double",
|
|
424
|
+
"groove",
|
|
425
|
+
"ridge",
|
|
426
|
+
"inset",
|
|
427
|
+
"outset"
|
|
428
|
+
]);
|
|
429
|
+
var LINE_WIDTH = /^(thin|medium|thick|-?\\d*\\.?\\d+[a-z%]*)$/i;
|
|
430
|
+
function splitTopLevel(value) {
|
|
431
|
+
const parts = [];
|
|
432
|
+
let depth = 0;
|
|
433
|
+
let current = "";
|
|
434
|
+
for (const char of value) {
|
|
435
|
+
if (char === "(") depth += 1;
|
|
436
|
+
else if (char === ")") depth -= 1;
|
|
437
|
+
if (depth === 0 && /\\s/.test(char)) {
|
|
438
|
+
if (current) parts.push(current);
|
|
439
|
+
current = "";
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
current += char;
|
|
443
|
+
}
|
|
444
|
+
if (current) parts.push(current);
|
|
445
|
+
return parts;
|
|
446
|
+
}
|
|
447
|
+
function lineShorthandColor(value) {
|
|
448
|
+
const rest = splitTopLevel(value).filter((part) => {
|
|
449
|
+
const lower = part.toLowerCase();
|
|
450
|
+
return !LINE_STYLE_KEYWORDS.has(lower) && !LINE_WIDTH.test(part);
|
|
451
|
+
});
|
|
452
|
+
return rest.length === 1 ? rest[0] : null;
|
|
453
|
+
}
|
|
454
|
+
function shorthandColorValue(shorthand, value) {
|
|
455
|
+
const trimmed = value.trim();
|
|
456
|
+
if (!trimmed) return null;
|
|
457
|
+
if (/url\\(|gradient\\(/i.test(trimmed)) return null;
|
|
458
|
+
if (/^var\\(.*\\)$/.test(trimmed)) return trimmed;
|
|
459
|
+
if (shorthand === "border" || shorthand === "outline") {
|
|
460
|
+
return lineShorthandColor(trimmed);
|
|
461
|
+
}
|
|
462
|
+
return /\\s/.test(trimmed) ? null : trimmed;
|
|
463
|
+
}
|
|
464
|
+
function selectorSpecificity(selector) {
|
|
465
|
+
const cleaned = selector.replace(/\\\\./g, "").replace(/\\[[^\\]]*\\]/g, " :attr ").replace(/\\(([^()]*)\\)/g, " ");
|
|
466
|
+
const count = (pattern) => (cleaned.match(pattern) ?? []).length;
|
|
467
|
+
const ids = count(/#[\\w-]+/g);
|
|
468
|
+
const classes = count(/\\.[\\w-]+/g) + count(/:(?!:)[\\w-]+/g);
|
|
469
|
+
const types = count(/(?:^|[\\s>+~])[a-zA-Z][\\w-]*/g) + count(/::[\\w-]+/g);
|
|
470
|
+
return ids * 1e4 + classes * 100 + types;
|
|
471
|
+
}
|
|
472
|
+
function matchedSpecificity(element, selectorText) {
|
|
473
|
+
let best = -1;
|
|
474
|
+
for (const selector of selectorText.split(",")) {
|
|
475
|
+
const trimmed = selector.trim();
|
|
476
|
+
if (!trimmed) continue;
|
|
477
|
+
try {
|
|
478
|
+
if (!element.matches(trimmed)) continue;
|
|
479
|
+
} catch {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
best = Math.max(best, selectorSpecificity(trimmed));
|
|
483
|
+
}
|
|
484
|
+
return best;
|
|
485
|
+
}
|
|
486
|
+
var INLINE_SPECIFICITY = Number.MAX_SAFE_INTEGER;
|
|
487
|
+
function beats(next, current) {
|
|
488
|
+
if (!current) return true;
|
|
489
|
+
if (next.important !== current.important) return next.important;
|
|
490
|
+
if (next.specificity !== current.specificity) {
|
|
491
|
+
return next.specificity > current.specificity;
|
|
492
|
+
}
|
|
493
|
+
return next.order >= current.order;
|
|
494
|
+
}
|
|
495
|
+
function readRules(element, rules, out, counter) {
|
|
496
|
+
if (!rules) return;
|
|
497
|
+
for (let index = 0; index < rules.length; index += 1) {
|
|
498
|
+
const rule = rules[index];
|
|
499
|
+
if (!rule) continue;
|
|
500
|
+
const declaresOwnStyles = Boolean(rule.selectorText && rule.style);
|
|
501
|
+
if (!declaresOwnStyles) {
|
|
502
|
+
if (!rule.cssRules) continue;
|
|
503
|
+
if (rule.conditionText === void 0) {
|
|
504
|
+
readRules(element, rule.cssRules, out, counter);
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
let applies = false;
|
|
508
|
+
try {
|
|
509
|
+
applies = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(rule.conditionText).matches : false;
|
|
510
|
+
} catch {
|
|
511
|
+
applies = false;
|
|
512
|
+
}
|
|
513
|
+
if (applies) readRules(element, rule.cssRules, out, counter);
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
if (rule.cssRules?.length) {
|
|
517
|
+
readRules(element, rule.cssRules, out, counter);
|
|
518
|
+
}
|
|
519
|
+
if (!rule.selectorText || !rule.style) continue;
|
|
520
|
+
const specificity = matchedSpecificity(element, rule.selectorText);
|
|
521
|
+
if (specificity < 0) continue;
|
|
522
|
+
const order = counter.order++;
|
|
523
|
+
const style = rule.style;
|
|
524
|
+
const priority = (name) => style.getPropertyPriority?.(name) === "important";
|
|
525
|
+
const offer = (property, value, cssName) => {
|
|
526
|
+
const candidate = {
|
|
527
|
+
value,
|
|
528
|
+
important: priority(cssName),
|
|
529
|
+
specificity,
|
|
530
|
+
order
|
|
531
|
+
};
|
|
532
|
+
if (beats(candidate, out[property])) out[property] = candidate;
|
|
533
|
+
};
|
|
534
|
+
for (const property of AUTHORED_COLOR_PROPERTIES) {
|
|
535
|
+
const declared = style[property];
|
|
536
|
+
if (isAuthoredColor(declared)) {
|
|
537
|
+
offer(property, declared, cssPropertyName(property));
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
const shorthand = SHORTHAND_FALLBACK[property];
|
|
541
|
+
if (!shorthand) continue;
|
|
542
|
+
const shorthandValue = style[shorthand];
|
|
543
|
+
if (typeof shorthandValue !== "string") continue;
|
|
544
|
+
const colorOnly = shorthandColorValue(shorthand, shorthandValue);
|
|
545
|
+
if (colorOnly) offer(property, colorOnly, shorthand);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
function cssPropertyName(property) {
|
|
550
|
+
return property.replace(/[A-Z]/g, (c) => \`-\${c.toLowerCase()}\`);
|
|
551
|
+
}
|
|
552
|
+
function authoredMatchesPainted(element, property, authored) {
|
|
553
|
+
const view = element.ownerDocument?.defaultView;
|
|
554
|
+
if (!view || typeof view.getComputedStyle !== "function") return true;
|
|
555
|
+
let painted = "";
|
|
556
|
+
try {
|
|
557
|
+
painted = view.getComputedStyle(element)[property] ?? "";
|
|
558
|
+
} catch {
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
if (!painted) return true;
|
|
562
|
+
const probe = element.ownerDocument.createElement("span");
|
|
563
|
+
const name = cssPropertyName(property);
|
|
564
|
+
probe.style.setProperty("display", "none");
|
|
565
|
+
probe.style.setProperty(name, authored);
|
|
566
|
+
if (!probe.style.getPropertyValue(name)) return false;
|
|
567
|
+
element.appendChild(probe);
|
|
568
|
+
let resolved = "";
|
|
569
|
+
try {
|
|
570
|
+
resolved = view.getComputedStyle(probe)[property] ?? "";
|
|
571
|
+
} catch {
|
|
572
|
+
resolved = "";
|
|
573
|
+
}
|
|
574
|
+
probe.remove();
|
|
575
|
+
return resolved === "" || resolved === painted;
|
|
576
|
+
}
|
|
577
|
+
function collectForElement(element) {
|
|
578
|
+
const ranked = {};
|
|
579
|
+
const counter = { order: 0 };
|
|
580
|
+
try {
|
|
581
|
+
const doc = element.ownerDocument;
|
|
582
|
+
const sheets = [
|
|
583
|
+
...Array.from(doc?.styleSheets ?? []),
|
|
584
|
+
...Array.from(
|
|
585
|
+
doc?.adoptedStyleSheets ?? []
|
|
586
|
+
)
|
|
587
|
+
];
|
|
588
|
+
for (const sheet of sheets) {
|
|
589
|
+
let rules;
|
|
590
|
+
try {
|
|
591
|
+
rules = sheet.cssRules;
|
|
592
|
+
} catch {
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
readRules(element, rules, ranked, counter);
|
|
596
|
+
}
|
|
597
|
+
} catch (error) {
|
|
598
|
+
console.warn(
|
|
599
|
+
"[design] could not read stylesheets for authored colours; token names will fall back to resolved values",
|
|
600
|
+
error
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
const inline = element.style;
|
|
604
|
+
if (inline) {
|
|
605
|
+
const order = counter.order++;
|
|
606
|
+
for (const property of AUTHORED_COLOR_PROPERTIES) {
|
|
607
|
+
const value = inline[property];
|
|
608
|
+
if (!isAuthoredColor(value)) continue;
|
|
609
|
+
const candidate = {
|
|
610
|
+
value,
|
|
611
|
+
important: inline.getPropertyPriority?.(cssPropertyName(property)) === "important",
|
|
612
|
+
specificity: INLINE_SPECIFICITY,
|
|
613
|
+
order
|
|
614
|
+
};
|
|
615
|
+
if (beats(candidate, ranked[property])) ranked[property] = candidate;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
const out = {};
|
|
619
|
+
for (const [property, candidate] of Object.entries(ranked)) {
|
|
620
|
+
out[property] = candidate.value;
|
|
621
|
+
}
|
|
622
|
+
return out;
|
|
623
|
+
}
|
|
624
|
+
function collectAuthoredColorStyles(element) {
|
|
625
|
+
const out = collectForElement(element);
|
|
626
|
+
for (const property of Object.keys(out)) {
|
|
627
|
+
if (!authoredMatchesPainted(element, property, out[property])) {
|
|
628
|
+
delete out[property];
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
for (const property of INHERITED_PROPERTIES) {
|
|
632
|
+
if (out[property]) continue;
|
|
633
|
+
let ancestor = element.parentElement;
|
|
634
|
+
while (ancestor) {
|
|
635
|
+
const inherited = collectForElement(ancestor)[property];
|
|
636
|
+
if (inherited) {
|
|
637
|
+
if (authoredMatchesPainted(element, property, inherited)) {
|
|
638
|
+
out[property] = inherited;
|
|
639
|
+
}
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
ancestor = ancestor.parentElement;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return out;
|
|
646
|
+
}
|
|
647
|
+
|
|
391
648
|
// app/components/design/bridge/editor-chrome.bridge.ts
|
|
392
649
|
(function() {
|
|
393
650
|
if (window.__anEditorChromeBridge) return;
|
|
@@ -1633,7 +1890,13 @@ export const editorChromeBridgeScript: string = `"use strict";
|
|
|
1633
1890
|
"width",
|
|
1634
1891
|
"height",
|
|
1635
1892
|
"transform",
|
|
1636
|
-
"whiteSpace"
|
|
1893
|
+
"whiteSpace",
|
|
1894
|
+
"color",
|
|
1895
|
+
"backgroundColor",
|
|
1896
|
+
"borderColor",
|
|
1897
|
+
"outlineColor",
|
|
1898
|
+
"boxShadow",
|
|
1899
|
+
"textShadow"
|
|
1637
1900
|
];
|
|
1638
1901
|
function collectInlineStyles(el) {
|
|
1639
1902
|
var styles = {};
|
|
@@ -1837,6 +2100,7 @@ export const editorChromeBridgeScript: string = `"use strict";
|
|
|
1837
2100
|
whiteSpace: cs.whiteSpace
|
|
1838
2101
|
},
|
|
1839
2102
|
inlineStyles: collectInlineStyles(el),
|
|
2103
|
+
authoredColorStyles: collectAuthoredColorStyles(el),
|
|
1840
2104
|
primitiveKind: el.getAttribute("data-an-primitive") || void 0,
|
|
1841
2105
|
portableStyleSnapshot: collectPortableStyleSnapshot(el),
|
|
1842
2106
|
boundingRect: {
|
|
@@ -8,6 +8,7 @@ import { nanoid } from "nanoid";
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
import { getDb, schema } from "../server/db/index.js";
|
|
11
|
+
import { assertStorableDesignSystemTokens } from "../server/lib/design-system-tokens.js";
|
|
11
12
|
import {
|
|
12
13
|
DESIGN_SYSTEM_TEMPLATE_IDS,
|
|
13
14
|
getProductionDesignSystemTemplate,
|
|
@@ -39,7 +40,13 @@ export const createDesignSystemSchema = z
|
|
|
39
40
|
.min(1, "data is required")
|
|
40
41
|
.optional()
|
|
41
42
|
.describe(
|
|
42
|
-
"JSON string of DesignSystemData (required without templateId; templates supply their verified token snapshot)"
|
|
43
|
+
"JSON string of DesignSystemData (required without templateId; templates supply their verified token snapshot). " +
|
|
44
|
+
"Include a `tokens` array whenever the source names its tokens: " +
|
|
45
|
+
// guard:allow-raw-color — an agent-facing payload example, not a styled value
|
|
46
|
+
'[{ "name": "interactive-01", "cssVar": "--cds-interactive-01", "value": "#0F62FE", ' +
|
|
47
|
+
'"type": "color", "group": "Colors/Interactive", "source": "Carbon v11" }]. ' +
|
|
48
|
+
"The seven `colors` roles are a summary for generation, not a place to squash a " +
|
|
49
|
+
"real system's vocabulary into — send both.",
|
|
43
50
|
),
|
|
44
51
|
assets: z
|
|
45
52
|
.string()
|
|
@@ -111,15 +118,7 @@ export default defineAction({
|
|
|
111
118
|
throw new Error("title and data are required");
|
|
112
119
|
}
|
|
113
120
|
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
const parsed = JSON.parse(resolvedData);
|
|
117
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
118
|
-
throw new Error();
|
|
119
|
-
}
|
|
120
|
-
} catch {
|
|
121
|
-
throw new Error("data must be a valid JSON object string");
|
|
122
|
-
}
|
|
121
|
+
assertStorableDesignSystemTokens(resolvedData);
|
|
123
122
|
if (assets) {
|
|
124
123
|
try {
|
|
125
124
|
JSON.parse(assets);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import {
|
|
3
|
+
brandKitRoleTokens,
|
|
4
|
+
resolveBrandKitTokens,
|
|
5
|
+
} from "@agent-native/core/brand-kit/tokens";
|
|
2
6
|
import {
|
|
3
7
|
hydrateBuilderDesignSystemReference,
|
|
4
8
|
parseBuilderDesignSystemProxyReference,
|
|
@@ -61,6 +65,54 @@ function formatTokenValues(tokenValues: Record<string, string>): string[] {
|
|
|
61
65
|
];
|
|
62
66
|
}
|
|
63
67
|
|
|
68
|
+
const MAX_LISTED_TOKENS = 40;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Require generated CSS to reference tokens rather than paste their values. A
|
|
72
|
+
* colour cannot identify a token afterwards — Carbon points four at one blue.
|
|
73
|
+
*/
|
|
74
|
+
function tokenReferenceDirective(data: unknown): string[] {
|
|
75
|
+
if (!data || typeof data !== "object") return [];
|
|
76
|
+
const kit = data as {
|
|
77
|
+
tokens?: unknown;
|
|
78
|
+
customCSS?: unknown;
|
|
79
|
+
colors?: object | null;
|
|
80
|
+
borders?: { radius?: string } | null;
|
|
81
|
+
spacing?: object | null;
|
|
82
|
+
};
|
|
83
|
+
const tokens = [
|
|
84
|
+
...resolveBrandKitTokens(kit),
|
|
85
|
+
...brandKitRoleTokens(kit),
|
|
86
|
+
].filter((token) => token.type === "color");
|
|
87
|
+
if (tokens.length === 0) return [];
|
|
88
|
+
|
|
89
|
+
const seen = new Set<string>();
|
|
90
|
+
const listed: string[] = [];
|
|
91
|
+
for (const token of tokens) {
|
|
92
|
+
if (seen.has(token.cssVar)) continue;
|
|
93
|
+
seen.add(token.cssVar);
|
|
94
|
+
listed.push(`- ${token.cssVar}: ${token.value}`);
|
|
95
|
+
if (listed.length >= MAX_LISTED_TOKENS) break;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return [
|
|
99
|
+
"",
|
|
100
|
+
"### Reference these color tokens, do not paste their values",
|
|
101
|
+
"Write the token reference with its value as a fallback:",
|
|
102
|
+
// guard:allow-raw-color — the do/don't example shown to the generating agent
|
|
103
|
+
" background: var(--color-accent, #0f62fe); /* yes */",
|
|
104
|
+
// guard:allow-raw-color — the counter-example it must not copy
|
|
105
|
+
" background: #0f62fe; /* no */",
|
|
106
|
+
"The fallback is required — exports and unlinked designs define no custom",
|
|
107
|
+
"properties, and a bare var() renders transparent there. Define every token",
|
|
108
|
+
"you reference in the screen's `:root` block too.",
|
|
109
|
+
...listed,
|
|
110
|
+
seen.size > MAX_LISTED_TOKENS
|
|
111
|
+
? `- …and ${seen.size - MAX_LISTED_TOKENS} more; call get-design-system for the full set.`
|
|
112
|
+
: "",
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
|
|
64
116
|
function buildDesignSystemAgentContext({
|
|
65
117
|
id,
|
|
66
118
|
title,
|
|
@@ -82,6 +134,7 @@ function buildDesignSystemAgentContext({
|
|
|
82
134
|
"## Selected Design System Context",
|
|
83
135
|
`Use "${title}" (id: ${id}) as the visual source of truth for this generation.`,
|
|
84
136
|
"Apply these tokens, assets, and usage notes before choosing colors, type, spacing, radius, imagery, or component language.",
|
|
137
|
+
...tokenReferenceDirective(parseJson(data)),
|
|
85
138
|
];
|
|
86
139
|
|
|
87
140
|
if (description?.trim()) {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import {
|
|
3
|
+
classifyBrandKitToken as classifyVar,
|
|
4
|
+
friendlyTokenName as friendlyName,
|
|
5
|
+
isColorTokenValue as isColorValue,
|
|
6
|
+
} from "@agent-native/core/brand-kit/tokens";
|
|
2
7
|
import { buildDeepLink } from "@agent-native/core/server";
|
|
3
8
|
import {
|
|
4
9
|
CODE_MAX_FILES,
|
|
@@ -116,70 +121,6 @@ function designDeepLink(designId: string): string {
|
|
|
116
121
|
});
|
|
117
122
|
}
|
|
118
123
|
|
|
119
|
-
function classifyVar(name: string, value: string): ImportedTokenType {
|
|
120
|
-
const n = name.toLowerCase();
|
|
121
|
-
if (
|
|
122
|
-
/color|bg|background|text|border|accent|primary|secondary|surface|muted|foreground|fill|stroke/i.test(
|
|
123
|
-
n,
|
|
124
|
-
) ||
|
|
125
|
-
isColorValue(value)
|
|
126
|
-
) {
|
|
127
|
-
return "color";
|
|
128
|
-
}
|
|
129
|
-
if (/font|size|leading|tracking|weight|heading|body|type/i.test(n)) {
|
|
130
|
-
return "typography";
|
|
131
|
-
}
|
|
132
|
-
if (/radius|rounded/i.test(n)) return "radius";
|
|
133
|
-
if (/spacing|gap|padding|margin|space/i.test(n)) return "spacing";
|
|
134
|
-
if (/shadow|blur|drop/i.test(n)) return "shadow";
|
|
135
|
-
return "other";
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function isColorValue(value: string): boolean {
|
|
139
|
-
const normalized = value.trim().toLowerCase();
|
|
140
|
-
if (
|
|
141
|
-
/^(#[0-9a-fA-F]{3,8}|rgba?\(|hsla?\(|oklch\(|color\()/i.test(normalized)
|
|
142
|
-
) {
|
|
143
|
-
return true;
|
|
144
|
-
}
|
|
145
|
-
return [
|
|
146
|
-
"red",
|
|
147
|
-
"blue",
|
|
148
|
-
"green",
|
|
149
|
-
"yellow",
|
|
150
|
-
"orange",
|
|
151
|
-
"purple",
|
|
152
|
-
"pink",
|
|
153
|
-
"cyan",
|
|
154
|
-
"magenta",
|
|
155
|
-
"teal",
|
|
156
|
-
"navy",
|
|
157
|
-
"maroon",
|
|
158
|
-
"coral",
|
|
159
|
-
"salmon",
|
|
160
|
-
"gold",
|
|
161
|
-
"silver",
|
|
162
|
-
"gray",
|
|
163
|
-
"grey",
|
|
164
|
-
"indigo",
|
|
165
|
-
"violet",
|
|
166
|
-
"lime",
|
|
167
|
-
"olive",
|
|
168
|
-
"aqua",
|
|
169
|
-
"fuchsia",
|
|
170
|
-
"crimson",
|
|
171
|
-
"turquoise",
|
|
172
|
-
"ivory",
|
|
173
|
-
"beige",
|
|
174
|
-
"lavender",
|
|
175
|
-
"tan",
|
|
176
|
-
"khaki",
|
|
177
|
-
"plum",
|
|
178
|
-
"orchid",
|
|
179
|
-
"sienna",
|
|
180
|
-
].includes(normalized);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
124
|
function toKebab(value: string): string {
|
|
184
125
|
return (
|
|
185
126
|
value
|
|
@@ -193,13 +134,6 @@ function toKebab(value: string): string {
|
|
|
193
134
|
);
|
|
194
135
|
}
|
|
195
136
|
|
|
196
|
-
function friendlyName(cssVar: string): string {
|
|
197
|
-
return cssVar
|
|
198
|
-
.replace(/^--/, "")
|
|
199
|
-
.replace(/[-_]/g, " ")
|
|
200
|
-
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
201
|
-
}
|
|
202
|
-
|
|
203
137
|
function normalizeValue(value: string): string {
|
|
204
138
|
return value
|
|
205
139
|
.trim()
|