@agent-native/core 0.153.9 → 0.154.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/analytics/AGENTS.md +20 -30
- package/corpus/templates/analytics/actions/search-dashboard-references.ts +58 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/lib/utils.ts +1 -1
- package/corpus/templates/analytics/app/pages/DataSources.tsx +2 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewSqlPopover.tsx +1 -1
- package/corpus/templates/analytics/app/pages/monitoring/errors/IssueList.tsx +4 -2
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -2
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +2 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +366 -3
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +3 -1
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -2
- package/corpus/templates/calendar/app/components/calendar/EventDialog.tsx +1 -1
- package/corpus/templates/calendar/app/lib/utils.ts +1 -1
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +32 -5
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +15 -15
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
- package/corpus/templates/clips/app/lib/utils.ts +5 -6
- package/corpus/templates/clips/app/root.tsx +4 -1
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -2
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +43 -8
- package/corpus/templates/clips/desktop/src/app.tsx +27 -13
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +23 -9
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +261 -11
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +21 -3
- package/corpus/templates/clips/shared/mic-audio-cleanup.ts +350 -0
- package/corpus/templates/content/app/i18n-data.ts +10 -10
- package/corpus/templates/content/scripts/check-native-deps.mjs +181 -15
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +79 -7
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +14 -2
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +2 -2
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +35 -11
- package/corpus/templates/design/actions/get-design-system.ts +103 -3
- package/corpus/templates/design/actions/import-design-tokens.ts +37 -8
- package/corpus/templates/design/actions/import-github.ts +32 -138
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +49 -16
- package/corpus/templates/design/actions/present-design-variants.ts +93 -4
- package/corpus/templates/design/actions/run-design-audit.ts +229 -0
- package/corpus/templates/design/actions/sync-design-system-with-builder.ts +64 -0
- package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +2 -1
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +2 -1
- package/corpus/templates/design/app/components/design/code-workbench/commands.ts +2 -2
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +17 -4
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +5 -1
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +5 -1
- package/corpus/templates/design/app/hooks/use-question-flow.ts +81 -5
- package/corpus/templates/design/app/i18n/en-US.ts +1 -1
- package/corpus/templates/design/app/i18n-data.ts +32 -1
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +10 -10
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +83 -11
- package/corpus/templates/design/app/pages/DesignSystems.tsx +14 -0
- package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +22 -0
- package/corpus/templates/design/changelog/2026-08-13-a-screen-that-would-render-behind-an-invisible-alpine-overla.md +6 -0
- package/corpus/templates/design/changelog/2026-08-13-attach-a-ui-screenshot-and-the-design-is-reproduced-not-rein.md +6 -0
- package/corpus/templates/design/changelog/2026-08-13-linking-a-design-system-means-you-never-get-generic-directio.md +6 -0
- package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-reports-when-a-screen-ignores-its-linke.md +6 -0
- package/corpus/templates/design/changelog/2026-08-13-the-design-system-preview-now-shows-how-many-named-tokens-we.md +6 -0
- package/corpus/templates/design/changelog/2026-08-13-your-design-system-prompt-and-reference-screenshots-now-reac.md +6 -0
- package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +18 -2
- package/corpus/templates/design/shared/design-review.ts +1 -0
- package/corpus/templates/design/shared/html-integrity.ts +171 -0
- package/corpus/templates/mail/app/lib/utils.ts +1 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +5 -2
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +10 -6
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +41 -3
- package/corpus/templates/slides/actions/sync-design-system-with-builder.ts +64 -0
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +193 -20
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +58 -33
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +28 -41
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +119 -91
- package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +16 -0
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +158 -31
- package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +30 -14
- package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +2 -2
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +4 -1
- package/corpus/templates/slides/app/components/editor/rich-text-selection.ts +14 -0
- package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +24 -0
- package/corpus/templates/slides/app/global.css +24 -0
- package/corpus/templates/slides/app/hooks/use-navigation-state.ts +5 -1
- package/corpus/templates/slides/app/i18n/en-US.ts +19 -10
- package/corpus/templates/slides/app/lib/deck-filter.ts +47 -0
- package/corpus/templates/slides/app/lib/export-google-slides-client.ts +15 -4
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +637 -1
- package/corpus/templates/slides/app/lib/image-download.ts +84 -0
- package/corpus/templates/slides/app/lib/utils.ts +1 -1
- package/corpus/templates/slides/app/pages/Index.tsx +25 -9
- package/corpus/templates/slides/netlify.toml +1 -0
- package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +14 -2
- package/dist/agent/production-agent.js +1 -1
- package/dist/brand-kit/types.d.ts +10 -2
- package/dist/cli/code.js +1 -1
- package/dist/cli/index.js +30 -1
- package/dist/cli/native-dependencies.d.ts +24 -0
- package/dist/cli/native-dependencies.js +264 -0
- package/dist/cli/plan-local.js +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.js +2 -2
- package/dist/cli/skills-content/help.d.ts +1 -1
- package/dist/cli/skills-content/help.js +2 -1
- package/dist/cli/skills.js +2 -1
- package/dist/cli/visualize-repo.js +1 -1
- package/dist/client/FeedbackButton.js +12 -12
- package/dist/client/NewWorkspaceAppFlow.js +4 -1
- package/dist/client/agent-chat-adapter.js +3 -4
- package/dist/client/agent-page/AgentTabsPage.d.ts +2 -2
- package/dist/client/agent-page/AgentTabsPage.js +12 -9
- package/dist/client/assistant-ui-recovery.d.ts +1 -1
- package/dist/client/assistant-ui-recovery.js +4 -0
- package/dist/client/chat/message-components.d.ts +11 -3
- package/dist/client/chat/message-components.js +31 -5
- package/dist/client/chat/tool-call-display.d.ts +2 -1
- package/dist/client/chat/tool-call-display.js +33 -15
- package/dist/client/db-admin/SqlEditor.js +1 -1
- package/dist/client/dev-overlay/DevOverlay.js +2 -2
- package/dist/client/extensions/ExtensionsSidebarSection.js +2 -1
- package/dist/client/extensions/portable-extension.js +4 -0
- package/dist/client/guided-questions.d.ts +8 -0
- package/dist/client/guided-questions.js +12 -3
- package/dist/client/onboarding/FirstRunOnboarding.js +2 -1
- package/dist/client/org/TeamPage.js +14 -3
- package/dist/client/settings/SettingsPanel.js +26 -1
- package/dist/client/sharing/ShareButton.js +16 -24
- package/dist/client/sse-event-processor.js +35 -15
- package/dist/client/tool-display.d.ts +9 -0
- package/dist/client/tool-display.js +39 -0
- package/dist/client/vite-dev-recovery-script.js +1 -1
- package/dist/extensions/html-shell.js +5 -0
- package/dist/integrations/catalog.js +9 -8
- package/dist/observability/routes.d.ts +2 -2
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-chat/prompt-resources.js +17 -16
- package/dist/server/agent-chat-plugin.d.ts +9 -0
- package/dist/server/agent-chat-plugin.js +31 -9
- package/dist/server/builder-design-systems.d.ts +33 -1
- package/dist/server/builder-design-systems.js +293 -11
- package/dist/server/design-token-utils.d.ts +11 -0
- package/dist/server/design-token-utils.js +68 -16
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/onboarding-html.js +2 -1
- package/dist/shared/docs-url.d.ts +24 -0
- package/dist/shared/docs-url.js +39 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/templates/default/app/routes/_index.tsx +2 -1
- package/package.json +2 -2
- package/src/templates/default/app/routes/_index.tsx +2 -1
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
|
|
12
12
|
import { upsertBuilderProxyDesignSystem } from "../server/lib/builder-design-system-proxy.js";
|
|
13
|
-
import importGithub from "./import-github.js";
|
|
14
13
|
|
|
15
14
|
const codeFileSchema = z.object({
|
|
16
15
|
filename: z.string().trim().min(1).describe("File name or relative path"),
|
|
@@ -33,6 +32,30 @@ const codeFileSchema = z.object({
|
|
|
33
32
|
),
|
|
34
33
|
});
|
|
35
34
|
|
|
35
|
+
const githubSourceSchema = z.object({
|
|
36
|
+
repoUrl: z.string().trim().min(1).describe("GitHub repository URL"),
|
|
37
|
+
ref: z
|
|
38
|
+
.string()
|
|
39
|
+
.trim()
|
|
40
|
+
.min(1)
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Optional branch, tag, or commit"),
|
|
43
|
+
include: z
|
|
44
|
+
.array(z.string().trim().min(1))
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Optional repository-relative files or folders to include"),
|
|
47
|
+
exclude: z
|
|
48
|
+
.array(z.string().trim().min(1))
|
|
49
|
+
.optional()
|
|
50
|
+
.describe("Optional repository-relative files or folders to exclude"),
|
|
51
|
+
instructions: z
|
|
52
|
+
.string()
|
|
53
|
+
.trim()
|
|
54
|
+
.min(1)
|
|
55
|
+
.optional()
|
|
56
|
+
.describe("Optional indexing guidance for this repository"),
|
|
57
|
+
});
|
|
58
|
+
|
|
36
59
|
export default defineAction({
|
|
37
60
|
description:
|
|
38
61
|
"Start Builder DSI design-system indexing from connected code, a GitHub repository, code/design files, and optional design.md guidance. " +
|
|
@@ -51,7 +74,15 @@ export default defineAction({
|
|
|
51
74
|
githubRepoUrl: z
|
|
52
75
|
.string()
|
|
53
76
|
.optional()
|
|
54
|
-
.describe("GitHub repository URL to index with Builder"),
|
|
77
|
+
.describe("Legacy single GitHub repository URL to index with Builder"),
|
|
78
|
+
githubSources: z
|
|
79
|
+
.array(githubSourceSchema)
|
|
80
|
+
.min(1)
|
|
81
|
+
.max(20)
|
|
82
|
+
.optional()
|
|
83
|
+
.describe(
|
|
84
|
+
"GitHub repositories to index in one design system. Each source may specify a branch/tag/commit and repository-relative files or folders.",
|
|
85
|
+
),
|
|
55
86
|
connectedProjectId: z
|
|
56
87
|
.string()
|
|
57
88
|
.optional()
|
|
@@ -71,21 +102,13 @@ export default defineAction({
|
|
|
71
102
|
projectName,
|
|
72
103
|
description,
|
|
73
104
|
githubRepoUrl,
|
|
105
|
+
githubSources,
|
|
74
106
|
connectedProjectId,
|
|
75
107
|
codeFiles,
|
|
76
108
|
designMd,
|
|
77
109
|
}) => {
|
|
78
|
-
const githubImport = githubRepoUrl
|
|
79
|
-
? await importGithub.run({ repoUrl: githubRepoUrl })
|
|
80
|
-
: null;
|
|
81
|
-
const githubFiles = githubImport?.rawFiles
|
|
82
|
-
? Object.entries(githubImport.rawFiles).map(([filename, content]) => ({
|
|
83
|
-
filename,
|
|
84
|
-
content,
|
|
85
|
-
}))
|
|
86
|
-
: [];
|
|
87
110
|
const files = buildBuilderDesignSystemIndexFiles({
|
|
88
|
-
codeFiles
|
|
111
|
+
codeFiles,
|
|
89
112
|
designMd,
|
|
90
113
|
// Agent action payloads intentionally stay at the core 2 MB inline
|
|
91
114
|
// budget. Fail loudly instead of silently dropping a larger `.fig`;
|
|
@@ -95,10 +118,8 @@ export default defineAction({
|
|
|
95
118
|
const result = await startBuilderDesignSystemIndex({
|
|
96
119
|
projectName,
|
|
97
120
|
description,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// so public repositories still work without a saved token.
|
|
101
|
-
githubRepoUrl: githubFiles.length > 0 ? undefined : githubRepoUrl,
|
|
121
|
+
githubRepoUrl,
|
|
122
|
+
githubRepos: githubSources,
|
|
102
123
|
connectedProjectId,
|
|
103
124
|
files,
|
|
104
125
|
});
|
|
@@ -111,12 +132,24 @@ export default defineAction({
|
|
|
111
132
|
orgId: getRequestOrgId(),
|
|
112
133
|
projectName,
|
|
113
134
|
description,
|
|
135
|
+
githubSources:
|
|
136
|
+
githubSources ?? (githubRepoUrl ? [{ repoUrl: githubRepoUrl }] : []),
|
|
137
|
+
sourceKind:
|
|
138
|
+
(githubSources?.length || githubRepoUrl) &&
|
|
139
|
+
(codeFiles?.length || designMd)
|
|
140
|
+
? "mixed"
|
|
141
|
+
: githubSources?.length || githubRepoUrl
|
|
142
|
+
? "github"
|
|
143
|
+
: codeFiles?.length || designMd
|
|
144
|
+
? "code"
|
|
145
|
+
: undefined,
|
|
114
146
|
});
|
|
115
147
|
|
|
116
148
|
return {
|
|
117
149
|
...result,
|
|
118
150
|
...proxy,
|
|
119
151
|
uploadedFileCount: files.length,
|
|
152
|
+
githubSourceCount: githubSources?.length ?? (githubRepoUrl ? 1 : 0),
|
|
120
153
|
};
|
|
121
154
|
},
|
|
122
155
|
});
|
|
@@ -46,6 +46,49 @@ const DEFAULT_RESPONSIVE_BREAKPOINTS = [MOBILE_WIDTH].map((widthPx) => ({
|
|
|
46
46
|
prefix: widthToPrefix(widthPx),
|
|
47
47
|
}));
|
|
48
48
|
|
|
49
|
+
const SPECIFICATION_SIGNAL_PATTERNS = [
|
|
50
|
+
/\b(?:attached|uploaded|reference|mockup|screenshot|wireframe|source of truth|source-of-truth)\b/i,
|
|
51
|
+
/\b(?:\d+\s*[- ]\s*col(?:umn)?|grid spec|layout spec|section order|feature list)\b/i,
|
|
52
|
+
/\b(?:design system|brand system|brand kit|visual language|tokens?)\b/i,
|
|
53
|
+
] as const;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Direction summaries are useful for genuinely open-ended exploration, but
|
|
57
|
+
* the fallback renderer cannot reproduce a supplied layout or system. Keep a
|
|
58
|
+
* complete-HTML requirement at the action boundary so a model cannot turn a
|
|
59
|
+
* detailed screen brief into generic cards just by omitting `content`.
|
|
60
|
+
*
|
|
61
|
+
* The weakest of the two signals, and never the only one: `prompt` is the chat
|
|
62
|
+
* caption this same model writes for the picker, so an agent that has just
|
|
63
|
+
* decided to explore writes an exploration-flavored caption and clears the
|
|
64
|
+
* check for free. `hasLinkedDesignSystem` below is the fact the server owns.
|
|
65
|
+
*/
|
|
66
|
+
export function hasSpecifiedDesignPrompt(prompt?: string): boolean {
|
|
67
|
+
const value = prompt?.trim() ?? "";
|
|
68
|
+
if (!value) return false;
|
|
69
|
+
return SPECIFICATION_SIGNAL_PATTERNS.some((pattern) => pattern.test(value));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The one specification signal that survives an intake round-trip. Attachments
|
|
74
|
+
* are turn-scoped (see `ActionRunContext.attachments`), so by the time the
|
|
75
|
+
* agent asks its questions and comes back to present variants, the reference
|
|
76
|
+
* screenshot the user supplied is no longer visible to this action — but the
|
|
77
|
+
* design system they linked still is.
|
|
78
|
+
*/
|
|
79
|
+
async function hasLinkedDesignSystem(designId: string): Promise<boolean> {
|
|
80
|
+
const [design] = await getDb()
|
|
81
|
+
.select({ designSystemId: schema.designs.designSystemId })
|
|
82
|
+
.from(schema.designs)
|
|
83
|
+
.where(
|
|
84
|
+
and(
|
|
85
|
+
eq(schema.designs.id, designId),
|
|
86
|
+
accessFilter(schema.designs, schema.designShares),
|
|
87
|
+
),
|
|
88
|
+
);
|
|
89
|
+
return Boolean(design?.designSystemId?.trim());
|
|
90
|
+
}
|
|
91
|
+
|
|
49
92
|
function hasBreakpointSet(value: unknown): boolean {
|
|
50
93
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
51
94
|
return false;
|
|
@@ -114,7 +157,7 @@ const variantSchema = z.object({
|
|
|
114
157
|
.string()
|
|
115
158
|
.optional()
|
|
116
159
|
.describe(
|
|
117
|
-
"
|
|
160
|
+
"Complete self-contained HTML document for this variant. Keep it compact: one representative screen or directional snapshot, not a full multi-screen app. Omit it ONLY for genuinely open-ended exploration, where Design renders a generic direction card from label/description/features — that card ignores any supplied reference, layout, or design system, so omitting content is rejected when the design has a linked design system or the prompt specifies one.",
|
|
118
161
|
),
|
|
119
162
|
width: z
|
|
120
163
|
.number()
|
|
@@ -825,9 +868,12 @@ export default defineAction({
|
|
|
825
868
|
"complex apps, " +
|
|
826
869
|
"make each variant a " +
|
|
827
870
|
"compact representative screen; pass concise labels/descriptions/features " +
|
|
828
|
-
"and omit content
|
|
829
|
-
"
|
|
830
|
-
"
|
|
871
|
+
"and omit content only for open-ended exploration. For a prompt with a " +
|
|
872
|
+
"specific product surface, reference, layout, or design system, provide " +
|
|
873
|
+
"complete self-contained HTML for every variant; the generic fallback is " +
|
|
874
|
+
"blocked there. Design will render compact screens from direction data only " +
|
|
875
|
+
"for open-ended exploration. Expand the chosen direction after the user " +
|
|
876
|
+
"picks. Screens from an earlier variant set are never " +
|
|
831
877
|
"deleted automatically: if you are knowingly replacing your own earlier " +
|
|
832
878
|
"set that the user never picked from or discussed, pass its set id in " +
|
|
833
879
|
"deleteSupersededSetIds; otherwise leave old sets in place.",
|
|
@@ -872,6 +918,24 @@ export default defineAction({
|
|
|
872
918
|
run: async ({ designId, prompt, variants, deleteSupersededSetIds }) => {
|
|
873
919
|
await assertAccess("design", designId, "editor");
|
|
874
920
|
|
|
921
|
+
const omittedContent = variants.filter(
|
|
922
|
+
(variant) => !variant.content?.trim(),
|
|
923
|
+
);
|
|
924
|
+
if (
|
|
925
|
+
omittedContent.length > 0 &&
|
|
926
|
+
(hasSpecifiedDesignPrompt(prompt) ||
|
|
927
|
+
(await hasLinkedDesignSystem(designId)))
|
|
928
|
+
) {
|
|
929
|
+
throw new Error(
|
|
930
|
+
"present-design-variants requires complete self-contained HTML for " +
|
|
931
|
+
"every variant when the design has a linked design system, or the " +
|
|
932
|
+
"prompt specifies a layout, reference, or product surface. The " +
|
|
933
|
+
"generic direction fallback ignores the brief and the system's " +
|
|
934
|
+
"tokens; use generate-design, or provide each variant's complete " +
|
|
935
|
+
"content.",
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
|
|
875
939
|
// Before any mutation. These are model-authored screens created by raw
|
|
876
940
|
// insert, so they need the same well-formedness gate as generate-design —
|
|
877
941
|
// though not its document-shape rules, since a variant may be a sketch with
|
|
@@ -1089,7 +1153,32 @@ export default defineAction({
|
|
|
1089
1153
|
editorView: "overview",
|
|
1090
1154
|
path: `/design/${encodeURIComponent(designId)}?view=overview`,
|
|
1091
1155
|
});
|
|
1156
|
+
// The pick opens a continuation turn that inherits nothing from this one,
|
|
1157
|
+
// and that turn is what expands the kept placeholder into the real screen.
|
|
1158
|
+
// Without this, the expansion is design-system-blind even though the user
|
|
1159
|
+
// linked one before generating.
|
|
1160
|
+
const [linkedDesign] = await db
|
|
1161
|
+
.select({ designSystemId: schema.designs.designSystemId })
|
|
1162
|
+
.from(schema.designs)
|
|
1163
|
+
.where(
|
|
1164
|
+
and(
|
|
1165
|
+
eq(schema.designs.id, designId),
|
|
1166
|
+
accessFilter(schema.designs, schema.designShares),
|
|
1167
|
+
),
|
|
1168
|
+
);
|
|
1169
|
+
const variantPickContext = [
|
|
1170
|
+
prompt?.trim()
|
|
1171
|
+
? `The user's original request for this design: "${prompt.trim()}". Expand the kept direction into that, not into a generic version of it.`
|
|
1172
|
+
: "",
|
|
1173
|
+
linkedDesign?.designSystemId
|
|
1174
|
+
? `This design is linked to design system "${linkedDesign.designSystemId}". Call \`get-design-system\` for that id and apply its tokens, typography, and usage notes while expanding the kept screen — do not substitute a generic palette or font.`
|
|
1175
|
+
: "",
|
|
1176
|
+
]
|
|
1177
|
+
.filter(Boolean)
|
|
1178
|
+
.join("\n");
|
|
1179
|
+
|
|
1092
1180
|
await writeAppStateForCurrentTab("guided-questions", {
|
|
1181
|
+
...(variantPickContext ? { submitContext: variantPickContext } : {}),
|
|
1093
1182
|
title: prompt ?? "Pick a direction",
|
|
1094
1183
|
description:
|
|
1095
1184
|
"All options are on the board. Choose one to keep; I will delete the others, read only the kept screen, and turn that direction into the final requested screen.",
|
|
@@ -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
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useActionMutation } from "@agent-native/core/client/hooks";
|
|
2
2
|
import { useFormatters, useT } from "@agent-native/core/client/i18n";
|
|
3
|
+
import { docsUrl } from "@agent-native/core/shared";
|
|
3
4
|
import { parseFigmaFileKey } from "@shared/figma-url";
|
|
4
5
|
import {
|
|
5
6
|
IconBrandFigma,
|
|
@@ -696,7 +697,7 @@ export function DesignImportPanel(p: DesignImportPanelProps) {
|
|
|
696
697
|
<p className="text-[11px] leading-snug text-muted-foreground">
|
|
697
698
|
{t("designEditor.import.visualEditGuidance")}{" "}
|
|
698
699
|
<a
|
|
699
|
-
href="
|
|
700
|
+
href={docsUrl("template-design")}
|
|
700
701
|
target="_blank"
|
|
701
702
|
rel="noreferrer"
|
|
702
703
|
className="font-medium text-foreground underline-offset-2 hover:underline"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import { docsUrl } from "@agent-native/core/shared";
|
|
2
3
|
import { VisualTweakControl } from "@agent-native/toolkit/design-tweaks";
|
|
3
4
|
import type { TweakDefinition } from "@shared/api";
|
|
4
5
|
import {
|
|
@@ -80,7 +81,7 @@ export function TweaksPanelContent({
|
|
|
80
81
|
>
|
|
81
82
|
{t("designEditor.tweaksHelp")}{" "}
|
|
82
83
|
<a
|
|
83
|
-
href="
|
|
84
|
+
href={docsUrl("template-design")}
|
|
84
85
|
target="_blank"
|
|
85
86
|
rel="noreferrer"
|
|
86
87
|
className="font-medium text-foreground/80 underline-offset-2 hover:text-foreground hover:underline"
|
|
@@ -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
|
|
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
|
/**
|