@agent-native/core 0.90.1 → 0.90.3

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 (714) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +26 -0
  3. package/corpus/core/docs/content/agent-native-toolkit.mdx +87 -80
  4. package/corpus/core/docs/content/locales/ar-SA/agent-native-toolkit.mdx +89 -80
  5. package/corpus/core/docs/content/locales/de-DE/agent-native-toolkit.mdx +89 -80
  6. package/corpus/core/docs/content/locales/es-ES/agent-native-toolkit.mdx +89 -80
  7. package/corpus/core/docs/content/locales/fr-FR/agent-native-toolkit.mdx +89 -80
  8. package/corpus/core/docs/content/locales/hi-IN/agent-native-toolkit.mdx +89 -80
  9. package/corpus/core/docs/content/locales/ja-JP/agent-native-toolkit.mdx +89 -80
  10. package/corpus/core/docs/content/locales/ko-KR/agent-native-toolkit.mdx +89 -80
  11. package/corpus/core/docs/content/locales/pt-BR/agent-native-toolkit.mdx +89 -80
  12. package/corpus/core/docs/content/locales/zh-CN/agent-native-toolkit.mdx +89 -80
  13. package/corpus/core/docs/content/locales/zh-TW/agent-native-toolkit.mdx +89 -80
  14. package/corpus/core/docs/content/toolkit-agent-ux.mdx +43 -0
  15. package/corpus/core/docs/content/toolkit-app-adapters.mdx +9 -0
  16. package/corpus/core/docs/content/toolkit-collaboration-ui.mdx +12 -0
  17. package/corpus/core/docs/content/toolkit-collaboration.mdx +74 -0
  18. package/corpus/core/docs/content/toolkit-command-navigation.mdx +106 -0
  19. package/corpus/core/docs/content/toolkit-comments-review.mdx +98 -0
  20. package/corpus/core/docs/content/toolkit-history.mdx +95 -0
  21. package/corpus/core/docs/content/toolkit-observability.mdx +41 -0
  22. package/corpus/core/docs/content/toolkit-org-team.mdx +80 -0
  23. package/corpus/core/docs/content/toolkit-resources.mdx +57 -0
  24. package/corpus/core/docs/content/toolkit-settings.mdx +65 -0
  25. package/corpus/core/docs/content/toolkit-setup-connections.mdx +109 -0
  26. package/corpus/core/docs/content/toolkit-sharing-ui.mdx +18 -6
  27. package/corpus/core/docs/content/toolkit-sharing.mdx +81 -0
  28. package/corpus/core/docs/content/toolkit-shell-hooks.mdx +9 -0
  29. package/corpus/core/docs/content/toolkit-ui.mdx +9 -0
  30. package/corpus/core/package.json +35 -1
  31. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +19 -1
  32. package/corpus/core/src/agent/engine/anthropic-engine.ts +25 -1
  33. package/corpus/core/src/agent/engine/registry.ts +23 -16
  34. package/corpus/core/src/agent/production-agent.ts +37 -6
  35. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  36. package/corpus/core/src/client/command-navigation/index.ts +15 -0
  37. package/corpus/core/src/client/comments-review/index.ts +1 -0
  38. package/corpus/core/src/client/history/VersionHistoryPanel.tsx +145 -0
  39. package/corpus/core/src/client/history/index.ts +18 -0
  40. package/corpus/core/src/client/history/use-history.ts +86 -0
  41. package/corpus/core/src/client/index.ts +76 -9
  42. package/corpus/core/src/client/org/OrgSwitcher.tsx +6 -6
  43. package/corpus/core/src/client/org/RequireActiveOrg.tsx +2 -2
  44. package/corpus/core/src/client/org/TeamPage.tsx +3 -3
  45. package/corpus/core/src/client/org/hooks.ts +32 -0
  46. package/corpus/core/src/client/org/index.ts +30 -1
  47. package/corpus/core/src/client/org-team/index.ts +1 -0
  48. package/corpus/core/src/client/review/ReviewStatusBadge.tsx +39 -0
  49. package/corpus/core/src/client/review/ReviewThreadPanel.tsx +278 -0
  50. package/corpus/core/src/client/review/index.ts +25 -0
  51. package/corpus/core/src/client/review/use-review.ts +114 -0
  52. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +11 -6
  53. package/corpus/core/src/client/settings/index.ts +11 -1
  54. package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +94 -0
  55. package/corpus/core/src/client/setup-connections/ProviderReadinessBadge.tsx +73 -0
  56. package/corpus/core/src/client/setup-connections/SetupConnectionsPage.tsx +94 -0
  57. package/corpus/core/src/client/setup-connections/index.ts +46 -0
  58. package/corpus/core/src/client/sharing/ShareButton.tsx +20 -14
  59. package/corpus/core/src/client/sharing/ShareDialog.tsx +3 -3
  60. package/corpus/core/src/client/use-action.ts +39 -2
  61. package/corpus/core/src/client/use-db-sync.ts +44 -18
  62. package/corpus/core/src/command-navigation/actions.ts +100 -0
  63. package/corpus/core/src/command-navigation/index.ts +12 -0
  64. package/corpus/core/src/comments-review/index.ts +1 -0
  65. package/corpus/core/src/db/create-get-db.ts +32 -2
  66. package/corpus/core/src/file-upload/builder.ts +24 -3
  67. package/corpus/core/src/file-upload/types.ts +3 -1
  68. package/corpus/core/src/history/actions/create-resource-version.ts +89 -0
  69. package/corpus/core/src/history/actions/get-resource-version.ts +60 -0
  70. package/corpus/core/src/history/actions/list-resource-history.ts +53 -0
  71. package/corpus/core/src/history/actions/list-resource-versions.ts +43 -0
  72. package/corpus/core/src/history/actions/restore-resource-version.ts +96 -0
  73. package/corpus/core/src/history/index.ts +27 -0
  74. package/corpus/core/src/history/registry.ts +99 -0
  75. package/corpus/core/src/history/store.ts +348 -0
  76. package/corpus/core/src/history/types.ts +69 -0
  77. package/corpus/core/src/index.ts +10 -0
  78. package/corpus/core/src/navigation/index.ts +170 -0
  79. package/corpus/core/src/org/index.ts +8 -0
  80. package/corpus/core/src/org/permissions.ts +30 -0
  81. package/corpus/core/src/org-team/index.ts +1 -0
  82. package/corpus/core/src/private-blob/registry.ts +1 -0
  83. package/corpus/core/src/review/actions/consume-review-feedback.ts +38 -0
  84. package/corpus/core/src/review/actions/create-review-comment.ts +107 -0
  85. package/corpus/core/src/review/actions/delete-review-comment.ts +55 -0
  86. package/corpus/core/src/review/actions/get-review-feedback.ts +54 -0
  87. package/corpus/core/src/review/actions/list-review-comments.ts +53 -0
  88. package/corpus/core/src/review/actions/reply-review-comment.ts +107 -0
  89. package/corpus/core/src/review/actions/resolve-review-thread.ts +60 -0
  90. package/corpus/core/src/review/actions/set-review-status.ts +58 -0
  91. package/corpus/core/src/review/index.ts +36 -0
  92. package/corpus/core/src/review/mentions.ts +44 -0
  93. package/corpus/core/src/review/registry.ts +99 -0
  94. package/corpus/core/src/review/store.ts +687 -0
  95. package/corpus/core/src/review/types.ts +97 -0
  96. package/corpus/core/src/server/action-discovery.ts +54 -0
  97. package/corpus/core/src/server/agent-engine-api-key-route.ts +5 -0
  98. package/corpus/core/src/server/credential-provider.ts +108 -0
  99. package/corpus/core/src/server/framework-request-handler.ts +52 -4
  100. package/corpus/core/src/setup-connections/index.ts +89 -0
  101. package/corpus/core/src/setup-connections/onboarding.ts +47 -0
  102. package/corpus/core/src/transcription/builder-transcription.ts +9 -2
  103. package/corpus/core/src/vite/action-types-plugin.ts +52 -0
  104. package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -1
  105. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +2 -2
  106. package/corpus/templates/analytics/app/global.css +22 -10
  107. package/corpus/templates/analytics/app/i18n/zh-TW.ts +6 -1
  108. package/corpus/templates/analytics/app/i18n-data.ts +81 -13
  109. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -2
  110. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
  111. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +528 -108
  112. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +466 -183
  113. package/corpus/templates/analytics/changelog/2026-07-07-session-replays-load-sooner-show-clearer-activity-timelines-.md +6 -0
  114. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +5 -0
  115. package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +34 -6
  116. package/corpus/templates/assets/AGENTS.md +23 -4
  117. package/corpus/templates/assets/actions/_generation-preset-settings.ts +48 -0
  118. package/corpus/templates/assets/actions/_helpers.ts +8 -1
  119. package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +64 -0
  120. package/corpus/templates/assets/actions/create-generation-preset.ts +11 -3
  121. package/corpus/templates/assets/actions/generate-image-batch.ts +7 -2
  122. package/corpus/templates/assets/actions/generate-image.ts +342 -25
  123. package/corpus/templates/assets/actions/get-library.ts +4 -3
  124. package/corpus/templates/assets/actions/navigate.ts +2 -1
  125. package/corpus/templates/assets/actions/refresh-generation-run.ts +5 -1
  126. package/corpus/templates/assets/actions/update-asset.ts +1 -0
  127. package/corpus/templates/assets/actions/update-generation-preset.ts +8 -1
  128. package/corpus/templates/assets/actions/view-screen.ts +8 -1
  129. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +9 -1
  130. package/corpus/templates/assets/app/hooks/use-image-model-menu.ts +1 -0
  131. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +11 -0
  132. package/corpus/templates/assets/app/i18n/zh-TW.ts +64 -0
  133. package/corpus/templates/assets/app/i18n-data.ts +650 -0
  134. package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -1
  135. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +44 -33
  136. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1474 -0
  137. package/corpus/templates/assets/app/routes/library.tsx +43 -1
  138. package/corpus/templates/assets/server/lib/generation.ts +366 -44
  139. package/corpus/templates/assets/server/lib/image-processing.ts +252 -0
  140. package/corpus/templates/assets/server/lib/preset-skeleton.ts +137 -0
  141. package/corpus/templates/assets/shared/api.ts +47 -0
  142. package/corpus/templates/brain/app/routes/settings.tsx +2 -2
  143. package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +32 -6
  144. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +120 -40
  145. package/corpus/templates/calendar/app/hooks/use-people.ts +55 -0
  146. package/corpus/templates/calendar/changelog/2026-07-07-guest-suggestions-now-find-coworkers-from-your-google-worksp.md +6 -0
  147. package/corpus/templates/calendar/changelog/2026-07-07-zoom-stays-selected-after-connecting-it-from-a-draft-event.md +6 -0
  148. package/corpus/templates/clips/actions/create-recording.ts +5 -1
  149. package/corpus/templates/clips/actions/finalize-recording.ts +140 -1
  150. package/corpus/templates/clips/actions/import-loom-recording.ts +1 -0
  151. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +1 -0
  152. package/corpus/templates/clips/actions/request-transcript.ts +31 -0
  153. package/corpus/templates/clips/actions/set-thumbnail.ts +2 -0
  154. package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -2
  155. package/corpus/templates/clips/app/components/library/organization-switcher.tsx +2 -2
  156. package/corpus/templates/clips/app/components/library/sort-menu.tsx +1 -1
  157. package/corpus/templates/clips/app/components/player/scrubber-position.ts +9 -0
  158. package/corpus/templates/clips/app/components/player/scrubber.tsx +5 -2
  159. package/corpus/templates/clips/app/components/player/share-dialog.tsx +69 -41
  160. package/corpus/templates/clips/app/components/player/video-player.tsx +38 -1
  161. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +114 -33
  162. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +3 -3
  163. package/corpus/templates/clips/app/components/sharing/slack-share-hint.tsx +9 -19
  164. package/corpus/templates/clips/app/routes/_app.library._index.tsx +27 -1
  165. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +9 -3
  166. package/corpus/templates/clips/app/routes/record.tsx +178 -80
  167. package/corpus/templates/clips/changelog/2026-07-02-recordings-now-wait-for-storage-media-to-serve-before-finishing.md +6 -0
  168. package/corpus/templates/clips/changelog/2026-07-07-a-pending-app-update-no-longer-blocks-screen-recording-updat.md +6 -0
  169. package/corpus/templates/clips/changelog/2026-07-07-chrome-extension-recordings-keep-audio-after-refreshing-tab.md +6 -0
  170. package/corpus/templates/clips/changelog/2026-07-07-clip-playback-progress-now-starts-and-finishes-at-the-right-times.md +6 -0
  171. package/corpus/templates/clips/changelog/2026-07-07-sharing-public-clips-now-stays-focused-on-the-normal-link-wi.md +6 -0
  172. package/corpus/templates/clips/chrome-extension/src/background.ts +18 -0
  173. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +23 -8
  174. package/corpus/templates/clips/desktop/src/app.tsx +40 -3
  175. package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +19 -7
  176. package/corpus/templates/clips/desktop/src/lib/permissions.ts +2 -0
  177. package/corpus/templates/clips/desktop/src/lib/updater.ts +25 -2
  178. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +7 -0
  179. package/corpus/templates/clips/server/routes/api/recordings/[recordingId]/thumbnail.post.ts +1 -0
  180. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +23 -6
  181. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +28 -3
  182. package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +13 -6
  183. package/corpus/templates/clips/shared/finalize-recovery.ts +35 -3
  184. package/corpus/templates/content/AGENTS.md +52 -52
  185. package/corpus/templates/content/actions/_builder-cms-read-client.ts +188 -8
  186. package/corpus/templates/content/actions/_builder-docs-client.ts +41 -3
  187. package/corpus/templates/content/actions/_database-source-utils.ts +998 -160
  188. package/corpus/templates/content/actions/_database-utils.ts +44 -4
  189. package/corpus/templates/content/actions/_notion-action-utils.ts +6 -2
  190. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -1
  191. package/corpus/templates/content/actions/attach-content-database-source.ts +85 -12
  192. package/corpus/templates/content/actions/change-content-database-source-role.ts +9 -1
  193. package/corpus/templates/content/actions/delete-content-database.ts +1 -0
  194. package/corpus/templates/content/actions/delete-document.ts +337 -52
  195. package/corpus/templates/content/actions/get-document.ts +11 -2
  196. package/corpus/templates/content/actions/list-content-databases.ts +57 -9
  197. package/corpus/templates/content/actions/prepare-builder-source-review.ts +23 -6
  198. package/corpus/templates/content/actions/refresh-content-database-source.ts +26 -3
  199. package/corpus/templates/content/actions/restore-content-database.ts +1 -0
  200. package/corpus/templates/content/actions/update-document.ts +43 -0
  201. package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +21 -0
  202. package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +0 -1
  203. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +70 -28
  204. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +69 -17
  205. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +4 -4
  206. package/corpus/templates/content/app/components/editor/body-hydration.ts +112 -0
  207. package/corpus/templates/content/app/components/editor/builder-body-hydration-pump.ts +52 -0
  208. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +9076 -2876
  209. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +41 -1
  210. package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +36 -11
  211. package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +21 -1
  212. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +11 -1
  213. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +11 -8
  214. package/corpus/templates/content/app/global.css +41 -0
  215. package/corpus/templates/content/app/hooks/use-content-database.ts +346 -42
  216. package/corpus/templates/content/app/hooks/use-db-sync.ts +9 -0
  217. package/corpus/templates/content/app/hooks/use-documents.ts +47 -0
  218. package/corpus/templates/content/app/hooks/use-notion.ts +12 -10
  219. package/corpus/templates/content/app/i18n/zh-TW.ts +35 -0
  220. package/corpus/templates/content/app/i18n-data.ts +355 -0
  221. package/corpus/templates/content/app/root.tsx +22 -0
  222. package/corpus/templates/content/changelog/2026-07-01-builder-database-sources-keep-loading-rows-in-the-background.md +6 -0
  223. package/corpus/templates/content/changelog/2026-07-01-builder-source-attach-starts-with-usable-rows.md +6 -0
  224. package/corpus/templates/content/changelog/2026-07-01-builder-source-attachment-now-stays-connected-after-large.md +6 -0
  225. package/corpus/templates/content/changelog/2026-07-01-builder-source-refreshes-now-show-progress-for-large-docs-blog.md +6 -0
  226. package/corpus/templates/content/changelog/2026-07-01-database-pages-now-delete-to-trash-without-route-errors.md +6 -0
  227. package/corpus/templates/content/changelog/2026-07-02-builder-source-refreshes-recover-from-stalled-background-row.md +6 -0
  228. package/corpus/templates/content/changelog/2026-07-02-builder-sourced-rows-open-with-sync-protection.md +6 -0
  229. package/corpus/templates/content/changelog/2026-07-02-database-rows-and-pages-open-faster-from-recently-loaded-dat.md +6 -0
  230. package/corpus/templates/content/changelog/2026-07-02-preview-panels-close-reliably-narrow-sidebar-row-actions-sta.md +6 -0
  231. package/corpus/templates/content/changelog/2026-07-06-builder-sync-progress-no-longer-shows-continue.md +6 -0
  232. package/corpus/templates/content/changelog/2026-07-07-permanently-deleting-trashed-databases-now-finishes-reliably.md +6 -0
  233. package/corpus/templates/content/shared/api.ts +13 -0
  234. package/corpus/templates/content/shared/builder-mdx.ts +16 -3
  235. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +23 -13
  236. package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +98 -0
  237. package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +162 -0
  238. package/corpus/templates/design/AGENTS.md +9 -2
  239. package/corpus/templates/design/actions/add-breakpoint.ts +6 -3
  240. package/corpus/templates/design/actions/apply-a11y-fix.ts +57 -37
  241. package/corpus/templates/design/actions/apply-component-prop-edit.ts +41 -20
  242. package/corpus/templates/design/actions/apply-motion-edit.ts +251 -38
  243. package/corpus/templates/design/actions/apply-shader-fill.ts +90 -66
  244. package/corpus/templates/design/actions/apply-visual-edit.ts +282 -55
  245. package/corpus/templates/design/actions/create-component.ts +29 -60
  246. package/corpus/templates/design/actions/create-file.ts +9 -2
  247. package/corpus/templates/design/actions/duplicate-design.ts +31 -2
  248. package/corpus/templates/design/actions/edit-design.ts +36 -38
  249. package/corpus/templates/design/actions/generate-design-title.ts +87 -0
  250. package/corpus/templates/design/actions/generate-design.ts +53 -18
  251. package/corpus/templates/design/actions/generate-screens.ts +108 -4
  252. package/corpus/templates/design/actions/get-motion-timeline.ts +35 -7
  253. package/corpus/templates/design/actions/insert-asset.ts +34 -29
  254. package/corpus/templates/design/actions/insert-design-native-asset.ts +129 -32
  255. package/corpus/templates/design/actions/insert-figma-library-asset.ts +35 -30
  256. package/corpus/templates/design/actions/present-design-variants.ts +7 -2
  257. package/corpus/templates/design/actions/remove-motion-timeline.ts +31 -36
  258. package/corpus/templates/design/actions/update-file.ts +190 -75
  259. package/corpus/templates/design/app/components/design/BreakpointBar.tsx +403 -0
  260. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +469 -222
  261. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +708 -53
  262. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +83 -3
  263. package/corpus/templates/design/app/components/design/EditPanel.tsx +1091 -6948
  264. package/corpus/templates/design/app/components/design/LayersPanel.tsx +419 -85
  265. package/corpus/templates/design/app/components/design/MotionDock.tsx +1124 -103
  266. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2610 -1398
  267. package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +22 -0
  268. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2182 -235
  269. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +187 -11
  270. package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +376 -1
  271. package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +746 -0
  272. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +17 -0
  273. package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +1 -1
  274. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +1 -3
  275. package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +1 -1
  276. package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +518 -0
  277. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +562 -0
  278. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +763 -0
  279. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +94 -0
  280. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +780 -0
  281. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +446 -0
  282. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +79 -0
  283. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +315 -0
  284. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +344 -0
  285. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +594 -0
  286. package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +119 -0
  287. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +248 -0
  288. package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +45 -0
  289. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +693 -0
  290. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +724 -0
  291. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +207 -0
  292. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +628 -0
  293. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +91 -0
  294. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +649 -0
  295. package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +118 -0
  296. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +88 -0
  297. package/corpus/templates/design/app/components/design/edit-panel/transform-helpers.ts +100 -0
  298. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +117 -0
  299. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +501 -0
  300. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +91 -40
  301. package/corpus/templates/design/app/components/design/inspector/BreakpointOverrideIndicator.tsx +82 -0
  302. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +189 -87
  303. package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +1097 -0
  304. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +44 -0
  305. package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +258 -0
  306. package/corpus/templates/design/app/components/design/inspector/MotionKeyframeDiamond.tsx +144 -0
  307. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +99 -9
  308. package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +116 -0
  309. package/corpus/templates/design/app/components/design/inspector/index.ts +26 -0
  310. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +29 -0
  311. package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +171 -0
  312. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +220 -0
  313. package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +35 -0
  314. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +134 -0
  315. package/corpus/templates/design/app/components/design/multi-screen/culling.ts +185 -0
  316. package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts +120 -0
  317. package/corpus/templates/design/app/components/design/multi-screen/types.ts +892 -0
  318. package/corpus/templates/design/app/components/design/multi-screen/vector-edit-geometry.ts +21 -0
  319. package/corpus/templates/design/app/components/design/types.ts +15 -0
  320. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +109 -5
  321. package/corpus/templates/design/app/global.css +8 -8
  322. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +259 -2
  323. package/corpus/templates/design/app/i18n/zh-TW.ts +163 -9
  324. package/corpus/templates/design/app/i18n-breakpoints.ts +205 -0
  325. package/corpus/templates/design/app/i18n-data.ts +1664 -152
  326. package/corpus/templates/design/app/i18n-motion.ts +563 -0
  327. package/corpus/templates/design/app/lib/design-import.ts +73 -0
  328. package/corpus/templates/design/app/pages/Index.tsx +41 -22
  329. package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +1 -1
  330. package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +1 -1
  331. package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +1 -1
  332. package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +1 -1
  333. package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +1 -1
  334. package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +1 -1
  335. package/corpus/templates/design/changelog/2026-07-05-designs-get-ai-generated-names-from-your-prompt.md +6 -0
  336. package/corpus/templates/design/changelog/2026-07-05-local-code-screens-keep-their-connection-when-duplicated-and.md +6 -0
  337. package/corpus/templates/design/changelog/2026-07-05-press-a-to-draw-screens-on-the-canvas-inside-a-screen-it-dr.md +6 -0
  338. package/corpus/templates/design/changelog/2026-07-06-3d-rotation-and-perspective-controls-on-every-element.md +6 -0
  339. package/corpus/templates/design/changelog/2026-07-06-ai-generated-and-duplicated-screens-now-get-their-editor-sel.md +6 -0
  340. package/corpus/templates/design/changelog/2026-07-06-applying-shaders-and-fills-in-quick-succession-can-no-longer.md +6 -0
  341. package/corpus/templates/design/changelog/2026-07-06-breakpoint-frames-in-the-overview-now-support-click-to-selec.md +6 -0
  342. package/corpus/templates/design/changelog/2026-07-06-breakpoint-segments-in-the-inspector-now-show-a-device-icon-.md +6 -0
  343. package/corpus/templates/design/changelog/2026-07-06-breakpoint-targeting-now-lives-in-one-compact-control-in-the.md +6 -0
  344. package/corpus/templates/design/changelog/2026-07-06-canvas-edits-can-no-longer-be-overwritten-by-stale-collabora.md +6 -0
  345. package/corpus/templates/design/changelog/2026-07-06-canvas-edits-inside-screens-save-reliably-again.md +6 -0
  346. package/corpus/templates/design/changelog/2026-07-06-cleaner-inspector-removed-the-ask-ai-box-from-the-design-tab.md +6 -0
  347. package/corpus/templates/design/changelog/2026-07-06-clicking-empty-canvas-space-now-deselects-an-element-inside-.md +6 -0
  348. package/corpus/templates/design/changelog/2026-07-06-dragging-an-element-onto-a-rectangle-now-nests-it-inside-wit.md +6 -0
  349. package/corpus/templates/design/changelog/2026-07-06-dragging-screens-and-objects-on-the-canvas-is-now-much-smoot.md +6 -0
  350. package/corpus/templates/design/changelog/2026-07-06-drawing-a-shape-or-text-on-the-canvas-no-longer-flashes-away.md +6 -0
  351. package/corpus/templates/design/changelog/2026-07-06-dropping-an-element-between-two-others-inside-a-screen-now-s.md +6 -0
  352. package/corpus/templates/design/changelog/2026-07-06-duplicating-a-screen-now-places-the-copy-exactly-where-you-d.md +6 -0
  353. package/corpus/templates/design/changelog/2026-07-06-edit-gradients-directly-on-elements-inside-screens-scale-str.md +6 -0
  354. package/corpus/templates/design/changelog/2026-07-06-edits-from-collaborators-and-the-agent-now-update-the-canvas.md +6 -0
  355. package/corpus/templates/design/changelog/2026-07-06-fixed-a-bug-where-most-style-position-and-text-edits-briefly.md +6 -0
  356. package/corpus/templates/design/changelog/2026-07-06-fixed-a-confusing-raw-error-message-when-a-file-upload-fails.md +6 -0
  357. package/corpus/templates/design/changelog/2026-07-06-fixed-a-rare-case-where-an-edit-could-be-silently-overwritte.md +6 -0
  358. package/corpus/templates/design/changelog/2026-07-06-fixed-inserted-assets-sometimes-disappearing-reappearing-or-.md +6 -0
  359. package/corpus/templates/design/changelog/2026-07-06-fixed-inspector-value-scrubbing-producing-erratic-numbers-an.md +6 -0
  360. package/corpus/templates/design/changelog/2026-07-06-fixed-padding-gap-and-min-max-size-scrubbing-not-updating-th.md +6 -0
  361. package/corpus/templates/design/changelog/2026-07-06-fixed-text-stroke-color-turning-black-and-glyphs-disappearin.md +6 -0
  362. package/corpus/templates/design/changelog/2026-07-06-fixed-the-collapsed-effects-and-stroke-inspector-sections-sh.md +6 -0
  363. package/corpus/templates/design/changelog/2026-07-06-fixed-the-new-design-dialog-closing-unexpectedly-when-using-.md +6 -0
  364. package/corpus/templates/design/changelog/2026-07-06-fixed-the-typography-section-missing-when-selecting-text-nes.md +6 -0
  365. package/corpus/templates/design/changelog/2026-07-06-full-view-no-longer-flashes-back-to-the-overview-on-entry-no.md +6 -0
  366. package/corpus/templates/design/changelog/2026-07-06-keyframe-animation-timeline-per-property-keyframes-easing-cu.md +6 -0
  367. package/corpus/templates/design/changelog/2026-07-06-moving-restyling-and-scrubbing-values-on-elements-in-ai-gene.md +6 -0
  368. package/corpus/templates/design/changelog/2026-07-06-padding-and-corner-radius-value-scrubbing-now-snaps-to-whole.md +6 -0
  369. package/corpus/templates/design/changelog/2026-07-06-padding-handles-now-only-resize-on-the-handle-line-itself-sh.md +6 -0
  370. package/corpus/templates/design/changelog/2026-07-06-preview-and-edit-hover-focus-and-pressed-states-with-real-cs.md +6 -0
  371. package/corpus/templates/design/changelog/2026-07-06-promoting-an-element-to-a-component-now-works-correctly-for-.md +6 -0
  372. package/corpus/templates/design/changelog/2026-07-06-reduced-url-history-churn-and-selection-overlay-flicker-whil.md +6 -0
  373. package/corpus/templates/design/changelog/2026-07-06-removed-the-decorative-dot-before-screen-names-in-the-overvi.md +6 -0
  374. package/corpus/templates/design/changelog/2026-07-06-responsive-breakpoints-edit-at-any-width-with-cascading-over.md +6 -0
  375. package/corpus/templates/design/changelog/2026-07-06-screen-labels-no-longer-turn-blue-when-you-hover-inside-the-.md +6 -0
  376. package/corpus/templates/design/changelog/2026-07-06-scrolling-a-screen-with-an-element-selected-stays-smooth-on-.md +6 -0
  377. package/corpus/templates/design/changelog/2026-07-06-selecting-multiple-elements-and-dragging-now-moves-the-whole.md +6 -0
  378. package/corpus/templates/design/changelog/2026-07-06-selection-borders-handles-and-value-readouts-now-stay-the-sa.md +6 -0
  379. package/corpus/templates/design/changelog/2026-07-06-selection-handles-and-editing-chrome-now-stay-a-constant-siz.md +6 -0
  380. package/corpus/templates/design/changelog/2026-07-06-shader-fills-and-effects-ai-authored-or-preset-glsl-shaders-.md +6 -0
  381. package/corpus/templates/design/changelog/2026-07-06-text-drawn-on-the-canvas-now-defaults-to-white-and-inter-in-.md +6 -0
  382. package/corpus/templates/design/changelog/2026-07-06-text-dropped-into-a-shape-now-stays-readable-instead-of-disa.md +6 -0
  383. package/corpus/templates/design/changelog/2026-07-06-the-add-tweak-controls-prompt-no-longer-jumps-to-the-corner-.md +6 -0
  384. package/corpus/templates/design/changelog/2026-07-06-the-padding-value-readout-now-stays-visible-while-you-hover-.md +6 -0
  385. package/corpus/templates/design/changelog/2026-07-06-the-selected-element-attachment-now-clears-from-the-chat-com.md +6 -0
  386. package/corpus/templates/design/changelog/2026-07-06-the-zoom-menu-is-more-compact-with-tight-rows-and-right-alig.md +6 -0
  387. package/corpus/templates/design/changelog/2026-07-06-undo-and-redo-no-longer-flash-the-screen-changes-now-apply-i.md +6 -0
  388. package/corpus/templates/design/changelog/2026-07-06-zoom-presets-like-zoom-to-50-now-stay-on-the-focused-screen-.md +6 -0
  389. package/corpus/templates/design/server/lib/import-design-files.ts +11 -2
  390. package/corpus/templates/design/server/source-workspace.ts +139 -59
  391. package/corpus/templates/design/shared/board-file.ts +11 -2
  392. package/corpus/templates/design/shared/breakpoint-media.ts +543 -0
  393. package/corpus/templates/design/shared/canvas-math.ts +354 -35
  394. package/corpus/templates/design/shared/code-layer.ts +277 -8
  395. package/corpus/templates/design/shared/design-state.ts +11 -4
  396. package/corpus/templates/design/shared/interaction-states.ts +548 -0
  397. package/corpus/templates/design/shared/motion-compiler.ts +186 -23
  398. package/corpus/templates/design/shared/motion-easing.ts +294 -0
  399. package/corpus/templates/design/shared/motion-timeline.ts +400 -34
  400. package/corpus/templates/design/shared/prompt-title.ts +95 -0
  401. package/corpus/templates/design/shared/responsive-classes.ts +448 -10
  402. package/corpus/templates/design/shared/screen-annotation.ts +46 -0
  403. package/corpus/templates/design/shared/shader-fills.ts +1009 -0
  404. package/corpus/templates/design/shared/shader-presets.ts +852 -4
  405. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +15 -2
  406. package/corpus/templates/slides/app/components/editor/ImportButton.tsx +10 -1
  407. package/corpus/templates/slides/app/lib/upload-response.ts +71 -0
  408. package/corpus/templates/slides/app/pages/DeckEditor.tsx +2 -2
  409. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  410. package/dist/agent/engine/ai-sdk-engine.js +15 -1
  411. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  412. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  413. package/dist/agent/engine/anthropic-engine.js +20 -1
  414. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  415. package/dist/agent/engine/registry.d.ts.map +1 -1
  416. package/dist/agent/engine/registry.js +22 -14
  417. package/dist/agent/engine/registry.js.map +1 -1
  418. package/dist/agent/production-agent.d.ts.map +1 -1
  419. package/dist/agent/production-agent.js +27 -7
  420. package/dist/agent/production-agent.js.map +1 -1
  421. package/dist/client/AgentPanel.js +1 -1
  422. package/dist/client/AgentPanel.js.map +1 -1
  423. package/dist/client/command-navigation/index.d.ts +4 -0
  424. package/dist/client/command-navigation/index.d.ts.map +1 -0
  425. package/dist/client/command-navigation/index.js +4 -0
  426. package/dist/client/command-navigation/index.js.map +1 -0
  427. package/dist/client/comments-review/index.d.ts +2 -0
  428. package/dist/client/comments-review/index.d.ts.map +1 -0
  429. package/dist/client/comments-review/index.js +2 -0
  430. package/dist/client/comments-review/index.js.map +1 -0
  431. package/dist/client/history/VersionHistoryPanel.d.ts +18 -0
  432. package/dist/client/history/VersionHistoryPanel.d.ts.map +1 -0
  433. package/dist/client/history/VersionHistoryPanel.js +24 -0
  434. package/dist/client/history/VersionHistoryPanel.js.map +1 -0
  435. package/dist/client/history/index.d.ts +3 -0
  436. package/dist/client/history/index.d.ts.map +1 -0
  437. package/dist/client/history/index.js +3 -0
  438. package/dist/client/history/index.js.map +1 -0
  439. package/dist/client/history/use-history.d.ts +46 -0
  440. package/dist/client/history/use-history.d.ts.map +1 -0
  441. package/dist/client/history/use-history.js +18 -0
  442. package/dist/client/history/use-history.js.map +1 -0
  443. package/dist/client/index.d.ts +6 -4
  444. package/dist/client/index.d.ts.map +1 -1
  445. package/dist/client/index.js +6 -4
  446. package/dist/client/index.js.map +1 -1
  447. package/dist/client/org/OrgSwitcher.js +5 -5
  448. package/dist/client/org/OrgSwitcher.js.map +1 -1
  449. package/dist/client/org/RequireActiveOrg.js +2 -2
  450. package/dist/client/org/RequireActiveOrg.js.map +1 -1
  451. package/dist/client/org/TeamPage.js +3 -3
  452. package/dist/client/org/TeamPage.js.map +1 -1
  453. package/dist/client/org/hooks.d.ts +12 -1
  454. package/dist/client/org/hooks.d.ts.map +1 -1
  455. package/dist/client/org/hooks.js +15 -0
  456. package/dist/client/org/hooks.js.map +1 -1
  457. package/dist/client/org/index.d.ts +4 -2
  458. package/dist/client/org/index.d.ts.map +1 -1
  459. package/dist/client/org/index.js +3 -1
  460. package/dist/client/org/index.js.map +1 -1
  461. package/dist/client/org-team/index.d.ts +2 -0
  462. package/dist/client/org-team/index.d.ts.map +1 -0
  463. package/dist/client/org-team/index.js +2 -0
  464. package/dist/client/org-team/index.js.map +1 -0
  465. package/dist/client/review/ReviewStatusBadge.d.ts +7 -0
  466. package/dist/client/review/ReviewStatusBadge.d.ts.map +1 -0
  467. package/dist/client/review/ReviewStatusBadge.js +19 -0
  468. package/dist/client/review/ReviewStatusBadge.js.map +1 -0
  469. package/dist/client/review/ReviewThreadPanel.d.ts +20 -0
  470. package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -0
  471. package/dist/client/review/ReviewThreadPanel.js +84 -0
  472. package/dist/client/review/ReviewThreadPanel.js.map +1 -0
  473. package/dist/client/review/index.d.ts +4 -0
  474. package/dist/client/review/index.d.ts.map +1 -0
  475. package/dist/client/review/index.js +4 -0
  476. package/dist/client/review/index.js.map +1 -0
  477. package/dist/client/review/use-review.d.ts +68 -0
  478. package/dist/client/review/use-review.d.ts.map +1 -0
  479. package/dist/client/review/use-review.js +22 -0
  480. package/dist/client/review/use-review.js.map +1 -0
  481. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  482. package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -6
  483. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  484. package/dist/client/settings/index.d.ts +1 -1
  485. package/dist/client/settings/index.d.ts.map +1 -1
  486. package/dist/client/settings/index.js +1 -1
  487. package/dist/client/settings/index.js.map +1 -1
  488. package/dist/client/setup-connections/BuilderConnectCard.d.ts +9 -0
  489. package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -0
  490. package/dist/client/setup-connections/BuilderConnectCard.js +25 -0
  491. package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -0
  492. package/dist/client/setup-connections/ProviderReadinessBadge.d.ts +8 -0
  493. package/dist/client/setup-connections/ProviderReadinessBadge.d.ts.map +1 -0
  494. package/dist/client/setup-connections/ProviderReadinessBadge.js +32 -0
  495. package/dist/client/setup-connections/ProviderReadinessBadge.js.map +1 -0
  496. package/dist/client/setup-connections/SetupConnectionsPage.d.ts +17 -0
  497. package/dist/client/setup-connections/SetupConnectionsPage.d.ts.map +1 -0
  498. package/dist/client/setup-connections/SetupConnectionsPage.js +10 -0
  499. package/dist/client/setup-connections/SetupConnectionsPage.js.map +1 -0
  500. package/dist/client/setup-connections/index.d.ts +7 -0
  501. package/dist/client/setup-connections/index.d.ts.map +1 -0
  502. package/dist/client/setup-connections/index.js +7 -0
  503. package/dist/client/setup-connections/index.js.map +1 -0
  504. package/dist/client/sharing/ShareButton.d.ts +4 -4
  505. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  506. package/dist/client/sharing/ShareButton.js +13 -7
  507. package/dist/client/sharing/ShareButton.js.map +1 -1
  508. package/dist/client/sharing/ShareDialog.js +3 -3
  509. package/dist/client/sharing/ShareDialog.js.map +1 -1
  510. package/dist/client/use-action.d.ts +3 -0
  511. package/dist/client/use-action.d.ts.map +1 -1
  512. package/dist/client/use-action.js +34 -3
  513. package/dist/client/use-action.js.map +1 -1
  514. package/dist/client/use-db-sync.d.ts +4 -0
  515. package/dist/client/use-db-sync.d.ts.map +1 -1
  516. package/dist/client/use-db-sync.js +37 -18
  517. package/dist/client/use-db-sync.js.map +1 -1
  518. package/dist/collab/routes.d.ts +1 -1
  519. package/dist/command-navigation/actions.d.ts +25 -0
  520. package/dist/command-navigation/actions.d.ts.map +1 -0
  521. package/dist/command-navigation/actions.js +64 -0
  522. package/dist/command-navigation/actions.js.map +1 -0
  523. package/dist/command-navigation/index.d.ts +4 -0
  524. package/dist/command-navigation/index.d.ts.map +1 -0
  525. package/dist/command-navigation/index.js +4 -0
  526. package/dist/command-navigation/index.js.map +1 -0
  527. package/dist/comments-review/index.d.ts +2 -0
  528. package/dist/comments-review/index.d.ts.map +1 -0
  529. package/dist/comments-review/index.js +2 -0
  530. package/dist/comments-review/index.js.map +1 -0
  531. package/dist/db/create-get-db.d.ts +25 -0
  532. package/dist/db/create-get-db.d.ts.map +1 -1
  533. package/dist/db/create-get-db.js +25 -2
  534. package/dist/db/create-get-db.js.map +1 -1
  535. package/dist/file-upload/builder.d.ts.map +1 -1
  536. package/dist/file-upload/builder.js +18 -2
  537. package/dist/file-upload/builder.js.map +1 -1
  538. package/dist/file-upload/types.d.ts +3 -0
  539. package/dist/file-upload/types.d.ts.map +1 -1
  540. package/dist/file-upload/types.js.map +1 -1
  541. package/dist/history/actions/create-resource-version.d.ts +10 -0
  542. package/dist/history/actions/create-resource-version.d.ts.map +1 -0
  543. package/dist/history/actions/create-resource-version.js +65 -0
  544. package/dist/history/actions/create-resource-version.js.map +1 -0
  545. package/dist/history/actions/get-resource-version.d.ts +10 -0
  546. package/dist/history/actions/get-resource-version.d.ts.map +1 -0
  547. package/dist/history/actions/get-resource-version.js +38 -0
  548. package/dist/history/actions/get-resource-version.js.map +1 -0
  549. package/dist/history/actions/list-resource-history.d.ts +10 -0
  550. package/dist/history/actions/list-resource-history.d.ts.map +1 -0
  551. package/dist/history/actions/list-resource-history.js +45 -0
  552. package/dist/history/actions/list-resource-history.js.map +1 -0
  553. package/dist/history/actions/list-resource-versions.d.ts +10 -0
  554. package/dist/history/actions/list-resource-versions.d.ts.map +1 -0
  555. package/dist/history/actions/list-resource-versions.js +35 -0
  556. package/dist/history/actions/list-resource-versions.js.map +1 -0
  557. package/dist/history/actions/restore-resource-version.d.ts +11 -0
  558. package/dist/history/actions/restore-resource-version.d.ts.map +1 -0
  559. package/dist/history/actions/restore-resource-version.js +59 -0
  560. package/dist/history/actions/restore-resource-version.js.map +1 -0
  561. package/dist/history/index.d.ts +4 -0
  562. package/dist/history/index.d.ts.map +1 -0
  563. package/dist/history/index.js +3 -0
  564. package/dist/history/index.js.map +1 -0
  565. package/dist/history/registry.d.ts +10 -0
  566. package/dist/history/registry.d.ts.map +1 -0
  567. package/dist/history/registry.js +61 -0
  568. package/dist/history/registry.js.map +1 -0
  569. package/dist/history/store.d.ts +34 -0
  570. package/dist/history/store.d.ts.map +1 -0
  571. package/dist/history/store.js +265 -0
  572. package/dist/history/store.js.map +1 -0
  573. package/dist/history/types.d.ts +55 -0
  574. package/dist/history/types.d.ts.map +1 -0
  575. package/dist/history/types.js +2 -0
  576. package/dist/history/types.js.map +1 -0
  577. package/dist/index.d.ts +6 -0
  578. package/dist/index.d.ts.map +1 -1
  579. package/dist/index.js +6 -0
  580. package/dist/index.js.map +1 -1
  581. package/dist/navigation/index.d.ts +53 -0
  582. package/dist/navigation/index.d.ts.map +1 -0
  583. package/dist/navigation/index.js +105 -0
  584. package/dist/navigation/index.js.map +1 -0
  585. package/dist/observability/routes.d.ts +3 -3
  586. package/dist/org/index.d.ts +1 -0
  587. package/dist/org/index.d.ts.map +1 -1
  588. package/dist/org/index.js +1 -0
  589. package/dist/org/index.js.map +1 -1
  590. package/dist/org/permissions.d.ts +7 -0
  591. package/dist/org/permissions.d.ts.map +1 -0
  592. package/dist/org/permissions.js +21 -0
  593. package/dist/org/permissions.js.map +1 -0
  594. package/dist/org-team/index.d.ts +2 -0
  595. package/dist/org-team/index.d.ts.map +1 -0
  596. package/dist/org-team/index.js +2 -0
  597. package/dist/org-team/index.js.map +1 -0
  598. package/dist/private-blob/registry.d.ts.map +1 -1
  599. package/dist/private-blob/registry.js +1 -0
  600. package/dist/private-blob/registry.js.map +1 -1
  601. package/dist/resources/handlers.d.ts +2 -2
  602. package/dist/review/actions/consume-review-feedback.d.ts +9 -0
  603. package/dist/review/actions/consume-review-feedback.d.ts.map +1 -0
  604. package/dist/review/actions/consume-review-feedback.js +29 -0
  605. package/dist/review/actions/consume-review-feedback.js.map +1 -0
  606. package/dist/review/actions/create-review-comment.d.ts +18 -0
  607. package/dist/review/actions/create-review-comment.d.ts.map +1 -0
  608. package/dist/review/actions/create-review-comment.js +81 -0
  609. package/dist/review/actions/create-review-comment.js.map +1 -0
  610. package/dist/review/actions/delete-review-comment.d.ts +10 -0
  611. package/dist/review/actions/delete-review-comment.d.ts.map +1 -0
  612. package/dist/review/actions/delete-review-comment.js +45 -0
  613. package/dist/review/actions/delete-review-comment.js.map +1 -0
  614. package/dist/review/actions/get-review-feedback.d.ts +10 -0
  615. package/dist/review/actions/get-review-feedback.d.ts.map +1 -0
  616. package/dist/review/actions/get-review-feedback.js +45 -0
  617. package/dist/review/actions/get-review-feedback.js.map +1 -0
  618. package/dist/review/actions/list-review-comments.d.ts +13 -0
  619. package/dist/review/actions/list-review-comments.d.ts.map +1 -0
  620. package/dist/review/actions/list-review-comments.js +44 -0
  621. package/dist/review/actions/list-review-comments.js.map +1 -0
  622. package/dist/review/actions/reply-review-comment.d.ts +16 -0
  623. package/dist/review/actions/reply-review-comment.d.ts.map +1 -0
  624. package/dist/review/actions/reply-review-comment.js +84 -0
  625. package/dist/review/actions/reply-review-comment.js.map +1 -0
  626. package/dist/review/actions/resolve-review-thread.d.ts +11 -0
  627. package/dist/review/actions/resolve-review-thread.d.ts.map +1 -0
  628. package/dist/review/actions/resolve-review-thread.js +46 -0
  629. package/dist/review/actions/resolve-review-thread.js.map +1 -0
  630. package/dist/review/actions/set-review-status.d.ts +9 -0
  631. package/dist/review/actions/set-review-status.d.ts.map +1 -0
  632. package/dist/review/actions/set-review-status.js +43 -0
  633. package/dist/review/actions/set-review-status.js.map +1 -0
  634. package/dist/review/index.d.ts +5 -0
  635. package/dist/review/index.d.ts.map +1 -0
  636. package/dist/review/index.js +4 -0
  637. package/dist/review/index.js.map +1 -0
  638. package/dist/review/mentions.d.ts +4 -0
  639. package/dist/review/mentions.d.ts.map +1 -0
  640. package/dist/review/mentions.js +39 -0
  641. package/dist/review/mentions.js.map +1 -0
  642. package/dist/review/registry.d.ts +10 -0
  643. package/dist/review/registry.d.ts.map +1 -0
  644. package/dist/review/registry.js +61 -0
  645. package/dist/review/registry.js.map +1 -0
  646. package/dist/review/store.d.ts +63 -0
  647. package/dist/review/store.d.ts.map +1 -0
  648. package/dist/review/store.js +558 -0
  649. package/dist/review/store.js.map +1 -0
  650. package/dist/review/types.d.ts +77 -0
  651. package/dist/review/types.d.ts.map +1 -0
  652. package/dist/review/types.js +2 -0
  653. package/dist/review/types.js.map +1 -0
  654. package/dist/secrets/routes.d.ts +9 -9
  655. package/dist/server/action-discovery.d.ts.map +1 -1
  656. package/dist/server/action-discovery.js +54 -0
  657. package/dist/server/action-discovery.js.map +1 -1
  658. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  659. package/dist/server/agent-engine-api-key-route.d.ts.map +1 -1
  660. package/dist/server/agent-engine-api-key-route.js +5 -0
  661. package/dist/server/agent-engine-api-key-route.js.map +1 -1
  662. package/dist/server/credential-provider.d.ts +26 -0
  663. package/dist/server/credential-provider.d.ts.map +1 -1
  664. package/dist/server/credential-provider.js +82 -0
  665. package/dist/server/credential-provider.js.map +1 -1
  666. package/dist/server/framework-request-handler.d.ts.map +1 -1
  667. package/dist/server/framework-request-handler.js +37 -1
  668. package/dist/server/framework-request-handler.js.map +1 -1
  669. package/dist/setup-connections/index.d.ts +6 -0
  670. package/dist/setup-connections/index.d.ts.map +1 -0
  671. package/dist/setup-connections/index.js +6 -0
  672. package/dist/setup-connections/index.js.map +1 -0
  673. package/dist/setup-connections/onboarding.d.ts +14 -0
  674. package/dist/setup-connections/onboarding.d.ts.map +1 -0
  675. package/dist/setup-connections/onboarding.js +23 -0
  676. package/dist/setup-connections/onboarding.js.map +1 -0
  677. package/dist/transcription/builder-transcription.d.ts +1 -0
  678. package/dist/transcription/builder-transcription.d.ts.map +1 -1
  679. package/dist/transcription/builder-transcription.js +5 -2
  680. package/dist/transcription/builder-transcription.js.map +1 -1
  681. package/dist/vite/action-types-plugin.d.ts.map +1 -1
  682. package/dist/vite/action-types-plugin.js +52 -0
  683. package/dist/vite/action-types-plugin.js.map +1 -1
  684. package/docs/content/agent-native-toolkit.mdx +87 -80
  685. package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +89 -80
  686. package/docs/content/locales/de-DE/agent-native-toolkit.mdx +89 -80
  687. package/docs/content/locales/es-ES/agent-native-toolkit.mdx +89 -80
  688. package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +89 -80
  689. package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +89 -80
  690. package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +89 -80
  691. package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +89 -80
  692. package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +89 -80
  693. package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +89 -80
  694. package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +89 -80
  695. package/docs/content/toolkit-agent-ux.mdx +43 -0
  696. package/docs/content/toolkit-app-adapters.mdx +9 -0
  697. package/docs/content/toolkit-collaboration-ui.mdx +12 -0
  698. package/docs/content/toolkit-collaboration.mdx +74 -0
  699. package/docs/content/toolkit-command-navigation.mdx +106 -0
  700. package/docs/content/toolkit-comments-review.mdx +98 -0
  701. package/docs/content/toolkit-history.mdx +95 -0
  702. package/docs/content/toolkit-observability.mdx +41 -0
  703. package/docs/content/toolkit-org-team.mdx +80 -0
  704. package/docs/content/toolkit-resources.mdx +57 -0
  705. package/docs/content/toolkit-settings.mdx +65 -0
  706. package/docs/content/toolkit-setup-connections.mdx +109 -0
  707. package/docs/content/toolkit-sharing-ui.mdx +18 -6
  708. package/docs/content/toolkit-sharing.mdx +81 -0
  709. package/docs/content/toolkit-shell-hooks.mdx +9 -0
  710. package/docs/content/toolkit-ui.mdx +9 -0
  711. package/package.json +36 -2
  712. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +0 -153
  713. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +0 -133
  714. package/corpus/templates/design/app/pages/DesignEditor.tsx +0 -22924
