@agent-native/core 0.92.6 → 0.92.8

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 (297) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +18 -0
  3. package/corpus/core/docs/content/a2a-protocol.mdx +12 -0
  4. package/corpus/core/docs/content/locales/ar-SA/a2a-protocol.mdx +12 -0
  5. package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +3 -3
  6. package/corpus/core/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -0
  7. package/corpus/core/docs/content/locales/de-DE/a2a-protocol.mdx +12 -0
  8. package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +3 -3
  9. package/corpus/core/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -0
  10. package/corpus/core/docs/content/locales/es-ES/a2a-protocol.mdx +12 -0
  11. package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +3 -3
  12. package/corpus/core/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -0
  13. package/corpus/core/docs/content/locales/fr-FR/a2a-protocol.mdx +12 -0
  14. package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +3 -3
  15. package/corpus/core/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -0
  16. package/corpus/core/docs/content/locales/hi-IN/a2a-protocol.mdx +12 -0
  17. package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +3 -3
  18. package/corpus/core/docs/content/locales/hi-IN/pure-agent-apps.mdx +4 -0
  19. package/corpus/core/docs/content/locales/ja-JP/a2a-protocol.mdx +12 -0
  20. package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +3 -3
  21. package/corpus/core/docs/content/locales/ja-JP/pure-agent-apps.mdx +4 -0
  22. package/corpus/core/docs/content/locales/ko-KR/a2a-protocol.mdx +12 -0
  23. package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +3 -3
  24. package/corpus/core/docs/content/locales/ko-KR/pure-agent-apps.mdx +4 -0
  25. package/corpus/core/docs/content/locales/pt-BR/a2a-protocol.mdx +12 -0
  26. package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +3 -3
  27. package/corpus/core/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -0
  28. package/corpus/core/docs/content/locales/zh-CN/a2a-protocol.mdx +12 -0
  29. package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +3 -3
  30. package/corpus/core/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -0
  31. package/corpus/core/docs/content/locales/zh-TW/a2a-protocol.mdx +12 -0
  32. package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +3 -3
  33. package/corpus/core/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -0
  34. package/corpus/core/docs/content/pr-visual-recap.mdx +3 -3
  35. package/corpus/core/docs/content/pure-agent-apps.mdx +22 -0
  36. package/corpus/core/package.json +1 -1
  37. package/corpus/core/src/agent/engine/builtin.ts +1 -1
  38. package/corpus/core/src/agent/model-config.ts +30 -30
  39. package/corpus/core/src/agent/production-agent.ts +129 -7
  40. package/corpus/core/src/cli/design-connect.ts +438 -83
  41. package/corpus/core/src/cli/recap.ts +2 -2
  42. package/corpus/core/src/cli/sync-builder-starter-manifest.ts +1 -1
  43. package/corpus/core/src/client/AgentPanel.tsx +8 -1
  44. package/corpus/core/src/client/agent-chat-adapter.ts +12 -7
  45. package/corpus/core/src/client/composer/TiptapComposer.tsx +1 -1
  46. package/corpus/core/src/client/index.ts +4 -0
  47. package/corpus/core/src/client/org/TeamPage.tsx +41 -26
  48. package/corpus/core/src/client/use-action.ts +113 -1
  49. package/corpus/core/src/db/client.ts +12 -2
  50. package/corpus/core/src/db/create-get-db.ts +4 -1
  51. package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +1 -1
  52. package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +1 -1
  53. package/corpus/core/src/server/transcribe-voice.ts +1 -1
  54. package/corpus/core/src/usage/store.ts +13 -4
  55. package/corpus/core/src/vite/client.ts +38 -1
  56. package/corpus/templates/analytics/AGENTS.md +3 -0
  57. package/corpus/templates/analytics/actions/get-data-program.ts +78 -0
  58. package/corpus/templates/analytics/actions/list-data-programs.ts +49 -0
  59. package/corpus/templates/analytics/app/global.css +11 -0
  60. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +163 -17
  61. package/corpus/templates/analytics/app/i18n/zh-TW.ts +2 -0
  62. package/corpus/templates/analytics/app/i18n-data.ts +20 -0
  63. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +96 -3
  64. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -1
  65. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +61 -4
  66. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +40 -2
  67. package/corpus/templates/analytics/changelog/2026-07-09-analytics-no-longer-forgets-successful-data-queries-when-a-l.md +6 -0
  68. package/corpus/templates/analytics/changelog/2026-07-09-dashboard-charts-keep-consistent-spacing-when-they-stack-ver.md +6 -0
  69. package/corpus/templates/analytics/changelog/2026-07-09-dashboard-charts-tables-and-extensions-can-be-selected-and-d.md +6 -0
  70. package/corpus/templates/analytics/changelog/2026-07-09-long-running-analytics-requests-now-recover-automatically-wh.md +6 -0
  71. package/corpus/templates/analytics/server/db/index.ts +6 -0
  72. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +1 -7
  73. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +102 -15
  74. package/corpus/templates/content/app/hooks/use-content-database.ts +52 -0
  75. package/corpus/templates/content/app/hooks/use-document-properties.ts +50 -6
  76. package/corpus/templates/content/app/i18n/zh-TW.ts +2 -0
  77. package/corpus/templates/content/app/i18n-data.ts +28 -0
  78. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-warnings-no-longer-flash-during-normal-synce.md +6 -0
  79. package/corpus/templates/content/changelog/2026-07-09-property-menus-and-delete-confirmations-now-open-visibly-and.md +6 -0
  80. package/corpus/templates/content/server/lib/notion-sync.ts +130 -28
  81. package/corpus/templates/design/actions/add-breakpoint.ts +48 -68
  82. package/corpus/templates/design/actions/add-localhost-screens.ts +496 -61
  83. package/corpus/templates/design/actions/apply-component-prop-edit.ts +2 -18
  84. package/corpus/templates/design/actions/apply-design-token-edit.ts +51 -82
  85. package/corpus/templates/design/actions/apply-tweaks.ts +63 -41
  86. package/corpus/templates/design/actions/capture-design-state.ts +2 -2
  87. package/corpus/templates/design/actions/connect-localhost.ts +58 -12
  88. package/corpus/templates/design/actions/create-component.ts +2 -18
  89. package/corpus/templates/design/actions/create-design-branch.ts +38 -18
  90. package/corpus/templates/design/actions/create-fusion-app.ts +15 -11
  91. package/corpus/templates/design/actions/delete-file.ts +19 -34
  92. package/corpus/templates/design/actions/deploy-design-preview.ts +59 -27
  93. package/corpus/templates/design/actions/deploy-fusion-app.ts +22 -15
  94. package/corpus/templates/design/actions/export-pdf.ts +2 -1
  95. package/corpus/templates/design/actions/export-zip.ts +9 -3
  96. package/corpus/templates/design/actions/generate-design.ts +107 -70
  97. package/corpus/templates/design/actions/get-component-details.ts +2 -18
  98. package/corpus/templates/design/actions/get-design-branch-diff.ts +2 -3
  99. package/corpus/templates/design/actions/get-design-surface-index.ts +2 -18
  100. package/corpus/templates/design/actions/get-design.ts +2 -1
  101. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +7 -1
  102. package/corpus/templates/design/actions/import-design-tokens.ts +114 -76
  103. package/corpus/templates/design/actions/index-components.ts +2 -18
  104. package/corpus/templates/design/actions/list-design-source-capabilities.ts +23 -28
  105. package/corpus/templates/design/actions/list-local-files.ts +9 -2
  106. package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +82 -98
  107. package/corpus/templates/design/actions/open-component-source.ts +2 -18
  108. package/corpus/templates/design/actions/open-visual-edit.ts +19 -1
  109. package/corpus/templates/design/actions/present-design-variants.ts +70 -70
  110. package/corpus/templates/design/actions/preview-component-prop-edit.ts +4 -14
  111. package/corpus/templates/design/actions/read-local-file.ts +9 -2
  112. package/corpus/templates/design/actions/remove-breakpoint.ts +40 -61
  113. package/corpus/templates/design/actions/rename-screen.ts +425 -0
  114. package/corpus/templates/design/actions/request-localhost-write-consent.ts +12 -2
  115. package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +9 -2
  116. package/corpus/templates/design/actions/sync-fusion-app.ts +56 -39
  117. package/corpus/templates/design/actions/update-design.ts +376 -61
  118. package/corpus/templates/design/actions/update-file.ts +455 -188
  119. package/corpus/templates/design/actions/write-local-file.ts +10 -2
  120. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +685 -608
  121. package/corpus/templates/design/app/components/design/LayersPanel.tsx +43 -15
  122. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +576 -2015
  123. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +62 -14
  124. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +263 -25
  125. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +34 -0
  126. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +40 -0
  127. package/corpus/templates/design/app/components/design/design-canvas/creation.ts +47 -0
  128. package/corpus/templates/design/app/components/design/design-canvas/element-payload.ts +27 -0
  129. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +62 -0
  130. package/corpus/templates/design/app/components/design/design-canvas/external-preview.ts +137 -0
  131. package/corpus/templates/design/app/components/design/design-canvas/file-drop.ts +10 -0
  132. package/corpus/templates/design/app/components/design/design-canvas/hit-test.ts +23 -0
  133. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +23 -0
  134. package/corpus/templates/design/app/components/design/design-canvas/iframe-pan.ts +177 -0
  135. package/corpus/templates/design/app/components/design/design-canvas/motion-types.ts +7 -0
  136. package/corpus/templates/design/app/components/design/design-canvas/pending-text-edit.ts +62 -0
  137. package/corpus/templates/design/app/components/design/index.ts +2 -2
  138. package/corpus/templates/design/app/components/design/multi-screen/canvas-tools.ts +100 -0
  139. package/corpus/templates/design/app/components/design/multi-screen/coordinate-transforms.ts +81 -0
  140. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +23 -13
  141. package/corpus/templates/design/app/components/design/multi-screen/culling.ts +238 -19
  142. package/corpus/templates/design/app/components/design/multi-screen/draft-primitives.ts +424 -0
  143. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +341 -0
  144. package/corpus/templates/design/app/components/design/multi-screen/iframe-targeting.ts +52 -0
  145. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +62 -0
  146. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +283 -0
  147. package/corpus/templates/design/app/components/design/multi-screen/screen-content-cache.ts +272 -0
  148. package/corpus/templates/design/app/components/design/multi-screen/types.ts +5 -1
  149. package/corpus/templates/design/app/components/design/multi-screen/wheel-gesture-state.ts +12 -0
  150. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +325 -84
  151. package/corpus/templates/design/app/hooks/use-navigation-state.ts +15 -1
  152. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +119 -47
  153. package/corpus/templates/design/app/i18n/zh-TW.ts +9 -0
  154. package/corpus/templates/design/app/i18n-data.ts +223 -0
  155. package/corpus/templates/design/app/lib/design-save-outbox.ts +379 -0
  156. package/corpus/templates/design/app/lib/png-clipboard.ts +113 -0
  157. package/corpus/templates/design/app/pages/design-editor/canvas-primitives.ts +229 -0
  158. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +918 -0
  159. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +43 -0
  160. package/corpus/templates/design/app/pages/design-editor/cross-screen-text-color.ts +336 -0
  161. package/corpus/templates/design/app/pages/design-editor/data-operations.ts +270 -0
  162. package/corpus/templates/design/app/pages/design-editor/dom-guards.ts +24 -0
  163. package/corpus/templates/design/app/pages/design-editor/dom-utils.ts +11 -0
  164. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +20 -0
  165. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +408 -0
  166. package/corpus/templates/design/app/pages/design-editor/export-capture.ts +115 -0
  167. package/corpus/templates/design/app/pages/design-editor/geometry-persistence.ts +93 -0
  168. package/corpus/templates/design/app/pages/design-editor/history.ts +169 -0
  169. package/corpus/templates/design/app/pages/design-editor/layout-operations.ts +185 -0
  170. package/corpus/templates/design/app/pages/design-editor/motion-state.ts +309 -0
  171. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +438 -0
  172. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +510 -0
  173. package/corpus/templates/design/app/pages/design-editor/portable-style.ts +115 -0
  174. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +337 -0
  175. package/corpus/templates/design/app/pages/design-editor/style-utils.ts +5 -0
  176. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +92 -0
  177. package/corpus/templates/design/app/pages/design-editor/tweak-save.ts +87 -0
  178. package/corpus/templates/design/app/pages/design-editor/types.ts +36 -0
  179. package/corpus/templates/design/app/routes/design.$id.tsx +11 -1
  180. package/corpus/templates/design/changelog/2026-07-09-design-edits-canvas-positions-tools-and-tab-context-now-pers.md +6 -0
  181. package/corpus/templates/design/changelog/2026-07-09-figma-style-shortcuts-drawing-annotations-and-cross-screen-d.md +6 -0
  182. package/corpus/templates/design/changelog/2026-07-09-hand-and-scale-tools-now-keep-their-active-toolbar-identity-.md +6 -0
  183. package/corpus/templates/design/changelog/2026-07-09-large-multi-screen-designs-now-stay-responsive-during-long-c.md +6 -0
  184. package/corpus/templates/design/changelog/2026-07-09-layer-drags-now-land-exactly-where-the-insertion-indicator-s.md +6 -0
  185. package/corpus/templates/design/server/db/index.ts +2 -52
  186. package/corpus/templates/design/server/db/schema.ts +16 -0
  187. package/corpus/templates/design/server/lib/design-data-access.ts +70 -0
  188. package/corpus/templates/design/server/lib/design-data-mutation.ts +249 -0
  189. package/corpus/templates/design/server/lib/fusion-screens.ts +89 -76
  190. package/corpus/templates/design/server/lib/import-design-files.ts +76 -35
  191. package/corpus/templates/design/server/lib/verify-write-grant.ts +7 -2
  192. package/corpus/templates/design/server/plugins/db.ts +19 -0
  193. package/corpus/templates/design/server/source-workspace.ts +12 -13
  194. package/corpus/templates/design/shared/resolve-tweaks.ts +15 -0
  195. package/corpus/templates/design/shared/screen-rename.ts +42 -0
  196. package/corpus/templates/design/shared/source-mode.ts +84 -4
  197. package/dist/agent/engine/builder-engine.d.ts +1 -1
  198. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  199. package/dist/agent/engine/builtin.js +1 -1
  200. package/dist/agent/engine/builtin.js.map +1 -1
  201. package/dist/agent/model-config.d.ts +9 -9
  202. package/dist/agent/model-config.d.ts.map +1 -1
  203. package/dist/agent/model-config.js +30 -30
  204. package/dist/agent/model-config.js.map +1 -1
  205. package/dist/agent/production-agent.d.ts +60 -1
  206. package/dist/agent/production-agent.d.ts.map +1 -1
  207. package/dist/agent/production-agent.js +106 -7
  208. package/dist/agent/production-agent.js.map +1 -1
  209. package/dist/cli/design-connect.d.ts +30 -1
  210. package/dist/cli/design-connect.d.ts.map +1 -1
  211. package/dist/cli/design-connect.js +359 -75
  212. package/dist/cli/design-connect.js.map +1 -1
  213. package/dist/cli/recap.js +2 -2
  214. package/dist/cli/recap.js.map +1 -1
  215. package/dist/cli/sync-builder-starter-manifest.js +1 -1
  216. package/dist/cli/sync-builder-starter-manifest.js.map +1 -1
  217. package/dist/client/AgentPanel.d.ts.map +1 -1
  218. package/dist/client/AgentPanel.js +6 -1
  219. package/dist/client/AgentPanel.js.map +1 -1
  220. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  221. package/dist/client/agent-chat-adapter.js +10 -5
  222. package/dist/client/agent-chat-adapter.js.map +1 -1
  223. package/dist/client/composer/TiptapComposer.js +1 -1
  224. package/dist/client/composer/TiptapComposer.js.map +1 -1
  225. package/dist/client/index.d.ts +1 -1
  226. package/dist/client/index.d.ts.map +1 -1
  227. package/dist/client/index.js +1 -1
  228. package/dist/client/index.js.map +1 -1
  229. package/dist/client/org/TeamPage.d.ts.map +1 -1
  230. package/dist/client/org/TeamPage.js +6 -5
  231. package/dist/client/org/TeamPage.js.map +1 -1
  232. package/dist/client/use-action.d.ts +32 -0
  233. package/dist/client/use-action.d.ts.map +1 -1
  234. package/dist/client/use-action.js +68 -1
  235. package/dist/client/use-action.js.map +1 -1
  236. package/dist/collab/awareness.d.ts +2 -2
  237. package/dist/collab/awareness.d.ts.map +1 -1
  238. package/dist/collab/routes.d.ts +1 -1
  239. package/dist/db/client.d.ts +3 -0
  240. package/dist/db/client.d.ts.map +1 -1
  241. package/dist/db/client.js +7 -2
  242. package/dist/db/client.js.map +1 -1
  243. package/dist/db/create-get-db.d.ts.map +1 -1
  244. package/dist/db/create-get-db.js +2 -2
  245. package/dist/db/create-get-db.js.map +1 -1
  246. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  247. package/dist/notifications/routes.d.ts +1 -1
  248. package/dist/observability/routes.d.ts +5 -5
  249. package/dist/progress/routes.d.ts +1 -1
  250. package/dist/resources/handlers.d.ts +1 -1
  251. package/dist/scripts/agent-engines/manage-agent-engine.js +1 -1
  252. package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
  253. package/dist/scripts/agent-engines/set-agent-engine.js +1 -1
  254. package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
  255. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  256. package/dist/server/transcribe-voice.js +1 -1
  257. package/dist/server/transcribe-voice.js.map +1 -1
  258. package/dist/usage/store.d.ts.map +1 -1
  259. package/dist/usage/store.js +13 -4
  260. package/dist/usage/store.js.map +1 -1
  261. package/dist/vite/client.d.ts.map +1 -1
  262. package/dist/vite/client.js +36 -1
  263. package/dist/vite/client.js.map +1 -1
  264. package/docs/content/a2a-protocol.mdx +12 -0
  265. package/docs/content/locales/ar-SA/a2a-protocol.mdx +12 -0
  266. package/docs/content/locales/ar-SA/pr-visual-recap.mdx +3 -3
  267. package/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -0
  268. package/docs/content/locales/de-DE/a2a-protocol.mdx +12 -0
  269. package/docs/content/locales/de-DE/pr-visual-recap.mdx +3 -3
  270. package/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -0
  271. package/docs/content/locales/es-ES/a2a-protocol.mdx +12 -0
  272. package/docs/content/locales/es-ES/pr-visual-recap.mdx +3 -3
  273. package/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -0
  274. package/docs/content/locales/fr-FR/a2a-protocol.mdx +12 -0
  275. package/docs/content/locales/fr-FR/pr-visual-recap.mdx +3 -3
  276. package/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -0
  277. package/docs/content/locales/hi-IN/a2a-protocol.mdx +12 -0
  278. package/docs/content/locales/hi-IN/pr-visual-recap.mdx +3 -3
  279. package/docs/content/locales/hi-IN/pure-agent-apps.mdx +4 -0
  280. package/docs/content/locales/ja-JP/a2a-protocol.mdx +12 -0
  281. package/docs/content/locales/ja-JP/pr-visual-recap.mdx +3 -3
  282. package/docs/content/locales/ja-JP/pure-agent-apps.mdx +4 -0
  283. package/docs/content/locales/ko-KR/a2a-protocol.mdx +12 -0
  284. package/docs/content/locales/ko-KR/pr-visual-recap.mdx +3 -3
  285. package/docs/content/locales/ko-KR/pure-agent-apps.mdx +4 -0
  286. package/docs/content/locales/pt-BR/a2a-protocol.mdx +12 -0
  287. package/docs/content/locales/pt-BR/pr-visual-recap.mdx +3 -3
  288. package/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -0
  289. package/docs/content/locales/zh-CN/a2a-protocol.mdx +12 -0
  290. package/docs/content/locales/zh-CN/pr-visual-recap.mdx +3 -3
  291. package/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -0
  292. package/docs/content/locales/zh-TW/a2a-protocol.mdx +12 -0
  293. package/docs/content/locales/zh-TW/pr-visual-recap.mdx +3 -3
  294. package/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -0
  295. package/docs/content/pr-visual-recap.mdx +3 -3
  296. package/docs/content/pure-agent-apps.mdx +22 -0
  297. package/package.json +2 -2
