@agent-native/core 0.100.1 → 0.101.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +73 -0
  3. package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +48 -30
  4. package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +7 -0
  5. package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +48 -30
  6. package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +7 -0
  7. package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +48 -30
  8. package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +14 -0
  9. package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +48 -30
  10. package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +7 -0
  11. package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +48 -30
  12. package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +7 -0
  13. package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +48 -30
  14. package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +7 -0
  15. package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +48 -30
  16. package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +7 -0
  17. package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +48 -30
  18. package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +14 -0
  19. package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +48 -30
  20. package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +7 -0
  21. package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +48 -30
  22. package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +7 -0
  23. package/corpus/core/docs/content/pr-visual-recap.mdx +50 -32
  24. package/corpus/core/docs/content/template-plan.mdx +14 -0
  25. package/corpus/core/docs/content/toolkit-comments-review.mdx +16 -2
  26. package/corpus/core/package.json +4 -1
  27. package/corpus/core/src/a2a/index.ts +2 -1
  28. package/corpus/core/src/a2a/server.ts +34 -9
  29. package/corpus/core/src/agent/production-agent.ts +56 -10
  30. package/corpus/core/src/cli/create.ts +47 -19
  31. package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -3
  32. package/corpus/core/src/cli/recap.ts +3 -4932
  33. package/corpus/core/src/cli/skills-content/visual-plan-skill.ts +6 -4
  34. package/corpus/core/src/cli/skills-content/visual-recap-skill.ts +7 -1
  35. package/corpus/core/src/client/AgentPanel.tsx +19 -17
  36. package/corpus/core/src/client/AssistantChat.tsx +4 -0
  37. package/corpus/core/src/client/analytics.ts +8 -1
  38. package/corpus/core/src/client/chat/markdown-renderer.tsx +54 -8
  39. package/corpus/core/src/client/chat/message-components.tsx +8 -2
  40. package/corpus/core/src/client/clipboard.ts +28 -1
  41. package/corpus/core/src/client/composer/PromptComposer.tsx +4 -0
  42. package/corpus/core/src/client/composer/TiptapComposer.tsx +30 -3
  43. package/corpus/core/src/client/guided-questions.tsx +4 -0
  44. package/corpus/core/src/client/i18n.tsx +2 -1
  45. package/corpus/core/src/client/index.ts +4 -0
  46. package/corpus/core/src/client/review/ReviewCommentComposer.tsx +108 -0
  47. package/corpus/core/src/client/review/ReviewThreadPanel.tsx +482 -150
  48. package/corpus/core/src/client/review/index.ts +8 -0
  49. package/corpus/core/src/client/review/use-review.ts +35 -1
  50. package/corpus/core/src/client/use-agent-chat-context.ts +3 -2
  51. package/corpus/core/src/integrations/webhook-handler.ts +4 -2
  52. package/corpus/core/src/review/actions/create-review-comment.ts +2 -1
  53. package/corpus/core/src/review/actions/get-review-feedback.ts +6 -11
  54. package/corpus/core/src/review/actions/list-review-comments.ts +38 -4
  55. package/corpus/core/src/review/actions/reply-review-comment.ts +33 -22
  56. package/corpus/core/src/review/actions/resolve-review-thread.ts +29 -2
  57. package/corpus/core/src/review/actions/send-review-thread-to-agent.ts +62 -0
  58. package/corpus/core/src/review/identity.ts +101 -0
  59. package/corpus/core/src/review/index.ts +14 -0
  60. package/corpus/core/src/review/store.ts +315 -8
  61. package/corpus/core/src/review/types.ts +4 -0
  62. package/corpus/core/src/secrets/crypto.ts +98 -47
  63. package/corpus/core/src/secrets/index.ts +2 -1
  64. package/corpus/core/src/secrets/schema.ts +3 -0
  65. package/corpus/core/src/secrets/storage.ts +164 -31
  66. package/corpus/core/src/server/action-discovery.ts +4 -0
  67. package/corpus/core/src/server/action-routes.ts +125 -4
  68. package/corpus/core/src/server/agent-chat/plugin-options.ts +16 -0
  69. package/corpus/core/src/server/agent-chat-plugin.ts +1 -0
  70. package/corpus/core/src/server/index.ts +8 -0
  71. package/corpus/core/src/vite/action-types-plugin.ts +4 -0
  72. package/corpus/templates/analytics/changelog/2026-07-14-daily-dashboard-email-captures-authenticate-with-a-sessi.md +6 -0
  73. package/corpus/templates/analytics/server/lib/dashboard-report.ts +156 -3
  74. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +402 -142
  75. package/corpus/templates/assets/app/components/layout/Layout.tsx +4 -0
  76. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +3 -1
  77. package/corpus/templates/assets/app/i18n/zh-TW.ts +1 -0
  78. package/corpus/templates/assets/app/i18n-data.ts +1 -0
  79. package/corpus/templates/assets/app/routes/library.tsx +187 -48
  80. package/corpus/templates/assets/changelog/2026-07-13-assets-keeps-the-right-chat-sidebar-closed-when-the-full-pag.md +6 -0
  81. package/corpus/templates/assets/changelog/2026-07-13-merged-the-candidates-panel-into-the-library-as-a-drafts-tab.md +6 -0
  82. package/corpus/templates/chat/app/lib/agent-page.tsx +40 -0
  83. package/corpus/templates/chat/app/routes/agent.tsx +5 -3
  84. package/corpus/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
  85. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +5 -0
  86. package/corpus/templates/clips/AGENTS.md +4 -0
  87. package/corpus/templates/clips/actions/get-feature-flags.ts +30 -0
  88. package/corpus/templates/clips/actions/lib/transcript-preview.ts +48 -0
  89. package/corpus/templates/clips/actions/request-transcript.ts +41 -1
  90. package/corpus/templates/clips/actions/view-screen.ts +5 -9
  91. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  92. package/corpus/templates/clips/app/components/player/video-player.tsx +39 -1
  93. package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +6 -12
  94. package/corpus/templates/clips/app/hooks/use-mse-video-source.ts +146 -0
  95. package/corpus/templates/clips/app/lib/capture-install-options.ts +21 -0
  96. package/corpus/templates/clips/app/lib/fmp4.ts +271 -0
  97. package/corpus/templates/clips/app/lib/mse-video-loader.ts +585 -0
  98. package/corpus/templates/clips/app/routes/_app.dictate.tsx +0 -4
  99. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -13
  100. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +2 -37
  101. package/corpus/templates/clips/app/routes/download.tsx +3 -2
  102. package/corpus/templates/clips/changelog/2026-07-10-shared-clips-now-start-playing-instantly-instead-of-showing-.md +6 -0
  103. package/corpus/templates/clips/changelog/2026-07-14-clips-now-distinguishes-transcript-previews-from-incomplete-.md +6 -0
  104. package/corpus/templates/clips/changelog/2026-07-14-desktop-app-prompts-stay-hidden-after-you-download-the-insta.md +6 -0
  105. package/corpus/templates/clips/desktop/src/lib/recorder.ts +5 -0
  106. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +10 -0
  107. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +4 -0
  108. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +2518 -0
  109. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/live_upload.rs +353 -0
  110. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +558 -67
  111. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +11 -1
  112. package/corpus/templates/clips/desktop/src-tauri/src/remote_flags.rs +188 -0
  113. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +1 -1
  114. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +1 -0
  115. package/corpus/templates/clips/server/plugins/agent-chat.ts +5 -0
  116. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +5 -1
  117. package/corpus/templates/clips/shared/feature-flags.ts +41 -0
  118. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +3 -0
  119. package/corpus/templates/design/.agents/skills/design-review-feedback/SKILL.md +42 -0
  120. package/corpus/templates/design/AGENTS.md +6 -0
  121. package/corpus/templates/design/README.md +1 -0
  122. package/corpus/templates/design/actions/navigate.ts +4 -4
  123. package/corpus/templates/design/actions/view-screen.ts +116 -2
  124. package/corpus/templates/design/agent-native.app-skill.json +5 -0
  125. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +52 -59
  126. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +13 -9
  127. package/corpus/templates/design/app/components/design/EditPanel.tsx +79 -11
  128. package/corpus/templates/design/app/components/design/LayersPanel.tsx +78 -65
  129. package/corpus/templates/design/app/components/design/ReviewCommentsPanel.tsx +161 -0
  130. package/corpus/templates/design/app/components/design/ReviewStatusControl.tsx +96 -0
  131. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +166 -0
  132. package/corpus/templates/design/app/components/layout/Layout.tsx +1 -0
  133. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +1077 -0
  134. package/corpus/templates/design/app/components/visual-editor/index.ts +1 -0
  135. package/corpus/templates/design/app/components/visual-editor/review-canvas-state.ts +42 -0
  136. package/corpus/templates/design/app/hooks/use-design-systems.ts +2 -2
  137. package/corpus/templates/design/app/hooks/use-navigation-state.ts +9 -6
  138. package/corpus/templates/design/app/hooks/use-question-flow.ts +7 -1
  139. package/corpus/templates/design/app/i18n/ar-SA.ts +56 -1
  140. package/corpus/templates/design/app/i18n/de-DE.ts +57 -1
  141. package/corpus/templates/design/app/i18n/en-US.ts +56 -1
  142. package/corpus/templates/design/app/i18n/es-ES.ts +56 -1
  143. package/corpus/templates/design/app/i18n/fr-FR.ts +57 -1
  144. package/corpus/templates/design/app/i18n/hi-IN.ts +56 -1
  145. package/corpus/templates/design/app/i18n/ja-JP.ts +56 -1
  146. package/corpus/templates/design/app/i18n/ko-KR.ts +56 -1
  147. package/corpus/templates/design/app/i18n/pt-BR.ts +57 -1
  148. package/corpus/templates/design/app/i18n/zh-CN.ts +56 -1
  149. package/corpus/templates/design/app/i18n/zh-TW.ts +51 -0
  150. package/corpus/templates/design/app/pages/Present.tsx +172 -14
  151. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +23 -0
  152. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +10 -0
  153. package/corpus/templates/design/app/pages/present-review-state.ts +18 -0
  154. package/corpus/templates/design/app/public-routes.ts +8 -0
  155. package/corpus/templates/design/app/root.tsx +7 -6
  156. package/corpus/templates/design/changelog/2026-07-13-dragged-design-assets-now-appear-at-the-visible-drop-locatio.md +6 -0
  157. package/corpus/templates/design/changelog/2026-07-13-reviewers-can-pin-comments-on-shared-designs-and-apply-verif.md +6 -0
  158. package/corpus/templates/design/server/plugins/agent-chat.ts +10 -0
  159. package/corpus/templates/design/server/plugins/auth.ts +7 -5
  160. package/corpus/templates/design/server/plugins/review.ts +9 -0
  161. package/corpus/templates/design/shared/review-anchor.ts +83 -0
  162. package/corpus/templates/design/shared/review-summary.ts +25 -0
  163. package/corpus/templates/forms/.agents/skills/form-publishing/SKILL.md +2 -0
  164. package/corpus/templates/forms/AGENTS.md +4 -0
  165. package/corpus/templates/forms/actions/create-form.ts +2 -1
  166. package/corpus/templates/forms/actions/update-form.ts +5 -2
  167. package/corpus/templates/forms/app/i18n/ar-SA.ts +3 -0
  168. package/corpus/templates/forms/app/i18n/de-DE.ts +3 -0
  169. package/corpus/templates/forms/app/i18n/en-US.ts +3 -0
  170. package/corpus/templates/forms/app/i18n/es-ES.ts +3 -0
  171. package/corpus/templates/forms/app/i18n/fr-FR.ts +3 -0
  172. package/corpus/templates/forms/app/i18n/hi-IN.ts +3 -0
  173. package/corpus/templates/forms/app/i18n/ja-JP.ts +3 -0
  174. package/corpus/templates/forms/app/i18n/ko-KR.ts +3 -0
  175. package/corpus/templates/forms/app/i18n/pt-BR.ts +3 -0
  176. package/corpus/templates/forms/app/i18n/zh-CN.ts +3 -0
  177. package/corpus/templates/forms/app/i18n/zh-TW.ts +3 -0
  178. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +19 -0
  179. package/corpus/templates/forms/changelog/2026-07-14-fixed-the-ask-forms-loading-input-appearing-above-the-welcom.md +6 -0
  180. package/corpus/templates/forms/changelog/2026-07-14-form-social-previews-now-show-the-form-title-description-and.md +6 -0
  181. package/corpus/templates/forms/changelog/2026-07-14-forms-can-email-the-form-owner-when-new-responses-arrive.md +6 -0
  182. package/corpus/templates/forms/server/handlers/submissions.ts +17 -0
  183. package/corpus/templates/forms/server/lib/form-og-image.ts +64 -6
  184. package/corpus/templates/forms/server/lib/public-form-ssr.ts +6 -2
  185. package/corpus/templates/forms/server/lib/response-email.ts +65 -0
  186. package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -0
  187. package/corpus/templates/forms/server/routes/api/forms/og/[...slug]/og.png.get.ts +184 -0
  188. package/corpus/templates/forms/shared/types.ts +2 -0
  189. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +6 -4
  190. package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +7 -1
  191. package/dist/a2a/index.d.ts +2 -1
  192. package/dist/a2a/index.d.ts.map +1 -1
  193. package/dist/a2a/index.js +1 -1
  194. package/dist/a2a/index.js.map +1 -1
  195. package/dist/a2a/server.d.ts +25 -0
  196. package/dist/a2a/server.d.ts.map +1 -1
  197. package/dist/a2a/server.js +30 -6
  198. package/dist/a2a/server.js.map +1 -1
  199. package/dist/agent/production-agent.d.ts +2 -0
  200. package/dist/agent/production-agent.d.ts.map +1 -1
  201. package/dist/agent/production-agent.js +37 -12
  202. package/dist/agent/production-agent.js.map +1 -1
  203. package/dist/cli/create.d.ts +5 -3
  204. package/dist/cli/create.d.ts.map +1 -1
  205. package/dist/cli/create.js +44 -19
  206. package/dist/cli/create.js.map +1 -1
  207. package/dist/cli/pr-visual-recap-workflow.d.ts +2 -2
  208. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  209. package/dist/cli/pr-visual-recap-workflow.js +2 -2
  210. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  211. package/dist/cli/recap.d.ts +3 -562
  212. package/dist/cli/recap.d.ts.map +1 -1
  213. package/dist/cli/recap.js +3 -3875
  214. package/dist/cli/recap.js.map +1 -1
  215. package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
  216. package/dist/cli/skills-content/visual-plan-skill.d.ts.map +1 -1
  217. package/dist/cli/skills-content/visual-plan-skill.js +6 -4
  218. package/dist/cli/skills-content/visual-plan-skill.js.map +1 -1
  219. package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
  220. package/dist/cli/skills-content/visual-recap-skill.d.ts.map +1 -1
  221. package/dist/cli/skills-content/visual-recap-skill.js +7 -1
  222. package/dist/cli/skills-content/visual-recap-skill.js.map +1 -1
  223. package/dist/client/AgentPanel.d.ts.map +1 -1
  224. package/dist/client/AgentPanel.js +3 -3
  225. package/dist/client/AgentPanel.js.map +1 -1
  226. package/dist/client/AssistantChat.d.ts +2 -0
  227. package/dist/client/AssistantChat.d.ts.map +1 -1
  228. package/dist/client/AssistantChat.js +2 -2
  229. package/dist/client/AssistantChat.js.map +1 -1
  230. package/dist/client/analytics.d.ts +5 -0
  231. package/dist/client/analytics.d.ts.map +1 -1
  232. package/dist/client/analytics.js +3 -1
  233. package/dist/client/analytics.js.map +1 -1
  234. package/dist/client/chat/markdown-renderer.d.ts +1 -0
  235. package/dist/client/chat/markdown-renderer.d.ts.map +1 -1
  236. package/dist/client/chat/markdown-renderer.js +36 -1
  237. package/dist/client/chat/markdown-renderer.js.map +1 -1
  238. package/dist/client/chat/message-components.d.ts.map +1 -1
  239. package/dist/client/chat/message-components.js +5 -2
  240. package/dist/client/chat/message-components.js.map +1 -1
  241. package/dist/client/clipboard.d.ts +3 -1
  242. package/dist/client/clipboard.d.ts.map +1 -1
  243. package/dist/client/clipboard.js +24 -1
  244. package/dist/client/clipboard.js.map +1 -1
  245. package/dist/client/composer/PromptComposer.d.ts +2 -0
  246. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  247. package/dist/client/composer/PromptComposer.js +2 -2
  248. package/dist/client/composer/PromptComposer.js.map +1 -1
  249. package/dist/client/composer/TiptapComposer.d.ts +3 -1
  250. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  251. package/dist/client/composer/TiptapComposer.js +20 -5
  252. package/dist/client/composer/TiptapComposer.js.map +1 -1
  253. package/dist/client/guided-questions.d.ts +3 -1
  254. package/dist/client/guided-questions.d.ts.map +1 -1
  255. package/dist/client/guided-questions.js +2 -1
  256. package/dist/client/guided-questions.js.map +1 -1
  257. package/dist/client/i18n.d.ts.map +1 -1
  258. package/dist/client/i18n.js +3 -1
  259. package/dist/client/i18n.js.map +1 -1
  260. package/dist/client/index.d.ts +1 -1
  261. package/dist/client/index.d.ts.map +1 -1
  262. package/dist/client/index.js +1 -1
  263. package/dist/client/index.js.map +1 -1
  264. package/dist/client/review/ReviewCommentComposer.d.ts +18 -0
  265. package/dist/client/review/ReviewCommentComposer.d.ts.map +1 -0
  266. package/dist/client/review/ReviewCommentComposer.js +30 -0
  267. package/dist/client/review/ReviewCommentComposer.js.map +1 -0
  268. package/dist/client/review/ReviewThreadPanel.d.ts +28 -1
  269. package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -1
  270. package/dist/client/review/ReviewThreadPanel.js +151 -39
  271. package/dist/client/review/ReviewThreadPanel.js.map +1 -1
  272. package/dist/client/review/index.d.ts +3 -2
  273. package/dist/client/review/index.d.ts.map +1 -1
  274. package/dist/client/review/index.js +2 -1
  275. package/dist/client/review/index.js.map +1 -1
  276. package/dist/client/review/use-review.d.ts +23 -0
  277. package/dist/client/review/use-review.d.ts.map +1 -1
  278. package/dist/client/review/use-review.js +3 -0
  279. package/dist/client/review/use-review.js.map +1 -1
  280. package/dist/client/use-agent-chat-context.d.ts +1 -1
  281. package/dist/client/use-agent-chat-context.d.ts.map +1 -1
  282. package/dist/client/use-agent-chat-context.js +4 -2
  283. package/dist/client/use-agent-chat-context.js.map +1 -1
  284. package/dist/collab/awareness.d.ts +2 -2
  285. package/dist/collab/awareness.d.ts.map +1 -1
  286. package/dist/collab/struct-routes.d.ts +1 -1
  287. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  288. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  289. package/dist/integrations/webhook-handler.js +3 -2
  290. package/dist/integrations/webhook-handler.js.map +1 -1
  291. package/dist/notifications/routes.d.ts +3 -3
  292. package/dist/observability/routes.d.ts +5 -5
  293. package/dist/progress/routes.d.ts +1 -1
  294. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  295. package/dist/resources/handlers.d.ts +2 -2
  296. package/dist/review/actions/create-review-comment.js +2 -1
  297. package/dist/review/actions/create-review-comment.js.map +1 -1
  298. package/dist/review/actions/get-review-feedback.js +6 -11
  299. package/dist/review/actions/get-review-feedback.js.map +1 -1
  300. package/dist/review/actions/list-review-comments.d.ts +1 -0
  301. package/dist/review/actions/list-review-comments.js +25 -4
  302. package/dist/review/actions/list-review-comments.js.map +1 -1
  303. package/dist/review/actions/reply-review-comment.js +12 -5
  304. package/dist/review/actions/reply-review-comment.js.map +1 -1
  305. package/dist/review/actions/resolve-review-thread.d.ts +3 -0
  306. package/dist/review/actions/resolve-review-thread.js +20 -3
  307. package/dist/review/actions/resolve-review-thread.js.map +1 -1
  308. package/dist/review/actions/send-review-thread-to-agent.d.ts +17 -0
  309. package/dist/review/actions/send-review-thread-to-agent.d.ts.map +1 -0
  310. package/dist/review/actions/send-review-thread-to-agent.js +49 -0
  311. package/dist/review/actions/send-review-thread-to-agent.js.map +1 -0
  312. package/dist/review/identity.d.ts +6 -0
  313. package/dist/review/identity.d.ts.map +1 -0
  314. package/dist/review/identity.js +68 -0
  315. package/dist/review/identity.js.map +1 -0
  316. package/dist/review/index.d.ts +3 -1
  317. package/dist/review/index.d.ts.map +1 -1
  318. package/dist/review/index.js +2 -1
  319. package/dist/review/index.js.map +1 -1
  320. package/dist/review/store.d.ts +36 -0
  321. package/dist/review/store.d.ts.map +1 -1
  322. package/dist/review/store.js +223 -8
  323. package/dist/review/store.js.map +1 -1
  324. package/dist/review/types.d.ts +4 -0
  325. package/dist/review/types.d.ts.map +1 -1
  326. package/dist/review/types.js.map +1 -1
  327. package/dist/secrets/crypto.d.ts +35 -13
  328. package/dist/secrets/crypto.d.ts.map +1 -1
  329. package/dist/secrets/crypto.js +78 -41
  330. package/dist/secrets/crypto.js.map +1 -1
  331. package/dist/secrets/index.d.ts.map +1 -1
  332. package/dist/secrets/index.js +2 -1
  333. package/dist/secrets/index.js.map +1 -1
  334. package/dist/secrets/routes.d.ts +9 -9
  335. package/dist/secrets/schema.d.ts +20 -1
  336. package/dist/secrets/schema.d.ts.map +1 -1
  337. package/dist/secrets/schema.js +3 -0
  338. package/dist/secrets/schema.js.map +1 -1
  339. package/dist/secrets/storage.d.ts +7 -7
  340. package/dist/secrets/storage.d.ts.map +1 -1
  341. package/dist/secrets/storage.js +121 -30
  342. package/dist/secrets/storage.js.map +1 -1
  343. package/dist/server/action-discovery.d.ts.map +1 -1
  344. package/dist/server/action-discovery.js +4 -0
  345. package/dist/server/action-discovery.js.map +1 -1
  346. package/dist/server/action-routes.d.ts +45 -0
  347. package/dist/server/action-routes.d.ts.map +1 -1
  348. package/dist/server/action-routes.js +71 -5
  349. package/dist/server/action-routes.js.map +1 -1
  350. package/dist/server/agent-chat/plugin-options.d.ts +16 -0
  351. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
  352. package/dist/server/agent-chat/plugin-options.js.map +1 -1
  353. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  354. package/dist/server/agent-chat-plugin.js +1 -0
  355. package/dist/server/agent-chat-plugin.js.map +1 -1
  356. package/dist/server/index.d.ts +3 -1
  357. package/dist/server/index.d.ts.map +1 -1
  358. package/dist/server/index.js +2 -0
  359. package/dist/server/index.js.map +1 -1
  360. package/dist/vite/action-types-plugin.d.ts.map +1 -1
  361. package/dist/vite/action-types-plugin.js +4 -0
  362. package/dist/vite/action-types-plugin.js.map +1 -1
  363. package/docs/content/locales/ar-SA/pr-visual-recap.mdx +48 -30
  364. package/docs/content/locales/ar-SA/template-plan.mdx +7 -0
  365. package/docs/content/locales/de-DE/pr-visual-recap.mdx +48 -30
  366. package/docs/content/locales/de-DE/template-plan.mdx +7 -0
  367. package/docs/content/locales/es-ES/pr-visual-recap.mdx +48 -30
  368. package/docs/content/locales/es-ES/template-plan.mdx +14 -0
  369. package/docs/content/locales/fr-FR/pr-visual-recap.mdx +48 -30
  370. package/docs/content/locales/fr-FR/template-plan.mdx +7 -0
  371. package/docs/content/locales/hi-IN/pr-visual-recap.mdx +48 -30
  372. package/docs/content/locales/hi-IN/template-plan.mdx +7 -0
  373. package/docs/content/locales/ja-JP/pr-visual-recap.mdx +48 -30
  374. package/docs/content/locales/ja-JP/template-plan.mdx +7 -0
  375. package/docs/content/locales/ko-KR/pr-visual-recap.mdx +48 -30
  376. package/docs/content/locales/ko-KR/template-plan.mdx +7 -0
  377. package/docs/content/locales/pt-BR/pr-visual-recap.mdx +48 -30
  378. package/docs/content/locales/pt-BR/template-plan.mdx +14 -0
  379. package/docs/content/locales/zh-CN/pr-visual-recap.mdx +48 -30
  380. package/docs/content/locales/zh-CN/template-plan.mdx +7 -0
  381. package/docs/content/locales/zh-TW/pr-visual-recap.mdx +48 -30
  382. package/docs/content/locales/zh-TW/template-plan.mdx +7 -0
  383. package/docs/content/pr-visual-recap.mdx +50 -32
  384. package/docs/content/template-plan.mdx +14 -0
  385. package/docs/content/toolkit-comments-review.mdx +16 -2
  386. package/package.json +4 -1
  387. package/corpus/templates/forms/server/routes/api/forms/og/[slug]/og.png.get.ts +0 -45
