@agent-native/core 0.80.9 → 0.80.10

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 (111) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +7 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/design-connect.ts +28 -2
  5. package/corpus/core/src/cli/skills.ts +45 -24
  6. package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +717 -0
  7. package/corpus/templates/design/actions/add-breakpoint.ts +143 -0
  8. package/corpus/templates/design/actions/add-localhost-screens.ts +5 -0
  9. package/corpus/templates/design/actions/apply-a11y-fix.ts +317 -0
  10. package/corpus/templates/design/actions/apply-component-prop-edit.ts +433 -0
  11. package/corpus/templates/design/actions/apply-design-state.ts +200 -0
  12. package/corpus/templates/design/actions/apply-design-token-edit.ts +202 -0
  13. package/corpus/templates/design/actions/apply-motion-edit.ts +450 -0
  14. package/corpus/templates/design/actions/apply-shader-fill.ts +404 -0
  15. package/corpus/templates/design/actions/apply-visual-edit.ts +191 -5
  16. package/corpus/templates/design/actions/capture-design-state.ts +224 -0
  17. package/corpus/templates/design/actions/connect-builder-app.ts +162 -0
  18. package/corpus/templates/design/actions/create-component.ts +447 -0
  19. package/corpus/templates/design/actions/create-design-branch.ts +263 -0
  20. package/corpus/templates/design/actions/create-design-state.ts +162 -0
  21. package/corpus/templates/design/actions/delete-design-state.ts +55 -0
  22. package/corpus/templates/design/actions/deploy-design-preview.ts +275 -0
  23. package/corpus/templates/design/actions/get-component-details.ts +242 -0
  24. package/corpus/templates/design/actions/get-design-branch-diff.ts +362 -0
  25. package/corpus/templates/design/actions/get-design-review.ts +314 -0
  26. package/corpus/templates/design/actions/get-design-surface-index.ts +582 -0
  27. package/corpus/templates/design/actions/get-motion-timeline.ts +99 -0
  28. package/corpus/templates/design/actions/index-components.ts +258 -0
  29. package/corpus/templates/design/actions/index-design-tokens.ts +273 -0
  30. package/corpus/templates/design/actions/list-design-extensions.ts +309 -0
  31. package/corpus/templates/design/actions/list-design-source-capabilities.ts +153 -0
  32. package/corpus/templates/design/actions/list-design-states.ts +72 -0
  33. package/corpus/templates/design/actions/migrate-inline-design-to-app.ts +298 -0
  34. package/corpus/templates/design/actions/open-component-source.ts +237 -0
  35. package/corpus/templates/design/actions/preview-component-prop-edit.ts +254 -0
  36. package/corpus/templates/design/actions/preview-design-token-edit.ts +113 -0
  37. package/corpus/templates/design/actions/preview-shader-fill.ts +189 -0
  38. package/corpus/templates/design/actions/remove-breakpoint.ts +103 -0
  39. package/corpus/templates/design/actions/remove-motion-timeline.ts +196 -0
  40. package/corpus/templates/design/actions/run-design-audit.ts +421 -0
  41. package/corpus/templates/design/actions/run-design-extension-action.ts +284 -0
  42. package/corpus/templates/design/actions/set-active-breakpoint.ts +39 -0
  43. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1414 -71
  44. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +632 -43
  45. package/corpus/templates/design/app/components/design/DrawOverlay.tsx +1 -1
  46. package/corpus/templates/design/app/components/design/EditPanel.tsx +1391 -52
  47. package/corpus/templates/design/app/components/design/LayersPanel.tsx +4 -3
  48. package/corpus/templates/design/app/components/design/LocalSourceEditBanner.tsx +157 -0
  49. package/corpus/templates/design/app/components/design/MotionDock.tsx +1062 -0
  50. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1052 -98
  51. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +759 -0
  52. package/corpus/templates/design/app/components/design/StatesPanel.tsx +579 -0
  53. package/corpus/templates/design/app/components/design/TokensPanel.tsx +573 -0
  54. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +241 -0
  55. package/corpus/templates/design/app/components/design/index.ts +16 -0
  56. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +15 -6
  57. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +66 -3
  58. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +145 -0
  59. package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +42 -12
  60. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +1 -1
  61. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +131 -0
  62. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +3 -1
  63. package/corpus/templates/design/app/i18n/zh-TW.ts +31 -0
  64. package/corpus/templates/design/app/i18n-data.ts +269 -0
  65. package/corpus/templates/design/app/pages/DesignEditor.tsx +2478 -386
  66. package/corpus/templates/design/changelog/2026-06-29-added-a-review-panel-in-the-design-editor-s-inspector-with-a.md +6 -0
  67. package/corpus/templates/design/changelog/2026-06-29-copying-and-pasting-layers-no-longer-shows-success-notificat.md +6 -0
  68. package/corpus/templates/design/changelog/2026-06-29-device-presets-in-all-screens-view-resize-the-selected-previ.md +6 -0
  69. package/corpus/templates/design/changelog/2026-06-29-layer-and-canvas-drags-keep-the-layer-list-stable-while-chan.md +6 -0
  70. package/corpus/templates/design/changelog/2026-06-29-layer-moves-can-be-undone-and-redone-without-flashing-the-ca.md +6 -0
  71. package/corpus/templates/design/changelog/2026-06-29-screen-previews-use-a-single-blue-hover-border-in-all-screen.md +6 -0
  72. package/corpus/templates/design/changelog/2026-06-29-selected-containers-show-draggable-padding-and-gap-guides-on.md +6 -0
  73. package/corpus/templates/design/changelog/2026-06-29-the-design-editor-adds-a-studio-layer-with-tokens-respon.md +6 -0
  74. package/corpus/templates/design/changelog/2026-06-30-accessibility-findings-now-offer-a-one-click-fix.md +6 -0
  75. package/corpus/templates/design/changelog/2026-06-30-component-instances-now-have-editable-props-in-the-inspe.md +6 -0
  76. package/corpus/templates/design/changelog/2026-06-30-inspect-code-now-shows-the-elements-opening-tag-at-a-gla.md +6 -0
  77. package/corpus/templates/design/changelog/2026-06-30-shader-fill-presets-can-now-be-applied-to-an-element.md +6 -0
  78. package/corpus/templates/design/changelog/2026-06-30-the-motion-timeline-can-now-add-a-track-to-any-element-a.md +6 -0
  79. package/corpus/templates/design/changelog/2026-06-30-visual-editor-selection-layer-paint-and-drawing-controls-are-more-reliable.md +6 -0
  80. package/corpus/templates/design/e2e/helpers.ts +10 -4
  81. package/corpus/templates/design/server/db/schema.ts +123 -0
  82. package/corpus/templates/design/server/plugins/db.ts +90 -0
  83. package/corpus/templates/design/shared/builder-app.ts +297 -0
  84. package/corpus/templates/design/shared/capability-resolver.ts +123 -0
  85. package/corpus/templates/design/shared/capture-sanitize.ts +70 -0
  86. package/corpus/templates/design/shared/code-layer.ts +1016 -71
  87. package/corpus/templates/design/shared/component-model.ts +239 -0
  88. package/corpus/templates/design/shared/design-review.ts +275 -0
  89. package/corpus/templates/design/shared/design-source-capabilities.ts +286 -0
  90. package/corpus/templates/design/shared/design-state.ts +112 -0
  91. package/corpus/templates/design/shared/design-surface-index.ts +258 -0
  92. package/corpus/templates/design/shared/motion-compiler.ts +278 -0
  93. package/corpus/templates/design/shared/motion-timeline.ts +193 -0
  94. package/corpus/templates/design/shared/responsive-classes.ts +452 -0
  95. package/corpus/templates/design/shared/shader-fill.ts +323 -0
  96. package/corpus/templates/design/shared/source-mode.ts +245 -0
  97. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +6 -2
  98. package/corpus/templates/plan/changelog/2026-06-29-plan-canvases-open-without-an-initial-pan-and-zoom-flicker.md +6 -0
  99. package/dist/cli/design-connect.d.ts.map +1 -1
  100. package/dist/cli/design-connect.js +28 -2
  101. package/dist/cli/design-connect.js.map +1 -1
  102. package/dist/cli/skills.d.ts.map +1 -1
  103. package/dist/cli/skills.js +37 -21
  104. package/dist/cli/skills.js.map +1 -1
  105. package/dist/collab/routes.d.ts +1 -1
  106. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  107. package/dist/notifications/routes.d.ts +2 -2
  108. package/dist/observability/routes.d.ts +7 -7
  109. package/dist/resources/handlers.d.ts +2 -2
  110. package/dist/server/transcribe-voice.d.ts +1 -1
  111. package/package.json +1 -1
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ Added a Review panel in the design editor's inspector with an on-demand accessibility audit and one-click inline fixes for contrast, tap-target, and focus issues
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ Copying and pasting layers no longer shows success notifications.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ Device presets in all-screens view resize the selected preview width and height together.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ Layer and canvas drags keep the layer list stable while changes save.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ Layer moves can be undone and redone without flashing the canvas preview.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ Screen previews use a single blue hover border in all-screens view.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ Selected containers show draggable padding and gap guides on hover.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ The Design editor gains a Studio layer — design tokens, multi-breakpoint responsive editing, code-backed components with Create component and Inspect code, accessibility and visual-diff review, and a Builder-powered real-app tier — with each capability gated to what the design's source can safely support.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-30
4
+ ---
5
+
6
+ Accessibility findings can now be fixed in one click for common inline issues like low contrast, small tap targets, and focus visibility.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-30
4
+ ---
5
+
6
+ Selecting a component instance now shows editable props in the inspector — change variants, toggle booleans, and edit labels, and the component updates live.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-06-30
4
+ ---
5
+
6
+ Inspect code now shows the selected element’s opening tag (tag name and attributes) at a glance.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-30
4
+ ---
5
+
6
+ Shader fill presets can now be applied to a selected element, not just previewed.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-30
4
+ ---
5
+
6
+ The motion timeline can now add an animation track to any selected element and write it to CSS, with a live scrub preview.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-30
4
+ ---
5
+
6
+ Visual editor selection, layer dragging, paint blend modes, and drawing controls are more reliable.
@@ -25,8 +25,10 @@ export async function readSeedDesignId(): Promise<string> {
25
25
  return designId;
26
26
  }
