@agent-native/core 0.80.9 → 0.80.11

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 (148) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +13 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +108 -15
  5. package/corpus/core/src/cli/design-connect.ts +28 -2
  6. package/corpus/core/src/cli/skills.ts +45 -24
  7. package/corpus/core/src/client/AssistantChat.tsx +123 -26
  8. package/corpus/core/src/client/sse-event-processor.ts +9 -0
  9. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +9 -2
  10. package/corpus/templates/assets/AGENTS.md +4 -0
  11. package/corpus/templates/assets/actions/_tool-activity.ts +46 -0
  12. package/corpus/templates/assets/actions/generate-image-batch.ts +26 -5
  13. package/corpus/templates/assets/actions/generate-image.ts +118 -67
  14. package/corpus/templates/assets/actions/list-draft-assets.ts +50 -0
  15. package/corpus/templates/assets/actions/rerun-generation-run.ts +52 -31
  16. package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +100 -0
  17. package/corpus/templates/assets/app/i18n/zh-TW.ts +7 -0
  18. package/corpus/templates/assets/app/i18n-data.ts +61 -0
  19. package/corpus/templates/assets/app/routes/_index.tsx +4 -0
  20. package/corpus/templates/assets/app/routes/library.tsx +145 -38
  21. package/corpus/templates/assets/changelog/2026-06-29-image-generation-can-now-render-requested-text-and-respect-b.md +6 -0
  22. package/corpus/templates/assets/changelog/2026-06-30-image-generation-no-longer-looks-stuck-while-the-provider-is-still-working.md +6 -0
  23. package/corpus/templates/assets/changelog/2026-06-30-tagged-presets-now-control-the-image-aspect-ratio.md +6 -0
  24. package/corpus/templates/assets/server/lib/generation.ts +135 -11
  25. package/corpus/templates/assets/shared/api.ts +4 -0
  26. package/corpus/templates/clips/actions/list-ai-requests.ts +60 -0
  27. package/corpus/templates/clips/app/hooks/use-auto-title.ts +24 -25
  28. package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +717 -0
  29. package/corpus/templates/design/actions/add-breakpoint.ts +143 -0
  30. package/corpus/templates/design/actions/add-localhost-screens.ts +5 -0
  31. package/corpus/templates/design/actions/apply-a11y-fix.ts +317 -0
  32. package/corpus/templates/design/actions/apply-component-prop-edit.ts +441 -0
  33. package/corpus/templates/design/actions/apply-design-state.ts +213 -0
  34. package/corpus/templates/design/actions/apply-design-token-edit.ts +202 -0
  35. package/corpus/templates/design/actions/apply-motion-edit.ts +413 -0
  36. package/corpus/templates/design/actions/apply-shader-fill.ts +404 -0
  37. package/corpus/templates/design/actions/apply-visual-edit.ts +191 -5
  38. package/corpus/templates/design/actions/capture-design-state.ts +224 -0
  39. package/corpus/templates/design/actions/connect-builder-app.ts +162 -0
  40. package/corpus/templates/design/actions/create-component.ts +447 -0
  41. package/corpus/templates/design/actions/create-design-branch.ts +263 -0
  42. package/corpus/templates/design/actions/create-design-state.ts +162 -0
  43. package/corpus/templates/design/actions/delete-design-state.ts +55 -0
  44. package/corpus/templates/design/actions/delete-design.ts +20 -0
  45. package/corpus/templates/design/actions/deploy-design-preview.ts +275 -0
  46. package/corpus/templates/design/actions/get-component-details.ts +251 -0
  47. package/corpus/templates/design/actions/get-design-branch-diff.ts +362 -0
  48. package/corpus/templates/design/actions/get-design-review.ts +314 -0
  49. package/corpus/templates/design/actions/get-design-surface-index.ts +582 -0
  50. package/corpus/templates/design/actions/get-motion-timeline.ts +99 -0
  51. package/corpus/templates/design/actions/index-components.ts +258 -0
  52. package/corpus/templates/design/actions/index-design-tokens.ts +277 -0
  53. package/corpus/templates/design/actions/list-design-extensions.ts +309 -0
  54. package/corpus/templates/design/actions/list-design-source-capabilities.ts +153 -0
  55. package/corpus/templates/design/actions/list-design-states.ts +90 -0
  56. package/corpus/templates/design/actions/migrate-inline-design-to-app.ts +298 -0
  57. package/corpus/templates/design/actions/open-component-source.ts +242 -0
  58. package/corpus/templates/design/actions/preview-component-prop-edit.ts +260 -0
  59. package/corpus/templates/design/actions/preview-design-token-edit.ts +113 -0
  60. package/corpus/templates/design/actions/preview-shader-fill.ts +187 -0
  61. package/corpus/templates/design/actions/remove-breakpoint.ts +103 -0
  62. package/corpus/templates/design/actions/remove-motion-timeline.ts +196 -0
  63. package/corpus/templates/design/actions/run-design-audit.ts +436 -0
  64. package/corpus/templates/design/actions/run-design-extension-action.ts +284 -0
  65. package/corpus/templates/design/actions/set-active-breakpoint.ts +39 -0
  66. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1550 -82
  67. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +649 -43
  68. package/corpus/templates/design/app/components/design/DrawOverlay.tsx +1 -1
  69. package/corpus/templates/design/app/components/design/EditPanel.tsx +1788 -52
  70. package/corpus/templates/design/app/components/design/LayersPanel.tsx +114 -33
  71. package/corpus/templates/design/app/components/design/LocalSourceEditBanner.tsx +157 -0
  72. package/corpus/templates/design/app/components/design/MotionDock.tsx +1071 -0
  73. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1053 -99
  74. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +766 -0
  75. package/corpus/templates/design/app/components/design/StatesPanel.tsx +581 -0
  76. package/corpus/templates/design/app/components/design/TokensPanel.tsx +578 -0
  77. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +241 -0
  78. package/corpus/templates/design/app/components/design/index.ts +16 -0
  79. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +15 -6
  80. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +66 -3
  81. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +145 -0
  82. package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +42 -12
  83. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +1 -1
  84. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +131 -0
  85. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +3 -1
  86. package/corpus/templates/design/app/i18n/zh-TW.ts +32 -0
  87. package/corpus/templates/design/app/i18n-data.ts +280 -0
  88. package/corpus/templates/design/app/pages/DesignEditor.tsx +3087 -490
  89. package/corpus/templates/design/changelog/2026-06-29-added-a-review-panel-in-the-design-editor-s-inspector-with-a.md +6 -0
  90. package/corpus/templates/design/changelog/2026-06-29-copying-and-pasting-layers-no-longer-shows-success-notificat.md +6 -0
  91. package/corpus/templates/design/changelog/2026-06-29-device-presets-in-all-screens-view-resize-the-selected-previ.md +6 -0
  92. package/corpus/templates/design/changelog/2026-06-29-layer-and-canvas-drags-keep-the-layer-list-stable-while-chan.md +6 -0
  93. package/corpus/templates/design/changelog/2026-06-29-layer-moves-can-be-undone-and-redone-without-flashing-the-ca.md +6 -0
  94. package/corpus/templates/design/changelog/2026-06-29-screen-previews-use-a-single-blue-hover-border-in-all-screen.md +6 -0
  95. package/corpus/templates/design/changelog/2026-06-29-selected-containers-show-draggable-padding-and-gap-guides-on.md +6 -0
  96. package/corpus/templates/design/changelog/2026-06-29-the-design-editor-adds-a-studio-layer-with-tokens-respon.md +6 -0
  97. package/corpus/templates/design/changelog/2026-06-30-accessibility-findings-now-offer-a-one-click-fix.md +6 -0
  98. package/corpus/templates/design/changelog/2026-06-30-component-instances-now-have-editable-props-in-the-inspe.md +6 -0
  99. package/corpus/templates/design/changelog/2026-06-30-design-token-edits-now-stay-visible-in-previews-and-token-li.md +6 -0
  100. package/corpus/templates/design/changelog/2026-06-30-inspect-code-now-shows-the-elements-opening-tag-at-a-gla.md +6 -0
  101. package/corpus/templates/design/changelog/2026-06-30-shader-fill-presets-can-now-be-applied-to-an-element.md +6 -0
  102. package/corpus/templates/design/changelog/2026-06-30-the-motion-timeline-can-now-add-a-track-to-any-element-a.md +6 -0
  103. package/corpus/templates/design/changelog/2026-06-30-visual-editor-selection-layer-paint-and-drawing-controls-are-more-reliable.md +6 -0
  104. package/corpus/templates/design/e2e/global-setup.ts +94 -2
  105. package/corpus/templates/design/e2e/helpers.ts +10 -4
  106. package/corpus/templates/design/server/db/schema.ts +123 -0
  107. package/corpus/templates/design/server/plugins/db.ts +90 -0
  108. package/corpus/templates/design/shared/builder-app.ts +297 -0
  109. package/corpus/templates/design/shared/capability-resolver.ts +123 -0
  110. package/corpus/templates/design/shared/capture-sanitize.ts +70 -0
  111. package/corpus/templates/design/shared/code-layer.ts +1016 -71
  112. package/corpus/templates/design/shared/component-model.ts +274 -0
  113. package/corpus/templates/design/shared/design-review.ts +275 -0
  114. package/corpus/templates/design/shared/design-source-capabilities.ts +286 -0
  115. package/corpus/templates/design/shared/design-state.ts +112 -0
  116. package/corpus/templates/design/shared/design-surface-index.ts +258 -0
  117. package/corpus/templates/design/shared/motion-compiler.ts +349 -0
  118. package/corpus/templates/design/shared/motion-timeline.ts +193 -0
  119. package/corpus/templates/design/shared/resolve-tweaks.ts +32 -9
  120. package/corpus/templates/design/shared/responsive-classes.ts +452 -0
  121. package/corpus/templates/design/shared/shader-fill.ts +323 -0
  122. package/corpus/templates/design/shared/source-mode.ts +245 -0
  123. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +6 -2
  124. package/corpus/templates/plan/changelog/2026-06-29-plan-canvases-open-without-an-initial-pan-and-zoom-flicker.md +6 -0
  125. package/dist/agent/production-agent.d.ts.map +1 -1
  126. package/dist/agent/production-agent.js +94 -10
  127. package/dist/agent/production-agent.js.map +1 -1
  128. package/dist/cli/design-connect.d.ts.map +1 -1
  129. package/dist/cli/design-connect.js +28 -2
  130. package/dist/cli/design-connect.js.map +1 -1
  131. package/dist/cli/skills.d.ts.map +1 -1
  132. package/dist/cli/skills.js +37 -21
  133. package/dist/cli/skills.js.map +1 -1
  134. package/dist/client/AssistantChat.d.ts +14 -0
  135. package/dist/client/AssistantChat.d.ts.map +1 -1
  136. package/dist/client/AssistantChat.js +115 -22
  137. package/dist/client/AssistantChat.js.map +1 -1
  138. package/dist/client/sse-event-processor.d.ts.map +1 -1
  139. package/dist/client/sse-event-processor.js +10 -0
  140. package/dist/client/sse-event-processor.js.map +1 -1
  141. package/dist/collab/routes.d.ts +1 -1
  142. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  143. package/dist/notifications/routes.d.ts +2 -2
  144. package/dist/observability/routes.d.ts +8 -8
  145. package/dist/resources/handlers.d.ts +2 -2
  146. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  147. package/dist/server/transcribe-voice.d.ts +1 -1
  148. package/package.json +1 -1
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Single source of truth for canvas-primitive visual styles.
3
+ *
4
+ * Both the board draft preview (DraftPrimitiveContent in MultiScreenCanvas.tsx)
5
+ * and the committed HTML renderer (appendCanvasPrimitiveToHtml in
6
+ * DesignEditor.tsx) must use these helpers so that what you see while drawing
7
+ * (preview) is pixel-identical to what gets committed — fixing the B5 color
8
+ * jump and the B6 ellipse border-radius jump.
9
+ *
10
+ * Design decisions:
11
+ * - Fill: `hsl(var(--primary) / 0.12)` — the calm, theme-adaptive tint already
12
+ * used by the draft preview. NOT the saturated blue previously hard-coded in
13
+ * appendCanvasPrimitiveToHtml (`rgba(37,99,235,0.16)`).
14
+ * - Stroke: `hsl(var(--primary) / 0.7)` — same CSS-var approach, adapts to
15
+ * light/dark mode automatically.
16
+ * - Stroke width: 1px for div-based shapes.
17
+ * - Ellipse: borderRadius "50%" in both paths — no more "oval on commit" jump.
18
+ * - Rect: borderRadius "2px" (small, matches the previous committed value; the
19
+ * preview used Tailwind `rounded-sm` which resolves to 2px).
20
+ * - Frame: dashed border, slightly lighter fill (matches preview semantics).
21
+ * - Text: inherits current color; dashed outline as placeholder affordance.
22
+ *
23
+ * CSS custom properties (`hsl(var(--primary) / …)`) work in the committed HTML
24
+ * because the iframe inherits the design-editor stylesheet that defines
25
+ * `--primary`. When a `fill` / `stroke` override is already present on the
26
+ * primitive those are passed through unchanged so user-chosen colors are never
27
+ * clobbered.
28
+ */
29
+
30
+ import type * as React from "react";
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Types
34
+ // ---------------------------------------------------------------------------
35
+
36
+ export type CanvasPrimitiveKind =
37
+ | "rect"
38
+ | "rectangle"
39
+ | "ellipse"
40
+ | "text"
41
+ | "frame";
42
+
43
+ /**
44
+ * Canonical visual properties for a div-based canvas primitive.
45
+ * All values are plain CSS strings (usable verbatim in both React CSSProperties
46
+ * and HTML style attributes).
47
+ */
48
+ export interface CanvasPrimitiveVisual {
49
+ background: string;
50
+ border: string;
51
+ borderRadius: string;
52
+ /** Only set for text primitives */
53
+ color?: string;
54
+ }
55
+
56
+ // ---------------------------------------------------------------------------
57
+ // Canonical tokens
58
+ // ---------------------------------------------------------------------------
59
+
60
+ /** Default fill tint — calm, theme-adaptive, not saturated. */
61
+ const DEFAULT_FILL = "hsl(var(--primary) / 0.12)";
62
+
63
+ /** Default stroke — uses the same hue at higher opacity for clarity. */
64
+ const DEFAULT_STROKE = "hsl(var(--primary) / 0.7)";
65
+
66
+ /** Stroke width in pixels for div-based shapes. */
67
+ const DEFAULT_STROKE_WIDTH_PX = 1;
68
+
69
+ /** Border shorthand shared by rect + ellipse. */
70
+ const DEFAULT_BORDER = `${DEFAULT_STROKE_WIDTH_PX}px solid ${DEFAULT_STROKE}`;
71
+
72
+ /** Frame gets a dashed border to signal it is a layout container. */
73
+ const FRAME_BORDER = `${DEFAULT_STROKE_WIDTH_PX}px dashed ${DEFAULT_STROKE}`;
74
+
75
+ /** Very faint fill for frames so the interior is readable. */
76
+ const FRAME_FILL = "hsl(var(--primary) / 0.05)";
77
+
78
+ /** Small radius matching Tailwind `rounded-sm` (2 px). */
79
+ const RECT_RADIUS = "2px";
80
+
81
+ // ---------------------------------------------------------------------------
82
+ // canvasPrimitiveVisual
83
+ // ---------------------------------------------------------------------------
84
+
85
+ /**
86
+ * Returns the canonical CanvasPrimitiveVisual for the given kind.
87
+ *
88
+ * Usage (React preview):
89
+ * const v = canvasPrimitiveVisual("ellipse");
90
+ * <div style={{ background: v.background, border: v.border, borderRadius: v.borderRadius }} />
91
+ *
92
+ * Usage (committed HTML):
93
+ * const v = canvasPrimitiveVisual("ellipse");
94
+ * el.style.background = v.background;
95
+ * el.style.border = v.border;
96
+ * el.style.borderRadius = v.borderRadius;
97
+ */
98
+ export function canvasPrimitiveVisual(
99
+ kind: CanvasPrimitiveKind,
100
+ ): CanvasPrimitiveVisual {
101
+ switch (kind) {
102
+ case "ellipse":
103
+ return {
104
+ background: DEFAULT_FILL,
105
+ border: DEFAULT_BORDER,
106
+ borderRadius: "50%",
107
+ };
108
+ case "frame":
109
+ return {
110
+ background: FRAME_FILL,
111
+ border: FRAME_BORDER,
112
+ borderRadius: RECT_RADIUS,
113
+ };
114
+ case "text":
115
+ return {
116
+ background: "transparent",
117
+ border: `${DEFAULT_STROKE_WIDTH_PX}px dashed hsl(var(--primary) / 0.6)`,
118
+ borderRadius: RECT_RADIUS,
119
+ color: "currentColor",
120
+ };
121
+ case "rect":
122
+ case "rectangle":
123
+ default:
124
+ return {
125
+ background: DEFAULT_FILL,
126
+ border: DEFAULT_BORDER,
127
+ borderRadius: RECT_RADIUS,
128
+ };
129
+ }
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // canvasPrimitiveStyleString
134
+ // ---------------------------------------------------------------------------
135
+
136
+ /**
137
+ * Returns an inline CSS style string for the given kind, ready to assign to
138
+ * `element.setAttribute("style", …)` or to splice into generated HTML.
139
+ *
140
+ * Override parameters let callers pass a user-chosen fill/stroke instead of
141
+ * the defaults — if undefined the canonical tokens are used.
142
+ *
143
+ * Example output for "ellipse":
144
+ * "background:hsl(var(--primary) / 0.12);border:1px solid hsl(var(--primary) / 0.7);border-radius:50%"
145
+ */
146
+ export function canvasPrimitiveStyleString(
147
+ kind: CanvasPrimitiveKind,
148
+ overrides?: { fill?: string; stroke?: string; strokeWidth?: number },
149
+ ): string {
150
+ const v = canvasPrimitiveVisual(kind);
151
+
152
+ // Apply caller overrides so user-chosen colours are preserved.
153
+ let background = v.background;
154
+ let border = v.border;
155
+
156
+ if (overrides?.fill) {
157
+ background = overrides.fill;
158
+ }
159
+ if (overrides?.stroke || overrides?.strokeWidth !== undefined) {
160
+ const stroke = overrides.stroke ?? DEFAULT_STROKE;
161
+ const width = overrides.strokeWidth ?? DEFAULT_STROKE_WIDTH_PX;
162
+ const style = kind === "frame" || kind === "text" ? "dashed" : "solid";
163
+ border = `${width}px ${style} ${stroke}`;
164
+ }
165
+
166
+ const parts: string[] = [
167
+ `background:${background}`,
168
+ `border:${border}`,
169
+ `border-radius:${v.borderRadius}`,
170
+ ];
171
+
172
+ if (v.color) {
173
+ parts.push(`color:${v.color}`);
174
+ }
175
+
176
+ return parts.join(";");
177
+ }
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // canvasPrimitiveReactStyle
181
+ // ---------------------------------------------------------------------------
182
+
183
+ /**
184
+ * Returns a React CSSProperties object for the given kind, ready to spread
185
+ * onto a JSX `style` prop.
186
+ *
187
+ * Override parameters preserve user-chosen fill/stroke colours.
188
+ *
189
+ * Example:
190
+ * <div style={canvasPrimitiveReactStyle("ellipse")} />
191
+ */
192
+ export function canvasPrimitiveReactStyle(
193
+ kind: CanvasPrimitiveKind,
194
+ overrides?: { fill?: string; stroke?: string; strokeWidth?: number },
195
+ ): React.CSSProperties {
196
+ const v = canvasPrimitiveVisual(kind);
197
+
198
+ let background = v.background as string | undefined;
199
+ let borderColor: string | undefined;
200
+ let borderWidth: number | string | undefined = DEFAULT_STROKE_WIDTH_PX;
201
+ let borderStyle: string | undefined = "solid";
202
+
203
+ if (kind === "frame" || kind === "text") {
204
+ borderStyle = "dashed";
205
+ }
206
+
207
+ if (overrides?.fill) {
208
+ background = overrides.fill;
209
+ }
210
+ if (overrides?.stroke) {
211
+ borderColor = overrides.stroke;
212
+ } else {
213
+ // Extract from canonical border shorthand: "Npx style color"
214
+ const borderParts = v.border.split(" ");
215
+ // e.g. ["1px", "solid", "hsl(...)"] — colour may have spaces inside parens
216
+ borderColor = borderParts.slice(2).join(" ");
217
+ }
218
+ if (overrides?.strokeWidth !== undefined) {
219
+ borderWidth = overrides.strokeWidth;
220
+ }
221
+
222
+ const style: React.CSSProperties = {
223
+ background,
224
+ border: undefined,
225
+ borderColor,
226
+ borderWidth,
227
+ borderStyle,
228
+ borderRadius: v.borderRadius,
229
+ };
230
+
231
+ if (v.color) {
232
+ style.color = v.color;
233
+ }
234
+
235
+ // For text kind, clear fill-as-background since text uses `color`
236
+ if (kind === "text") {
237
+ style.background = "transparent";
238
+ }
239
+
240
+ return style;
241
+ }
@@ -1,8 +1,24 @@
1
1
  export { DesignCanvas } from "./DesignCanvas";
2
+ export type {
3
+ MotionTrackWire,
4
+ IframeHotkeyPayload,
5
+ IframeContextMenuPayload,
6
+ } from "./DesignCanvas";
2
7
  export { DeviceFrame } from "./DeviceFrame";
3
8
  export { DrawOverlay } from "./DrawOverlay";
4
9
  export { EditPanel } from "./EditPanel";
10
+ export type { InspectorTab } from "./EditPanel";
11
+ export {
12
+ MotionDock,
13
+ type MotionDockProps,
14
+ type MotionDockTrack,
15
+ } from "./MotionDock";
5
16
  export { QuestionFlow } from "./QuestionFlow";
17
+ export { ReviewPanel } from "./ReviewPanel";
18
+ export type { ReviewPanelProps, ReviewVersionOption } from "./ReviewPanel";
19
+ export { StatesPanel } from "./StatesPanel";
20
+ export type { StatesPanelProps } from "./StatesPanel";
21
+ export { TokensPanel, type TokensPanelProps } from "./TokensPanel";
6
22
  export { TweaksPanel } from "./TweaksPanel";
7
23
  export type {
8
24
  DrawAnnotation,
@@ -147,6 +147,7 @@ export interface AutoLayoutMatrixProps {
147
147
  onPaddingLinkedChange: (linked: boolean) => void;
148
148
  onClipContentChange?: (clipContent: boolean) => void;
149
149
  onDistribute?: (axis: DistributionAxis) => void;
150
+ onGapModeChange?: (mode: "fixed" | "auto", axis: DistributionAxis) => void;
150
151
  onChildSizingChange: (
151
152
  axis: AutoLayoutSizingAxis,
152
153
  sizing: AutoLayoutSizing,
@@ -246,6 +247,7 @@ export function AutoLayoutMatrix({
246
247
  onPaddingLinkedChange,
247
248
  onClipContentChange,
248
249
  onDistribute,
250
+ onGapModeChange,
249
251
  onChildSizingChange,
250
252
  onChildMinMaxChange,
251
253
  onApplyVariable,
@@ -455,6 +457,7 @@ export function AutoLayoutMatrix({
455
457
  value={value.gap}
456
458
  onGapChange={onGapChange}
457
459
  onDistribute={onDistribute}
460
+ onGapModeChange={onGapModeChange}
458
461
  label={copy.gap}
459
462
  disabled={disabled}
460
463
  direction={value.direction}
@@ -858,6 +861,7 @@ function GapField({
858
861
  value,
859
862
  onGapChange,
860
863
  onDistribute,
864
+ onGapModeChange,
861
865
  label,
862
866
  disabled,
863
867
  direction,
@@ -866,6 +870,7 @@ function GapField({
866
870
  value: number;
867
871
  onGapChange: (gap: number) => void;
868
872
  onDistribute?: (axis: DistributionAxis) => void;
873
+ onGapModeChange?: (mode: "fixed" | "auto", axis: DistributionAxis) => void;
869
874
  label: string;
870
875
  disabled: boolean;
871
876
  direction: AutoLayoutDirection;
@@ -928,16 +933,20 @@ function GapField({
928
933
  <DropdownMenuCheckboxItem
929
934
  checked={gapMode !== "auto"}
930
935
  className="text-[12px]"
931
- onSelect={() => {
932
- /* Fixed gap: keep current numeric value (already numeric). */
933
- }}
936
+ onSelect={() => onGapModeChange?.("fixed", direction)}
934
937
  >
935
938
  {"Fixed" /* i18n-ignore design gap mode label */}
936
939
  </DropdownMenuCheckboxItem>
937
940
  <DropdownMenuCheckboxItem
938
941
  checked={gapMode === "auto"}
939
942
  className="text-[12px]"
940
- onSelect={() => onDistribute(direction)}
943
+ onSelect={() => {
944
+ if (onGapModeChange) {
945
+ onGapModeChange("auto", direction);
946
+ return;
947
+ }
948
+ onDistribute?.(direction);
949
+ }}
941
950
  >
942
951
  {"Auto" /* i18n-ignore design gap mode label */}
943
952
  </DropdownMenuCheckboxItem>
@@ -1227,9 +1236,9 @@ export function SizingField({
1227
1236
  tooltipLabel={`${axis} size`}
1228
1237
  icon={null}
1229
1238
  value={Math.round(resolvedSize ?? 0)}
1230
- onChange={(next) => onSizeChange!(Math.max(1, Math.round(next)))}
1239
+ onChange={(next) => onSizeChange!(Math.max(0, Math.round(next)))}
1231
1240
  unit="px"
1232
- min={1}
1241
+ min={0}
1233
1242
  step={1}
1234
1243
  precision={0}
1235
1244
  disabled={disabled}
@@ -28,6 +28,13 @@ import {
28
28
  PopoverContent,
29
29
  PopoverTrigger,
30
30
  } from "@/components/ui/popover";
31
+ import {
32
+ Select,
33
+ SelectContent,
34
+ SelectItem,
35
+ SelectTrigger,
36
+ SelectValue,
37
+ } from "@/components/ui/select";
31
38
  import {
32
39
  Tooltip,
33
40
  TooltipContent,
@@ -651,6 +658,24 @@ const NOISE_FALLBACK_CSS =
651
658
  "repeating-conic-gradient(#0000 0% 25%, #00000010 0% 50%) 0 0 / 6px 6px, #8a8a8a";
652
659
  const PATTERN_FALLBACK_CSS =
653
660
  "repeating-linear-gradient(45deg, #00000014 0 6px, #ffffff14 6px 12px), #9aa0a6";
661
+ const BLEND_MODE_OPTIONS = [
662
+ { value: "normal", label: "Normal" },
663
+ { value: "multiply", label: "Multiply" },
664
+ { value: "screen", label: "Screen" },
665
+ { value: "overlay", label: "Overlay" },
666
+ { value: "darken", label: "Darken" },
667
+ { value: "lighten", label: "Lighten" },
668
+ { value: "color-dodge", label: "Color dodge" }, // i18n-ignore design blend mode label
669
+ { value: "color-burn", label: "Color burn" }, // i18n-ignore design blend mode label
670
+ { value: "hard-light", label: "Hard light" }, // i18n-ignore design blend mode label
671
+ { value: "soft-light", label: "Soft light" }, // i18n-ignore design blend mode label
672
+ { value: "difference", label: "Difference" },
673
+ { value: "exclusion", label: "Exclusion" },
674
+ { value: "hue", label: "Hue" },
675
+ { value: "saturation", label: "Saturation" },
676
+ { value: "color", label: "Color" },
677
+ { value: "luminosity", label: "Luminosity" },
678
+ ] as const;
654
679
 
655
680
  // ─── Main component ────────────────────────────────────────────────────────────
656
681
 
@@ -662,9 +687,9 @@ export function DesignColorPicker({
662
687
  label: _label,
663
688
  opacity,
664
689
  onOpacityChange,
665
- blendMode: _blendMode,
666
- onBlendModeChange: _onBlendModeChange,
667
- showBlendMode: _showBlendMode,
690
+ blendMode,
691
+ onBlendModeChange,
692
+ showBlendMode = false,
668
693
  fillRows: _fillRows,
669
694
  selectedFillId: _selectedFillId,
670
695
  onFillSelect: _onFillSelect,
@@ -693,6 +718,11 @@ export function DesignColorPicker({
693
718
  const hsv = rgbaToHsv(color);
694
719
  const hsl = rgbaToHsl(color);
695
720
  const effectiveOpacity = opacity ?? alphaToOpacity(color.a);
721
+ const blendModeValue = BLEND_MODE_OPTIONS.some(
722
+ (option) => option.value === blendMode,
723
+ )
724
+ ? blendMode
725
+ : "normal";
696
726
 
697
727
  const [mode, setMode] = useState<DesignColorMode>("hex");
698
728
  const [hexDraft, setHexDraft] = useState(() => toDisplayHex(color));
@@ -1506,6 +1536,39 @@ export function DesignColorPicker({
1506
1536
  </div>
1507
1537
  )}
1508
1538
 
1539
+ {showBlendMode && onBlendModeChange && (
1540
+ <div className="border-t border-border/70 px-3 py-2.5">
1541
+ <div className="flex items-center gap-1.5">
1542
+ <span className="min-w-0 flex-1 text-[11px] text-muted-foreground">
1543
+ {copy.blendMode}
1544
+ </span>
1545
+ <Select
1546
+ value={blendModeValue}
1547
+ disabled={disabled}
1548
+ onValueChange={onBlendModeChange}
1549
+ >
1550
+ <SelectTrigger
1551
+ aria-label={copy.blendMode}
1552
+ className="h-6 min-w-0 flex-1 rounded-md border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 text-[11px] shadow-none focus:ring-1 focus:ring-[var(--design-editor-accent-color)]"
1553
+ >
1554
+ <SelectValue />
1555
+ </SelectTrigger>
1556
+ <SelectContent>
1557
+ {BLEND_MODE_OPTIONS.map((option) => (
1558
+ <SelectItem
1559
+ key={option.value}
1560
+ value={option.value}
1561
+ className="text-[11px]"
1562
+ >
1563
+ {option.label}
1564
+ </SelectItem>
1565
+ ))}
1566
+ </SelectContent>
1567
+ </Select>
1568
+ </div>
1569
+ </div>
1570
+ )}
1571
+
1509
1572
  {/* ── Document colors ──────────────────────────────────────── */}
1510
1573
  {/* Renders the palette of colors already used in the design.
1511
1574
  When `documentColors` is provided, those swatches are shown;
@@ -0,0 +1,145 @@
1
+ import { useT } from "@agent-native/core/client";
2
+ import {
3
+ IconClipboard,
4
+ IconChevronDown,
5
+ IconSparkles,
6
+ } from "@tabler/icons-react";
7
+ import { useState } from "react";
8
+
9
+ import { Button } from "@/components/ui/button";
10
+ import {
11
+ Collapsible,
12
+ CollapsibleContent,
13
+ CollapsibleTrigger,
14
+ } from "@/components/ui/collapsible";
15
+ import { Textarea } from "@/components/ui/textarea";
16
+ import { useAgentEditRequest } from "@/hooks/useAgentEditRequest";
17
+ import { cn } from "@/lib/utils";
18
+
19
+ export interface InspectorAiActionsProps {
20
+ /** CSS selector for the currently-selected element, if any. */
21
+ selector?: string;
22
+ /** Source id (data-code-layer-id) of the selected element, if any. */
23
+ sourceId?: string;
24
+ /** Active file id. */
25
+ fileId?: string;
26
+ /** Active file name (e.g. "index.html"). */
27
+ filename?: string;
28
+ /** The design id. */
29
+ designId?: string;
30
+ /** When false, the controls are disabled. */
31
+ canEdit: boolean;
32
+ }
33
+
34
+ /**
35
+ * Compact inspector block that lets the user type a freeform AI edit request
36
+ * and either route it to the agent chat sidebar ("Apply with AI") or copy the
37
+ * prompt to the clipboard ("Copy prompt").
38
+ *
39
+ * Collapsed by default to keep the inspector clean.
40
+ */
41
+ export function InspectorAiActions({
42
+ selector,
43
+ sourceId,
44
+ fileId,
45
+ filename,
46
+ designId,
47
+ canEdit,
48
+ }: InspectorAiActionsProps) {
49
+ const t = useT();
50
+ const { sendEdit, copyPrompt } = useAgentEditRequest();
51
+ const [open, setOpen] = useState(false);
52
+ const [request, setRequest] = useState("");
53
+
54
+ const args = {
55
+ message: request,
56
+ selector,
57
+ sourceId,
58
+ fileId,
59
+ filename,
60
+ designId,
61
+ };
62
+ const disabled = !canEdit || !request.trim();
63
+
64
+ return (
65
+ <Collapsible open={open} onOpenChange={setOpen} className="w-full">
66
+ <CollapsibleTrigger asChild>
67
+ <button
68
+ type="button"
69
+ className={cn(
70
+ "flex w-full items-center justify-between gap-1 rounded px-2 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground transition-colors",
71
+ )}
72
+ >
73
+ <span className="flex items-center gap-1.5">
74
+ <IconSparkles className="size-3.5 shrink-0" />
75
+ {t("designEditor.localSourceEdit.askAi")}
76
+ </span>
77
+ <IconChevronDown
78
+ className={cn(
79
+ "size-3.5 shrink-0 transition-transform duration-150",
80
+ open && "rotate-180",
81
+ )}
82
+ />
83
+ </button>
84
+ </CollapsibleTrigger>
85
+ <CollapsibleContent className="overflow-hidden data-[state=closed]:animate-none">
86
+ <div className="flex flex-col gap-2 px-1 pb-2 pt-1">
87
+ <Textarea
88
+ value={request}
89
+ onChange={(e) => setRequest(e.target.value)}
90
+ placeholder={
91
+ selector
92
+ ? t("designEditor.localSourceEdit.describeElementChange")
93
+ : t("designEditor.localSourceEdit.describeChange")
94
+ }
95
+ className="min-h-[64px] resize-none text-xs"
96
+ disabled={!canEdit}
97
+ onKeyDown={(e) => {
98
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && !disabled) {
99
+ e.preventDefault();
100
+ void sendEdit(args);
101
+ setRequest("");
102
+ setOpen(false);
103
+ }
104
+ }}
105
+ />
106
+ <div className="flex gap-1.5">
107
+ <Button
108
+ size="sm"
109
+ variant="default"
110
+ className="flex-1 gap-1.5 text-xs"
111
+ disabled={disabled}
112
+ onClick={() => {
113
+ void sendEdit(args);
114
+ setRequest("");
115
+ setOpen(false);
116
+ }}
117
+ >
118
+ <IconSparkles className="size-3.5 shrink-0" />
119
+ {t("designEditor.localSourceEdit.applyWithAi")}
120
+ </Button>
121
+ <Button
122
+ size="sm"
123
+ variant="outline"
124
+ className="gap-1.5 text-xs"
125
+ disabled={disabled}
126
+ onClick={() => {
127
+ void copyPrompt(args);
128
+ }}
129
+ title={t("designEditor.localSourceEdit.copyPromptTooltip")}
130
+ >
131
+ <IconClipboard className="size-3.5 shrink-0" />
132
+ {t("designEditor.localSourceEdit.copyPrompt")}
133
+ </Button>
134
+ </div>
135
+ {selector && (
136
+ <p className="text-[10px] leading-tight text-muted-foreground truncate">
137
+ {t("designEditor.localSourceEdit.targeting")}{" "}
138
+ <code className="font-mono">{selector}</code>
139
+ </p>
140
+ )}
141
+ </div>
142
+ </CollapsibleContent>
143
+ </Collapsible>
144
+ );
145
+ }
@@ -6,6 +6,30 @@ right spot plus copy-pasteable code to insert or replace.
6
6
 
7
7
  Do NOT rely on line numbers — they drift. Use the search strings instead.
8
8
 
9
+ ## Status: shader fills are user-reachable and applyable
10
+
11
+ The **Shader Fills** card in `DesignExtensionsPanel.tsx` (inspector slot
12
+ `design.editor.inspector`) is the live entry point: open it, **Browse Shaders**,
13
+ pick a preset, tune it with `ShaderControls`, and the iframe previews the fill as
14
+ a CSS gradient (`preview-shader-fill`). With an element selected on the canvas, the
15
+ card's **Apply fill** button persists that fill via the `apply-shader-fill` action.
16
+
17
+ `apply-shader-fill` is **no longer gated** — it is SAFETY-gated, not Builder-gated:
18
+
19
+ - It asserts **editor** access on the target design (`accessFilter` read +
20
+ `assertAccess("design", id, "editor")`) before any write.
21
+ - It only persists **HTML design-file** sources. localhost / fusion / inline-html
22
+ sources return the preview value and write nothing.
23
+ - It writes a single inline-style `background` onto the target node through the
24
+ deterministic HTML editor (`applyVisualEdit`, the same persist path as
25
+ `apply-visual-edit`), reusing the strict CSS-colour allowlist from
26
+ `shared/shader-fill.ts` so `descriptor.colors` can never inject CSS.
27
+
28
+ Sections (a), (b), (d), and (f) below remain optional ways to surface shaders in
29
+ _other_ inspector seams (the colour picker fill type, the effects panel, the
30
+ artboard WebGL bridge, design-system tokens) and are not required for the
31
+ card-based apply path that ships today.
32
+
9
33
  ---
10
34
 
11
35
  ## (a) DesignColorPicker.tsx — Add `"shader"` fill type
@@ -351,20 +375,17 @@ if (fill.type === "shader") {
351
375
 
352
376
  ---
353
377
 
354
- ## (e) Changelog entry (DEFERRED)
355
-
356
- The shader feature is not yet user-reachable — it still needs all seam files wired.
357
- Do NOT add a changelog entry yet.
378
+ ## (e) Changelog entry
358
379
 
359
- Once the seam files are integrated and the feature is accessible from the UI (a user
360
- can apply a shader fill from the fill picker or add a shader effect from the effects
361
- panel), run:
380
+ The shader-fills card is now user-reachable and the apply path persists, so a
381
+ user-facing changelog entry is warranted. From the `templates/design/` directory:
362
382
 
363
383
  ```bash
364
- agent-native changelog add "Add GPU shader fills and effects — 8 presets (MeshGradient, GrainGradient, Voronoi, Metaballs, Warp, GodRays, Dithering, PaperTexture) with live preview and agent-tunable params" --type added
384
+ agent-native changelog add "Add GPU shader fills — 8 presets (MeshGradient, GrainGradient, Voronoi, Metaballs, Warp, GodRays, Dithering, PaperTexture) with live preview and one-click apply to the selected element" --type added
365
385
  ```
366
386
 
367
- from the `templates/design/` directory.
387
+ (The effects-panel surface from section (b) is still optional; scope the changelog
388
+ line to what actually ships.)
368
389
 
369
390
  ---
370
391
 
@@ -407,14 +428,23 @@ shader token picker.
407
428
 
408
429
  ## Integration Checklist
409
430
 
431
+ Shipped (card-based preview + apply path):
432
+
433
+ - [x] `DesignExtensionsPanel.tsx` — Shader Fills card opens `ShaderFillsPanel`
434
+ - [x] `DesignExtensionsPanel.tsx` — "Apply fill" persists via `apply-shader-fill`
435
+ - [x] `actions/apply-shader-fill.ts` — persists fill as CSS background (editor-gated)
436
+ - [x] `actions/preview-shader-fill.ts` — preview-only CSS (unchanged)
437
+ - [x] `actions/apply-shader.ts` exists (planning/codegen snippet)
438
+ - [x] `actions/get-shader.ts` exists
439
+
440
+ Optional follow-ups (other inspector seams):
441
+
410
442
  - [ ] `DesignColorPicker.tsx` — `DesignFillType` includes `"shader"`
411
443
  - [ ] `DesignColorPicker.tsx` — `DesignPaintType` includes `"shader"`
412
444
  - [ ] `DesignColorPicker.tsx` — shader button renders in paint-type row
413
445
  - [ ] `DesignColorPicker.tsx` — `ShaderControls` renders when `paintType === "shader"`
414
446
  - [ ] `EditPanel.tsx` — `ShaderEffectRow` renders in `EffectsProperties`
415
447
  - [ ] `EditPanel.tsx` — "Add Shader" option in effects dropdown
416
- - [ ] `actions/apply-shader.ts` exists
417
- - [ ] `actions/get-shader.ts` exists
418
448
  - [ ] `DesignCanvas.tsx` — shader bridge script injected into artboard
419
449
  - [ ] `DesignCanvas.tsx` — `data-shader` attribute serialized for shader fills
420
- - [ ] Changelog entry added (after feature is user-reachable)
450
+ - [ ] Changelog entry added (see section (e))
@@ -453,7 +453,7 @@ export function DrawOverlay({
453
453
  const toolbar = (
454
454
  <div
455
455
  data-draw-toolbar
456
- className="pointer-events-auto fixed bottom-4 left-1/2 z-[110] flex -translate-x-1/2 items-center gap-2 rounded-xl border border-border bg-popover px-3 py-2 shadow-2xl"
456
+ className="pointer-events-auto fixed bottom-20 left-1/2 z-[110] flex max-w-[calc(100vw-1rem)] -translate-x-1/2 flex-wrap items-center justify-center gap-2 rounded-xl border border-border bg-popover px-3 py-2 shadow-2xl"
457
457
  >
458
458
  {/* Color picker */}
459
459
  <div className="flex gap-1">