@@ -1,13 +1,22 @@
1
1
  import {
2
2
  readClientAppState,
3
- sendToAgentChat,
3
+ setAgentChatContextItem,
4
4
  useActionMutation,
5
5
  useActionQuery,
6
6
  useT,
7
7
  } from "@agent-native/core/client";
8
- import { IconMessageCircle, IconPhoto, IconTrash } from "@tabler/icons-react";
8
+ import {
9
+ IconChevronLeft,
10
+ IconChevronRight,
11
+ IconDeviceFloppy,
12
+ IconMessageCircle,
13
+ IconPhoto,
14
+ IconTrash,
15
+ IconX,
16
+ } from "@tabler/icons-react";
9
17
  import { useQuery, useQueryClient } from "@tanstack/react-query";
10
18
  import { useEffect, useMemo, useRef, useState } from "react";
19
+ import { Link } from "react-router";
11
20
  import { toast } from "sonner";
12
21
 
13
22
  import {
@@ -22,7 +31,20 @@ import {
22
31
  } from "@/components/ui/alert-dialog";
23
32
  import { Badge } from "@/components/ui/badge";
24
33
  import { Button } from "@/components/ui/button";
34
+ import {
35
+ Dialog,
36
+ DialogClose,
37
+ DialogContent,
38
+ DialogDescription,
39
+ DialogTitle,
40
+ } from "@/components/ui/dialog";
25
41
  import { Spinner } from "@/components/ui/spinner";
42
+ import {
43
+ Tooltip,
44
+ TooltipContent,
45
+ TooltipProvider,
46
+ TooltipTrigger,
47
+ } from "@/components/ui/tooltip";
26
48
  import { assetPreviewSources } from "@/lib/asset-preview-sources";
27
49
 
28
50
  import type {
@@ -34,6 +56,8 @@ type LibraryListResult = {
34
56
  libraries?: ImageLibrarySummary[];
35
57
  };
36
58
 
59
+ type VariantSlot = AssetVariantState["slots"][number];
60
+
37
61
  function variantStateKey(threadId: string | null) {
38
62
  return threadId ? `asset-variants:${threadId}` : "asset-variants";
39
63
  }
@@ -46,15 +70,13 @@ function variantStateKey(threadId: string | null) {
46
70
  // its finished image arrives.
47
71
  const STALE_PENDING_RUN_MS = 10 * 60 * 1000;
48
72
 
49
- function slotTime(slot: AssetVariantState["slots"][number]): number {
73
+ function slotTime(slot: VariantSlot): number {
50
74
  const raw = slot.createdAt ?? slot.updatedAt ?? "";
51
75
  const time = Date.parse(raw);
52
76
  return Number.isNaN(time) ? 0 : time;
53
77
  }
54
78
 
55
- function stalePendingRunId(
56
- slot: AssetVariantState["slots"][number],
57
- ): string | null {
79
+ function stalePendingRunId(slot: VariantSlot): string | null {
58
80
  if (slot.status !== "pending") return null;
59
81
  if (!slot.runId) return null;
60
82
  const timestamp = slotTime(slot);
@@ -66,6 +88,7 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
66
88
  const t = useT();
67
89
  const queryClient = useQueryClient();
68
90
  const [clearAllOpen, setClearAllOpen] = useState(false);
91
+ const [previewSlotId, setPreviewSlotId] = useState<string | null>(null);
69
92
  const stateKey = variantStateKey(threadId);
70
93
  const stateQueryKey = useMemo(() => ["app-state", stateKey], [stateKey]);
71
94
  const { data: variants } = useQuery({
@@ -137,6 +160,11 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
137
160
  );
138
161
  }, [belongsToThread, queryClient, refreshGeneration, slots, stateQueryKey]);
139
162
 
163
+ const previewSlot = useMemo(
164
+ () => slots.find((slot) => slot.slotId === previewSlotId) ?? null,
165
+ [previewSlotId, slots],
166
+ );
167
+
140
168
  if (!belongsToThread || !variants) return null;
141
169
  if (slots.length === 0) return null;
142
170
 
@@ -169,6 +197,65 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
169
197
  );
170
198
  }
171
199
 
200
+ function saveSlot(slot: VariantSlot, onDone?: () => void) {
201
+ if (!slot.assetId && !slot.slotId) return;
202
+ saveGenerated.mutate(
203
+ {
204
+ ...(slot.assetId ? { assetId: slot.assetId } : {}),
205
+ ...(slot.slotId ? { slotId: slot.slotId } : {}),
206
+ threadId,
207
+ },
208
+ {
209
+ onSuccess: () => {
210
+ toast.success(t("library.savedGeneratedAsset"));
211
+ void queryClient.invalidateQueries({ queryKey: stateQueryKey });
212
+ void queryClient.invalidateQueries({
213
+ queryKey: ["action", "get-library"],
214
+ });
215
+ onDone?.();
216
+ },
217
+ onError: (error) =>
218
+ toast.error(error.message || t("library.couldNotSaveCandidate")),
219
+ },
220
+ );
221
+ }
222
+
223
+ function dismissSlotById(slot: VariantSlot, onDone?: () => void) {
224
+ dismissSlot.mutate(
225
+ { slotId: slot.slotId, threadId },
226
+ {
227
+ onSuccess: () => {
228
+ void queryClient.invalidateQueries({ queryKey: stateQueryKey });
229
+ onDone?.();
230
+ },
231
+ onError: (error) =>
232
+ toast.error(error.message || t("library.couldNotDismissCandidate")),
233
+ },
234
+ );
235
+ }
236
+
237
+ function refineSlot(slot: VariantSlot, variantNumber: number) {
238
+ if (!slot.assetId) return;
239
+ const variantLabel = t("library.variantWithNumber", {
240
+ number: variantNumber,
241
+ });
242
+ setAgentChatContextItem({
243
+ key: "refine-asset:" + slot.assetId,
244
+ title: t("library.refine") + " : " + variantLabel,
245
+ context: [
246
+ "## Assets candidate",
247
+ `Asset ID: ${slot.assetId}`,
248
+ `Run ID: ${slot.runId ?? "unknown"}`,
249
+ `Prompt: ${variants?.prompt ?? ""}`,
250
+ libraryTitle ? `Brand kit: ${libraryTitle}` : "",
251
+ "Use refine-image with assetId when the user describes the change.",
252
+ ]
253
+ .filter(Boolean)
254
+ .join("\n"),
255
+ openSidebar: true,
256
+ });
257
+ }
258
+
172
259
  return (
173
260
  <>
174
261
  <AlertDialog open={clearAllOpen} onOpenChange={setClearAllOpen}>
@@ -206,6 +293,27 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
206
293
  </AlertDialogContent>
207
294
  </AlertDialog>
208
295
 
296
+ <GenerationPreviewDialog
297
+ slot={previewSlot}
298
+ slots={slots}
299
+ prompt={variants.prompt}
300
+ libraryTitle={libraryTitle}
301
+ isSaving={saveGenerated.isPending}
302
+ isDismissing={dismissSlot.isPending}
303
+ onOpenChange={(open) => {
304
+ if (!open) setPreviewSlotId(null);
305
+ }}
306
+ onSelect={setPreviewSlotId}
307
+ onSave={(slot) => saveSlot(slot, () => setPreviewSlotId(null))}
308
+ onRefine={(slot, variantNumber) => {
309
+ refineSlot(slot, variantNumber);
310
+ setPreviewSlotId(null);
311
+ }}
312
+ onDismiss={(slot) =>
313
+ dismissSlotById(slot, () => setPreviewSlotId(null))
314
+ }
315
+ />
316
+
209
317
  <section className="mx-auto mb-4 w-full max-w-[760px] px-4">
210
318
  <div className="overflow-hidden rounded-xl border border-border bg-card shadow-sm">
211
319
  <div className="flex items-start justify-between gap-3 border-b border-border/80 px-3 py-3">
@@ -244,64 +352,18 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
244
352
  </div>
245
353
 
246
354
  <div className="max-h-[min(640px,52vh)] overflow-y-auto p-3">
247
- <div
248
- className={
249
- slots.length === 1
250
- ? "grid grid-cols-1 gap-3"
251
- : "grid grid-cols-1 gap-3 sm:grid-cols-2"
252
- }
253
- >
355
+ <div className="assets-library-grid grid grid-cols-2 gap-3 sm:grid-cols-3">
254
356
  {slots.map((slot, index) => (
255
- <GenerationResultItem
357
+ <GenerationDraftCard
256
358
  key={slot.slotId}
257
359
  slot={slot}
258
- index={index}
259
- prompt={variants.prompt}
260
- libraryTitle={libraryTitle}
360
+ variantNumber={index + 1}
261
361
  isSaving={saveGenerated.isPending}
262
362
  isDismissing={dismissSlot.isPending}
263
- onSave={() => {
264
- if (!slot.assetId && !slot.slotId) return;
265
- saveGenerated.mutate(
266
- {
267
- ...(slot.assetId ? { assetId: slot.assetId } : {}),
268
- ...(slot.slotId ? { slotId: slot.slotId } : {}),
269
- threadId,
270
- },
271
- {
272
- onSuccess: () => {
273
- toast.success(t("library.savedGeneratedAsset"));
274
- void queryClient.invalidateQueries({
275
- queryKey: stateQueryKey,
276
- });
277
- void queryClient.invalidateQueries({
278
- queryKey: ["action", "get-library"],
279
- });
280
- },
281
- onError: (error) =>
282
- toast.error(
283
- error.message || t("library.couldNotSaveCandidate"),
284
- ),
285
- },
286
- );
287
- }}
288
- onDismiss={() => {
289
- dismissSlot.mutate(
290
- { slotId: slot.slotId, threadId },
291
- {
292
- onSuccess: () => {
293
- void queryClient.invalidateQueries({
294
- queryKey: stateQueryKey,
295
- });
296
- },
297
- onError: (error) =>
298
- toast.error(
299
- error.message ||
300
- t("library.couldNotDismissCandidate"),
301
- ),
302
- },
303
- );
304
- }}
363
+ onPreview={() => setPreviewSlotId(slot.slotId)}
364
+ onSave={() => saveSlot(slot)}
365
+ onRefine={() => refineSlot(slot, index + 1)}
366
+ onDismiss={() => dismissSlotById(slot)}
305
367
  />
306
368
  ))}
307
369
  </div>
@@ -312,117 +374,315 @@ export function GenerationResults({ threadId }: { threadId: string | null }) {
312
374
  );
313
375
  }
314
376
 
315
- function GenerationResultItem({
377
+ function GenerationDraftCard({
316
378
  slot,
317
- index,
318
- prompt,
319
- libraryTitle,
379
+ variantNumber,
320
380
  isSaving,
321
381
  isDismissing,
382
+ onPreview,
322
383
  onSave,
384
+ onRefine,
323
385
  onDismiss,
324
386
  }: {
325
- slot: AssetVariantState["slots"][number];
326
- index: number;
327
- prompt: string;
328
- libraryTitle: string | null;
387
+ slot: VariantSlot;
388
+ variantNumber: number;
329
389
  isSaving: boolean;
330
390
  isDismissing: boolean;
391
+ onPreview: () => void;
331
392
  onSave: () => void;
393
+ onRefine: () => void;
332
394
  onDismiss: () => void;
333
395
  }) {
334
- const ready = slot.status === "ready" && Boolean(slot.assetId);
335
396
  const t = useT();
397
+ const ready = slot.status === "ready" && Boolean(slot.assetId);
398
+ const variantLabel = t("library.variantWithNumber", {
399
+ number: variantNumber,
400
+ });
336
401
  return (
337
- <article className="overflow-hidden rounded-lg border border-border/80 bg-background/70">
338
- <div className="relative aspect-[16/10] overflow-hidden bg-muted/40">
339
- <GenerationSlotPreview slot={slot} />
340
- <div className="absolute left-2 top-2 flex items-center gap-1 rounded-full border border-border/70 bg-background/90 px-2 py-1 text-[11px] font-medium shadow-sm">
341
- {slot.status === "pending" ? <Spinner className="h-3 w-3" /> : null}
342
- <span>
343
- {slot.status === "pending"
344
- ? t("library.generating")
345
- : slot.status === "ready"
346
- ? t("library.candidateWithNumber", { number: index + 1 })
347
- : t("library.failed")}
348
- </span>
349
- </div>
350
- </div>
351
- <div className="space-y-2 p-2.5">
352
- <div className="min-w-0 text-xs">
353
- <div className="truncate font-medium">
354
- {slot.status === "ready"
355
- ? t("library.readyToSave")
356
- : slot.status === "pending"
357
- ? t("library.stillRendering")
358
- : t("library.generationFailed")}
402
+ <div className="overflow-hidden rounded-lg border border-border/80 bg-background transition hover:border-foreground/25 hover:bg-muted/10">
403
+ <div className="group relative">
404
+ <button
405
+ type="button"
406
+ aria-label={t("library.selectAsset", { title: variantLabel })}
407
+ title={variantLabel}
408
+ onClick={() => {
409
+ if (ready) onPreview();
410
+ }}
411
+ className="block w-full text-left focus-visible:outline-none"
412
+ >
413
+ <div className="aspect-[4/3] bg-muted/40">
414
+ <GenerationSlotPreview slot={slot} />
359
415
  </div>
360
- <div className="mt-0.5 truncate text-muted-foreground">
361
- {libraryTitle || t("library.noBrandKit")}
362
- {prompt ? ` / ${prompt}` : null}
416
+ </button>
417
+
418
+ {slot.status !== "ready" ? (
419
+ <div className="absolute left-2 top-2 z-10 flex items-center gap-1 rounded-full border border-border/70 bg-background/90 px-2 py-1 text-[11px] font-medium shadow-sm">
420
+ {slot.status === "pending" ? <Spinner className="h-3 w-3" /> : null}
421
+ <span>
422
+ {slot.status === "pending"
423
+ ? t("library.generating")
424
+ : t("library.failed")}
425
+ </span>
363
426
  </div>
364
- </div>
365
- <div className="flex flex-wrap items-center gap-1.5">
366
- <Button
367
- size="sm"
368
- className="h-7 px-2 text-xs"
369
- disabled={!ready || isSaving}
370
- onClick={onSave}
371
- >
372
- {isSaving ? <Spinner className="h-3 w-3" /> : t("library.save")}
373
- </Button>
374
- <Button
375
- variant="outline"
376
- size="sm"
377
- className="h-7 gap-1 px-2 text-xs"
378
- disabled={!slot.assetId}
379
- onClick={() =>
380
- sendToAgentChat({
381
- message: `Refine generated asset ${slot.assetId}: `,
382
- context: [
383
- "## Assets candidate",
384
- `Asset ID: ${slot.assetId}`,
385
- `Run ID: ${slot.runId ?? "unknown"}`,
386
- `Prompt: ${prompt}`,
387
- libraryTitle ? `Brand kit: ${libraryTitle}` : "",
388
- "Use refine-image with assetId when the user describes the change.",
389
- ]
390
- .filter(Boolean)
391
- .join("\n"),
392
- submit: false,
393
- openSidebar: true,
394
- })
395
- }
396
- >
397
- <IconMessageCircle className="h-3.5 w-3.5" />
398
- {t("library.refine")}
399
- </Button>
400
- <Button
401
- variant="ghost"
402
- size="icon"
403
- className="h-7 w-7 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
404
- disabled={isDismissing}
405
- onClick={onDismiss}
427
+ ) : null}
428
+
429
+ {ready ? (
430
+ <TooltipProvider>
431
+ <div className="absolute right-2 top-2 z-10 flex items-center gap-1.5 opacity-0 transition group-hover:opacity-100 focus-within:opacity-100">
432
+ <Tooltip>
433
+ <TooltipTrigger asChild>
434
+ <button
435
+ type="button"
436
+ aria-label={t("library.save")}
437
+ disabled={isSaving}
438
+ onClick={(event) => {
439
+ event.stopPropagation();
440
+ onSave();
441
+ }}
442
+ className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-background/90 text-foreground shadow-sm transition hover:bg-primary hover:text-primary-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-60"
443
+ >
444
+ {isSaving ? (
445
+ <Spinner className="h-4 w-4" />
446
+ ) : (
447
+ <IconDeviceFloppy className="h-4 w-4" />
448
+ )}
449
+ </button>
450
+ </TooltipTrigger>
451
+ <TooltipContent>{t("library.save")}</TooltipContent>
452
+ </Tooltip>
453
+ <Tooltip>
454
+ <TooltipTrigger asChild>
455
+ <button
456
+ type="button"
457
+ aria-label={t("library.refine")}
458
+ onClick={(event) => {
459
+ event.stopPropagation();
460
+ onRefine();
461
+ }}
462
+ className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-background/90 text-foreground shadow-sm transition hover:bg-primary hover:text-primary-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
463
+ >
464
+ <IconMessageCircle className="h-4 w-4" />
465
+ </button>
466
+ </TooltipTrigger>
467
+ <TooltipContent>{t("library.refine")}</TooltipContent>
468
+ </Tooltip>
469
+ <Tooltip>
470
+ <TooltipTrigger asChild>
471
+ <button
472
+ type="button"
473
+ aria-label={t("library.deleteCandidate")}
474
+ disabled={isDismissing}
475
+ onClick={(event) => {
476
+ event.stopPropagation();
477
+ onDismiss();
478
+ }}
479
+ className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-background/90 text-foreground shadow-sm transition hover:bg-destructive hover:text-destructive-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-60"
480
+ >
481
+ <IconTrash className="h-4 w-4" />
482
+ </button>
483
+ </TooltipTrigger>
484
+ <TooltipContent>{t("library.deleteCandidate")}</TooltipContent>
485
+ </Tooltip>
486
+ </div>
487
+ </TooltipProvider>
488
+ ) : (
489
+ <button
490
+ type="button"
406
491
  aria-label={t("library.deleteCandidate")}
492
+ disabled={isDismissing}
493
+ onClick={(event) => {
494
+ event.stopPropagation();
495
+ onDismiss();
496
+ }}
497
+ className="absolute right-2 top-2 z-10 inline-flex h-8 w-8 items-center justify-center rounded-full bg-background/90 text-muted-foreground shadow-sm transition hover:bg-destructive hover:text-destructive-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-60"
407
498
  >
408
- <IconTrash className="h-3.5 w-3.5" />
409
- </Button>
410
- </div>
499
+ <IconTrash className="h-4 w-4" />
500
+ </button>
501
+ )}
502
+ </div>
503
+ <div className="px-2 py-1.5 text-center text-xs font-medium text-muted-foreground">
504
+ {variantLabel}
411
505
  </div>
412
- </article>
506
+ </div>
413
507
  );
414
508
  }
415
509
 
416
- function GenerationSlotPreview({
510
+ function GenerationPreviewDialog({
417
511
  slot,
512
+ slots,
513
+ prompt,
514
+ libraryTitle,
515
+ isSaving,
516
+ isDismissing,
517
+ onOpenChange,
518
+ onSelect,
519
+ onSave,
520
+ onRefine,
521
+ onDismiss,
418
522
  }: {
419
- slot: AssetVariantState["slots"][number];
523
+ slot: VariantSlot | null;
524
+ slots: VariantSlot[];
525
+ prompt: string;
526
+ libraryTitle: string | null;
527
+ isSaving: boolean;
528
+ isDismissing: boolean;
529
+ onOpenChange: (open: boolean) => void;
530
+ onSelect: (slotId: string) => void;
531
+ onSave: (slot: VariantSlot) => void;
532
+ onRefine: (slot: VariantSlot, variantNumber: number) => void;
533
+ onDismiss: (slot: VariantSlot) => void;
420
534
  }) {
535
+ const t = useT();
536
+ const previewableSlots = useMemo(
537
+ () =>
538
+ slots.filter((item) => item.status === "ready" && Boolean(item.assetId)),
539
+ [slots],
540
+ );
541
+ const previewIndex = slot
542
+ ? previewableSlots.findIndex((item) => item.slotId === slot.slotId)
543
+ : -1;
544
+ const hasPrev = previewIndex > 0;
545
+ const hasNext =
546
+ previewIndex >= 0 && previewIndex < previewableSlots.length - 1;
547
+ const showPreviousSlot = () => {
548
+ if (hasPrev) onSelect(previewableSlots[previewIndex - 1].slotId);
549
+ };
550
+ const showNextSlot = () => {
551
+ if (hasNext) onSelect(previewableSlots[previewIndex + 1].slotId);
552
+ };
553
+ const sources = slot ? assetPreviewSources(slot, "preview") : [];
554
+ const src = sources[0];
555
+ const variantNumber = slot
556
+ ? slots.findIndex((item) => item.slotId === slot.slotId) + 1
557
+ : 0;
558
+ const variantLabel = t("library.variantWithNumber", {
559
+ number: variantNumber,
560
+ });
561
+ return (
562
+ <Dialog open={Boolean(slot)} onOpenChange={onOpenChange}>
563
+ {slot ? (
564
+ <DialogContent
565
+ hideClose
566
+ onKeyDown={(event) => {
567
+ if (event.key === "ArrowLeft") showPreviousSlot();
568
+ if (event.key === "ArrowRight") showNextSlot();
569
+ }}
570
+ className="flex max-h-[85vh] w-[calc(100vw-24px)] max-w-4xl flex-col gap-0 overflow-hidden p-0"
571
+ >
572
+ <DialogTitle className="sr-only">{variantLabel}</DialogTitle>
573
+ <DialogDescription className="sr-only">
574
+ {prompt || t("library.readyToSave")}
575
+ </DialogDescription>
576
+
577
+ <div className="flex shrink-0 items-center justify-between gap-3 border-b border-border/80 px-4 py-3">
578
+ <p className="min-w-0 truncate text-sm font-medium">
579
+ {variantLabel}
580
+ </p>
581
+ <div className="flex shrink-0 items-center gap-2">
582
+ <Button
583
+ size="sm"
584
+ disabled={isSaving || !slot.assetId}
585
+ onClick={() => onSave(slot)}
586
+ >
587
+ {isSaving ? <Spinner className="h-4 w-4" /> : t("library.save")}
588
+ </Button>
589
+ <Button
590
+ variant="outline"
591
+ size="sm"
592
+ className="gap-1"
593
+ disabled={!slot.assetId}
594
+ onClick={() => onRefine(slot, variantNumber)}
595
+ >
596
+ <IconMessageCircle className="h-4 w-4" />
597
+ {t("library.refine")}
598
+ </Button>
599
+ {slot.assetId ? (
600
+ <Button asChild variant="outline" size="sm">
601
+ <Link
602
+ to={`/asset/${encodeURIComponent(slot.assetId)}`}
603
+ onClick={() => onOpenChange(false)}
604
+ >
605
+ {t("library.viewDetails")}
606
+ </Link>
607
+ </Button>
608
+ ) : null}
609
+ <Button
610
+ variant="outline"
611
+ size="icon"
612
+ className="h-8 w-8 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
613
+ disabled={isDismissing}
614
+ onClick={() => onDismiss(slot)}
615
+ aria-label={t("library.deleteCandidate")}
616
+ >
617
+ {isDismissing ? (
618
+ <Spinner className="h-4 w-4" />
619
+ ) : (
620
+ <IconTrash className="h-4 w-4" />
621
+ )}
622
+ </Button>
623
+ <DialogClose
624
+ aria-label={t("library.closePreview")}
625
+ className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-accent hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
626
+ >
627
+ <IconX className="h-4 w-4" />
628
+ </DialogClose>
629
+ </div>
630
+ </div>
631
+
632
+ <div className="flex min-h-0 flex-1 items-center justify-center overflow-auto bg-muted/20 p-4 sm:p-6">
633
+ {src ? (
634
+ <img
635
+ src={src}
636
+ alt={prompt || ""}
637
+ className="max-h-full max-w-full rounded-lg bg-black object-contain"
638
+ />
639
+ ) : (
640
+ <div className="flex h-64 w-full items-center justify-center rounded-lg bg-muted">
641
+ <IconPhoto className="h-8 w-8 text-muted-foreground" />
642
+ </div>
643
+ )}
644
+ </div>
645
+
646
+ {hasPrev || hasNext ? (
647
+ <div className="flex shrink-0 items-center justify-center gap-2 border-t border-border/80 py-3">
648
+ <button
649
+ type="button"
650
+ aria-label={t("library.previousImage")}
651
+ onClick={showPreviousSlot}
652
+ disabled={!hasPrev}
653
+ className="inline-flex h-9 w-9 items-center justify-center rounded-full text-foreground transition hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-40"
654
+ >
655
+ <IconChevronLeft className="h-5 w-5" />
656
+ </button>
657
+ <button
658
+ type="button"
659
+ aria-label={t("library.nextImage")}
660
+ onClick={showNextSlot}
661
+ disabled={!hasNext}
662
+ className="inline-flex h-9 w-9 items-center justify-center rounded-full text-foreground transition hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-40"
663
+ >
664
+ <IconChevronRight className="h-5 w-5" />
665
+ </button>
666
+ </div>
667
+ ) : null}
668
+ </DialogContent>
669
+ ) : null}
670
+ </Dialog>
671
+ );
672
+ }
673
+
674
+ function GenerationSlotPreview({ slot }: { slot: VariantSlot }) {
421
675
  const t = useT();
422
676
  const sources = assetPreviewSources(slot, "thumbnail");
423
677
  const src = sources[0];
424
678
  if (src) {
425
- return <img src={src} alt="" className="h-full w-full object-contain" />;
679
+ return (
680
+ <img
681
+ src={src}
682
+ alt=""
683
+ className="h-full w-full object-contain transition group-hover:scale-[1.02]"
684
+ />
685
+ );
426
686
  }
427
687
  if (slot.status === "failed") {
428
688
  return (
@@ -67,6 +67,10 @@ export function Layout({ children }: LayoutProps) {
67
67
  useAgentChatHomeHandoffLinks({
68
68
  storageKey: ASSETS_CHAT_STORAGE_KEY,
69
69
  isChatPath: (pathname) => pathname === "/" || pathname.startsWith("/chat/"),
70
+ // A direct /chat/:id link already names the thread and must preserve the
71
+ // normal chat-to-sidebar transition. Only the empty home chat needs the
72
+ // recent marker that proves an agent handoff is in flight.
73
+ requireActiveHandoff: location.pathname === "/",
70
74
  });
71
75
 
72
76
  useEffect(() => {
@@ -1,4 +1,5 @@
1
1
  import {
2
+ isAgentChatHomeHandoffActive,
2
3
  markAgentChatHomeHandoff,
3
4
  useAgentRouteState,
4
5
  getBrowserTabId,
@@ -190,7 +191,8 @@ export function useNavigationState() {
190
191
  onNavigate: (_command, path) => {
191
192
  if (
192
193
  isCreatePath(location.pathname) &&
193
- !isCreatePath(pathnameFromPath(path))
194
+ !isCreatePath(pathnameFromPath(path)) &&
195
+ isAgentChatHomeHandoffActive(ASSETS_CHAT_STORAGE_KEY)
194
196
  ) {
195
197
  markAgentChatHomeHandoff(ASSETS_CHAT_STORAGE_KEY);
196
198
  }