27
27
 
28
+ const DESIGN_PREVIEW_IFRAME_SELECTOR = "iframe[data-design-preview-iframe]";
29
+
28
30
  export function designFrame(page: Page): FrameLocator {
29
- return page.frameLocator("iframe[data-design-preview-iframe]");
31
+ return page.locator(DESIGN_PREVIEW_IFRAME_SELECTOR).first().contentFrame();
30
32
  }
31
33
 
32
34
  /** Open the editor for a design and wait for the toolbar + iframe to be ready. */
@@ -40,7 +42,7 @@ export async function gotoEditor(page: Page, designId: string): Promise<void> {
40
42
 
41
43
  async function waitForDesignBridgeReady(page: Page): Promise<void> {
42
44
  await expect(
43
- page.locator("iframe[data-design-preview-iframe]"),
45
+ page.locator(DESIGN_PREVIEW_IFRAME_SELECTOR).first(),
44
46
  ).toBeVisible();
45
47
  await expect(
46
48
  designFrame(page).locator('[data-agent-native-edit-overlay="shield"]'),
@@ -71,8 +73,12 @@ export async function enterDirectMode(page: Page): Promise<void> {
71
73
  await expect
72
74
  .poll(
73
75
  async () =>
74
- (await page.locator("iframe[data-design-preview-iframe]").boundingBox())
75
- ?.width ?? 0,
76
+ (
77
+ await page
78
+ .locator(DESIGN_PREVIEW_IFRAME_SELECTOR)
79
+ .first()
80
+ .boundingBox()
81
+ )?.width ?? 0,
76
82
  { timeout: 10_000 },
77
83
  )
78
84
  .toBeGreaterThan(600);
@@ -81,3 +81,126 @@ export const designLocalhostConnections = table(
81
81
  updatedAt: text("updated_at").default(now()),
82
82
  },
83
83
  );
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // New tables — additive only; never alter existing tables.
87
+ // All ownable tables are read/written through accessFilter / assertAccess.
88
+ // ---------------------------------------------------------------------------
89
+
90
+ /**
91
+ * Real-app component metadata indexed from TS prop types, cva/tailwind-variants
92
+ * variants, and Storybook stories. Scoped to one design + source ref.
93
+ */
94
+ export const componentIndex = table("component_index", {
95
+ id: text("id").primaryKey(),
96
+ designId: text("design_id").notNull(),
97
+ /** Opaque string identifying the source connection (localhost bridge id, fusion project id, etc.) */
98
+ sourceRef: text("source_ref"),
99
+ name: text("name").notNull(),
100
+ filePath: text("file_path"),
101
+ exportName: text("export_name"),
102
+ /** JSON: parsed prop types / cva variant definitions */
103
+ props: text("props"),
104
+ /** JSON: variant groups (size, color, state, …) */
105
+ variants: text("variants"),
106
+ /** JSON: Storybook stories referencing this component */
107
+ stories: text("stories"),
108
+ /** JSON: runtime CSS selectors used to outline instances on the canvas */
109
+ runtimeSelectors: text("runtime_selectors"),
110
+ createdAt: text("created_at").default(now()),
111
+ updatedAt: text("updated_at").default(now()),
112
+ ...ownableColumns(),
113
+ });
114
+
115
+ /**
116
+ * Motion timeline — scoped to one design + source ref + screen/file.
117
+ * A design may have many timelines (one per screen or animation target).
118
+ * The compiled CSS is the runtime truth; tracks JSON aids editing only.
119
+ */
120
+ export const motionTimeline = table("motion_timeline", {
121
+ id: text("id").primaryKey(),
122
+ designId: text("design_id").notNull(),
123
+ /** Opaque source connection ref; null for inline designs. */
124
+ sourceRef: text("source_ref"),
125
+ /** File path for real-app CSS modules; null for inline (managed <style> block). */
126
+ filePath: text("file_path"),
127
+ /**
128
+ * JSON array of track objects:
129
+ * [{ target_node_id, property, keyframes: [{ t, value, ease }] }]
130
+ */
131
+ tracks: text("tracks").notNull().default("[]"),
132
+ /** Total animation duration in milliseconds. */
133
+ durationMs: integer("duration_ms").notNull().default(300),
134
+ /** Default easing function name (e.g. "ease", "ease-in-out", "linear"). */
135
+ defaultEase: text("default_ease").notNull().default("ease"),
136
+ /**
137
+ * Hash of the compiled CSS output; used to detect drift between the tracks
138
+ * JSON and the compiled CSS block. apply-motion-edit updates both atomically.
139
+ */
140
+ compiledHash: text("compiled_hash"),
141
+ createdAt: text("created_at").default(now()),
142
+ updatedAt: text("updated_at").default(now()),
143
+ ...ownableColumns(),
144
+ });
145
+
146
+ /**
147
+ * Design states, fixtures, and live captures.
148
+ * States are alternate x-data / DOM snapshots (e.g. Default, Loading, Empty).
149
+ * Fixtures hold static data payloads for real-app preview.
150
+ * Captures are live snapshots of a running app's route + props + API data.
151
+ */
152
+ export const designState = table("design_state", {
153
+ id: text("id").primaryKey(),
154
+ designId: text("design_id").notNull(),
155
+ /** Opaque source connection ref. */
156
+ sourceRef: text("source_ref"),
157
+ name: text("name").notNull(),
158
+ /** 'state' | 'fixture' | 'capture' */
159
+ kind: text("kind", { enum: ["state", "fixture", "capture"] })
160
+ .notNull()
161
+ .default("state"),
162
+ /** Active breakpoint when this state was created: 'auto' | 'desktop' | 'tablet' | 'mobile' */
163
+ breakpoint: text("breakpoint", {
164
+ enum: ["auto", "desktop", "tablet", "mobile"],
165
+ })
166
+ .notNull()
167
+ .default("auto"),
168
+ /** Route path captured (real-app captures only). */
169
+ route: text("route"),
170
+ /** JSON: static data fixture payload (fixture kind). */
171
+ fixtureData: text("fixture_data"),
172
+ /** JSON: live-captured route props + API response (capture kind). */
173
+ captureData: text("capture_data"),
174
+ /** Opaque reference to a design_version snapshot or stored preview artifact. */
175
+ previewRef: text("preview_ref"),
176
+ createdAt: text("created_at").default(now()),
177
+ updatedAt: text("updated_at").default(now()),
178
+ ...ownableColumns(),
179
+ });
180
+
181
+ /**
182
+ * Cached accessibility audit + visual diff results for a design.
183
+ * Keyed by design + optional base/compare design_versions pair + source ref.
184
+ * status: 'pending' | 'ready' | 'error'
185
+ */
186
+ export const designReviewSnapshot = table("design_review_snapshot", {
187
+ id: text("id").primaryKey(),
188
+ designId: text("design_id").notNull(),
189
+ /** design_versions.id for the base (older) snapshot in a diff. */
190
+ baseVersionId: text("base_version_id"),
191
+ /** design_versions.id for the compare (newer) snapshot in a diff. */
192
+ compareVersionId: text("compare_version_id"),
193
+ /** Opaque source connection ref. */
194
+ sourceRef: text("source_ref"),
195
+ /** JSON: array of a11y findings (contrast, tap targets, roles, alt, focus). */
196
+ a11yFindings: text("a11y_findings"),
197
+ /** JSON: visual diff surface data (changed regions, before/after refs). */
198
+ visualDiff: text("visual_diff"),
199
+ /** 'pending' | 'ready' | 'error' */
200
+ status: text("status", { enum: ["pending", "ready", "error"] })
201
+ .notNull()
202
+ .default("pending"),
203
+ createdAt: text("created_at").default(now()),
204
+ updatedAt: text("updated_at").default(now()),
205
+ ...ownableColumns(),
206
+ });
@@ -121,6 +121,96 @@ CREATE INDEX IF NOT EXISTS design_systems_owner_org_updated_idx ON design_system
121
121
  CREATE INDEX IF NOT EXISTS design_shares_resource_principal_idx ON design_shares (resource_id, principal_type, principal_id);
122
122
  CREATE INDEX IF NOT EXISTS design_system_shares_resource_principal_idx ON design_system_shares (resource_id, principal_type, principal_id)`,
123
123
  },
124
+ // v12: component_index — real-app component metadata (name, file path,
125
+ // export name, parsed prop types, cva/tailwind-variants variants, Storybook
126
+ // stories, runtime selectors). Ownable; access-checked via accessFilter /
127
+ // assertAccess.
128
+ {
129
+ version: 12,
130
+ sql: `CREATE TABLE IF NOT EXISTS component_index (
131
+ id TEXT PRIMARY KEY,
132
+ design_id TEXT NOT NULL,
133
+ source_ref TEXT,
134
+ name TEXT NOT NULL,
135
+ file_path TEXT,
136
+ export_name TEXT,
137
+ props TEXT,
138
+ variants TEXT,
139
+ stories TEXT,
140
+ runtime_selectors TEXT,
141
+ created_at TEXT DEFAULT (datetime('now')),
142
+ updated_at TEXT DEFAULT (datetime('now')),
143
+ owner_email TEXT NOT NULL DEFAULT 'local@localhost',
144
+ org_id TEXT,
145
+ visibility TEXT NOT NULL DEFAULT 'private'
146
+ )`,
147
+ },
148
+ // v13: motion_timeline — CSS-first keyframe animation tracks scoped to one
149
+ // design + source + screen/file. tracks JSON is the editing representation;
150
+ // the compiled CSS block (managed <style data-agent-native-motion>) is the
151
+ // runtime truth. compiled_hash guards drift between the two. Ownable.
152
+ {
153
+ version: 13,
154
+ sql: `CREATE TABLE IF NOT EXISTS motion_timeline (
155
+ id TEXT PRIMARY KEY,
156
+ design_id TEXT NOT NULL,
157
+ source_ref TEXT,
158
+ file_path TEXT,
159
+ tracks TEXT NOT NULL DEFAULT '[]',
160
+ duration_ms INTEGER NOT NULL DEFAULT 300,
161
+ default_ease TEXT NOT NULL DEFAULT 'ease',
162
+ compiled_hash TEXT,
163
+ created_at TEXT DEFAULT (datetime('now')),
164
+ updated_at TEXT DEFAULT (datetime('now')),
165
+ owner_email TEXT NOT NULL DEFAULT 'local@localhost',
166
+ org_id TEXT,
167
+ visibility TEXT NOT NULL DEFAULT 'private'
168
+ )`,
169
+ },
170
+ // v14: design_state — design states (alternate x-data/DOM snapshots for
171
+ // Default / Loading / Empty / Error), static data fixtures, and live
172
+ // captures of running-app route + props + API data. Ownable.
173
+ {
174
+ version: 14,
175
+ sql: `CREATE TABLE IF NOT EXISTS design_state (
176
+ id TEXT PRIMARY KEY,
177
+ design_id TEXT NOT NULL,
178
+ source_ref TEXT,
179
+ name TEXT NOT NULL,
180
+ kind TEXT NOT NULL DEFAULT 'state',
181
+ breakpoint TEXT NOT NULL DEFAULT 'auto',
182
+ route TEXT,
183
+ fixture_data TEXT,
184
+ capture_data TEXT,
185
+ preview_ref TEXT,
186
+ created_at TEXT DEFAULT (datetime('now')),
187
+ updated_at TEXT DEFAULT (datetime('now')),
188
+ owner_email TEXT NOT NULL DEFAULT 'local@localhost',
189
+ org_id TEXT,
190
+ visibility TEXT NOT NULL DEFAULT 'private'
191
+ )`,
192
+ },
193
+ // v15: design_review_snapshot — cached a11y audit results and visual diff
194
+ // data keyed by design + optional base/compare design_versions pair.
195
+ // status: 'pending' | 'ready' | 'error'. Ownable.
196
+ {
197
+ version: 15,
198
+ sql: `CREATE TABLE IF NOT EXISTS design_review_snapshot (
199
+ id TEXT PRIMARY KEY,
200
+ design_id TEXT NOT NULL,
201
+ base_version_id TEXT,
202
+ compare_version_id TEXT,
203
+ source_ref TEXT,
204
+ a11y_findings TEXT,
205
+ visual_diff TEXT,
206
+ status TEXT NOT NULL DEFAULT 'pending',
207
+ created_at TEXT DEFAULT (datetime('now')),
208
+ updated_at TEXT DEFAULT (datetime('now')),
209
+ owner_email TEXT NOT NULL DEFAULT 'local@localhost',
210
+ org_id TEXT,
211
+ visibility TEXT NOT NULL DEFAULT 'private'
212
+ )`,
213
+ },
124
214
  ],
125
215
  { table: "design_migrations" },
126
216
  );
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Builder app helpers for the Design Studio.
3
+ *
4
+ * Provides two capabilities:
5
+ *
6
+ * 1. **Connection status** — resolve whether Builder is configured for the
7
+ * current request (credentials + project ID), without duplicating Builder
8
+ * auth logic. Delegates entirely to the core `resolveBuilderCredentials` /
9
+ * `resolveIsBuilderBranchingEnabled` helpers; no credential values are
10
+ * surfaced in return types.
11
+ *
12
+ * 2. **Migration seed** — build the prompt seed handed to the Builder cloud
13
+ * agent when migrating an inline Alpine/HTML design to a real React + Tailwind
14
+ * app. The seed carries:
15
+ * - The design's semantic HTML (up to `MAX_HTML_BYTES` per file).
16
+ * - The `:root` CSS custom-property block extracted from each file.
17
+ * - Any Brand Kit token summary from the linked design system.
18
+ * - Human-readable migration instructions for the Builder cloud agent.
19
+ *
20
+ * Nothing in this module calls the Builder API directly. API calls happen in
21
+ * `migrate-inline-design-to-app.ts` (action) via `runBuilderAgent`.
22
+ *
23
+ * Security note: credential values are never included in return types or seed
24
+ * content. The `resolveBuilderStatus` function returns only boolean flags and
25
+ * the pre-built `connectUrl` from the core browser helpers.
26
+ */
27
+
28
+ import {
29
+ resolveBuilderBranchProjectId,
30
+ resolveIsBuilderBranchingEnabled,
31
+ } from "@agent-native/core/server";
32
+ import { resolveHasCompleteBuilderConnection } from "@agent-native/core/server";
33
+ import { getRequestUserEmail } from "@agent-native/core/server/request-context";
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Types
37
+ // ---------------------------------------------------------------------------
38
+
39
+ export interface BuilderConnectionStatus {
40
+ /** True when Builder credentials are configured and valid. */
41
+ connected: boolean;
42
+ /** True when a Builder branch project is also configured (= agents can run). */
43
+ builderEnabled: boolean;
44
+ /** The resolved Builder branch project ID, empty string when not configured. */
45
+ branchProjectId: string;
46
+ /** The email of the currently authenticated user (for routing the Builder job). */
47
+ ownerEmail: string | null;
48
+ }
49
+
50
+ export interface MigrationSeed {
51
+ /**
52
+ * The full migration prompt to hand to `runBuilderAgent`.
53
+ * Contains the design HTML, extracted CSS vars, token summary, and
54
+ * detailed instructions for the Builder cloud agent.
55
+ */
56
+ prompt: string;
57
+ /**
58
+ * The number of design files included in the seed (capped at
59
+ * `MAX_SEED_FILES`).
60
+ */
61
+ fileCount: number;
62
+ /**
63
+ * Total HTML bytes included in the seed (before any truncation).
64
+ */
65
+ totalBytes: number;
66
+ }
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Constants
70
+ // ---------------------------------------------------------------------------
71
+
72
+ /**
73
+ * Maximum number of files to include in the migration seed.
74
+ * Inline designs rarely exceed this; the cap keeps the Builder prompt
75
+ * within a safe token budget.
76
+ */
77
+ const MAX_SEED_FILES = 10;
78
+
79
+ /**
80
+ * Maximum bytes per HTML file included in the migration seed.
81
+ * Files larger than this are truncated with an ellipsis notice so the
82
+ * Builder agent still sees the overall structure without exceeding its
83
+ * prompt limit.
84
+ */
85
+ const MAX_HTML_BYTES_PER_FILE = 80_000;
86
+
87
+ // ---------------------------------------------------------------------------
88
+ // Connection status
89
+ // ---------------------------------------------------------------------------
90
+
91
+ /**
92
+ * Return the Builder connection status for the current request context.
93
+ *
94
+ * Delegates to core helpers (`resolveHasCompleteBuilderConnection`,
95
+ * `resolveIsBuilderBranchingEnabled`, `resolveBuilderBranchProjectId`) so
96
+ * credential resolution lives in one place and is never duplicated here.
97
+ *
98
+ * Usage:
99
+ * ```ts
100
+ * const status = await resolveBuilderStatus();
101
+ * if (!status.builderEnabled) {
102
+ * return { cta: "connect-builder" };
103
+ * }
104
+ * ```
105
+ */
106
+ export async function resolveBuilderStatus(): Promise<BuilderConnectionStatus> {
107
+ const [connected, builderEnabled, branchProjectId] = await Promise.all([
108
+ resolveHasCompleteBuilderConnection(),
109
+ resolveIsBuilderBranchingEnabled(),
110
+ resolveBuilderBranchProjectId(),
111
+ ]);
112
+
113
+ return {
114
+ connected,
115
+ builderEnabled,
116
+ branchProjectId,
117
+ ownerEmail: getRequestUserEmail() ?? null,
118
+ };
119
+ }
120
+
121
+ // ---------------------------------------------------------------------------
122
+ // CSS-var extraction
123
+ // ---------------------------------------------------------------------------
124
+
125
+ /**
126
+ * Extract the `:root` block(s) from a raw HTML/CSS string.
127
+ *
128
+ * Returns only the lines inside `:root { … }` declarations to keep the
129
+ * migration seed focused on custom-property tokens rather than the full
130
+ * stylesheet. Falls back to the empty string when no `:root` block is found.
131
+ */
132
+ function extractRootCssVars(html: string): string {
133
+ // Match one or more :root { … } blocks (non-greedy, handles multi-block files).
134
+ const matches = [...html.matchAll(/:root\s*\{([^}]*)\}/g)];
135
+ if (matches.length === 0) return "";
136
+
137
+ const lines: string[] = [];
138
+ for (const match of matches) {
139
+ const block = match[1] ?? "";
140
+ for (const line of block.split("\n")) {
141
+ const trimmed = line.trim();
142
+ // Only include CSS custom property declarations (--var: value;).
143
+ if (trimmed.startsWith("--") && trimmed.includes(":")) {
144
+ lines.push(` ${trimmed}`);
145
+ }
146
+ }
147
+ }
148
+ return lines.length > 0 ? `:root {\n${lines.join("\n")}\n}` : "";
149
+ }
150
+
151
+ // ---------------------------------------------------------------------------
152
+ // Migration seed builder
153
+ // ---------------------------------------------------------------------------
154
+
155
+ /**
156
+ * Build the migration seed prompt for the Builder cloud agent.
157
+ *
158
+ * @param params.title - Human-readable design title (used in the prompt header).
159
+ * @param params.files - Current design files (id + filename + content + fileType).
160
+ * @param params.resolvedCssVars - Resolved CSS-var → value map from tweak selections.
161
+ * @param params.brandKitSummary - Optional human-readable token summary from the
162
+ * linked design system (e.g. from `index-design-tokens`).
163
+ *
164
+ * The caller is responsible for fetching the design snapshot first. This
165
+ * function is pure (no DB or network calls) so it is easy to test and reuse.
166
+ */
167
+ export function buildMigrationSeed(params: {
168
+ title: string;
169
+ files: Array<{
170
+ filename: string;
171
+ content: string;
172
+ fileType: string;
173
+ }>;
174
+ resolvedCssVars?: Record<string, string>;
175
+ brandKitSummary?: string;
176
+ }): MigrationSeed {
177
+ const { title, files, resolvedCssVars, brandKitSummary } = params;
178
+
179
+ // Limit file count.
180
+ const seedFiles = files.slice(0, MAX_SEED_FILES);
181
+ let totalBytes = 0;
182
+
183
+ // Build per-file sections.
184
+ const fileSections: string[] = [];
185
+ for (const file of seedFiles) {
186
+ const rawBytes = new TextEncoder().encode(file.content).length;
187
+ totalBytes += rawBytes;
188
+
189
+ let content = file.content;
190
+ let truncationNote = "";
191
+ if (rawBytes > MAX_HTML_BYTES_PER_FILE) {
192
+ content = file.content.slice(0, MAX_HTML_BYTES_PER_FILE);
193
+ truncationNote = `\n<!-- [TRUNCATED: original file was ${rawBytes} bytes; only the first ${MAX_HTML_BYTES_PER_FILE} bytes are shown] -->`;
194
+ }
195
+
196
+ // Extract :root CSS vars from each file for the token block.
197
+ const cssVars = extractRootCssVars(content);
198
+
199
+ const section = [
200
+ `### File: ${file.filename}`,
201
+ "```html",
202
+ content,
203
+ truncationNote,
204
+ "```",
205
+ ...(cssVars
206
+ ? [
207
+ "",
208
+ "**Extracted CSS custom properties (design tokens):**",
209
+ "```css",
210
+ cssVars,
211
+ "```",
212
+ ]
213
+ : []),
214
+ ]
215
+ .filter((l) => l !== "")
216
+ .join("\n");
217
+
218
+ fileSections.push(section);
219
+ }
220
+
221
+ // Build the resolved CSS vars block (from tweak selections).
222
+ let resolvedVarsBlock = "";
223
+ if (resolvedCssVars && Object.keys(resolvedCssVars).length > 0) {
224
+ const lines = Object.entries(resolvedCssVars)
225
+ .map(([k, v]) => ` ${k}: ${v};`)
226
+ .join("\n");
227
+ resolvedVarsBlock = [
228
+ "",
229
+ "## User-tuned token overrides (apply these values in the real app)",
230
+ "```css",
231
+ `:root {\n${lines}\n}`,
232
+ "```",
233
+ ].join("\n");
234
+ }
235
+
236
+ // Brand Kit summary block.
237
+ const brandKitBlock =
238
+ brandKitSummary && brandKitSummary.trim()
239
+ ? [
240
+ "",
241
+ "## Brand Kit / Design System tokens",
242
+ brandKitSummary.trim(),
243
+ ].join("\n")
244
+ : "";
245
+
246
+ // Build the full prompt.
247
+ const omittedFilesNote =
248
+ files.length > MAX_SEED_FILES
249
+ ? `\n> Note: ${files.length - MAX_SEED_FILES} additional file(s) were omitted to stay within the prompt limit.\n`
250
+ : "";
251
+
252
+ const prompt = [
253
+ `# Migrate inline design to React app: "${title}"`,
254
+ "",
255
+ "You are a Builder cloud agent. The user has an inline Alpine.js / Tailwind HTML prototype",
256
+ "that they want to migrate to a real React + TypeScript + Tailwind app.",
257
+ "",
258
+ "## Your task",
259
+ "1. Convert the HTML prototype below into a production-quality React + TypeScript + Tailwind",
260
+ " application, preserving the visual design and UX faithfully.",
261
+ "2. Map Alpine.js `x-data` / `x-show` / `x-bind` patterns to React state, conditional",
262
+ " rendering, and event handlers.",
263
+ "3. Extract inline styles and repeated patterns into reusable React components with",
264
+ " Tailwind classes. Preserve the `:root` CSS custom properties as a `globals.css` file",
265
+ " and wire them into the Tailwind theme config.",
266
+ "4. Carry all design tokens (color, typography, spacing, radius) from the `:root` block",
267
+ " into the generated `globals.css` and `tailwind.config.ts` so the visual identity is",
268
+ " identical to the original prototype.",
269
+ "5. Do not introduce placeholder data or lorem ipsum. Keep the structural content",
270
+ " from the original design.",
271
+ "6. Use shadcn/ui primitives (Button, Card, Dialog, Input, etc.) where the HTML uses",
272
+ " semantically equivalent patterns.",
273
+ "7. The app must compile and render without errors.",
274
+ "",
275
+ "## Source design files",
276
+ omittedFilesNote,
277
+ ...fileSections,
278
+ resolvedVarsBlock,
279
+ brandKitBlock,
280
+ "",
281
+ "## Output expectations",
282
+ "- A fully working React app in the current project branch.",
283
+ "- `src/app/globals.css` containing all `:root` CSS custom properties from above.",
284
+ "- `tailwind.config.ts` wiring those vars into the Tailwind theme.",
285
+ "- At least one page component that renders the migrated design.",
286
+ "- All Alpine `x-data` patterns converted to React hooks / state.",
287
+ "- All Tailwind CDN classes preserved as-is in JSX.",
288
+ ]
289
+ .filter((l) => l !== undefined && l !== null)
290
+ .join("\n");
291
+
292
+ return {
293
+ prompt,
294
+ fileCount: seedFiles.length,
295
+ totalBytes,
296
+ };
297
+ }