@@ -0,0 +1,337 @@
1
+ import type { CanvasFrameGeometryById } from "@shared/canvas-frames";
2
+ import type { CodeLayerProjection } from "@shared/code-layer";
3
+ import type { DesignSourceType } from "@shared/source-mode";
4
+
5
+ import type { ScreenGeometrySelection } from "@/components/design/EditPanel";
6
+ import { getInitialFrameGeometry } from "@/components/design/multi-screen/frame-geometry";
7
+ import type { ElementInfo } from "@/components/design/types";
8
+ import { prettyScreenName } from "@/lib/screen-names";
9
+
10
+ import type { DesignTool, EditorMode } from "./types";
11
+
12
+ // PF11: cache the FNV hash by content-string value. Two calls with an equal
13
+ // (===, i.e. SameValueZero) content string always hash to the same
14
+ // signature, so a plain value-keyed Map is a correct cache — no need for
15
+ // reference-identity tricks. Bounded LRU-ish eviction (drop oldest entry)
16
+ // keeps this from growing unboundedly across a long editing session with
17
+ // many distinct HTML revisions.
18
+ const CONTENT_SIGNATURE_CACHE_MAX = 200;
19
+ const contentSignatureCache = new Map<string, string>();
20
+ export function getContentSignature(content: string): string {
21
+ const cached = contentSignatureCache.get(content);
22
+ if (cached !== undefined) return cached;
23
+ let hash = 2166136261;
24
+ for (let index = 0; index < content.length; index += 1) {
25
+ hash ^= content.charCodeAt(index);
26
+ hash = Math.imul(hash, 16777619) >>> 0;
27
+ }
28
+ const signature = `${content.length}:${hash.toString(36)}`;
29
+ if (contentSignatureCache.size >= CONTENT_SIGNATURE_CACHE_MAX) {
30
+ const oldestKey = contentSignatureCache.keys().next().value;
31
+ if (oldestKey !== undefined) contentSignatureCache.delete(oldestKey);
32
+ }
33
+ contentSignatureCache.set(content, signature);
34
+ return signature;
35
+ }
36
+
37
+ export function getOverviewScreenRuntimeReplacementKey({
38
+ screenId,
39
+ updatedAt,
40
+ content,
41
+ }: {
42
+ screenId: string;
43
+ updatedAt?: string | null;
44
+ content: string;
45
+ }) {
46
+ return [screenId, updatedAt ?? "", getContentSignature(content)].join(":");
47
+ }
48
+
49
+ export function shouldUseOverviewRuntimeReplacement({
50
+ sourceType,
51
+ externalSnapshotHtml,
52
+ }: {
53
+ sourceType?: DesignSourceType | null;
54
+ externalSnapshotHtml?: string | null;
55
+ }) {
56
+ return sourceType === "inline" && !externalSnapshotHtml;
57
+ }
58
+
59
+ /**
60
+ * Only inline HTML screens may contribute a fresh client snapshot to the
61
+ * atomic screen-rename action. Localhost/fusion design_files rows intentionally
62
+ * store a route URL marker rather than the rendered preview HTML; sending that
63
+ * live snapshot as an override would silently convert the screen to inline
64
+ * source and break /visual-edit.
65
+ */
66
+ export function shouldIncludeScreenRenameContentOverride(args: {
67
+ fileType: string;
68
+ sourceType: DesignSourceType;
69
+ persistedContent: string;
70
+ freshContent: string;
71
+ }): boolean {
72
+ return (
73
+ args.fileType.toLowerCase() === "html" &&
74
+ args.sourceType === "inline" &&
75
+ args.freshContent !== args.persistedContent
76
+ );
77
+ }
78
+
79
+ export function dedupeStringIds(ids: string[]): string[] {
80
+ return Array.from(new Set(ids.filter(Boolean)));
81
+ }
82
+
83
+ export function sameStringIds(a: string[], b: string[]) {
84
+ return a.length === b.length && a.every((value, index) => value === b[index]);
85
+ }
86
+
87
+ export function isScreenRootElementInfo(info: ElementInfo | null | undefined) {
88
+ const tagName = info?.tagName?.toUpperCase();
89
+ return tagName === "BODY" || tagName === "HTML";
90
+ }
91
+
92
+ export function shouldMirrorSelectedElementToAgentChat(
93
+ info: ElementInfo | null | undefined,
94
+ ): info is ElementInfo {
95
+ if (!info || isScreenRootElementInfo(info)) return false;
96
+ const text = info.textContent?.replace(/\s+/g, " ").trim();
97
+ if (!text) return true;
98
+ const labelText = text.replace(/^[^A-Za-z0-9]+/, "").toLowerCase();
99
+ return !labelText.startsWith("nothing here yet");
100
+ }
101
+
102
+ export function shouldIgnoreOverviewLayerCreationEcho(args: {
103
+ pendingLayerId: string | null | undefined;
104
+ pendingScreenId: string | null | undefined;
105
+ screenId: string;
106
+ info?: ElementInfo | null;
107
+ event: "select" | "clear";
108
+ }) {
109
+ if (!args.pendingLayerId) return false;
110
+ if (args.pendingScreenId && args.pendingScreenId !== args.screenId) {
111
+ return false;
112
+ }
113
+ return args.event === "clear" || isScreenRootElementInfo(args.info);
114
+ }
115
+
116
+ export function getSelectedScreenIdsForEditorState(args: {
117
+ activeFileId: string | null | undefined;
118
+ overviewSelectedScreenIds: string[];
119
+ viewMode: "single" | "overview";
120
+ }) {
121
+ const { activeFileId, overviewSelectedScreenIds, viewMode } = args;
122
+ if (viewMode === "overview") {
123
+ return overviewSelectedScreenIds.length
124
+ ? overviewSelectedScreenIds
125
+ : activeFileId
126
+ ? [activeFileId]
127
+ : [];
128
+ }
129
+ return activeFileId ? [activeFileId] : [];
130
+ }
131
+
132
+ export function resolveAvailableActiveFileId(args: {
133
+ activeFileId: string | null | undefined;
134
+ availableFileIds: Iterable<string>;
135
+ defaultFileId: string | null | undefined;
136
+ }): string | null {
137
+ const availableFileIds = new Set(args.availableFileIds);
138
+ if (args.activeFileId && availableFileIds.has(args.activeFileId)) {
139
+ return args.activeFileId;
140
+ }
141
+ return args.defaultFileId && availableFileIds.has(args.defaultFileId)
142
+ ? args.defaultFileId
143
+ : null;
144
+ }
145
+
146
+ export function getSelectedScreenGeometryForInspector(args: {
147
+ selectedInspectorElementCount: number;
148
+ selectedScreenIds: string[];
149
+ overviewScreens: Array<{
150
+ id: string;
151
+ filename: string;
152
+ title?: string;
153
+ width?: number;
154
+ height?: number;
155
+ }>;
156
+ canvasFrameGeometryById: CanvasFrameGeometryById;
157
+ }): ScreenGeometrySelection | null {
158
+ if (args.selectedInspectorElementCount > 0) return null;
159
+ if (args.selectedScreenIds.length !== 1) return null;
160
+ const screenId = args.selectedScreenIds[0];
161
+ if (!screenId) return null;
162
+ const screenIndex = args.overviewScreens.findIndex(
163
+ (screen) => screen.id === screenId,
164
+ );
165
+ if (screenIndex < 0) return null;
166
+ const screen = args.overviewScreens[screenIndex];
167
+ if (!screen) return null;
168
+ const fallbackGeometry = getInitialFrameGeometry(screenIndex, {
169
+ width: screen.width ?? 1280,
170
+ height: screen.height ?? 2560,
171
+ });
172
+ const persistedGeometry = args.canvasFrameGeometryById[screenId] ?? {};
173
+ const geometry = {
174
+ ...fallbackGeometry,
175
+ ...persistedGeometry,
176
+ };
177
+ return {
178
+ id: screen.id,
179
+ title: screen.title ?? prettyScreenName(screen.filename),
180
+ x: geometry.x,
181
+ y: geometry.y,
182
+ width: geometry.width,
183
+ height: geometry.height,
184
+ };
185
+ }
186
+
187
+ function fileIdFromLayerSelectionId(
188
+ layerId: string,
189
+ fileIds: Set<string>,
190
+ ): string | null {
191
+ const normalized = layerId.startsWith("code:")
192
+ ? layerId.slice("code:".length)
193
+ : layerId;
194
+ return fileIds.has(normalized) ? normalized : null;
195
+ }
196
+
197
+ export function getOverviewScreenIdsFromLayerSelection(args: {
198
+ fileIds: string[];
199
+ layerIds: string[];
200
+ }) {
201
+ const fileIds = new Set(args.fileIds);
202
+ const seen = new Set<string>();
203
+ const selectedScreenIds: string[] = [];
204
+ args.layerIds.forEach((layerId) => {
205
+ const fileId = fileIdFromLayerSelectionId(layerId, fileIds);
206
+ if (!fileId || seen.has(fileId)) return;
207
+ seen.add(fileId);
208
+ selectedScreenIds.push(fileId);
209
+ });
210
+ return selectedScreenIds;
211
+ }
212
+
213
+ export function getOverviewEnterTarget(args: {
214
+ activeFileId: string | null | undefined;
215
+ overviewSelectedScreenIds: string[];
216
+ }) {
217
+ const { activeFileId, overviewSelectedScreenIds } = args;
218
+ if (overviewSelectedScreenIds.length === 0) {
219
+ return activeFileId ?? null;
220
+ }
221
+ if (activeFileId && overviewSelectedScreenIds.includes(activeFileId)) {
222
+ return activeFileId;
223
+ }
224
+ return (
225
+ overviewSelectedScreenIds[overviewSelectedScreenIds.length - 1] ?? null
226
+ );
227
+ }
228
+
229
+ export function getSidebarCodeLayerSelectionState(args: {
230
+ currentViewMode: "single" | "overview";
231
+ ownerFileId?: string | null;
232
+ overviewSelectedScreenIds: string[];
233
+ screenFileIds?: string[];
234
+ }) {
235
+ const {
236
+ currentViewMode,
237
+ ownerFileId,
238
+ overviewSelectedScreenIds,
239
+ screenFileIds,
240
+ } = args;
241
+ const ownerScreenId =
242
+ ownerFileId && (!screenFileIds || screenFileIds.includes(ownerFileId))
243
+ ? ownerFileId
244
+ : null;
245
+ return {
246
+ viewMode: currentViewMode,
247
+ overviewSelectedScreenIds:
248
+ currentViewMode === "overview" && ownerScreenId
249
+ ? [ownerScreenId]
250
+ : currentViewMode === "overview" && ownerFileId
251
+ ? []
252
+ : overviewSelectedScreenIds,
253
+ };
254
+ }
255
+
256
+ export function shouldLimitEditorChromeUntilContentReady(args: {
257
+ fileCount: number;
258
+ hasActiveCanvasContent: boolean;
259
+ generating: boolean;
260
+ pendingGenerationActive: boolean;
261
+ }) {
262
+ const {
263
+ fileCount,
264
+ generating,
265
+ hasActiveCanvasContent,
266
+ pendingGenerationActive,
267
+ } = args;
268
+ return (
269
+ (fileCount === 0 || !hasActiveCanvasContent) &&
270
+ (generating || pendingGenerationActive)
271
+ );
272
+ }
273
+
274
+ export function shouldEscapeToOverview(args: {
275
+ activeTool: DesignTool;
276
+ drawMode: boolean;
277
+ mode: EditorMode;
278
+ pinMode: boolean;
279
+ selectedElement: ElementInfo | null;
280
+ viewMode: "single" | "overview";
281
+ }) {
282
+ const { activeTool, drawMode, mode, pinMode, selectedElement, viewMode } =
283
+ args;
284
+ return (
285
+ viewMode === "single" &&
286
+ !selectedElement &&
287
+ !drawMode &&
288
+ !pinMode &&
289
+ mode === "edit" &&
290
+ activeTool === "move"
291
+ );
292
+ }
293
+
294
+ /**
295
+ * B5-1: an empty-canvas click (a marquee/hit-test that resolved to zero
296
+ * elements) must deselect ANY current selection kind — a selected
297
+ * overview screen frame AND a selected element inside a screen (set via the
298
+ * iframe bridge). `handleLayerMarqueeSelectionChange` already clears the
299
+ * host-side `selectedElement` state when nothing was hit and the gesture
300
+ * isn't additive; this helper decides whether it must ALSO signal the
301
+ * iframe/bridge overlays to clear their own selection highlight
302
+ * (`overviewClearSelectionRequest`) — otherwise a previously-selected
303
+ * in-screen element keeps showing its selection chrome inside the iframe
304
+ * even though the host's `selectedElement` is already null. True whenever
305
+ * the resolved hit-set is empty and the gesture isn't additive (an additive
306
+ * click/shift-click on empty space is a no-op, matching Escape and the
307
+ * MultiScreenCanvas-level `shouldClearSelectionOnEmptyCanvasClick`).
308
+ *
309
+ * Exported for unit testing.
310
+ */
311
+ export function shouldClearBridgeSelectionOnEmptyMarquee(args: {
312
+ resolvedCount: number;
313
+ additive: boolean | undefined;
314
+ }): boolean {
315
+ return args.resolvedCount === 0 && !args.additive;
316
+ }
317
+
318
+ /**
319
+ * Build the set of all node ids (both projection ids and data-agent-native-node-id
320
+ * attribute values) that exist in the given projection. Used by handleGroupSelection
321
+ * and handleUngroupSelection to filter selectedLayerIdsState to the active file's
322
+ * nodes before passing them to wrapNodes / unwrap, preventing cross-file stale ids
323
+ * from causing spurious "conflict" errors.
324
+ *
325
+ * Exported for unit testing.
326
+ */
327
+ export function buildActiveFileNodeIdSet(
328
+ projection: CodeLayerProjection,
329
+ ): Set<string> {
330
+ const ids = new Set<string>();
331
+ for (const n of projection.nodes) {
332
+ ids.add(n.id);
333
+ const attrId = n.dataAttributes["data-agent-native-node-id"];
334
+ if (attrId) ids.add(attrId);
335
+ }
336
+ return ids;
337
+ }
@@ -0,0 +1,5 @@
1
+ export function camelStyleProperty(property: string): string {
2
+ return property.replace(/-([a-z])/g, (_, letter: string) =>
3
+ letter.toUpperCase(),
4
+ );
5
+ }
@@ -0,0 +1,92 @@
1
+ import type { CreationTool } from "@/components/design/design-canvas/creation";
2
+
3
+ import type { DesignTool, EditorMode } from "./types";
4
+
5
+ export const MOVE_GROUP_TOOL_PRESENTATIONS = {
6
+ move: {
7
+ labelKey: "designEditor.tools.move",
8
+ shortcut: "V",
9
+ },
10
+ hand: {
11
+ labelKey: "designEditor.tools.hand",
12
+ shortcut: "H",
13
+ },
14
+ scale: {
15
+ labelKey: "designEditor.tools.scale",
16
+ shortcut: "K",
17
+ },
18
+ } as const;
19
+
20
+ export type MoveGroupTool = keyof typeof MOVE_GROUP_TOOL_PRESENTATIONS;
21
+
22
+ export function getMoveGroupToolPresentation(activeTool: DesignTool) {
23
+ const moveGroupTool: MoveGroupTool =
24
+ activeTool === "hand" || activeTool === "scale" ? activeTool : "move";
25
+ return {
26
+ tool: moveGroupTool,
27
+ ...MOVE_GROUP_TOOL_PRESENTATIONS[moveGroupTool],
28
+ };
29
+ }
30
+
31
+ const DESIGN_EDITOR_TOOLS = new Set<DesignTool>([
32
+ "move",
33
+ "frame",
34
+ "rect",
35
+ "line",
36
+ "arrow",
37
+ "ellipse",
38
+ "polygon",
39
+ "star",
40
+ "text",
41
+ "pen",
42
+ "hand",
43
+ "comment",
44
+ "draw",
45
+ "scale",
46
+ ]);
47
+
48
+ export function normalizeDesignTool(value: unknown): DesignTool | null {
49
+ return typeof value === "string" &&
50
+ DESIGN_EDITOR_TOOLS.has(value as DesignTool)
51
+ ? (value as DesignTool)
52
+ : null;
53
+ }
54
+
55
+ export function isSingleScreenAnnotationTool(tool: DesignTool): boolean {
56
+ return tool === "draw" || tool === "comment";
57
+ }
58
+
59
+ export function getDesignToolActivationState(tool: DesignTool): {
60
+ mode: EditorMode;
61
+ drawMode: boolean;
62
+ pinMode: boolean;
63
+ } {
64
+ if (tool === "draw") {
65
+ return { mode: "annotate", drawMode: true, pinMode: false };
66
+ }
67
+ if (tool === "comment") {
68
+ return { mode: "annotate", drawMode: false, pinMode: true };
69
+ }
70
+ return { mode: "edit", drawMode: false, pinMode: false };
71
+ }
72
+
73
+ export function getSingleScreenCreationTool(args: {
74
+ activeTool: DesignTool;
75
+ viewMode: "single" | "overview";
76
+ hasActiveFile: boolean;
77
+ }): CreationTool | null {
78
+ if (args.viewMode !== "single" || !args.hasActiveFile) return null;
79
+ switch (args.activeTool) {
80
+ case "rect":
81
+ return "rectangle";
82
+ case "ellipse":
83
+ case "line":
84
+ case "arrow":
85
+ case "text":
86
+ case "pen":
87
+ case "frame":
88
+ return args.activeTool;
89
+ default:
90
+ return null;
91
+ }
92
+ }
@@ -0,0 +1,87 @@
1
+ import type { TweakSelections } from "@shared/resolve-tweaks";
2
+
3
+ export interface PendingTweakSave {
4
+ selections: TweakSelections;
5
+ revision: number;
6
+ expectedSelectionsHash: string;
7
+ }
8
+
9
+ export function createQueuedTweakSave(
10
+ selections: TweakSelections,
11
+ revision: number,
12
+ confirmedSelectionsHash: string,
13
+ existingDebouncedSave: PendingTweakSave | null,
14
+ ): PendingTweakSave {
15
+ return {
16
+ selections,
17
+ revision,
18
+ // Multiple knob ticks inside one debounce window are one full-snapshot
19
+ // edit and must retain the base observed by the first tick.
20
+ expectedSelectionsHash:
21
+ existingDebouncedSave?.expectedSelectionsHash ?? confirmedSelectionsHash,
22
+ };
23
+ }
24
+
25
+ export function rebaseTweakSaveForSend(
26
+ pending: PendingTweakSave,
27
+ confirmedSelectionsHash: string,
28
+ ): PendingTweakSave {
29
+ return {
30
+ ...pending,
31
+ // Saves are serialized. Resolve the base only when this request reaches
32
+ // the front of the chain so it follows a verified predecessor success,
33
+ // but not a predecessor that failed.
34
+ expectedSelectionsHash: confirmedSelectionsHash,
35
+ };
36
+ }
37
+
38
+ export function retainLatestFailedTweakSave(
39
+ queued: PendingTweakSave | null,
40
+ failed: PendingTweakSave,
41
+ ): PendingTweakSave {
42
+ return queued && queued.revision > failed.revision ? queued : failed;
43
+ }
44
+
45
+ export function clearCompletedTweakSave(
46
+ queued: PendingTweakSave | null,
47
+ completedRevision: number,
48
+ ): PendingTweakSave | null {
49
+ return queued?.revision === completedRevision ? null : queued;
50
+ }
51
+
52
+ type TweakSaveKeepaliveAttempt =
53
+ | { accepted: true; completion: Promise<unknown> }
54
+ | { accepted: false; completion: null };
55
+
56
+ export async function sendJournaledTweakSaveKeepalive(options: {
57
+ journal: () => Promise<boolean>;
58
+ send: () => TweakSaveKeepaliveAttempt;
59
+ acknowledge: () => Promise<unknown>;
60
+ }): Promise<boolean> {
61
+ // Pagehide may freeze the document at any await boundary. Establish the
62
+ // durable retry entry first so either the keepalive finishes or a later
63
+ // editor session can replay the exact same operation.
64
+ if (!(await options.journal())) return false;
65
+ const attempt = options.send();
66
+ if (!attempt.accepted) return false;
67
+ await attempt.completion;
68
+ await options.acknowledge();
69
+ return true;
70
+ }
71
+
72
+ export type TweakSaveFailureKind =
73
+ | "conflict"
74
+ | "durable-retry"
75
+ | "tab-memory-only";
76
+
77
+ export function classifyTweakSaveFailure(
78
+ error: unknown,
79
+ journaled: boolean,
80
+ ): TweakSaveFailureKind {
81
+ const status =
82
+ error && typeof error === "object" && "status" in error
83
+ ? (error as { status?: unknown }).status
84
+ : undefined;
85
+ if (status === 409) return "conflict";
86
+ return journaled ? "durable-retry" : "tab-memory-only";
87
+ }
@@ -0,0 +1,36 @@
1
+ export type EditorMode = "annotate" | "edit" | "interact";
2
+
3
+ export type DesignTool =
4
+ | "move"
5
+ | "frame"
6
+ | "rect"
7
+ | "line"
8
+ | "arrow"
9
+ | "ellipse"
10
+ | "polygon"
11
+ | "star"
12
+ | "text"
13
+ | "pen"
14
+ | "hand"
15
+ | "comment"
16
+ | "draw"
17
+ | "scale";
18
+
19
+ export type ShapeTool =
20
+ | "rect"
21
+ | "line"
22
+ | "arrow"
23
+ | "ellipse"
24
+ | "polygon"
25
+ | "star";
26
+
27
+ export type DesignLeftPanel =
28
+ | "file"
29
+ | "agent"
30
+ | "assets"
31
+ | "tools"
32
+ | "tokens"
33
+ | "import"
34
+ | "code";
35
+
36
+ export const SHOW_DESIGN_CODE_LEFT_PANEL = true;
@@ -1,6 +1,16 @@
1
- export { default } from "../pages/DesignEditor";
2
1
  import { messagesByLocale } from "@/i18n-data";