@@ -0,0 +1,1097 @@
1
+ /**
2
+ * GlslShaderPanel — the code-backed "Shader" paint-type / effect-type picker.
3
+ *
4
+ * Mirrors Figma UI3's Shader picker structure:
5
+ * • "Created by you" — shaders already saved in this design's screen HTML
6
+ * (parsed live from the source via shared/shader-fills.ts), plus the
7
+ * "Create new" AI tile that prefills the agent chat.
8
+ * • "Presets" — the curated GLSL preset library from
9
+ * shared/shader-presets.ts (our "By Figma" equivalent).
10
+ *
11
+ * Unlike the legacy ShaderFillsPanel (paper-design GPU presets persisted as
12
+ * CSS gradient approximations), applying a shader here persists the real GLSL
13
+ * fragment source + uniforms manifest into the screen HTML — readable and
14
+ * editable in the Code panel — and the embedded WebGL runtime renders it live
15
+ * in the editor and in exported standalone HTML.
16
+ *
17
+ * Persistence path (no new actions):
18
+ * read-source-file → applyShaderToHtml() → apply-source-edit(full-replace,
19
+ * expectedVersionHash) → onApplied(fileId, content) so the host editor can
20
+ * sync its local/collab state (same contract as component prop edits).
21
+ *
22
+ * Live preview path: postMessage broadcast to every screen iframe; the
23
+ * shader-fill-preview bridge forwards to the injected runtime, which only
24
+ * mounts on the iframe that actually contains the target node.
25
+ *
26
+ * See the "Cross-pipeline write-race guard" section below for why this
27
+ * picker's persist flow registers itself in a small exclusion registry that
28
+ * DesignEditor.tsx's commitVisualStyles checks before its own competing
29
+ * content write (Fill section Add layer / Remove layer, concurrent with an
30
+ * in-flight shader apply, otherwise corrupts the document).
31
+ */
32
+
33
+ import {
34
+ callAction,
35
+ useActionMutation,
36
+ useActionQuery,
37
+ useT,
38
+ } from "@agent-native/core/client";
39
+ import {
40
+ applyShaderToHtml,
41
+ annotateNodeWithShader,
42
+ defaultUniformValues,
43
+ listShaderMounts,
44
+ listShadersInHtml,
45
+ newShaderId,
46
+ removeShaderFromNode,
47
+ type GlslShaderDef,
48
+ type GlslShaderMode,
49
+ type GlslUniformValue,
50
+ } from "@shared/shader-fills";
51
+ import {
52
+ GLSL_SHADER_PRESETS,
53
+ type GlslShaderPreset,
54
+ type GlslShaderPresetCategory,
55
+ } from "@shared/shader-presets";
56
+ import {
57
+ IconArrowLeft,
58
+ IconCode,
59
+ IconPlus,
60
+ IconSearch,
61
+ IconTrash,
62
+ IconWaveSine,
63
+ IconX,
64
+ } from "@tabler/icons-react";
65
+ import { useMemo, useState } from "react";
66
+ import { toast } from "sonner";
67
+
68
+ import { Input } from "@/components/ui/input";
69
+ import {
70
+ Popover,
71
+ PopoverContent,
72
+ PopoverTrigger,
73
+ } from "@/components/ui/popover";
74
+ import {
75
+ Tooltip,
76
+ TooltipContent,
77
+ TooltipTrigger,
78
+ } from "@/components/ui/tooltip";
79
+ import { sendToDesignAgentChat } from "@/lib/agent-chat";
80
+ import { cn } from "@/lib/utils";
81
+
82
+ import { ScrubInput, type ScrubInputChangeMeta } from "./ScrubInput";
83
+
84
+ // ─── Cross-pipeline write-race guard ──────────────────────────────────────────
85
+ //
86
+ // This picker's persist flow (read-source-file GET -> pure transform ->
87
+ // apply-source-edit POST) is a SEPARATE round trip from the base Fill
88
+ // section's style commits (DesignEditor.tsx's commitVisualStyles ->
89
+ // update-file), and both ultimately feed the SAME per-file Yjs collab
90
+ // document — one via a diff-based server-side `applyText`, the other via the
91
+ // host's own synchronous, untracked full-document `ydoc.transact` rewrite
92
+ // (see DesignEditor.tsx's applyLocalContentUpdate/commitVisualStyles
93
+ // "Untracked full rewrite" comments). If a base style edit (e.g. Fill's Add
94
+ // layer / Remove layer) fires WHILE a shader apply/remove/knob-commit for the
95
+ // SAME file is still in flight, the two writes are computed from a common
96
+ // ancestor but never see each other before landing: the shader write's
97
+ // server-side diff and the style edit's own client-side full-document Y.Text
98
+ // rewrite merge as two divergent CRDT deltas, which do not converge to either
99
+ // intended document — verified to reproduce as a corrupted, doubled document
100
+ // (two concatenated <!DOCTYPE>...</html> copies) via the real
101
+ // applyShaderToHtml/applyVisualEdit/applyTextToYDoc functions.
102
+ //
103
+ // `withShaderWriteLock`/`isShaderWriteInFlight` below is a small, file-scoped
104
+ // exclusion registry (no new action, no new GlslShaderPanelContext field —
105
+ // EditPanel.tsx's context plumbing is unchanged) that DesignEditor.tsx's
106
+ // commitVisualStyles imports directly to defer its own competing write until
107
+ // this picker's in-flight persist for the same file has fully settled
108
+ // (including the onApplied host-sync), closing the race at its source
109
+ // instead of papering over the corrupted result afterward.
110
+
111
+ /**
112
+ * Per-file registry of in-flight shader persist operations (read-source-file
113
+ * GET through apply-source-edit POST through the onApplied host-sync
114
+ * callback). Module-scoped rather than threaded through
115
+ * GlslShaderPanelContext so DesignEditor.tsx can await it without EditPanel.tsx
116
+ * needing to forward a new prop.
117
+ */
118
+ const shaderWriteLocks = new Map<string, Promise<void>>();
119
+
120
+ /** True while a shader persist for this file id has not yet settled. */
121
+ export function isShaderWriteInFlight(fileId: string | undefined): boolean {
122
+ return !!fileId && shaderWriteLocks.has(fileId);
123
+ }
124
+
125
+ /**
126
+ * Resolves once every shader persist registered for this file id (at the time
127
+ * of the call) has settled. Callers that are about to perform their own
128
+ * competing full-document content write for the same file should await this
129
+ * first so their write is always computed from — and lands after — the
130
+ * shader's already-settled result, instead of racing it.
131
+ */
132
+ export async function waitForShaderWriteToSettle(
133
+ fileId: string | undefined,
134
+ ): Promise<void> {
135
+ if (!fileId) return;
136
+ const pending = shaderWriteLocks.get(fileId);
137
+ if (pending) await pending.catch(() => {});
138
+ }
139
+
140
+ /**
141
+ * Registers `fn` as the in-flight shader write for `fileId` until it settles,
142
+ * chaining behind any already-registered write for the same file so two
143
+ * shader operations on one file stay ordered too. Mirrors the identity-guard
144
+ * pattern DesignEditor.tsx's fileSaveChainsRef already uses for update-file
145
+ * saves: only clear the registry entry if nothing newer replaced it while
146
+ * this one was running.
147
+ */
148
+ function withShaderWriteLock<T>(
149
+ fileId: string,
150
+ fn: () => Promise<T>,
151
+ ): Promise<T> {
152
+ const previous = shaderWriteLocks.get(fileId) ?? Promise.resolve();
153
+ const run = previous.catch(() => {}).then(fn);
154
+ const settleMarker = run.then(
155
+ () => undefined,
156
+ () => undefined,
157
+ );
158
+ shaderWriteLocks.set(fileId, settleMarker);
159
+ void settleMarker.finally(() => {
160
+ if (shaderWriteLocks.get(fileId) === settleMarker) {
161
+ shaderWriteLocks.delete(fileId);
162
+ }
163
+ });
164
+ return run;
165
+ }
166
+
167
+ // ─── Context contract ─────────────────────────────────────────────────────────
168
+
169
+ export interface GlslShaderPanelContext {
170
+ designId?: string;
171
+ fileId?: string;
172
+ /** Stable data-agent-native-node-id of the target element. */
173
+ nodeId?: string;
174
+ selector?: string;
175
+ /**
176
+ * Called after a persisted apply with the patched file content so the host
177
+ * editor can sync local/collab state (same contract as component prop
178
+ * edits' onComponentPropApplied).
179
+ */
180
+ onApplied?: (fileId: string, content: string, updatedAt?: string) => void;
181
+ /**
182
+ * Optional: focus the Code panel on this screen's source so the user can
183
+ * edit the GLSL directly. When absent the Edit-code affordance still
184
+ * explains where the source lives.
185
+ */
186
+ onEditCode?: (shaderId: string) => void;
187
+ }
188
+
189
+ // ─── Screen-source plumbing ───────────────────────────────────────────────────
190
+
191
+ interface SourceFileResult {
192
+ fileId?: string;
193
+ path?: string;
194
+ content?: string;
195
+ versionHash?: string;
196
+ }
197
+
198
+ /** Broadcast a bridge message to every screen iframe (runtime filters by target). */
199
+ export function broadcastShaderMessage(message: Record<string, unknown>): void {
200
+ if (typeof document === "undefined") return;
201
+ const frames = document.querySelectorAll<HTMLIFrameElement>("iframe");
202
+ frames.forEach((frame) => {
203
+ try {
204
+ frame.contentWindow?.postMessage(message, "*");
205
+ } catch {
206
+ /* inaccessible frame — ignore */
207
+ }
208
+ });
209
+ }
210
+
211
+ /**
212
+ * Read the target screen's saved shaders + element mounts. Co-located data
213
+ * hook for the shader picker and the effects rows.
214
+ */
215
+ export function useScreenGlslShaders(context: GlslShaderPanelContext) {
216
+ const enabled = Boolean(context.designId && context.fileId);
217
+ const query = useActionQuery<SourceFileResult>(
218
+ "read-source-file",
219
+ { designId: context.designId ?? "", fileId: context.fileId ?? "" },
220
+ { enabled },
221
+ );
222
+ const content = enabled ? (query.data?.content ?? "") : "";
223
+ const shaders = useMemo(() => listShadersInHtml(content), [content]);
224
+ const mounts = useMemo(() => listShaderMounts(content), [content]);
225
+ return { ...query, enabled, content, shaders, mounts };
226
+ }
227
+
228
+ /**
229
+ * One persisted shader write: fresh read → pure HTML transform → guarded
230
+ * full-replace write → host sync + iframe rescan.
231
+ */
232
+ export function usePersistShaderEdit(context: GlslShaderPanelContext) {
233
+ const t = useT();
234
+ const applyEdit = useActionMutation("apply-source-edit");
235
+ const [busy, setBusy] = useState(false);
236
+
237
+ const persist = async (
238
+ transform: (html: string) => { html: string; errors: string[] },
239
+ ): Promise<boolean> => {
240
+ if (!context.designId || !context.fileId) {
241
+ toast.error(t("editPanel.shaders.selectElementFirst"));
242
+ return false;
243
+ }
244
+ const fileId = context.fileId;
245
+ const designId = context.designId;
246
+ setBusy(true);
247
+ try {
248
+ // Serialize against any other in-flight shader write for this file, AND
249
+ // register this operation in the shared registry so DesignEditor.tsx's
250
+ // commitVisualStyles (Add layer / Remove layer / any other base style
251
+ // commit) can await waitForShaderWriteToSettle(fileId) before doing its
252
+ // own competing full-document ydoc rewrite — see the module doc comment
253
+ // above for why an un-serialized race here corrupts the document.
254
+ return await withShaderWriteLock(fileId, async () => {
255
+ // read-source-file is a read-only GET action (see useScreenGlslShaders'
256
+ // useActionQuery above and the code-workbench inline provider's
257
+ // readFile) — it must be called imperatively via callAction with
258
+ // method: "GET", never useActionMutation (which always POSTs and the
259
+ // server rejects with "Method not allowed. Use GET.").
260
+ const source = await callAction<SourceFileResult>(
261
+ "read-source-file",
262
+ { designId, fileId },
263
+ { method: "GET" },
264
+ );
265
+ const baseHtml = source.content ?? "";
266
+ const transformed = transform(baseHtml);
267
+ if (transformed.errors.length > 0) {
268
+ toast.error(transformed.errors[0]);
269
+ return false;
270
+ }
271
+ if (transformed.html === baseHtml) return true;
272
+ const written = (await applyEdit.mutateAsync({
273
+ designId,
274
+ fileId,
275
+ edit: { kind: "full-replace", content: transformed.html },
276
+ ...(source.versionHash
277
+ ? { expectedVersionHash: source.versionHash }
278
+ : {}),
279
+ })) as { fileId?: string; updatedAt?: string };
280
+ context.onApplied?.(
281
+ written.fileId ?? fileId,
282
+ transformed.html,
283
+ typeof written.updatedAt === "string" ? written.updatedAt : undefined,
284
+ );
285
+ broadcastShaderMessage({ type: "glsl-shader-preview-clear" });
286
+ broadcastShaderMessage({ type: "glsl-shader-rescan" });
287
+ return true;
288
+ });
289
+ } catch (error) {
290
+ toast.error(
291
+ error instanceof Error
292
+ ? error.message
293
+ : t("editPanel.shaders.saveFailed"),
294
+ );
295
+ return false;
296
+ } finally {
297
+ setBusy(false);
298
+ }
299
+ };
300
+
301
+ return { persist, busy };
302
+ }
303
+
304
+ // ─── Small building blocks ────────────────────────────────────────────────────
305
+
306
+ function normalizeHex(value: string): string {
307
+ const hex = value.trim();
308
+ if (/^#[0-9a-fA-F]{3}$/.test(hex)) {
309
+ return (
310
+ "#" +
311
+ hex
312
+ .slice(1)
313
+ .split("")
314
+ .map((c) => c + c)
315
+ .join("")
316
+ ).toLowerCase();
317
+ }
318
+ if (/^#[0-9a-fA-F]{6}$/.test(hex)) return hex.toLowerCase();
319
+ return "#808080";
320
+ }
321
+
322
+ function ColorKnob({
323
+ label,
324
+ value,
325
+ disabled,
326
+ onChange,
327
+ }: {
328
+ label: string;
329
+ value: string;
330
+ disabled?: boolean;
331
+ onChange: (next: string, phase: "preview" | "commit") => void;
332
+ }) {
333
+ const hex = normalizeHex(value);
334
+ return (
335
+ <div className="flex h-6 items-center gap-1.5">
336
+ <span className="w-20 shrink-0 truncate !text-[11px] text-muted-foreground">
337
+ {label}
338
+ </span>
339
+ <label
340
+ className={cn(
341
+ "relative size-4 shrink-0 cursor-pointer overflow-hidden rounded-[3px] border border-[var(--design-editor-control-border)]",
342
+ disabled && "pointer-events-none opacity-40",
343
+ )}
344
+ style={{ background: hex }}
345
+ >
346
+ <input
347
+ type="color"
348
+ value={hex}
349
+ disabled={disabled}
350
+ aria-label={label}
351
+ className="absolute inset-0 size-full cursor-pointer opacity-0"
352
+ onChange={(event) => onChange(event.target.value, "preview")}
353
+ onBlur={(event) => onChange(event.target.value, "commit")}
354
+ />
355
+ </label>
356
+ <Input
357
+ value={hex.toUpperCase()}
358
+ disabled={disabled}
359
+ aria-label={`${label} hex`}
360
+ className="h-6 min-w-0 flex-1 border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 !text-[11px] uppercase shadow-none md:!text-[11px]"
361
+ onChange={(event) => {
362
+ const next = event.target.value;
363
+ if (
364
+ /^#[0-9a-fA-F]{6}$/.test(next) ||
365
+ /^#[0-9a-fA-F]{3}$/.test(next)
366
+ ) {
367
+ onChange(normalizeHex(next), "commit");
368
+ }
369
+ }}
370
+ />
371
+ </div>
372
+ );
373
+ }
374
+
375
+ /**
376
+ * Localized preset-category labels. `shared/shader-presets.ts` only carries
377
+ * the stable English `GLSL_SHADER_PRESET_CATEGORY_LABELS` (shared code, not
378
+ * i18n-aware), so the picker maps category -> catalog key here instead.
379
+ */
380
+ function useShaderPresetCategoryLabel() {
381
+ const t = useT();
382
+ return (category: GlslShaderPresetCategory): string => {
383
+ switch (category) {
384
+ case "gradient-flow":
385
+ return t("editPanel.shaders.categories.gradientFlow");
386
+ case "waves":
387
+ return t("editPanel.shaders.categories.waves");
388
+ case "noise":
389
+ return t("editPanel.shaders.categories.noise");
390
+ case "pattern":
391
+ return t("editPanel.shaders.categories.pattern");
392
+ case "texture":
393
+ return t("editPanel.shaders.categories.texture");
394
+ case "retro":
395
+ return t("editPanel.shaders.categories.retro");
396
+ default:
397
+ return category;
398
+ }
399
+ };
400
+ }
401
+
402
+ function PresetThumb({
403
+ preset,
404
+ disabled,
405
+ onPick,
406
+ }: {
407
+ preset: GlslShaderPreset;
408
+ disabled?: boolean;
409
+ onPick: (preset: GlslShaderPreset) => void;
410
+ }) {
411
+ const categoryLabel = useShaderPresetCategoryLabel();
412
+ return (
413
+ <Tooltip>
414
+ <TooltipTrigger asChild>
415
+ <button
416
+ type="button"
417
+ disabled={disabled}
418
+ aria-label={preset.label}
419
+ onClick={() => onPick(preset)}
420
+ className={cn(
421
+ "group flex flex-col gap-1 text-left focus-visible:outline-none",
422
+ disabled && "pointer-events-none opacity-40",
423
+ )}
424
+ >
425
+ <div
426
+ className="aspect-[4/3] w-full rounded-md border border-border/60 transition-colors group-hover:border-foreground/40"
427
+ style={{ background: preset.previewCss }}
428
+ />
429
+ <span className="truncate text-[10px] text-muted-foreground group-hover:text-foreground">
430
+ {preset.label}
431
+ </span>
432
+ </button>
433
+ </TooltipTrigger>
434
+ <TooltipContent>
435
+ {categoryLabel(preset.category)} — {preset.description}
436
+ </TooltipContent>
437
+ </Tooltip>
438
+ );
439
+ }
440
+
441
+ // ─── Uniform knobs (shared by fill picker + effect rows) ─────────────────────
442
+
443
+ export function GlslShaderKnobs({
444
+ def,
445
+ values,
446
+ disabled,
447
+ onValuesChange,
448
+ }: {
449
+ def: GlslShaderDef;
450
+ values: Record<string, GlslUniformValue>;
451
+ disabled?: boolean;
452
+ /**
453
+ * phase "preview" = live scrub tick (cheap, broadcast-only);
454
+ * phase "commit" = gesture end (persist).
455
+ */
456
+ onValuesChange: (
457
+ next: Record<string, GlslUniformValue>,
458
+ changedName: string,
459
+ phase: "preview" | "commit",
460
+ ) => void;
461
+ }) {
462
+ const t = useT();
463
+ const entries = Object.entries(def.uniforms);
464
+ if (entries.length === 0) {
465
+ return (
466
+ <p className="px-0.5 py-1 !text-[11px] text-muted-foreground">
467
+ {t("editPanel.shaders.noUniforms")}
468
+ </p>
469
+ );
470
+ }
471
+ const emit = (
472
+ name: string,
473
+ value: GlslUniformValue,
474
+ phase: "preview" | "commit",
475
+ ) => {
476
+ onValuesChange({ ...values, [name]: value }, name, phase);
477
+ };
478
+ return (
479
+ <div className="space-y-1">
480
+ {entries.map(([name, u]) => {
481
+ const label = u.label ?? name.replace(/^u_/, "").replace(/_/g, " ");
482
+ const current = values[name] ?? u.value;
483
+ if (u.type === "color") {
484
+ return (
485
+ <ColorKnob
486
+ key={name}
487
+ label={label}
488
+ value={typeof current === "string" ? current : "#808080"}
489
+ disabled={disabled}
490
+ onChange={(next, phase) => emit(name, next, phase)}
491
+ />
492
+ );
493
+ }
494
+ if (u.type === "vec2") {
495
+ const pair = Array.isArray(current) ? current : [0, 0];
496
+ const emitAxis = (
497
+ axis: 0 | 1,
498
+ value: number,
499
+ meta: ScrubInputChangeMeta,
500
+ ) => {
501
+ const next: [number, number] = [pair[0] ?? 0, pair[1] ?? 0];
502
+ next[axis] = value;
503
+ emit(name, next, meta.phase);
504
+ };
505
+ return (
506
+ <div key={name} className="flex h-6 items-center gap-1.5">
507
+ <span className="w-20 shrink-0 truncate !text-[11px] text-muted-foreground">
508
+ {label}
509
+ </span>
510
+ <ScrubInput
511
+ label="X"
512
+ value={Number(pair[0]) || 0}
513
+ step={0.01}
514
+ precision={2}
515
+ disabled={disabled}
516
+ onChange={(value, meta) => emitAxis(0, value, meta)}
517
+ labelClassName="w-3"
518
+ inputClassName="h-6"
519
+ className="min-w-0 flex-1"
520
+ />
521
+ <ScrubInput
522
+ label="Y"
523
+ value={Number(pair[1]) || 0}
524
+ step={0.01}
525
+ precision={2}
526
+ disabled={disabled}
527
+ onChange={(value, meta) => emitAxis(1, value, meta)}
528
+ labelClassName="w-3"
529
+ inputClassName="h-6"
530
+ className="min-w-0 flex-1"
531
+ />
532
+ </div>
533
+ );
534
+ }
535
+ return (
536
+ <ScrubInput
537
+ key={name}
538
+ label={label}
539
+ value={typeof current === "number" ? current : Number(current) || 0}
540
+ min={u.min}
541
+ max={u.max}
542
+ step={u.step ?? 0.01}
543
+ precision={u.step !== undefined && u.step >= 1 ? 0 : 2}
544
+ disabled={disabled}
545
+ onChange={(value, meta) => emit(name, value, meta.phase)}
546
+ labelClassName="w-20"
547
+ inputClassName="h-6"
548
+ />
549
+ );
550
+ })}
551
+ </div>
552
+ );
553
+ }
554
+
555
+ // ─── Main panel ───────────────────────────────────────────────────────────────
556
+
557
+ export interface GlslShaderPanelProps {
558
+ mode: GlslShaderMode;
559
+ context: GlslShaderPanelContext;
560
+ /**
561
+ * Return to the previous picker view (fill picker) or close the popover.
562
+ * `hasShader` reports whether the target element has a persisted shader of
563
+ * this panel's mode — callers use it to decide whether to revert the paint
564
+ * type (never revert when a shader is actually applied).
565
+ */
566
+ onBack: (hasShader: boolean) => void;
567
+ disabled?: boolean;
568
+ }
569
+
570
+ export function GlslShaderPanel({
571
+ mode,
572
+ context,
573
+ onBack,
574
+ disabled = false,
575
+ }: GlslShaderPanelProps) {
576
+ const t = useT();
577
+ const categoryLabel = useShaderPresetCategoryLabel();
578
+ const [search, setSearch] = useState("");
579
+ // View state: detail view shows whenever the target node has a shader of
580
+ // this mode (or one was just applied) UNLESS the user explicitly backed
581
+ // out to browse for a replacement.
582
+ const [browsing, setBrowsing] = useState(false);
583
+ const [justAppliedId, setJustAppliedId] = useState<string | null>(null);
584
+ const screen = useScreenGlslShaders(context);
585
+ const { persist, busy } = usePersistShaderEdit(context);
586
+
587
+ const nodeId = context.nodeId;
588
+ const modeAttrTitle =
589
+ mode === "effect"
590
+ ? t("editPanel.shaders.effectsTitle")
591
+ : t("editPanel.shaders.fillsTitle");
592
+
593
+ // The shader currently mounted on the target node (persisted state).
594
+ const nodeMount = useMemo(
595
+ () =>
596
+ screen.mounts.find(
597
+ (mount) => mount.nodeId === nodeId && mount.mode === mode,
598
+ ),
599
+ [screen.mounts, nodeId, mode],
600
+ );
601
+ const activeId = browsing
602
+ ? null
603
+ : (justAppliedId ?? nodeMount?.shaderId ?? null);
604
+ const activeDef = useMemo(
605
+ () => screen.shaders.find((shader) => shader.id === activeId) ?? null,
606
+ [screen.shaders, activeId],
607
+ );
608
+ const [draftValues, setDraftValues] = useState<Record<
609
+ string,
610
+ GlslUniformValue
611
+ > | null>(null);
612
+ const values = useMemo(() => {
613
+ if (!activeDef) return {};
614
+ return {
615
+ ...defaultUniformValues(activeDef),
616
+ ...(nodeMount?.shaderId === activeDef.id ? (nodeMount.values ?? {}) : {}),
617
+ ...(draftValues ?? {}),
618
+ };
619
+ }, [activeDef, nodeMount, draftValues]);
620
+
621
+ const presets = useMemo(() => {
622
+ const byMode = GLSL_SHADER_PRESETS.filter((preset) => preset.mode === mode);
623
+ const query = search.trim().toLowerCase();
624
+ if (!query) return byMode;
625
+ return byMode.filter(
626
+ (preset) =>
627
+ preset.label.toLowerCase().includes(query) ||
628
+ preset.description.toLowerCase().includes(query) ||
629
+ categoryLabel(preset.category).toLowerCase().includes(query),
630
+ );
631
+ }, [mode, search, categoryLabel]);
632
+
633
+ const savedShaders = useMemo(() => {
634
+ const byMode = screen.shaders.filter((shader) => shader.mode === mode);
635
+ const query = search.trim().toLowerCase();
636
+ if (!query) return byMode;
637
+ return byMode.filter((shader) => shader.name.toLowerCase().includes(query));
638
+ }, [screen.shaders, mode, search]);
639
+
640
+ const fallbackFromDef = (def: GlslShaderDef): string | undefined => {
641
+ for (const u of Object.values(def.uniforms)) {
642
+ if (u.type === "color" && typeof u.value === "string") return u.value;
643
+ }
644
+ return undefined;
645
+ };
646
+
647
+ const applyDef = async (def: GlslShaderDef) => {
648
+ if (!nodeId) {
649
+ toast.error(t("editPanel.shaders.selectCanvasElementFirst"));
650
+ return;
651
+ }
652
+ const ok = await persist((html) =>
653
+ applyShaderToHtml(html, {
654
+ nodeId,
655
+ def,
656
+ ...(def.mode === "fill" ? { fallbackColor: fallbackFromDef(def) } : {}),
657
+ }),
658
+ );
659
+ if (ok) {
660
+ setJustAppliedId(def.id);
661
+ setBrowsing(false);
662
+ setDraftValues(null);
663
+ void screen.refetch();
664
+ }
665
+ };
666
+
667
+ const applyPreset = (preset: GlslShaderPreset) => {
668
+ void applyDef({
669
+ id: newShaderId(),
670
+ name: preset.label,
671
+ mode,
672
+ glsl: preset.glsl,
673
+ uniforms: preset.uniforms,
674
+ });
675
+ };
676
+
677
+ const applySaved = (def: GlslShaderDef) => {
678
+ void applyDef(def);
679
+ };
680
+
681
+ const removeFromNode = async () => {
682
+ if (!nodeId) return;
683
+ // Scope removal to this panel's mode — a fill and an effect can coexist
684
+ // on one node (see shared/shader-fills.ts), so clearing the fill picker
685
+ // must not also wipe a coexisting shader effect (and vice versa).
686
+ const ok = await persist((html) =>
687
+ removeShaderFromNode(html, nodeId, mode),
688
+ );
689
+ if (ok) {
690
+ setJustAppliedId(null);
691
+ setBrowsing(true);
692
+ setDraftValues(null);
693
+ void screen.refetch();
694
+ }
695
+ };
696
+
697
+ const createWithAi = () => {
698
+ sendToDesignAgentChat({
699
+ message:
700
+ mode === "effect"
701
+ ? "Create a custom shader effect for the selected element."
702
+ : "Create a custom shader fill for the selected element.",
703
+ context: [
704
+ "Use the code-backed GLSL shader format from the shader-fills skill:",
705
+ 'persist a <script type="application/x-agent-native-shader"> block',
706
+ "(uniforms manifest comment + GLSL fragment source) in the screen",
707
+ "HTML and reference it from the element.",
708
+ context.designId ? `designId: ${context.designId}` : "",
709
+ context.fileId ? `fileId: ${context.fileId}` : "",
710
+ nodeId ? `target nodeId (data-agent-native-node-id): ${nodeId}` : "",
711
+ `mode: ${mode}`,
712
+ ]
713
+ .filter(Boolean)
714
+ .join("\n"),
715
+ submit: false,
716
+ });
717
+ };
718
+
719
+ const handleValuesChange = (
720
+ next: Record<string, GlslUniformValue>,
721
+ changedName: string,
722
+ phase: "preview" | "commit",
723
+ ) => {
724
+ if (!activeDef) return;
725
+ setDraftValues(next);
726
+ // Live-update the mounted shader in whichever iframe hosts the node.
727
+ broadcastShaderMessage({
728
+ type: "glsl-shader-set-uniform",
729
+ filter: { shaderId: activeDef.id, ...(nodeId ? { nodeId } : {}) },
730
+ name: changedName,
731
+ value: next[changedName],
732
+ });
733
+ if (phase === "commit" && nodeId) {
734
+ void persist((html) =>
735
+ annotateNodeWithShader(html, {
736
+ nodeId,
737
+ shaderId: activeDef.id,
738
+ mode,
739
+ values: next,
740
+ }),
741
+ ).then((ok) => {
742
+ if (ok) void screen.refetch();
743
+ });
744
+ }
745
+ };
746
+
747
+ // ── Detail view — knobs for the applied shader ────────────────────────────
748
+ if (activeDef) {
749
+ return (
750
+ <div className="flex flex-col">
751
+ <div className="flex h-6 items-center gap-1.5 px-3">
752
+ <button
753
+ type="button"
754
+ aria-label={t("editPanel.shaders.backToBrowser")}
755
+ onClick={() => {
756
+ setBrowsing(true);
757
+ setJustAppliedId(null);
758
+ setDraftValues(null);
759
+ }}
760
+ 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"
761
+ >
762
+ <IconArrowLeft className="size-3.5" />
763
+ </button>
764
+ <span className="flex-1 truncate !text-[11px] font-semibold text-foreground">
765
+ {activeDef.name}
766
+ </span>
767
+ {context.onEditCode ? (
768
+ <Tooltip>
769
+ <TooltipTrigger asChild>
770
+ <button
771
+ type="button"
772
+ aria-label={t("editPanel.shaders.editCode")}
773
+ onClick={() => context.onEditCode?.(activeDef.id)}
774
+ 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"
775
+ >
776
+ <IconCode className="size-3.5" />
777
+ </button>
778
+ </TooltipTrigger>
779
+ <TooltipContent>{t("editPanel.shaders.editCode")}</TooltipContent>
780
+ </Tooltip>
781
+ ) : null}
782
+ <Tooltip>
783
+ <TooltipTrigger asChild>
784
+ <button
785
+ type="button"
786
+ aria-label={t("editPanel.shaders.removeShader")}
787
+ disabled={disabled || busy || !nodeMount}
788
+ onClick={() => void removeFromNode()}
789
+ className="flex size-5 items-center justify-center rounded text-muted-foreground hover:bg-[var(--design-editor-control-bg)] hover:text-destructive focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40"
790
+ >
791
+ <IconTrash className="size-3" />
792
+ </button>
793
+ </TooltipTrigger>
794
+ <TooltipContent>
795
+ {t("editPanel.shaders.removeShader")}
796
+ </TooltipContent>
797
+ </Tooltip>
798
+ <button
799
+ type="button"
800
+ aria-label={t("editPanel.shaders.closePanel")}
801
+ onClick={() => onBack(Boolean(nodeMount))}
802
+ 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"
803
+ >
804
+ <IconX className="size-3" />
805
+ </button>
806
+ </div>
807
+ <div className="space-y-2 border-t border-border/70 p-2">
808
+ <GlslShaderKnobs
809
+ def={activeDef}
810
+ values={values}
811
+ disabled={disabled || busy}
812
+ onValuesChange={handleValuesChange}
813
+ />
814
+ {!context.onEditCode ? (
815
+ <p className="px-0.5 !text-[10px] leading-snug text-muted-foreground">
816
+ {t("editPanel.shaders.codeHint")}
817
+ </p>
818
+ ) : null}
819
+ </div>
820
+ </div>
821
+ );
822
+ }
823
+
824
+ // ── Browse view ────────────────────────────────────────────────────────────
825
+ return (
826
+ <div className="flex flex-col">
827
+ {/* Header */}
828
+ <div className="flex h-6 items-center gap-1 px-3">
829
+ <span className="flex-1 truncate !text-[11px] font-semibold text-foreground">
830
+ {modeAttrTitle}
831
+ </span>
832
+ <button
833
+ type="button"
834
+ aria-label={t("editPanel.shaders.closePanel")}
835
+ onClick={() => onBack(Boolean(nodeMount))}
836
+ 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"
837
+ >
838
+ <IconX className="size-3" />
839
+ </button>
840
+ </div>
841
+
842
+ {/* Search */}
843
+ <div className="border-t border-border/70 px-3 py-2">
844
+ <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">
845
+ <IconSearch className="size-3 shrink-0 text-muted-foreground" />
846
+ <Input
847
+ value={search}
848
+ disabled={disabled}
849
+ placeholder={"Search" /* i18n-ignore */}
850
+ aria-label={"Search shaders" /* i18n-ignore */}
851
+ className="h-full min-w-0 flex-1 border-0 bg-transparent p-0 !text-[11px] shadow-none focus-visible:ring-0 md:!text-[11px]"
852
+ onChange={(event) => setSearch(event.target.value)}
853
+ />
854
+ {search && (
855
+ <button
856
+ type="button"
857
+ aria-label={"Clear search" /* i18n-ignore */}
858
+ onClick={() => setSearch("")}
859
+ className="flex size-4 items-center justify-center rounded text-muted-foreground hover:text-foreground"
860
+ >
861
+ <IconX className="size-3" />
862
+ </button>
863
+ )}
864
+ </div>
865
+ </div>
866
+
867
+ <div className="max-h-[380px] overflow-y-auto px-3 pb-3">
868
+ {/* Created by you */}
869
+ {!search && (
870
+ <section className="mb-3">
871
+ <p className="mb-1.5 text-[10px] font-semibold text-muted-foreground">
872
+ {t("editPanel.shaders.createdByYou")}
873
+ </p>
874
+ <div className="grid grid-cols-2 gap-2">
875
+ <button
876
+ type="button"
877
+ disabled={disabled}
878
+ onClick={createWithAi}
879
+ className={cn(
880
+ "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",
881
+ "hover:border-foreground/40 hover:text-foreground",
882
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
883
+ disabled && "pointer-events-none opacity-40",
884
+ )}
885
+ >
886
+ <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">
887
+ {t("editPanel.shaders.ai")}
888
+ </span>
889
+ <IconPlus className="size-4" />
890
+ <span className="text-[10px]">
891
+ {t("editPanel.shaders.createNew")}
892
+ </span>
893
+ </button>
894
+ {savedShaders.map((shader) => (
895
+ <Tooltip key={shader.id}>
896
+ <TooltipTrigger asChild>
897
+ <button
898
+ type="button"
899
+ disabled={disabled || busy}
900
+ aria-label={shader.name}
901
+ onClick={() => applySaved(shader)}
902
+ className={cn(
903
+ "group flex flex-col gap-1 text-left focus-visible:outline-none",
904
+ (disabled || busy) && "pointer-events-none opacity-40",
905
+ )}
906
+ >
907
+ <div className="flex aspect-[4/3] w-full items-center justify-center rounded-md border border-border/60 bg-[var(--design-editor-control-bg)] transition-colors group-hover:border-foreground/40">
908
+ <IconWaveSine className="size-4 text-muted-foreground" />
909
+ </div>
910
+ <span className="truncate text-[10px] text-muted-foreground group-hover:text-foreground">
911
+ {shader.name}
912
+ </span>
913
+ </button>
914
+ </TooltipTrigger>
915
+ <TooltipContent>
916
+ {t("editPanel.shaders.savedInThisDesign")}
917
+ </TooltipContent>
918
+ </Tooltip>
919
+ ))}
920
+ </div>
921
+ </section>
922
+ )}
923
+
924
+ {/* Saved shaders matching a search */}
925
+ {search && savedShaders.length > 0 && (
926
+ <section className="mb-3">
927
+ <p className="mb-1.5 text-[10px] font-semibold text-muted-foreground">
928
+ {t("editPanel.shaders.createdByYou")}
929
+ </p>
930
+ <div className="grid grid-cols-2 gap-2">
931
+ {savedShaders.map((shader) => (
932
+ <button
933
+ key={shader.id}
934
+ type="button"
935
+ disabled={disabled || busy}
936
+ aria-label={shader.name}
937
+ onClick={() => applySaved(shader)}
938
+ className="group flex flex-col gap-1 text-left focus-visible:outline-none"
939
+ >
940
+ <div className="flex aspect-[4/3] w-full items-center justify-center rounded-md border border-border/60 bg-[var(--design-editor-control-bg)] transition-colors group-hover:border-foreground/40">
941
+ <IconWaveSine className="size-4 text-muted-foreground" />
942
+ </div>
943
+ <span className="truncate text-[10px] text-muted-foreground group-hover:text-foreground">
944
+ {shader.name}
945
+ </span>
946
+ </button>
947
+ ))}
948
+ </div>
949
+ </section>
950
+ )}
951
+
952
+ {/* Presets */}
953
+ <section>
954
+ <p className="mb-1.5 text-[10px] font-semibold text-muted-foreground">
955
+ {t("editPanel.shaders.presets")}
956
+ </p>
957
+ {presets.length === 0 ? (
958
+ <p className="py-4 text-center !text-[11px] text-muted-foreground">
959
+ {t("editPanel.shaders.noMatches")}
960
+ </p>
961
+ ) : (
962
+ <div className="grid grid-cols-2 gap-2">
963
+ {presets.map((preset) => (
964
+ <PresetThumb
965
+ key={preset.name}
966
+ preset={preset}
967
+ disabled={disabled || busy}
968
+ onPick={applyPreset}
969
+ />
970
+ ))}
971
+ </div>
972
+ )}
973
+ </section>
974
+ </div>
975
+ </div>
976
+ );
977
+ }
978
+
979
+ // ─── Effects-section integration ─────────────────────────────────────────────
980
+
981
+ /**
982
+ * Shader rows + picker for EditPanel's Effects section. Rendered alongside
983
+ * the shadow/blur rows; the "Add effect ▸ Shader" menu item controls
984
+ * `pickerOpen`. One shader effect per element (it can coexist with a shader
985
+ * fill — different attribute namespaces).
986
+ */
987
+ export function GlslShaderEffectSection({
988
+ context,
989
+ pickerOpen,
990
+ onPickerOpenChange,
991
+ disabled = false,
992
+ }: {
993
+ context: GlslShaderPanelContext;
994
+ pickerOpen: boolean;
995
+ onPickerOpenChange: (open: boolean) => void;
996
+ disabled?: boolean;
997
+ }) {
998
+ const t = useT();
999
+ const screen = useScreenGlslShaders(context);
1000
+ const { persist, busy } = usePersistShaderEdit(context);
1001
+ const nodeId = context.nodeId;
1002
+
1003
+ const effectMount = useMemo(
1004
+ () =>
1005
+ screen.mounts.find(
1006
+ (mount) => mount.nodeId === nodeId && mount.mode === "effect",
1007
+ ),
1008
+ [screen.mounts, nodeId],
1009
+ );
1010
+ const effectDef = useMemo(
1011
+ () =>
1012
+ effectMount
1013
+ ? (screen.shaders.find(
1014
+ (shader) => shader.id === effectMount.shaderId,
1015
+ ) ?? null)
1016
+ : null,
1017
+ [screen.shaders, effectMount],
1018
+ );
1019
+
1020
+ const removeEffect = () => {
1021
+ if (!nodeId) return;
1022
+ void persist((html) => removeShaderFromNode(html, nodeId, "effect")).then(
1023
+ (ok) => {
1024
+ if (ok) void screen.refetch();
1025
+ },
1026
+ );
1027
+ };
1028
+
1029
+ if (!effectMount && !pickerOpen) return null;
1030
+
1031
+ return (
1032
+ <>
1033
+ {effectDef && effectMount ? (
1034
+ /* design effect row: [name + knobs popover trigger (flex-1)] [remove] */
1035
+ <Popover>
1036
+ <div className="group flex items-center gap-1.5">
1037
+ <PopoverTrigger asChild>
1038
+ <button
1039
+ type="button"
1040
+ className="flex h-6 min-w-0 flex-1 items-center gap-1.5 rounded-md border border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 text-left !text-[11px] hover:bg-[var(--design-editor-panel-raised-bg)]"
1041
+ >
1042
+ <IconWaveSine className="size-3.5 shrink-0 text-muted-foreground" />
1043
+ <span className="min-w-0 flex-1 truncate font-medium text-foreground">
1044
+ {effectDef.name}
1045
+ </span>
1046
+ <span className="shrink-0 text-muted-foreground">
1047
+ {t("editPanel.labels.shaderEffectType")}
1048
+ </span>
1049
+ </button>
1050
+ </PopoverTrigger>
1051
+ <Tooltip>
1052
+ <TooltipTrigger asChild>
1053
+ <button
1054
+ type="button"
1055
+ aria-label={t("editPanel.shaders.removeShaderEffect")}
1056
+ disabled={disabled || busy}
1057
+ onClick={removeEffect}
1058
+ className="flex size-5 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:text-destructive focus-visible:opacity-100 group-hover:opacity-100 disabled:pointer-events-none"
1059
+ >
1060
+ <IconTrash className="size-3" />
1061
+ </button>
1062
+ </TooltipTrigger>
1063
+ <TooltipContent>
1064
+ {t("editPanel.shaders.removeShaderEffect")}
1065
+ </TooltipContent>
1066
+ </Tooltip>
1067
+ </div>
1068
+ <PopoverContent
1069
+ side="left"
1070
+ align="start"
1071
+ sideOffset={8}
1072
+ className="z-[10000] w-[252px] p-0 shadow-xl"
1073
+ >
1074
+ <GlslShaderPanel
1075
+ mode="effect"
1076
+ context={context}
1077
+ disabled={disabled}
1078
+ onBack={() => onPickerOpenChange(false)}
1079
+ />
1080
+ </PopoverContent>
1081
+ </Popover>
1082
+ ) : null}
1083
+
1084
+ {pickerOpen && !effectMount ? (
1085
+ /* Inline browse panel while choosing the first shader effect. */
1086
+ <div className="rounded-md border border-[var(--design-editor-control-border)] bg-popover py-1.5">
1087
+ <GlslShaderPanel
1088
+ mode="effect"
1089
+ context={context}
1090
+ disabled={disabled}
1091
+ onBack={() => onPickerOpenChange(false)}
1092
+ />
1093
+ </div>
1094
+ ) : null}
1095
+ </>
1096
+ );
1097
+ }