@agent-native/core 0.153.10 → 0.154.1

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.
Files changed (149) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/AGENTS.md +20 -30
  3. package/corpus/templates/analytics/actions/search-dashboard-references.ts +58 -0
  4. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
  5. package/corpus/templates/analytics/app/lib/utils.ts +1 -1
  6. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewSqlPopover.tsx +1 -1
  7. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +2 -0
  8. package/corpus/templates/analytics/server/lib/dashboards-store.ts +366 -3
  9. package/corpus/templates/analytics/server/lib/real-data-actions.ts +3 -1
  10. package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -2
  11. package/corpus/templates/calendar/app/components/calendar/EventDialog.tsx +1 -1
  12. package/corpus/templates/calendar/app/lib/utils.ts +1 -1
  13. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +32 -5
  14. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +15 -15
  15. package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
  16. package/corpus/templates/clips/app/lib/utils.ts +5 -6
  17. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +43 -8
  18. package/corpus/templates/clips/desktop/src/app.tsx +26 -12
  19. package/corpus/templates/clips/desktop/src/lib/recorder.ts +23 -9
  20. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +261 -11
  21. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +21 -3
  22. package/corpus/templates/clips/shared/mic-audio-cleanup.ts +350 -0
  23. package/corpus/templates/content/actions/resolve-content-landing.ts +162 -32
  24. package/corpus/templates/content/app/i18n-data.ts +10 -10
  25. package/corpus/templates/content/scripts/check-native-deps.mjs +181 -15
  26. package/corpus/templates/content/shared/content-landing.ts +5 -0
  27. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +79 -7
  28. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +14 -2
  29. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +2 -2
  30. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +35 -11
  31. package/corpus/templates/design/actions/get-design-system.ts +103 -3
  32. package/corpus/templates/design/actions/import-design-tokens.ts +37 -8
  33. package/corpus/templates/design/actions/import-github.ts +32 -138
  34. package/corpus/templates/design/actions/index-design-system-with-builder.ts +49 -16
  35. package/corpus/templates/design/actions/present-design-variants.ts +93 -4
  36. package/corpus/templates/design/actions/run-design-audit.ts +229 -0
  37. package/corpus/templates/design/actions/sync-design-system-with-builder.ts +64 -0
  38. package/corpus/templates/design/app/components/design/code-workbench/commands.ts +2 -2
  39. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +17 -4
  40. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +5 -1
  41. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +5 -1
  42. package/corpus/templates/design/app/hooks/use-question-flow.ts +81 -5
  43. package/corpus/templates/design/app/i18n/en-US.ts +1 -1
  44. package/corpus/templates/design/app/i18n-data.ts +32 -1
  45. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +10 -10
  46. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +83 -11
  47. package/corpus/templates/design/app/pages/DesignSystems.tsx +14 -0
  48. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +22 -0
  49. package/corpus/templates/design/changelog/2026-08-13-a-screen-that-would-render-behind-an-invisible-alpine-overla.md +6 -0
  50. package/corpus/templates/design/changelog/2026-08-13-attach-a-ui-screenshot-and-the-design-is-reproduced-not-rein.md +6 -0
  51. package/corpus/templates/design/changelog/2026-08-13-linking-a-design-system-means-you-never-get-generic-directio.md +6 -0
  52. package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-reports-when-a-screen-ignores-its-linke.md +6 -0
  53. package/corpus/templates/design/changelog/2026-08-13-the-design-system-preview-now-shows-how-many-named-tokens-we.md +6 -0
  54. package/corpus/templates/design/changelog/2026-08-13-your-design-system-prompt-and-reference-screenshots-now-reac.md +6 -0
  55. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +18 -2
  56. package/corpus/templates/design/shared/design-review.ts +1 -0
  57. package/corpus/templates/design/shared/html-integrity.ts +171 -0
  58. package/corpus/templates/mail/app/lib/utils.ts +1 -1
  59. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +10 -6
  60. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +41 -3
  61. package/corpus/templates/slides/actions/patch-deck.ts +12 -3
  62. package/corpus/templates/slides/actions/sync-design-system-with-builder.ts +64 -0
  63. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
  64. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +193 -20
  65. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +73 -1
  66. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +64 -34
  67. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +34 -41
  68. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +119 -91
  69. package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +16 -0
  70. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +15 -0
  71. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +158 -31
  72. package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +30 -14
  73. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +2 -2
  74. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +4 -1
  75. package/corpus/templates/slides/app/components/editor/rich-text-selection.ts +14 -0
  76. package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +24 -0
  77. package/corpus/templates/slides/app/context/DeckContext.tsx +19 -31
  78. package/corpus/templates/slides/app/global.css +24 -0
  79. package/corpus/templates/slides/app/hooks/use-navigation-state.ts +20 -17
  80. package/corpus/templates/slides/app/i18n/en-US.ts +19 -10
  81. package/corpus/templates/slides/app/lib/deck-filter.ts +47 -0
  82. package/corpus/templates/slides/app/lib/export-google-slides-client.ts +15 -4
  83. package/corpus/templates/slides/app/lib/export-pptx-client.ts +637 -1
  84. package/corpus/templates/slides/app/lib/image-download.ts +84 -0
  85. package/corpus/templates/slides/app/lib/utils.ts +1 -1
  86. package/corpus/templates/slides/app/pages/DeckEditor.tsx +9 -0
  87. package/corpus/templates/slides/app/pages/Index.tsx +25 -9
  88. package/corpus/templates/slides/changelog/2026-08-13-clicking-a-slide-thumbnail-now-focuses-it-so-the-slide-shortc.md +6 -0
  89. package/corpus/templates/slides/changelog/2026-08-13-duplicating-or-restoring-a-slide-now-keeps-its-transition-ani.md +6 -0
  90. package/corpus/templates/slides/changelog/2026-08-13-opening-presentation-mode-from-the-agent-now-starts-on-the-re.md +6 -0
  91. package/corpus/templates/slides/changelog/2026-08-13-slide-transitions-can-be-set-from-a-visible-control-in-the-sl.md +6 -0
  92. package/corpus/templates/slides/netlify.toml +1 -0
  93. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +14 -2
  94. package/dist/a2a/audience.d.ts +2 -0
  95. package/dist/a2a/audience.js +23 -0
  96. package/dist/a2a/client.d.ts +4 -0
  97. package/dist/a2a/client.js +32 -14
  98. package/dist/a2a/server.d.ts +9 -4
  99. package/dist/a2a/server.js +52 -25
  100. package/dist/a2a/types.d.ts +4 -4
  101. package/dist/agent/production-agent.js +1 -1
  102. package/dist/brand-kit/types.d.ts +10 -2
  103. package/dist/cli/code.js +1 -1
  104. package/dist/cli/index.js +30 -1
  105. package/dist/cli/native-dependencies.d.ts +24 -0
  106. package/dist/cli/native-dependencies.js +264 -0
  107. package/dist/cli/plan-local.js +1 -1
  108. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  109. package/dist/cli/skills-content/design-visual-edit-skill.js +2 -2
  110. package/dist/cli/visualize-repo.js +1 -1
  111. package/dist/client/FeedbackButton.js +12 -12
  112. package/dist/client/agent-chat-adapter.js +3 -4
  113. package/dist/client/assistant-ui-recovery.d.ts +1 -1
  114. package/dist/client/assistant-ui-recovery.js +4 -0
  115. package/dist/client/chat/message-components.d.ts +11 -3
  116. package/dist/client/chat/message-components.js +31 -5
  117. package/dist/client/chat/tool-call-display.d.ts +2 -1
  118. package/dist/client/chat/tool-call-display.js +33 -15
  119. package/dist/client/db-admin/SqlEditor.js +1 -1
  120. package/dist/client/dev-overlay/DevOverlay.js +2 -2
  121. package/dist/client/extensions/portable-extension.js +4 -0
  122. package/dist/client/guided-questions.d.ts +8 -0
  123. package/dist/client/guided-questions.js +12 -3
  124. package/dist/client/sharing/ShareButton.js +16 -24
  125. package/dist/client/sse-event-processor.js +35 -15
  126. package/dist/client/tool-display.d.ts +9 -0
  127. package/dist/client/tool-display.js +39 -0
  128. package/dist/client/vite-dev-recovery-script.js +1 -1
  129. package/dist/collab/awareness.d.ts +2 -2
  130. package/dist/collab/routes.d.ts +1 -1
  131. package/dist/collab/struct-routes.d.ts +1 -1
  132. package/dist/extensions/html-shell.js +5 -0
  133. package/dist/notifications/routes.d.ts +1 -1
  134. package/dist/observability/routes.d.ts +3 -3
  135. package/dist/provider-api/actions/custom-provider-registration.d.ts +2 -2
  136. package/dist/server/agent-capabilities.js +0 -0
  137. package/dist/server/agent-chat/action-filters-a2a.d.ts +14 -13
  138. package/dist/server/agent-chat/action-filters-a2a.js +59 -23
  139. package/dist/server/agent-chat/prompt-resources.js +17 -16
  140. package/dist/server/agent-chat-plugin.d.ts +9 -0
  141. package/dist/server/agent-chat-plugin.js +29 -7
  142. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  143. package/dist/server/builder-design-systems.d.ts +33 -1
  144. package/dist/server/builder-design-systems.js +293 -11
  145. package/dist/server/design-token-utils.d.ts +11 -0
  146. package/dist/server/design-token-utils.js +68 -16
  147. package/dist/server/index.d.ts +1 -1
  148. package/dist/server/index.js +1 -1
  149. package/package.json +2 -2
