@agent-native/core 0.80.0 → 0.80.2

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 (228) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +28 -0
  3. package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  4. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +14 -1
  5. package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  6. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +15 -1
  7. package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  8. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +15 -1
  9. package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  10. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +15 -1
  11. package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  12. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +15 -1
  13. package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  14. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +16 -1
  15. package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  16. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +14 -1
  17. package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  18. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +14 -1
  19. package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  20. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +14 -1
  21. package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  22. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +14 -1
  23. package/corpus/core/docs/content/skills-guide.mdx +5 -0
  24. package/corpus/core/docs/content/template-design.mdx +14 -1
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/application-state/handlers.ts +1 -1
  27. package/corpus/core/src/brand-kit/index.ts +1 -1
  28. package/corpus/core/src/cli/skills.ts +18 -0
  29. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  30. package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
  31. package/corpus/core/src/client/extensions/portable-extension.ts +1 -1
  32. package/corpus/core/src/code-agents/transcript-normalizer.ts +1 -1
  33. package/corpus/core/src/extensions/path.ts +1 -4
  34. package/corpus/core/src/integrations/a2a-continuation-processor.ts +1 -1
  35. package/corpus/core/src/mcp/build-server.ts +2 -2
  36. package/corpus/core/src/mcp/embed-app.ts +35 -1
  37. package/corpus/core/src/org/handlers.ts +5 -5
  38. package/corpus/core/src/org/plugin.ts +2 -2
  39. package/corpus/core/src/scripts/call-agent.ts +1 -1
  40. package/corpus/core/src/scripts/db/safety.ts +1 -1
  41. package/corpus/core/src/server/auth.ts +3 -0
  42. package/corpus/core/src/server/builder-design-systems.ts +308 -0
  43. package/corpus/core/src/server/core-routes-plugin.ts +29 -3
  44. package/corpus/core/src/server/index.ts +8 -0
  45. package/corpus/core/src/styles/agent-native.css +14 -7
  46. package/corpus/core/src/usage/store.ts +2 -2
  47. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +298 -15
  48. package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -4
  49. package/corpus/templates/analytics/actions/mutate-dashboard.ts +18 -2
  50. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +12 -2
  51. package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +66 -0
  52. package/corpus/templates/analytics/app/i18n/zh-TW.ts +37 -0
  53. package/corpus/templates/analytics/app/i18n-data.ts +404 -0
  54. package/corpus/templates/analytics/app/pages/Settings.tsx +333 -0
  55. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +2 -1
  56. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +121 -3
  57. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +49 -26
  58. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +137 -36
  59. package/corpus/templates/analytics/changelog/2026-06-29-analytics-agents-can-now-place-new-charts-into-requested-das.md +6 -0
  60. package/corpus/templates/analytics/changelog/2026-06-29-analytics-queries-now-report-failed-source-query-errors-inst.md +6 -0
  61. package/corpus/templates/analytics/changelog/2026-06-29-charts-can-now-be-dropped-between-full-dashboard-rows-to-cre.md +6 -0
  62. package/corpus/templates/analytics/changelog/2026-06-29-dashboard-email-reports-include-the-full-dashboard-image-wit.md +6 -0
  63. package/corpus/templates/analytics/changelog/2026-06-29-main-surface-border-reaches-the-top.md +6 -0
  64. package/corpus/templates/analytics/changelog/2026-06-29-session-replay-now-records-playable-sessions-in-production-a.md +6 -0
  65. package/corpus/templates/analytics/changelog/2026-06-29-set-up-session-replay-storage-from-settings-connect-builder-.md +6 -0
  66. package/corpus/templates/analytics/scripts/cleanup-empty-session-recordings.ts +102 -0
  67. package/corpus/templates/analytics/server/lib/dashboard-report.ts +49 -26
  68. package/corpus/templates/analytics/server/lib/real-data-actions.ts +81 -20
  69. package/corpus/templates/analytics/server/lib/replay-storage.ts +79 -0
  70. package/corpus/templates/analytics/server/lib/s3-upload-provider.ts +255 -0
  71. package/corpus/templates/analytics/server/lib/session-replay.ts +29 -8
  72. package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
  73. package/corpus/templates/analytics/server/plugins/onboarding.ts +97 -0
  74. package/corpus/templates/clips/desktop/src/lib/recorder.ts +24 -0
  75. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +105 -5
  76. package/corpus/templates/content/actions/set-image-alt-text.ts +1 -1
  77. package/corpus/templates/content/shared/document-export.ts +1 -1
  78. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -18
  79. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +17 -0
  80. package/corpus/templates/design/actions/add-localhost-screens.ts +32 -5
  81. package/corpus/templates/design/actions/index-design-system-with-builder.ts +259 -0
  82. package/corpus/templates/design/actions/update-file.ts +9 -2
  83. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +33 -75
  84. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +72 -9
  85. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +41 -39
  86. package/corpus/templates/design/app/components/design/EditPanel.tsx +1029 -249
  87. package/corpus/templates/design/app/components/design/LayersPanel.tsx +450 -101
  88. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +978 -154
  89. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -9
  90. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +133 -66
  91. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +1234 -344
  92. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +144 -94
  93. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +301 -72
  94. package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +876 -286
  95. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +601 -0
  96. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +242 -0
  97. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +30 -7
  98. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +212 -155
  99. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +423 -0
  100. package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +487 -107
  101. package/corpus/templates/design/app/components/design/inspector/index.ts +27 -0
  102. package/corpus/templates/design/app/global.css +29 -3
  103. package/corpus/templates/design/app/i18n/zh-TW.ts +13 -0
  104. package/corpus/templates/design/app/i18n-data.ts +239 -1
  105. package/corpus/templates/design/app/pages/DesignEditor.tsx +1023 -353
  106. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +57 -221
  107. package/corpus/templates/design/changelog/2026-06-29-canvas-selection-no-longer-flashes-broken-prototype-html-whe.md +6 -0
  108. package/corpus/templates/design/changelog/2026-06-29-command-menu-selections-are-clearer-in-dark-mode.md +6 -0
  109. package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +6 -0
  110. package/corpus/templates/design/changelog/2026-06-29-element-selection-now-stays-on-the-intended-layer-after-sele.md +6 -0
  111. package/corpus/templates/design/changelog/2026-06-29-screen-overview-full-view-controls-now-sit-above-frames-inst.md +6 -0
  112. package/corpus/templates/design/changelog/2026-06-29-screen-overview-tools-can-draw-shapes-create-screens-and-kee.md +6 -0
  113. package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-now-creates-anchor-based-bezier-vectors-with-ha.md +6 -0
  114. package/corpus/templates/design/server/handlers/import-figma-system.ts +38 -85
  115. package/corpus/templates/design/server/lib/design-snapshot.ts +9 -1
  116. package/corpus/templates/design/shared/code-layer.ts +209 -9
  117. package/corpus/templates/design/shared/html-content.ts +21 -0
  118. package/corpus/templates/design/shared/pen-path.ts +207 -0
  119. package/corpus/templates/mail/app/lib/utils.ts +2 -2
  120. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -10
  121. package/corpus/templates/slides/AGENTS.md +3 -2
  122. package/corpus/templates/slides/actions/import-file.ts +26 -17
  123. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +259 -0
  124. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +35 -167
  125. package/corpus/templates/slides/app/components/design-system/fig-import-response.ts +9 -13
  126. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -1
  127. package/corpus/templates/slides/app/i18n/ar-SA.ts +9 -37
  128. package/corpus/templates/slides/app/i18n/de-DE.ts +10 -25
  129. package/corpus/templates/slides/app/i18n/en-US.ts +9 -26
  130. package/corpus/templates/slides/app/i18n/es-ES.ts +10 -28
  131. package/corpus/templates/slides/app/i18n/fr-FR.ts +10 -28
  132. package/corpus/templates/slides/app/i18n/hi-IN.ts +9 -26
  133. package/corpus/templates/slides/app/i18n/ja-JP.ts +10 -22
  134. package/corpus/templates/slides/app/i18n/ko-KR.ts +9 -23
  135. package/corpus/templates/slides/app/i18n/pt-BR.ts +10 -28
  136. package/corpus/templates/slides/app/i18n/zh-CN.ts +9 -22
  137. package/corpus/templates/slides/app/i18n/zh-TW.ts +9 -22
  138. package/corpus/templates/slides/server/handlers/import-figma-system.ts +44 -12
  139. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  140. package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +1 -1
  141. package/dist/application-state/handlers.js +1 -1
  142. package/dist/application-state/handlers.js.map +1 -1
  143. package/dist/brand-kit/index.d.ts +1 -1
  144. package/dist/brand-kit/index.js +1 -1
  145. package/dist/brand-kit/index.js.map +1 -1
  146. package/dist/cli/skills.d.ts.map +1 -1
  147. package/dist/cli/skills.js +18 -0
  148. package/dist/cli/skills.js.map +1 -1
  149. package/dist/client/AgentPanel.js +1 -1
  150. package/dist/client/AgentPanel.js.map +1 -1
  151. package/dist/client/MultiTabAssistantChat.js +1 -1
  152. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  153. package/dist/client/extensions/portable-extension.d.ts +1 -1
  154. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  155. package/dist/client/extensions/portable-extension.js.map +1 -1
  156. package/dist/code-agents/transcript-normalizer.js +1 -1
  157. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  158. package/dist/collab/awareness.d.ts +2 -2
  159. package/dist/collab/awareness.d.ts.map +1 -1
  160. package/dist/collab/routes.d.ts +1 -1
  161. package/dist/extensions/path.d.ts +1 -1
  162. package/dist/extensions/path.d.ts.map +1 -1
  163. package/dist/extensions/path.js.map +1 -1
  164. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  165. package/dist/integrations/a2a-continuation-processor.js +1 -1
  166. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  167. package/dist/mcp/build-server.d.ts +1 -1
  168. package/dist/mcp/build-server.d.ts.map +1 -1
  169. package/dist/mcp/build-server.js +1 -1
  170. package/dist/mcp/build-server.js.map +1 -1
  171. package/dist/mcp/embed-app.d.ts.map +1 -1
  172. package/dist/mcp/embed-app.js +35 -1
  173. package/dist/mcp/embed-app.js.map +1 -1
  174. package/dist/notifications/routes.d.ts +3 -3
  175. package/dist/observability/routes.d.ts +8 -8
  176. package/dist/org/handlers.js +5 -5
  177. package/dist/org/handlers.js.map +1 -1
  178. package/dist/org/plugin.js +2 -2
  179. package/dist/org/plugin.js.map +1 -1
  180. package/dist/resources/handlers.d.ts +1 -1
  181. package/dist/scripts/call-agent.js +1 -1
  182. package/dist/scripts/call-agent.js.map +1 -1
  183. package/dist/scripts/db/safety.js +1 -1
  184. package/dist/scripts/db/safety.js.map +1 -1
  185. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  186. package/dist/server/auth.d.ts.map +1 -1
  187. package/dist/server/auth.js +3 -0
  188. package/dist/server/auth.js.map +1 -1
  189. package/dist/server/builder-design-systems.d.ts +28 -0
  190. package/dist/server/builder-design-systems.d.ts.map +1 -0
  191. package/dist/server/builder-design-systems.js +217 -0
  192. package/dist/server/builder-design-systems.js.map +1 -0
  193. package/dist/server/core-routes-plugin.d.ts +1 -0
  194. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  195. package/dist/server/core-routes-plugin.js +20 -3
  196. package/dist/server/core-routes-plugin.js.map +1 -1
  197. package/dist/server/index.d.ts +1 -0
  198. package/dist/server/index.d.ts.map +1 -1
  199. package/dist/server/index.js +1 -0
  200. package/dist/server/index.js.map +1 -1
  201. package/dist/styles/agent-native.css +14 -7
  202. package/dist/usage/store.js +2 -2
  203. package/dist/usage/store.js.map +1 -1
  204. package/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  205. package/docs/content/locales/ar-SA/template-design.mdx +14 -1
  206. package/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  207. package/docs/content/locales/de-DE/template-design.mdx +15 -1
  208. package/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  209. package/docs/content/locales/es-ES/template-design.mdx +15 -1
  210. package/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  211. package/docs/content/locales/fr-FR/template-design.mdx +15 -1
  212. package/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  213. package/docs/content/locales/hi-IN/template-design.mdx +15 -1
  214. package/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  215. package/docs/content/locales/ja-JP/template-design.mdx +16 -1
  216. package/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  217. package/docs/content/locales/ko-KR/template-design.mdx +14 -1
  218. package/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  219. package/docs/content/locales/pt-BR/template-design.mdx +14 -1
  220. package/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  221. package/docs/content/locales/zh-CN/template-design.mdx +14 -1
  222. package/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  223. package/docs/content/locales/zh-TW/template-design.mdx +14 -1
  224. package/docs/content/skills-guide.mdx +5 -0
  225. package/docs/content/template-design.mdx +14 -1
  226. package/package.json +1 -1
  227. package/corpus/templates/design/actions/import-figma-file.ts +0 -166
  228. package/corpus/templates/slides/server/lib/fig-design-system.ts +0 -110