3
2
 
3
+ import DesignEditorRoute from "../pages/DesignEditor";
4
+
5
+ /**
6
+ * Keep the route module itself as a React Fast Refresh boundary. A bare
7
+ * re-export does not register a local component, so an invalidated editor
8
+ * dependency can otherwise propagate into a full-page reload.
9
+ */
10
+ export default function DesignRoute() {
11
+ return <DesignEditorRoute />;
12
+ }
13
+
4
14
  export function meta() {
5
15
  return [{ title: messagesByLocale["en-US"].routeTitles.designEditor }];
6
16
  }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-09
4
+ ---
5
+
6
+ Design edits, canvas positions, tools, and tab context now persist reliably across navigation and reloads.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-09
4
+ ---
5
+
6
+ Figma-style shortcuts, drawing annotations, and cross-screen drag interactions are now faster and more reliable.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-09
4
+ ---
5
+
6
+ Hand and Scale tools now keep their active toolbar identity and show their keyboard shortcuts.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-09
4
+ ---
5
+
6
+ Large multi-screen designs now stay responsive during long canvas sessions without accumulating hidden previews.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-09
4
+ ---
5
+
6
+ Layer drags now land exactly where the insertion indicator shows, including multi-selection reorders.
@@ -5,61 +5,12 @@ import {
5
5
  DESIGN_AGENT_CONTEXT_ENDPOINT,
6
6
  DESIGN_AGENT_RESOURCE_KIND,
7
7
  } from "../../shared/agent-readable.js";