@@ -489,6 +489,203 @@ export function checkTokenDrift(
489
489
  return findings;
490
490
  }
491
491
 
492
+ // ---------------------------------------------------------------------------
493
+ // Design-system adherence check
494
+ // ---------------------------------------------------------------------------
495
+ //
496
+ // checkTokenDrift above only proves the screens agree with EACH OTHER. A design
497
+ // can be perfectly self-consistent and still ignore the brand it is linked to,
498
+ // which is the most-reported failure of generation: the user links their system,
499
+ // the agent writes its own palette, and nothing anywhere notices. This compares
500
+ // the saved HTML against the linked kit's own values so the drift is reportable
501
+ // instead of invisible.
502
+
503
+ /** The subset of a Brand Kit this check can verify against rendered HTML. */
504
+ export interface DesignSystemExpectation {
505
+ title: string;
506
+ fonts: string[];
507
+ /** Lowercase six-digit hex values, normalized from the kit's own notation. */
508
+ colors: string[];
509
+ /** Custom-property names the kit names, e.g. `--color-primary`. */
510
+ cssVars: string[];
511
+ /**
512
+ * The kit's stored `data` could not be parsed. Adherence is then UNKNOWN,
513
+ * which is not the same as satisfied — a silent empty expectation would
514
+ * report a corrupt design system as a clean audit.
515
+ */
516
+ unreadable?: boolean;
517
+ }
518
+
519
+ function normalizeHex(value: unknown): string | null {
520
+ if (typeof value !== "string") return null;
521
+ const match = value.trim().match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
522
+ if (!match) return null;
523
+ const hex = match[1].toLowerCase();
524
+ // Expand shorthand so 3- and 6-digit forms compare in one space.
525
+ return hex.length === 3
526
+ ? `#${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`
527
+ : `#${hex}`;
528
+ }
529
+
530
+ function expandShortHexes(html: string): string {
531
+ return html.replace(
532
+ /#([0-9a-f])([0-9a-f])([0-9a-f])\b/gi,
533
+ (_m, r: string, g: string, b: string) =>
534
+ `#${r}${r}${g}${g}${b}${b}`.toLowerCase(),
535
+ );
536
+ }
537
+
538
+ /**
539
+ * Build the checkable expectation from a kit's stored `data` JSON. Returns null
540
+ * when the kit carries nothing verifiable — "we cannot check" must not render
541
+ * as "the design complies".
542
+ */
543
+ export function designSystemExpectation(
544
+ title: string,
545
+ data: string | null | undefined,
546
+ ): DesignSystemExpectation | null {
547
+ const unreadable: DesignSystemExpectation = {
548
+ title,
549
+ fonts: [],
550
+ colors: [],
551
+ cssVars: [],
552
+ unreadable: true,
553
+ };
554
+ let parsed: Record<string, unknown> | null = null;
555
+ try {
556
+ parsed = data ? (JSON.parse(data) as Record<string, unknown>) : null;
557
+ } catch {
558
+ return unreadable;
559
+ }
560
+ if (!parsed) return null;
561
+
562
+ const typography = (parsed.typography ?? {}) as Record<string, unknown>;
563
+ const fonts = [typography.headingFont, typography.bodyFont]
564
+ .filter((font): font is string => typeof font === "string" && !!font.trim())
565
+ .map((font) => font.trim());
566
+
567
+ const colorRoles = (parsed.colors ?? {}) as Record<string, unknown>;
568
+ const colors = Object.values(colorRoles)
569
+ .map(normalizeHex)
570
+ .filter((hex): hex is string => hex !== null);
571
+
572
+ const cssVars = Array.isArray(parsed.tokens)
573
+ ? parsed.tokens
574
+ .map((token) =>
575
+ token && typeof token === "object"
576
+ ? (token as { cssVar?: unknown }).cssVar
577
+ : null,
578
+ )
579
+ .filter(
580
+ (cssVar): cssVar is string =>
581
+ typeof cssVar === "string" && cssVar.startsWith("--"),
582
+ )
583
+ : [];
584
+
585
+ if (fonts.length === 0 && colors.length === 0 && cssVars.length === 0) {
586
+ return null;
587
+ }
588
+ return {
589
+ title,
590
+ fonts: Array.from(new Set(fonts)),
591
+ colors: Array.from(new Set(colors)),
592
+ cssVars: Array.from(new Set(cssVars)),
593
+ };
594
+ }
595
+
596
+ /**
597
+ * Flag a screen that uses none of its linked design system's fonts, or none of
598
+ * its colors. Deliberately a "none of them" test rather than a per-value one:
599
+ * a design legitimately uses a subset of a kit, but a design using zero of it
600
+ * is not following it at all. Pure so it can be unit tested without a DB.
601
+ */
602
+ export function checkDesignSystemAdherence(
603
+ html: string,
604
+ expectation: DesignSystemExpectation | null,
605
+ filename: string,
606
+ ): A11yFinding[] {
607
+ if (!expectation) return [];
608
+ if (expectation.unreadable) {
609
+ return [
610
+ {
611
+ id: `design-system-drift:${filename}:unreadable`,
612
+ severity: "warning",
613
+ category: "design-system-drift",
614
+ message: `"${expectation.title}" could not be read, so adherence was not checked.`,
615
+ detail:
616
+ "The linked design system's stored token data is not valid JSON. " +
617
+ "Adherence is unknown, not satisfied — re-import or re-index the " +
618
+ "system before treating this screen as on-brand.",
619
+ selector: ":root",
620
+ fixAvailable: false,
621
+ },
622
+ ];
623
+ }
624
+ const findings: A11yFinding[] = [];
625
+ const haystack = expandShortHexes(html.toLowerCase());
626
+
627
+ if (expectation.fonts.length > 0) {
628
+ const used = expectation.fonts.filter((font) =>
629
+ haystack.includes(font.toLowerCase()),
630
+ );
631
+ if (used.length === 0) {
632
+ findings.push({
633
+ id: `design-system-drift:${filename}:fonts`,
634
+ severity: "warning",
635
+ category: "design-system-drift",
636
+ message: `${filename} uses none of "${expectation.title}"'s fonts.`,
637
+ detail:
638
+ `The linked design system specifies ${expectation.fonts.join(", ")}, ` +
639
+ `but ${filename} references none of them. Load and apply the system's ` +
640
+ "typography, or tell the user you are deviating and why.",
641
+ selector: ":root",
642
+ fixAvailable: false,
643
+ });
644
+ }
645
+ }
646
+
647
+ if (expectation.colors.length > 0) {
648
+ const used = expectation.colors.filter((hex) => haystack.includes(hex));
649
+ if (used.length === 0) {
650
+ findings.push({
651
+ id: `design-system-drift:${filename}:colors`,
652
+ severity: "warning",
653
+ category: "design-system-drift",
654
+ message: `${filename} uses none of "${expectation.title}"'s colors.`,
655
+ detail:
656
+ `The linked design system defines ${expectation.colors.slice(0, 6).join(", ")}` +
657
+ `${expectation.colors.length > 6 ? ", …" : ""}, but ${filename} uses none of ` +
658
+ "them. Put the system's values in the `:root` token block instead of an " +
659
+ "invented palette.",
660
+ selector: ":root",
661
+ fixAvailable: false,
662
+ });
663
+ }
664
+ }
665
+
666
+ if (expectation.cssVars.length > 0) {
667
+ const used = expectation.cssVars.filter((cssVar) =>
668
+ haystack.includes(cssVar.toLowerCase()),
669
+ );
670
+ if (used.length === 0) {
671
+ findings.push({
672
+ id: `design-system-drift:${filename}:css-vars`,
673
+ severity: "info",
674
+ category: "design-system-drift",
675
+ message: `${filename} uses none of "${expectation.title}"'s token names.`,
676
+ detail:
677
+ `The system names ${expectation.cssVars.length} custom properties (e.g. ` +
678
+ `${expectation.cssVars.slice(0, 4).join(", ")}). Reusing those exact names ` +
679
+ "keeps the prototype swappable with the real codebase.",
680
+ selector: ":root",
681
+ fixAvailable: false,
682
+ });
683
+ }
684
+ }
685
+
686
+ return findings;
687
+ }
688
+
492
689
  // ---------------------------------------------------------------------------