@@ -0,0 +1,423 @@
1
+ import { useActionMutation } from "@agent-native/core/client";
2
+ import {
3
+ Dithering,
4
+ GodRays,
5
+ GrainGradient,
6
+ MeshGradient,
7
+ Metaballs,
8
+ PaperTexture,
9
+ Voronoi,
10
+ Warp,
11
+ } from "@paper-design/shaders-react";
12
+ import {
13
+ SHADER_PRESET_MAP,
14
+ SHADER_PRESETS,
15
+ type ShaderDescriptor,
16
+ type ShaderPresetDef,
17
+ type ShaderPresetName,
18
+ } from "@shared/shader-presets";
19
+ import { buildFallbackGradient, isWebGLAvailable } from "@shared/shader-safety";
20
+ import {
21
+ IconArrowLeft,
22
+ IconPlus,
23
+ IconSearch,
24
+ IconX,
25
+ } from "@tabler/icons-react";
26
+ import { Component, useMemo, useState, type ReactNode } from "react";
27
+
28
+ import { Input } from "@/components/ui/input";
29
+ import {
30
+ Tooltip,
31
+ TooltipContent,
32
+ TooltipTrigger,
33
+ } from "@/components/ui/tooltip";
34
+ import { cn } from "@/lib/utils";
35
+
36
+ import { ShaderControls } from "./ShaderControls";
37
+
38
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
+ type AnyShaderComponent = React.ComponentType<Record<string, any>>;
40
+
41
+ const SHADER_COMPONENTS: Record<ShaderPresetName, AnyShaderComponent> = {
42
+ MeshGradient: MeshGradient as AnyShaderComponent,
43
+ GrainGradient: GrainGradient as AnyShaderComponent,
44
+ Voronoi: Voronoi as AnyShaderComponent,
45
+ Metaballs: Metaballs as AnyShaderComponent,
46
+ Warp: Warp as AnyShaderComponent,
47
+ GodRays: GodRays as AnyShaderComponent,
48
+ Dithering: Dithering as AnyShaderComponent,
49
+ PaperTexture: PaperTexture as AnyShaderComponent,
50
+ };
51
+
52
+ // ─── Descriptor helpers ────────────────────────────────────────────────────────
53
+
54
+ /** Build a fresh descriptor with the preset's default params + colors. */
55
+ export function descriptorFromPreset(
56
+ preset: ShaderPresetDef,
57
+ ): ShaderDescriptor {
58
+ const params: Record<string, number | boolean | string> = {};
59
+ for (const p of preset.params) {
60
+ if (p.kind !== "colors" && !Array.isArray(p.default)) {
61
+ params[p.key] = p.default as number | boolean | string;
62
+ }
63
+ }
64
+ return {
65
+ preset: preset.name,
66
+ params,
67
+ colors: preset.defaultColors ?? undefined,
68
+ speed: 0,
69
+ frame: 0,
70
+ };
71
+ }
72
+
73
+ /**
74
+ * A static CSS fallback fill for a shader descriptor. The live WebGL shader is
75
+ * GPU-only; for the element fill we apply a representative gradient so the
76
+ * picker stays functional everywhere. The full descriptor is validated and
77
+ * surfaced via the apply-shader action so the agent can write real shader code.
78
+ */
79
+ export function shaderDescriptorToCss(descriptor: ShaderDescriptor): string {
80
+ const preset = SHADER_PRESET_MAP[descriptor.preset];
81
+ const colors =
82
+ descriptor.colors && descriptor.colors.length > 0
83
+ ? descriptor.colors
84
+ : (preset?.defaultColors ?? []);
85
+ return buildFallbackGradient(colors, preset?.defaultColorBack);
86
+ }
87
+
88
+ // ─── Live thumbnail ────────────────────────────────────────────────────────────
89
+
90
+ /** Catches WebGL/render errors from the live shader and shows a CSS fallback. */
91
+ class ShaderBoundary extends Component<
92
+ { fallback: ReactNode; children: ReactNode },
93
+ { failed: boolean }
94
+ > {
95
+ state = { failed: false };
96
+ static getDerivedStateFromError() {
97
+ return { failed: true };
98
+ }
99
+ render() {
100
+ return this.state.failed ? this.props.fallback : this.props.children;
101
+ }
102
+ }
103
+
104
+ function ShaderThumbnail({
105
+ preset,
106
+ selected,
107
+ }: {
108
+ preset: ShaderPresetDef;
109
+ selected: boolean;
110
+ }) {
111
+ const ShaderComponent = SHADER_COMPONENTS[preset.name];
112
+ const webglOk = isWebGLAvailable();
113
+
114
+ const fallback = buildFallbackGradient(
115
+ preset.defaultColors ?? [preset.defaultColorFront ?? "#888888"],
116
+ preset.defaultColorBack,
117
+ );
118
+
119
+ const shaderProps = useMemo(() => {
120
+ const p: Record<string, unknown> = {};
121
+ for (const def of preset.params) {
122
+ if (def.kind !== "colors" && !Array.isArray(def.default)) {
123
+ p[def.key] = def.default;
124
+ }
125
+ }
126
+ if (preset.defaultColors) p.colors = preset.defaultColors;
127
+ if (preset.defaultColorBack) p.colorBack = preset.defaultColorBack;
128
+ if (preset.defaultColorFront) p.colorFront = preset.defaultColorFront;
129
+ // Static thumbnail — no animation churn in a grid of 8 live canvases.
130
+ p.speed = 0;
131
+ p.frame = 0;
132
+ return p;
133
+ }, [preset]);
134
+
135
+ const fallbackEl = (
136
+ <div className="absolute inset-0" style={{ background: fallback }} />
137
+ );
138
+
139
+ return (
140
+ <div
141
+ className={cn(
142
+ "relative aspect-[4/3] w-full overflow-hidden rounded-md border transition-colors",
143
+ selected
144
+ ? "border-primary shadow-[0_0_0_1px_var(--primary)]"
145
+ : "border-border/60 group-hover:border-foreground/40",
146
+ )}
147
+ >
148
+ {webglOk ? (
149
+ <ShaderBoundary fallback={fallbackEl}>
150
+ <ShaderComponent
151
+ {...shaderProps}
152
+ style={{
153
+ position: "absolute",
154
+ inset: 0,
155
+ width: "100%",
156
+ height: "100%",
157
+ }}
158
+ />
159
+ </ShaderBoundary>
160
+ ) : (
161
+ fallbackEl
162
+ )}
163
+ </div>
164
+ );
165
+ }
166
+
167
+ // ─── Component ─────────────────────────────────────────────────────────────────
168
+
169
+ export interface ShaderFillsPanelProps {
170
+ /** Currently-applied shader descriptor, if the fill is already a shader. */
171
+ descriptor?: ShaderDescriptor;
172
+ /** Emits when the user applies/tunes a shader. value = CSS fallback fill. */
173
+ onApply: (descriptor: ShaderDescriptor, css: string) => void;
174
+ /** Close the shader panel and return to the color picker. */
175
+ onBack: () => void;
176
+ /** Optional design context forwarded to the apply-shader action. */
177
+ applyContext?: {
178
+ designId?: string;
179
+ fileId?: string;
180
+ nodeId?: string;
181
+ selector?: string;
182
+ };
183
+ disabled?: boolean;
184
+ }
185
+
186
+ export function ShaderFillsPanel({
187
+ descriptor,
188
+ onApply,
189
+ onBack,
190
+ applyContext,
191
+ disabled = false,
192
+ }: ShaderFillsPanelProps) {
193
+ const [search, setSearch] = useState("");
194
+ const [active, setActive] = useState<ShaderDescriptor | null>(
195
+ descriptor ?? null,
196
+ );
197
+ const applyShader = useActionMutation("apply-shader");
198
+
199
+ const filtered = useMemo(() => {
200
+ const query = search.trim().toLowerCase();
201
+ if (!query) return SHADER_PRESETS;
202
+ return SHADER_PRESETS.filter(
203
+ (preset) =>
204
+ preset.label.toLowerCase().includes(query) ||
205
+ preset.description.toLowerCase().includes(query),
206
+ );
207
+ }, [search]);
208
+
209
+ /** Validate + surface the descriptor for the agent, then emit the CSS fill. */
210
+ const commit = (next: ShaderDescriptor) => {
211
+ setActive(next);
212
+ const css = shaderDescriptorToCss(next);
213
+ onApply(next, css);
214
+ // Fire-and-forget validation/codegen so the agent can write real shader
215
+ // code. The picker fill is applied immediately above regardless of result.
216
+ applyShader.mutate(
217
+ {
218
+ surface: SHADER_PRESET_MAP[next.preset]?.isEffect ? "effect" : "fill",
219
+ descriptor: {
220
+ preset: next.preset,
221
+ params: next.params,
222
+ colors: next.colors,
223
+ speed: next.speed,
224
+ frame: next.frame,
225
+ fit: next.fit,
226
+ scale: next.scale,
227
+ rotation: next.rotation,
228
+ offsetX: next.offsetX,
229
+ offsetY: next.offsetY,
230
+ },
231
+ ...(applyContext?.designId || applyContext?.fileId
232
+ ? {
233
+ source: {
234
+ kind: "design-file" as const,
235
+ designId: applyContext.designId,
236
+ fileId: applyContext.fileId,
237
+ },
238
+ }
239
+ : {}),
240
+ ...(applyContext?.nodeId || applyContext?.selector
241
+ ? {
242
+ target: {
243
+ nodeId: applyContext.nodeId,
244
+ selector: applyContext.selector,
245
+ },
246
+ }
247
+ : {}),
248
+ },
249
+ { onError: () => undefined },
250
+ );
251
+ };
252
+
253
+ // ── Detail view: a preset is selected → tune it with ShaderControls ───────
254
+ if (active) {
255
+ const preset = SHADER_PRESET_MAP[active.preset];
256
+ return (
257
+ <div className="flex flex-col">
258
+ {/* Detail header: ← preset-name × */}
259
+ <div className="flex h-6 items-center gap-1.5 px-3">
260
+ <button
261
+ type="button"
262
+ aria-label={"Back to shader fills" /* i18n-ignore */}
263
+ onClick={() => setActive(null)}
264
+ className="flex size-5 items-center justify-center rounded text-muted-foreground hover:bg-[var(--design-editor-control-bg)] hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
265
+ >
266
+ <IconArrowLeft className="size-3.5" />
267
+ </button>
268
+ <span className="flex-1 truncate text-[11px] font-semibold text-foreground">
269
+ {preset?.label ?? active.preset}
270
+ </span>
271
+ <button
272
+ type="button"
273
+ aria-label={"Close shader fills" /* i18n-ignore */}
274
+ onClick={onBack}
275
+ className="flex size-5 items-center justify-center rounded text-muted-foreground hover:bg-[var(--design-editor-control-bg)] hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
276
+ >
277
+ <IconX className="size-3" />
278
+ </button>
279
+ </div>
280
+ <div className="border-t border-border/70 p-2">
281
+ <ShaderControls
282
+ descriptor={active}
283
+ onChange={(next) => commit(next)}
284
+ />
285
+ </div>
286
+ </div>
287
+ );
288
+ }
289
+
290
+ // ── Browse view: Figma-style title + search + Created by you + By Figma ───
291
+ return (
292
+ <div className="flex flex-col">
293
+ {/* Header: "Shader fills" title + + button + × button */}
294
+ <div className="flex h-6 items-center gap-1 px-3">
295
+ <span className="flex-1 truncate text-[11px] font-semibold text-foreground">
296
+ {"Shader fills" /* i18n-ignore Figma panel title */}
297
+ </span>
298
+ <Tooltip>
299
+ <TooltipTrigger asChild>
300
+ <button
301
+ type="button"
302
+ aria-label={"Create new shader" /* i18n-ignore */}
303
+ disabled={disabled}
304
+ onClick={() => commit(descriptorFromPreset(SHADER_PRESETS[0]))}
305
+ className="flex size-5 items-center justify-center rounded text-muted-foreground hover:bg-[var(--design-editor-control-bg)] hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40"
306
+ >
307
+ <IconPlus className="size-3.5" />
308
+ </button>
309
+ </TooltipTrigger>
310
+ <TooltipContent>
311
+ {"Create new shader" /* i18n-ignore */}
312
+ </TooltipContent>
313
+ </Tooltip>
314
+ <button
315
+ type="button"
316
+ aria-label={"Close shader fills" /* i18n-ignore */}
317
+ onClick={onBack}
318
+ className="flex size-5 items-center justify-center rounded text-muted-foreground hover:bg-[var(--design-editor-control-bg)] hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
319
+ >
320
+ <IconX className="size-3" />
321
+ </button>
322
+ </div>
323
+
324
+ {/* Search field */}
325
+ <div className="border-t border-border/70 px-3 py-2">
326
+ <div className="flex h-6 items-center gap-1.5 rounded-md border border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-2">
327
+ <IconSearch className="size-3 shrink-0 text-muted-foreground" />
328
+ <Input
329
+ value={search}
330
+ disabled={disabled}
331
+ placeholder={"Search" /* i18n-ignore */}
332
+ aria-label={"Search shaders" /* i18n-ignore */}
333
+ className="h-full min-w-0 flex-1 border-0 bg-transparent p-0 text-[11px] shadow-none focus-visible:ring-0"
334
+ onChange={(event) => setSearch(event.target.value)}
335
+ />
336
+ {search && (
337
+ <button
338
+ type="button"
339
+ aria-label={"Clear search" /* i18n-ignore */}
340
+ onClick={() => setSearch("")}
341
+ className="flex size-4 items-center justify-center rounded text-muted-foreground hover:text-foreground"
342
+ >
343
+ <IconX className="size-3" />
344
+ </button>
345
+ )}
346
+ </div>
347
+ </div>
348
+
349
+ <div className="max-h-[360px] overflow-y-auto px-3 pb-3">
350
+ {/* ── Created by you ── */}
351
+ {!search && (
352
+ <section className="mb-3">
353
+ <p className="mb-1.5 text-[10px] font-semibold text-muted-foreground">
354
+ {"Created by you" /* i18n-ignore Figma section */}
355
+ </p>
356
+ {/* 2-col grid — "Create new" tile occupies the first cell */}
357
+ <div className="grid grid-cols-2 gap-2">
358
+ <button
359
+ type="button"
360
+ disabled={disabled}
361
+ onClick={() => commit(descriptorFromPreset(SHADER_PRESETS[0]))}
362
+ className={cn(
363
+ "group relative flex aspect-[4/3] w-full flex-col items-center justify-center gap-1 rounded-md border border-dashed border-[var(--design-editor-control-border)] text-muted-foreground transition-colors",
364
+ "hover:border-foreground/40 hover:text-foreground",
365
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
366
+ disabled && "pointer-events-none opacity-40",
367
+ )}
368
+ >
369
+ {/* AI badge — top-right corner */}
370
+ <span className="absolute right-1.5 top-1.5 rounded bg-[var(--design-editor-control-bg)] px-1 py-px text-[9px] font-semibold leading-none text-muted-foreground">
371
+ {"AI" /* i18n-ignore */}
372
+ </span>
373
+ <IconPlus className="size-4" />
374
+ <span className="text-[10px]">
375
+ {"Create new" /* i18n-ignore Figma create tile */}
376
+ </span>
377
+ </button>
378
+ </div>
379
+ </section>
380
+ )}
381
+
382
+ {/* ── By Figma — 2-col preset thumbnail grid ── */}
383
+ <section>
384
+ {!search && (
385
+ <p className="mb-1.5 text-[10px] font-semibold text-muted-foreground">
386
+ {"By Figma" /* i18n-ignore Figma section */}
387
+ </p>
388
+ )}
389
+ {filtered.length === 0 ? (
390
+ <p className="py-4 text-center text-[11px] text-muted-foreground">
391
+ {"No shaders match your search" /* i18n-ignore */}
392
+ </p>
393
+ ) : (
394
+ <div className="grid grid-cols-2 gap-2">
395
+ {filtered.map((preset) => (
396
+ <Tooltip key={preset.name}>
397
+ <TooltipTrigger asChild>
398
+ <button
399
+ type="button"
400
+ disabled={disabled}
401
+ aria-label={preset.label}
402
+ onClick={() => commit(descriptorFromPreset(preset))}
403
+ className={cn(
404
+ "group flex flex-col gap-1 text-left focus-visible:outline-none",
405
+ disabled && "pointer-events-none opacity-40",
406
+ )}
407
+ >
408
+ <ShaderThumbnail preset={preset} selected={false} />
409
+ <span className="truncate text-[10px] text-muted-foreground group-hover:text-foreground">
410
+ {preset.label}
411
+ </span>
412
+ </button>
413
+ </TooltipTrigger>
414
+ <TooltipContent>{preset.description}</TooltipContent>
415
+ </Tooltip>
416
+ ))}
417
+ </div>
418
+ )}
419
+ </section>
420
+ </div>
421
+ </div>
422
+ );
423
+ }