8
+ import { publicDesignAccessRole } from "../lib/design-data-access.js";
8
9
  import * as schema from "./schema.js";
9
10
 
10
11
  export const getDb = createGetDb(schema);
11
12
  export { schema };
12
13
 
13
- function isLocalDevRuntime(): boolean {
14
- return (
15
- process.env.NODE_ENV !== "production" &&
16
- !process.env.NETLIFY &&
17
- !process.env.VERCEL &&
18
- !process.env.AWS_LAMBDA_FUNCTION_NAME &&
19
- !process.env.CF_PAGES
20
- );
21
- }
22
-
23
- function isLoopbackHttpUrl(value: unknown): boolean {
24
- if (typeof value !== "string" || !value.trim()) return false;
25
- try {
26
- const url = new URL(value);
27
- if (url.protocol !== "http:" && url.protocol !== "https:") return false;
28
- const host = url.hostname.toLowerCase();
29
- return (
30
- host === "localhost" ||
31
- host === "::1" ||
32
- host === "[::1]" ||
33
- /^127(?:\.\d{1,3}){3}$/.test(host)
34
- );
35
- } catch {
36
- return false;
37
- }
38
- }
39
-
40
- function isLoopbackLocalhostDesign(design: any): boolean {
41
- if (!isLocalDevRuntime()) return false;
42
- let data: any;
43
- try {
44
- data = typeof design?.data === "string" ? JSON.parse(design.data) : null;
45
- } catch {
46
- return false;
47
- }
48
- if (!data || data.sourceMode !== "localhost") return false;
49
-
50
- const metadataEntries = Object.values(
51
- data.localhostScreens ?? data.screenMetadata ?? {},
52
- ) as any[];
53
- if (metadataEntries.length === 0) return false;
54
-
55
- return metadataEntries.every((entry) => {
56
- if (!entry || entry.sourceType !== "localhost") return false;
57
- const previewUrl = entry.previewUrl ?? entry.url;
58
- if (!isLoopbackHttpUrl(previewUrl)) return false;
59
- return !entry.bridgeUrl || isLoopbackHttpUrl(entry.bridgeUrl);
60
- });
61
- }
62
-
63
14
  registerShareableResource({
64
15
  type: "design",
65
16
  resourceTable: schema.designs,
@@ -72,8 +23,7 @@ registerShareableResource({
72
23
  getContextPath: () => DESIGN_AGENT_CONTEXT_ENDPOINT,
73
24
  },
74
25
  getDb,
75
- publicAccessRole: (design) =>
76
- isLoopbackLocalhostDesign(design) ? "editor" : "viewer",
26
+ publicAccessRole: publicDesignAccessRole,
77
27
  });
78
28
 
79
29
  registerShareableResource({