493
690
  // Live-content helper (matches the pattern in other actions)
494
691
  // ---------------------------------------------------------------------------
@@ -519,6 +716,11 @@ export default defineAction({
519
716
  "form labels, focus-visibility gaps, reduced-motion coverage, and — for " +
520
717
  "multi-screen designs — token drift (the audited screen's :root custom " +
521
718
  "properties diverging from index.html's). " +
719
+ "When the design is linked to a design system, also reports " +
720
+ "design-system drift: the screen using none of the linked system's fonts, " +
721
+ "colors, or token names. Treat that finding as a generation bug to fix, " +
722
+ "not a style opinion — reload the system with `get-design-system` and " +
723
+ "apply its values rather than explaining the deviation. " +
522
724
  "Returns A11yFinding[] that can be shown in the Review panel or persisted " +
523
725
  "via create-design-review-snapshot. No writes are performed.",
524
726
  schema: z.object({
@@ -612,6 +814,32 @@ export default defineAction({
612
814
  finding.id.includes(`token-drift:${file.filename}:`),
613
815
  );
614
816
 
817
+ // Adherence to the LINKED system, which token drift cannot see.
818
+ const [linkedSystem] = await db
819
+ .select({
820
+ title: schema.designSystems.title,
821
+ data: schema.designSystems.data,
822
+ })
823
+ .from(schema.designs)
824
+ .innerJoin(
825
+ schema.designSystems,
826
+ eq(schema.designs.designSystemId, schema.designSystems.id),
827
+ )
828
+ .where(
829
+ and(
830
+ eq(schema.designs.id, designId),
831
+ accessFilter(schema.designSystems, schema.designSystemShares),
832
+ ),
833
+ )
834
+ .limit(1);
835
+ const designSystemFindings = linkedSystem
836
+ ? checkDesignSystemAdherence(
837
+ html,
838
+ designSystemExpectation(linkedSystem.title, linkedSystem.data),
839
+ file.filename,
840
+ )
841
+ : [];
842
+
615
843
  // Run all audit checks over the static HTML.
616
844
  const findings: A11yFinding[] = [
617
845
  ...checkMissingAlt(html),
@@ -621,6 +849,7 @@ export default defineAction({
621
849
  ...checkFocusVisibility(html),
622
850
  ...checkContrastHint(html),
623
851
  ...tokenDriftFindings,
852
+ ...designSystemFindings,
624
853
  ];
625
854
 
626
855
  // Summarise by severity for the agent context.
@@ -0,0 +1,64 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import {
3
+ parseBuilderDesignSystemProxyReference,
4
+ startBuilderDesignSystemIndex,
5
+ } from "@agent-native/core/server";
6
+ import { getRequestUserEmail } from "@agent-native/core/server/request-context";
7
+ import { assertAccess, resolveAccess } from "@agent-native/core/sharing";
8
+ import { z } from "zod";
9
+
10
+ import "../server/db/index.js";
11
+ import { upsertBuilderProxyDesignSystem } from "../server/lib/builder-design-system-proxy.js";
12
+
13
+ export default defineAction({
14
+ description:
15
+ "Re-index a Builder-backed GitHub design system using its persisted repository, ref, and file/folder scope. Requires editor access.",
16
+ schema: z.object({
17
+ id: z.string().min(1).describe("Local design system id"),
18
+ }),
19
+ run: async ({ id }) => {
20
+ await assertAccess("design-system", id, "editor");
21
+ const access = await resolveAccess("design-system", id);
22
+ if (!access) throw new Error("Design system not found");
23
+
24
+ const reference = parseBuilderDesignSystemProxyReference(
25
+ access.resource.data,
26
+ );
27
+ if (!reference?.githubSources?.length) {
28
+ throw new Error(
29
+ "This design system has no persisted GitHub source scope to sync.",
30
+ );
31
+ }
32
+ if (reference.sourceKind === "mixed") {
33
+ throw new Error(
34
+ "This design system combines GitHub with non-replayable sources. Re-import all sources before syncing it.",
35
+ );
36
+ }
37
+
38
+ const result = await startBuilderDesignSystemIndex({
39
+ projectName: access.resource.title,
40
+ description: access.resource.description ?? undefined,
41
+ githubRepos: reference.githubSources,
42
+ });
43
+ const ownerEmail = getRequestUserEmail();
44
+ if (!ownerEmail) throw new Error("no authenticated user");
45
+
46
+ const proxy = await upsertBuilderProxyDesignSystem({
47
+ result,
48
+ ownerEmail: access.resource.ownerEmail,
49
+ orgId: access.resource.orgId,
50
+ projectName: access.resource.title,
51
+ description: access.resource.description ?? undefined,
52
+ sourceKind: reference.sourceKind,
53
+ githubSources: reference.githubSources,
54
+ localDesignSystemId: id,
55
+ });
56
+
57
+ return {
58
+ ...result,
59
+ ...proxy,
60
+ synced: true,
61
+ githubSourceCount: reference.githubSources.length,
62
+ };
63
+ },
64
+ });
@@ -169,7 +169,7 @@ const MAC_KEY_LABELS: Record<string, string> = {
169
169
  escape: "⎋",
170
170
  };
171
171
 
172
- /** Human-readable keybinding, e.g. "⇧⌘P" on macOS or "Ctrl+Shift+P". */
172
+ /** Human-readable keybinding, e.g. "⇧⌘P" on macOS or "Ctrl Shift P". */
173
173
  export function formatKeybinding(binding: string): string {
174
174
  const parsed = parseKeybinding(binding);
175
175
  const keyLabel =
@@ -191,7 +191,7 @@ export function formatKeybinding(binding: string): string {
191
191
  if (parsed.alt) parts.push("Alt");
192
192
  if (parsed.shift) parts.push("Shift");
193
193
  parts.push(keyLabel.length === 1 ? keyLabel.toUpperCase() : keyLabel);
194
- return parts.join("+");
194
+ return parts.join(" ");
195
195
  }
196
196
 
197
197
  /**
@@ -270,9 +270,11 @@ const DEFAULT_LABELS: DesignColorPickerLabels = {
270
270
  diamond: "Diamond", // i18n-ignore fallback component label
271
271
  };
272
272
 
273
- // checkerboard: explicit light/dark tiles for legibility.
274
- const CHECKER_A = "#d4d4d4";
275
- const CHECKER_B = "#a3a3a3";
273
+ // Keep transparency tiles light on both light and dark editor surfaces.
274
+ // guard:allow-raw-color — fixed light checkerboard tile keeps transparency visible.
275
+ const CHECKER_A = "#e5e5e5";
276
+ // guard:allow-raw-color — fixed light checkerboard tile keeps transparency visible.
277
+ const CHECKER_B = "#f5f5f5";
276
278
  const CHECKERBOARD_IMAGE = `linear-gradient(45deg, ${CHECKER_A} 25%, transparent 25%), linear-gradient(-45deg, ${CHECKER_A} 25%, transparent 25%), linear-gradient(45deg, transparent 75%, ${CHECKER_A} 75%), linear-gradient(-45deg, transparent 75%, ${CHECKER_A} 75%)`;
277
279
 
278
280
  // ─── Paint-type icon SVGs (Tabler style, distinct per type) ────────────────────
@@ -1676,6 +1678,7 @@ export function DesignColorPicker({
1676
1678
  max={100}
1677
1679
  disabled={disabled}
1678
1680
  backgroundImage={alphaTrackBackground(fieldColor)}
1681
+ backgroundColor={CHECKER_B}
1679
1682
  backgroundSize="8px 8px, 8px 8px, 8px 8px, 8px 8px, 100% 100%"
1680
1683
  backgroundPosition="0 0, 0 4px, 4px -4px, -4px 0, 0 0"
1681
1684
  onChange={(next) => {
@@ -2062,6 +2065,7 @@ function ColorTrack({
2062
2065
  max,
2063
2066
  disabled,
2064
2067
  backgroundImage,
2068
+ backgroundColor,
2065
2069
  backgroundSize,
2066
2070
  backgroundPosition,
2067
2071
  onChange,
@@ -2073,6 +2077,7 @@ function ColorTrack({
2073
2077
  max: number;
2074
2078
  disabled: boolean;
2075
2079
  backgroundImage: string;
2080
+ backgroundColor?: string;
2076
2081
  backgroundSize?: string;
2077
2082
  backgroundPosition?: string;
2078
2083
  onChange: (value: number) => void;
@@ -2160,7 +2165,12 @@ function ColorTrack({
2160
2165
  "active:cursor-grabbing",
2161
2166
  disabled && "cursor-not-allowed opacity-60",
2162
2167
  )}
2163
- style={{ backgroundImage, backgroundSize, backgroundPosition }}
2168
+ style={{
2169
+ backgroundImage,
2170
+ backgroundColor,
2171
+ backgroundSize,
2172
+ backgroundPosition,
2173
+ }}
2164
2174
  >
2165
2175
  {/* Thumb overhangs the track slightly, matching the design editor */}
2166
2176
  <span
@@ -2552,6 +2562,7 @@ function triggerSwatchStyle(
2552
2562
  if (!lower || lower === "transparent") {
2553
2563
  return {
2554
2564
  backgroundImage: CHECKERBOARD_IMAGE,
2565
+ backgroundColor: CHECKER_B,
2555
2566
  backgroundSize: "8px 8px",
2556
2567
  };
2557
2568
  }
@@ -2577,6 +2588,7 @@ function swatchStyle(value: string): {
2577
2588
  if (parsed && parsed.a < 1) {
2578
2589
  return {
2579
2590
  backgroundImage: `${CHECKERBOARD_IMAGE}, linear-gradient(${rgbaToCss(parsed)}, ${rgbaToCss(parsed)})`,
2591
+ backgroundColor: CHECKER_B,
2580
2592
  backgroundSize: "8px 8px, 8px 8px, 8px 8px, 8px 8px, 100% 100%",
2581
2593
  backgroundPosition: "0 0, 0 4px, 4px -4px, -4px 0, 0 0",
2582
2594
  };
@@ -2590,6 +2602,7 @@ function swatchStyle(value: string): {
2590
2602
  // that would otherwise render as a blank swatch.
2591
2603
  return {
2592
2604
  backgroundImage: CHECKERBOARD_IMAGE,
2605
+ backgroundColor: CHECKER_B,
2593
2606
  backgroundSize: "8px 8px",
2594
2607
  };
2595
2608
  }
@@ -75,7 +75,10 @@ export interface GradientEditSessionTarget {
75
75
 
76
76
  // ─── Checkerboard (matches DesignColorPicker) ───────────────────────────────────
77
77
 
78
- const CHECKER_A = "#d4d4d4";
78
+ // guard:allow-raw-color — fixed light checkerboard tile keeps transparency visible.
79
+ const CHECKER_A = "#e5e5e5";
80
+ // guard:allow-raw-color — fixed light checkerboard tile keeps transparency visible.
81
+ const CHECKER_B = "#f5f5f5";
79
82
  const CHECKERBOARD_IMAGE = `linear-gradient(45deg, ${CHECKER_A} 25%, transparent 25%), linear-gradient(-45deg, ${CHECKER_A} 25%, transparent 25%), linear-gradient(45deg, transparent 75%, ${CHECKER_A} 75%), linear-gradient(-45deg, transparent 75%, ${CHECKER_A} 75%)`;
80
83
  const CHECKER_SIZE = "8px 8px, 8px 8px, 8px 8px, 8px 8px";
81
84
  const CHECKER_POS = "0 0, 0 4px, 4px -4px, -4px 0";
@@ -635,6 +638,7 @@ export function GradientEditor({
635
638
  style={{
636
639
  height: BAR_HEIGHT,
637
640
  backgroundImage: CHECKERBOARD_IMAGE,
641
+ backgroundColor: CHECKER_B,
638
642
  backgroundSize: CHECKER_SIZE,
639
643
  backgroundPosition: CHECKER_POS,
640
644
  }}
@@ -71,7 +71,10 @@ function readFileAsDataUrl(file: File): Promise<string> {
71
71
  });
72
72
  }
73
73
 
74
- const CHECKER_A = "#d4d4d4";
74
+ // guard:allow-raw-color — fixed light checkerboard tile keeps transparency visible.
75
+ const CHECKER_A = "#e5e5e5";
76
+ // guard:allow-raw-color — fixed light checkerboard tile keeps transparency visible.
77
+ const CHECKER_B = "#f5f5f5";
75
78
  const CHECKERBOARD_IMAGE = `linear-gradient(45deg, ${CHECKER_A} 25%, transparent 25%), linear-gradient(-45deg, ${CHECKER_A} 25%, transparent 25%), linear-gradient(45deg, transparent 75%, ${CHECKER_A} 75%), linear-gradient(-45deg, transparent 75%, ${CHECKER_A} 75%)`;
76
79
  const FIT_MARKER_RE =
77
80
  /\/\*\s*agent-native-image-fit:(fill|fit|crop|tile)\s*\*\//i;
@@ -345,6 +348,7 @@ export function ImageFillControls({
345
348
  backgroundImage: value.url
346
349
  ? `url("${escapeForQuotedUrl(value.url.trim())}")`
347
350
  : CHECKERBOARD_IMAGE,
351
+ backgroundColor: value.url ? undefined : CHECKER_B,
348
352
  backgroundSize: value.url
349
353
  ? value.fit === "fit"
350
354
  ? "contain"
@@ -7,6 +7,7 @@ import { type PromptComposerSubmitOptions } from "@agent-native/core/client/comp
7
7
  import { useCallback } from "react";
8
8
 
9
9
  import { sendToDesignAgentChat } from "@/lib/agent-chat";
10
+ import { loadDesignSystemGenerationContext } from "@/pages/design-editor/generation-prompt-directives";
10
11
 
11
12
  export interface QuestionFlowModelSelection {
12
13
  model?: string;
@@ -14,6 +15,23 @@ export interface QuestionFlowModelSelection {
14
15
  effort?: PromptComposerSubmitOptions["effort"];
15
16
  }
16
17
 
18
+ /**
19
+ * What the user actually supplied at kickoff. The intake turn is forced to emit
20
+ * only a questionnaire and then stop, so THIS continuation is the turn that
21
+ * writes HTML — and a fresh thread inherits nothing. Re-sending the brief here
22
+ * is the only thing that puts the user's own prompt, reference screenshots, and
23
+ * design system in front of the model at the moment it generates.
24
+ */
25
+ export interface QuestionFlowGenerationBrief {
26
+ /** The user's original words, replayed verbatim — never a paraphrase. */
27
+ prompt?: string;
28
+ designSystemId?: string | null;
29
+ /** Data URLs; re-attached so the reference screenshot survives the hop. */
30
+ images?: string[];
31
+ /** Extracted text from uploaded files (specs, outlines, token dumps). */
32
+ uploadedFileContext?: string;
33
+ }
34
+
17
35
  interface UseQuestionFlowOptions {
18
36
  enabled?: boolean;
19
37
  continuationTabId?: string | null;
@@ -25,12 +43,49 @@ interface UseQuestionFlowOptions {
25
43
  * filled after render, so a snapshot taken here would be the pre-kickoff one.
26
44
  */
27
45
  getModelSelection?: () => QuestionFlowModelSelection | null | undefined;
46
+ /** Read at send time, for the same reason as `getModelSelection`. */
47
+ getGenerationBrief?: () => QuestionFlowGenerationBrief | null | undefined;
28
48
  }
29
49
 
30
50
  function designQuestionsStateKey(designId: string | undefined): string {
31
51
  return designId ? `show-questions:${designId}` : "show-questions";
32
52
  }
33
53
 
54
+ /**
55
+ * Render the kickoff brief as prompt context for the continuation turn.
56
+ * Exported and pure so the carry-through is testable without a DOM — this is
57
+ * the whole fix for "the agent ignored my design system / screenshot / brief".
58
+ */
59
+ export function buildGenerationBriefContext(
60
+ brief: QuestionFlowGenerationBrief | null | undefined,
61
+ designSystemContext: string,
62
+ ): string {
63
+ return [
64
+ brief?.prompt?.trim()
65
+ ? [
66
+ "## The user's original request (verbatim)",
67
+ "This is the spec for what to build. The answers below refine it;",
68
+ "they do not replace it. Do not restate it as a looser paraphrase.",
69
+ "",
70
+ brief.prompt.trim(),
71
+ ].join("\n")
72
+ : "",
73
+ brief?.images?.length
74
+ ? [
75
+ `## ${brief.images.length} reference image(s) re-attached to this message`,
76
+ "Treat an attached UI screenshot as a layout specification to",
77
+ "reproduce — its structure, hierarchy, density, and component",
78
+ "grammar — not as loose inspiration. Match it unless an answer",
79
+ "below explicitly overrides a part of it.",
80
+ ].join("\n")
81
+ : "",
82
+ brief?.uploadedFileContext?.trim() ?? "",
83
+ designSystemContext,
84
+ ]
85
+ .filter(Boolean)
86
+ .join("\n\n");
87
+ }
88
+
34
89
  const RESPONSIVE_GENERATION_REQUIREMENTS =
35
90
  'Responsive behavior is mandatory for every web design. Read the form-factor answer above: for Desktop or Both/responsive, call generate-design with `primaryViewport: "desktop"` and a 1440x1024 canvas frame; use `primaryViewport: "mobile"` only for an explicitly mobile-primary choice. Use mobile-first responsive CSS, then take desktop and mobile screenshots and fix any overflow before reporting the design complete.';
36
91
 
@@ -47,6 +102,7 @@ export function useQuestionFlow(
47
102
  continuationTabId,
48
103
  onContinue,
49
104
  getModelSelection,
105
+ getGenerationBrief,
50
106
  }: UseQuestionFlowOptions = {},
51
107
  ) {
52
108
  const stateKey = designQuestionsStateKey(designId);
@@ -79,9 +135,21 @@ export function useQuestionFlow(
79
135
  });
80
136
 
81
137
  const sendContinuation = useCallback(
82
- (message: string, context?: string) => {
138
+ async (message: string, context?: string) => {
83
139
  const selection = getModelSelection?.() ?? {};
84
140
  const { model, engine, effort } = selection;
141
+ const brief = getGenerationBrief?.() ?? null;
142
+ // Re-hydrated rather than snapshotted at kickoff: the user can link or
143
+ // change the design system while the questionnaire is open. This never
144
+ // throws — a load failure returns instruction text telling the agent to
145
+ // stop rather than improvise a generic style.
146
+ const designSystemContext = brief?.designSystemId
147
+ ? await loadDesignSystemGenerationContext(brief.designSystemId)
148
+ : "";
149
+ const briefContext = buildGenerationBriefContext(
150
+ brief,
151
+ designSystemContext,
152
+ );
85
153
  // Always request `newTab` (mirroring useAgentGenerating.submit's
86
154
  // default). Without it, when there is no continuationTabId yet the
87
155
  // message goes to whatever tab is currently active, but the id we
@@ -96,9 +164,10 @@ export function useQuestionFlow(
96
164
  // real destination thread.
97
165
  const tabId = sendToDesignAgentChat({
98
166
  message,
99
- context,
167
+ context: [briefContext, context].filter(Boolean).join("\n\n"),
100
168
  submit: true,
101
169
  newTab: true,
170
+ ...(brief?.images?.length ? { images: brief.images } : {}),
102
171
  ...(continuationTabId ? { tabId: continuationTabId } : {}),
103
172
  ...(model ? { model } : {}),
104
173
  ...(engine ? { engine } : {}),
@@ -107,7 +176,14 @@ export function useQuestionFlow(
107
176
  onContinue?.(tabId);
108
177
  flow.clear();
109
178
  },
110
- [continuationTabId, designId, flow, getModelSelection, onContinue],
179
+ [
180
+ continuationTabId,
181
+ designId,
182
+ flow,
183
+ getGenerationBrief,
184
+ getModelSelection,
185
+ onContinue,
186
+ ],
111
187
  );
112
188
 
113
189
  const handleSubmit = useCallback(
@@ -129,13 +205,13 @@ export function useQuestionFlow(
129
205
  .filter(Boolean)
130
206
  .join("\n");
131
207
 
132
- sendContinuation("Here are my answers — go ahead.", context);
208
+ void sendContinuation("Here are my answers — go ahead.", context);
133
209
  },
134
210
  [designId, sendContinuation],
135
211
  );
136
212
 
137
213
  const handleSkip = useCallback(() => {
138
- sendContinuation(
214
+ void sendContinuation(
139
215
  "Skip the questions — decide for me.",
140
216
  designId
141
217
  ? `The user skipped the pre-generation questions for design ${designId}. Proceed with reasonable defaults. ${RESPONSIVE_GENERATION_REQUIREMENTS} Generate one polished first direction unless the original prompt explicitly requested options.`
@@ -25,7 +25,7 @@ const messages = {
25
25
  commentMode: "Comment",
26
26
  sendToAgent: "Send to agent",
27
27
  sendingToAgent: "Sending to agent…",
28
- enterToPost: "Enter to post · Shift+Enter for a new line",
28
+ enterToPost: "Enter to post · Shift Enter for a new line",
29
29
  post: "Post",
30
30
  posting: "Posting…",
31
31
  postFailed: "Could not post this comment",