@agent-native/core 0.8.2 → 0.10.0

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 (305) hide show
  1. package/README.md +4 -4
  2. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  3. package/dist/agent/engine/builder-engine.js +5 -4
  4. package/dist/agent/engine/builder-engine.js.map +1 -1
  5. package/dist/agent/engine/registry.d.ts +6 -3
  6. package/dist/agent/engine/registry.d.ts.map +1 -1
  7. package/dist/agent/engine/registry.js +8 -17
  8. package/dist/agent/engine/registry.js.map +1 -1
  9. package/dist/agent/production-agent.d.ts +1 -1
  10. package/dist/agent/production-agent.d.ts.map +1 -1
  11. package/dist/agent/production-agent.js +28 -11
  12. package/dist/agent/production-agent.js.map +1 -1
  13. package/dist/agent/run-manager.d.ts +10 -0
  14. package/dist/agent/run-manager.d.ts.map +1 -1
  15. package/dist/agent/run-manager.js +89 -7
  16. package/dist/agent/run-manager.js.map +1 -1
  17. package/dist/agent/run-store.d.ts +4 -1
  18. package/dist/agent/run-store.d.ts.map +1 -1
  19. package/dist/agent/run-store.js +6 -5
  20. package/dist/agent/run-store.js.map +1 -1
  21. package/dist/agent/thread-data-builder.d.ts +12 -0
  22. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  23. package/dist/agent/thread-data-builder.js +96 -0
  24. package/dist/agent/thread-data-builder.js.map +1 -1
  25. package/dist/cli/create.d.ts +9 -0
  26. package/dist/cli/create.d.ts.map +1 -1
  27. package/dist/cli/create.js +29 -11
  28. package/dist/cli/create.js.map +1 -1
  29. package/dist/cli/index.js +177 -22
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/cli/workspace-dev.js +66 -5
  32. package/dist/cli/workspace-dev.js.map +1 -1
  33. package/dist/client/AgentPanel.d.ts.map +1 -1
  34. package/dist/client/AgentPanel.js +6 -20
  35. package/dist/client/AgentPanel.js.map +1 -1
  36. package/dist/client/AssistantChat.d.ts.map +1 -1
  37. package/dist/client/AssistantChat.js +146 -107
  38. package/dist/client/AssistantChat.js.map +1 -1
  39. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  40. package/dist/client/agent-chat-adapter.js +143 -22
  41. package/dist/client/agent-chat-adapter.js.map +1 -1
  42. package/dist/client/agent-sidebar-state.d.ts +3 -0
  43. package/dist/client/agent-sidebar-state.d.ts.map +1 -0
  44. package/dist/client/agent-sidebar-state.js +24 -0
  45. package/dist/client/agent-sidebar-state.js.map +1 -0
  46. package/dist/client/analytics.d.ts +39 -0
  47. package/dist/client/analytics.d.ts.map +1 -1
  48. package/dist/client/analytics.js +74 -0
  49. package/dist/client/analytics.js.map +1 -1
  50. package/dist/client/components/PresenceBar.d.ts.map +1 -1
  51. package/dist/client/components/PresenceBar.js +21 -15
  52. package/dist/client/components/PresenceBar.js.map +1 -1
  53. package/dist/client/components/ui/tooltip.d.ts +2 -1
  54. package/dist/client/components/ui/tooltip.d.ts.map +1 -1
  55. package/dist/client/components/ui/tooltip.js +9 -2
  56. package/dist/client/components/ui/tooltip.js.map +1 -1
  57. package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
  58. package/dist/client/composer/ComposerPlusMenu.js +51 -17
  59. package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
  60. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  61. package/dist/client/composer/PromptComposer.js +30 -0
  62. package/dist/client/composer/PromptComposer.js.map +1 -1
  63. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  64. package/dist/client/composer/TiptapComposer.js +31 -5
  65. package/dist/client/composer/TiptapComposer.js.map +1 -1
  66. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  67. package/dist/client/composer/VoiceButton.js +9 -8
  68. package/dist/client/composer/VoiceButton.js.map +1 -1
  69. package/dist/client/dev-overlay/DevOverlay.d.ts.map +1 -1
  70. package/dist/client/dev-overlay/DevOverlay.js +4 -3
  71. package/dist/client/dev-overlay/DevOverlay.js.map +1 -1
  72. package/dist/client/error-format.d.ts.map +1 -1
  73. package/dist/client/error-format.js +6 -0
  74. package/dist/client/error-format.js.map +1 -1
  75. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  76. package/dist/client/extensions/EmbeddedExtension.js +14 -3
  77. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  78. package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -1
  79. package/dist/client/extensions/ExtensionEditor.js +6 -5
  80. package/dist/client/extensions/ExtensionEditor.js.map +1 -1
  81. package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -1
  82. package/dist/client/extensions/ExtensionSlot.js +2 -1
  83. package/dist/client/extensions/ExtensionSlot.js.map +1 -1
  84. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  85. package/dist/client/extensions/ExtensionViewer.js +40 -19
  86. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  87. package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
  88. package/dist/client/extensions/ExtensionsSidebarSection.js +52 -51
  89. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  90. package/dist/client/index.d.ts +2 -1
  91. package/dist/client/index.d.ts.map +1 -1
  92. package/dist/client/index.js +2 -1
  93. package/dist/client/index.js.map +1 -1
  94. package/dist/client/integrations/IntegrationCard.d.ts.map +1 -1
  95. package/dist/client/integrations/IntegrationCard.js +2 -1
  96. package/dist/client/integrations/IntegrationCard.js.map +1 -1
  97. package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
  98. package/dist/client/integrations/IntegrationsPanel.js +3 -2
  99. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  100. package/dist/client/notifications/NotificationsBell.d.ts.map +1 -1
  101. package/dist/client/notifications/NotificationsBell.js +42 -6
  102. package/dist/client/notifications/NotificationsBell.js.map +1 -1
  103. package/dist/client/onboarding/OnboardingPanel.d.ts.map +1 -1
  104. package/dist/client/onboarding/OnboardingPanel.js +3 -2
  105. package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
  106. package/dist/client/onboarding/SetupButton.d.ts.map +1 -1
  107. package/dist/client/onboarding/SetupButton.js +14 -13
  108. package/dist/client/onboarding/SetupButton.js.map +1 -1
  109. package/dist/client/org/InvitationBanner.d.ts +8 -2
  110. package/dist/client/org/InvitationBanner.d.ts.map +1 -1
  111. package/dist/client/org/InvitationBanner.js +28 -7
  112. package/dist/client/org/InvitationBanner.js.map +1 -1
  113. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  114. package/dist/client/org/OrgSwitcher.js +29 -5
  115. package/dist/client/org/OrgSwitcher.js.map +1 -1
  116. package/dist/client/org/TeamPage.d.ts.map +1 -1
  117. package/dist/client/org/TeamPage.js +9 -7
  118. package/dist/client/org/TeamPage.js.map +1 -1
  119. package/dist/client/resources/ResourceEditor.d.ts.map +1 -1
  120. package/dist/client/resources/ResourceEditor.js +2 -1
  121. package/dist/client/resources/ResourceEditor.js.map +1 -1
  122. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  123. package/dist/client/resources/ResourcesPanel.js +48 -14
  124. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  125. package/dist/client/resources/use-mcp-servers.d.ts +2 -0
  126. package/dist/client/resources/use-mcp-servers.d.ts.map +1 -1
  127. package/dist/client/resources/use-mcp-servers.js +59 -3
  128. package/dist/client/resources/use-mcp-servers.js.map +1 -1
  129. package/dist/client/settings/AgentsSection.d.ts.map +1 -1
  130. package/dist/client/settings/AgentsSection.js +8 -7
  131. package/dist/client/settings/AgentsSection.js.map +1 -1
  132. package/dist/client/settings/AutomationsSection.d.ts.map +1 -1
  133. package/dist/client/settings/AutomationsSection.js +4 -3
  134. package/dist/client/settings/AutomationsSection.js.map +1 -1
  135. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  136. package/dist/client/settings/SecretsSection.js +11 -1
  137. package/dist/client/settings/SecretsSection.js.map +1 -1
  138. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  139. package/dist/client/settings/SettingsPanel.js +15 -12
  140. package/dist/client/settings/SettingsPanel.js.map +1 -1
  141. package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
  142. package/dist/client/settings/VoiceTranscriptionSection.js +13 -30
  143. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  144. package/dist/client/settings/index.d.ts +1 -1
  145. package/dist/client/settings/index.d.ts.map +1 -1
  146. package/dist/client/settings/index.js.map +1 -1
  147. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  148. package/dist/client/settings/useBuilderStatus.js +27 -1
  149. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  150. package/dist/client/sharing/ShareButton.d.ts +4 -0
  151. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  152. package/dist/client/sharing/ShareButton.js +5 -1
  153. package/dist/client/sharing/ShareButton.js.map +1 -1
  154. package/dist/client/sse-event-processor.d.ts +1 -1
  155. package/dist/client/sse-event-processor.d.ts.map +1 -1
  156. package/dist/client/sse-event-processor.js +59 -11
  157. package/dist/client/sse-event-processor.js.map +1 -1
  158. package/dist/client/use-db-sync.d.ts.map +1 -1
  159. package/dist/client/use-db-sync.js +100 -19
  160. package/dist/client/use-db-sync.js.map +1 -1
  161. package/dist/client/use-session.d.ts.map +1 -1
  162. package/dist/client/use-session.js +14 -2
  163. package/dist/client/use-session.js.map +1 -1
  164. package/dist/collab/client.d.ts +1 -0
  165. package/dist/collab/client.d.ts.map +1 -1
  166. package/dist/collab/client.js +18 -1
  167. package/dist/collab/client.js.map +1 -1
  168. package/dist/deploy/build.d.ts.map +1 -1
  169. package/dist/deploy/build.js +5 -0
  170. package/dist/deploy/build.js.map +1 -1
  171. package/dist/deploy/route-discovery.d.ts.map +1 -1
  172. package/dist/deploy/route-discovery.js +1 -0
  173. package/dist/deploy/route-discovery.js.map +1 -1
  174. package/dist/deploy/workspace-core.d.ts +1 -1
  175. package/dist/deploy/workspace-core.d.ts.map +1 -1
  176. package/dist/deploy/workspace-core.js +1 -0
  177. package/dist/deploy/workspace-core.js.map +1 -1
  178. package/dist/extensions/actions.d.ts.map +1 -1
  179. package/dist/extensions/actions.js +17 -3
  180. package/dist/extensions/actions.js.map +1 -1
  181. package/dist/extensions/routes.js +1 -1
  182. package/dist/extensions/routes.js.map +1 -1
  183. package/dist/extensions/schema.d.ts +14 -14
  184. package/dist/extensions/schema.d.ts.map +1 -1
  185. package/dist/extensions/schema.js +4 -4
  186. package/dist/extensions/schema.js.map +1 -1
  187. package/dist/extensions/store.d.ts.map +1 -1
  188. package/dist/extensions/store.js +23 -0
  189. package/dist/extensions/store.js.map +1 -1
  190. package/dist/extensions/theme.d.ts +8 -1
  191. package/dist/extensions/theme.d.ts.map +1 -1
  192. package/dist/extensions/theme.js +43 -34
  193. package/dist/extensions/theme.js.map +1 -1
  194. package/dist/mcp-client/routes.d.ts +1 -0
  195. package/dist/mcp-client/routes.d.ts.map +1 -1
  196. package/dist/mcp-client/routes.js +28 -1
  197. package/dist/mcp-client/routes.js.map +1 -1
  198. package/dist/org/auto-join-domain.d.ts +28 -0
  199. package/dist/org/auto-join-domain.d.ts.map +1 -0
  200. package/dist/org/auto-join-domain.js +92 -0
  201. package/dist/org/auto-join-domain.js.map +1 -0
  202. package/dist/org/index.d.ts +2 -0
  203. package/dist/org/index.d.ts.map +1 -1
  204. package/dist/org/index.js +1 -0
  205. package/dist/org/index.js.map +1 -1
  206. package/dist/scripts/db/exec.d.ts.map +1 -1
  207. package/dist/scripts/db/exec.js +27 -1
  208. package/dist/scripts/db/exec.js.map +1 -1
  209. package/dist/scripts/db/index.d.ts.map +1 -1
  210. package/dist/scripts/db/index.js +1 -0
  211. package/dist/scripts/db/index.js.map +1 -1
  212. package/dist/scripts/db/reset-dev-owner.d.ts +27 -0
  213. package/dist/scripts/db/reset-dev-owner.d.ts.map +1 -0
  214. package/dist/scripts/db/reset-dev-owner.js +225 -0
  215. package/dist/scripts/db/reset-dev-owner.js.map +1 -0
  216. package/dist/scripts/db/scoping.d.ts.map +1 -1
  217. package/dist/scripts/db/scoping.js +15 -30
  218. package/dist/scripts/db/scoping.js.map +1 -1
  219. package/dist/scripts/dev-session.d.ts +46 -0
  220. package/dist/scripts/dev-session.d.ts.map +1 -0
  221. package/dist/scripts/dev-session.js +81 -0
  222. package/dist/scripts/dev-session.js.map +1 -0
  223. package/dist/scripts/runner.d.ts.map +1 -1
  224. package/dist/scripts/runner.js +21 -0
  225. package/dist/scripts/runner.js.map +1 -1
  226. package/dist/secrets/register.d.ts +1 -1
  227. package/dist/secrets/register.d.ts.map +1 -1
  228. package/dist/secrets/register.js +4 -2
  229. package/dist/secrets/register.js.map +1 -1
  230. package/dist/secrets/routes.d.ts.map +1 -1
  231. package/dist/secrets/routes.js +32 -0
  232. package/dist/secrets/routes.js.map +1 -1
  233. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  234. package/dist/server/agent-chat-plugin.js +77 -102
  235. package/dist/server/agent-chat-plugin.js.map +1 -1
  236. package/dist/server/auth.d.ts.map +1 -1
  237. package/dist/server/auth.js +33 -0
  238. package/dist/server/auth.js.map +1 -1
  239. package/dist/server/better-auth-instance.d.ts.map +1 -1
  240. package/dist/server/better-auth-instance.js +11 -0
  241. package/dist/server/better-auth-instance.js.map +1 -1
  242. package/dist/server/builder-browser.d.ts.map +1 -1
  243. package/dist/server/builder-browser.js +169 -68
  244. package/dist/server/builder-browser.js.map +1 -1
  245. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  246. package/dist/server/core-routes-plugin.js +56 -13
  247. package/dist/server/core-routes-plugin.js.map +1 -1
  248. package/dist/server/credential-provider.d.ts +49 -6
  249. package/dist/server/credential-provider.d.ts.map +1 -1
  250. package/dist/server/credential-provider.js +133 -38
  251. package/dist/server/credential-provider.js.map +1 -1
  252. package/dist/server/design-token-utils.d.ts +13 -2
  253. package/dist/server/design-token-utils.d.ts.map +1 -1
  254. package/dist/server/design-token-utils.js +48 -16
  255. package/dist/server/design-token-utils.js.map +1 -1
  256. package/dist/server/framework-request-handler.d.ts.map +1 -1
  257. package/dist/server/framework-request-handler.js +31 -0
  258. package/dist/server/framework-request-handler.js.map +1 -1
  259. package/dist/server/google-realtime-session.d.ts.map +1 -1
  260. package/dist/server/google-realtime-session.js +19 -6
  261. package/dist/server/google-realtime-session.js.map +1 -1
  262. package/dist/server/index.d.ts +2 -0
  263. package/dist/server/index.d.ts.map +1 -1
  264. package/dist/server/index.js +2 -0
  265. package/dist/server/index.js.map +1 -1
  266. package/dist/server/onboarding-html.d.ts.map +1 -1
  267. package/dist/server/onboarding-html.js +142 -14
  268. package/dist/server/onboarding-html.js.map +1 -1
  269. package/dist/server/request-context.d.ts +17 -0
  270. package/dist/server/request-context.d.ts.map +1 -1
  271. package/dist/server/request-context.js +40 -1
  272. package/dist/server/request-context.js.map +1 -1
  273. package/dist/server/sentry-plugin.d.ts +11 -0
  274. package/dist/server/sentry-plugin.d.ts.map +1 -0
  275. package/dist/server/sentry-plugin.js +116 -0
  276. package/dist/server/sentry-plugin.js.map +1 -0
  277. package/dist/server/sentry.d.ts +92 -0
  278. package/dist/server/sentry.d.ts.map +1 -0
  279. package/dist/server/sentry.js +287 -0
  280. package/dist/server/sentry.js.map +1 -0
  281. package/dist/server/transcribe-voice.d.ts +2 -4
  282. package/dist/server/transcribe-voice.d.ts.map +1 -1
  283. package/dist/server/transcribe-voice.js +4 -16
  284. package/dist/server/transcribe-voice.js.map +1 -1
  285. package/dist/server/voice-providers-status.d.ts.map +1 -1
  286. package/dist/server/voice-providers-status.js +19 -35
  287. package/dist/server/voice-providers-status.js.map +1 -1
  288. package/dist/styles/agent-native.css +15 -0
  289. package/docs/content/cloneable-saas.md +7 -9
  290. package/docs/content/deployment.md +6 -2
  291. package/docs/content/dispatch.md +1 -1
  292. package/docs/content/extensions.md +177 -142
  293. package/docs/content/faq.md +2 -2
  294. package/docs/content/getting-started.md +13 -11
  295. package/docs/content/multi-app-workspace.md +2 -2
  296. package/docs/content/observability.md +47 -0
  297. package/docs/content/pure-agent-apps.md +1 -1
  298. package/docs/content/template-clips.md +3 -3
  299. package/docs/content/template-design.md +3 -3
  300. package/docs/content/template-dispatch.md +1 -1
  301. package/docs/content/template-forms.md +1 -1
  302. package/docs/content/template-mail.md +1 -1
  303. package/docs/content/what-is-agent-native.md +4 -4
  304. package/docs/content/workspace.md +1 -1
  305. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ExtensionEditor.js","sourceRoot":"","sources":["../../../src/client/extensions/ExtensionEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { agentNativePath } from \"../api-path.js\";\nimport { useState, useEffect } from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { Link, useNavigate } from \"react-router\";\nimport {\n IconArrowLeft,\n IconDeviceFloppy,\n IconDots,\n IconTrash,\n IconX,\n} from \"@tabler/icons-react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../components/ui/popover.js\";\nimport { cn } from \"../utils.js\";\n\ninterface SlotDeclaration {\n id: string;\n extensionId: string;\n slotId: string;\n}\n\ninterface Extension {\n id: string;\n name: string;\n description?: string;\n content?: string;\n}\n\nexport interface ExtensionEditorProps {\n extensionId?: string;\n}\n\nexport function ExtensionEditor({ extensionId }: ExtensionEditorProps) {\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const isEdit = !!extensionId;\n\n const [name, setName] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [content, setContent] = useState(\"\");\n const [saving, setSaving] = useState(false);\n const [deleting, setDeleting] = useState(false);\n const [menuOpen, setMenuOpen] = useState(false);\n const [confirmingDelete, setConfirmingDelete] = useState(false);\n\n const { data: slots = [] } = useQuery<SlotDeclaration[]>({\n queryKey: [\"extension-slots\", extensionId],\n queryFn: async () => {\n const res = await fetch(\n agentNativePath(`/_agent-native/slots/extension/${extensionId}`),\n );\n if (!res.ok) return [];\n return res.json();\n },\n enabled: isEdit && menuOpen,\n });\n\n const { data: existingTool } = useQuery<Extension>({\n queryKey: [\"extension\", extensionId],\n queryFn: async () => {\n const res = await fetch(\n agentNativePath(`/_agent-native/extensions/${extensionId}`),\n );\n if (!res.ok) throw new Error(\"Failed to fetch extension\");\n return res.json();\n },\n enabled: isEdit,\n });\n\n useEffect(() => {\n if (existingTool) {\n setName(existingTool.name ?? \"\");\n setDescription(existingTool.description ?? \"\");\n setContent(existingTool.content ?? \"\");\n }\n }, [existingTool]);\n\n const handleSave = async () => {\n if (!name.trim()) return;\n setSaving(true);\n try {\n const body = JSON.stringify({\n name: name.trim(),\n description: description.trim() || undefined,\n content,\n });\n\n if (isEdit) {\n const res = await fetch(\n agentNativePath(`/_agent-native/extensions/${extensionId}`),\n {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n },\n );\n if (!res.ok) throw new Error(\"Update failed\");\n queryClient.invalidateQueries({ queryKey: [\"extension\", extensionId] });\n queryClient.invalidateQueries({ queryKey: [\"extensions\"] });\n navigate(`/extensions/${extensionId}`);\n } else {\n const res = await fetch(agentNativePath(\"/_agent-native/extensions\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n });\n if (!res.ok) throw new Error(\"Create failed\");\n const created = await res.json();\n queryClient.invalidateQueries({ queryKey: [\"extensions\"] });\n navigate(`/extensions/${created.id}`);\n }\n } finally {\n setSaving(false);\n }\n };\n\n const handleDelete = async () => {\n if (!extensionId) return;\n setDeleting(true);\n try {\n const prev = queryClient.getQueryData<Extension[]>([\"extensions\"]);\n queryClient.setQueryData<Extension[]>([\"extensions\"], (old) =>\n (old ?? []).filter((t) => t.id !== extensionId),\n );\n\n const res = await fetch(\n agentNativePath(`/_agent-native/extensions/${extensionId}`),\n {\n method: \"DELETE\",\n },\n );\n if (!res.ok) {\n if (prev) queryClient.setQueryData([\"extensions\"], prev);\n throw new Error(\"Delete failed\");\n }\n\n queryClient.invalidateQueries({ queryKey: [\"extensions\"] });\n slots.forEach((s) =>\n queryClient.invalidateQueries({\n queryKey: [\"slot-installs\", s.slotId],\n }),\n );\n navigate(\"/extensions\");\n } finally {\n setDeleting(false);\n setConfirmingDelete(false);\n setMenuOpen(false);\n }\n };\n\n const handleRemoveFromSlot = async (slotId: string) => {\n if (!extensionId) return;\n try {\n await fetch(\n agentNativePath(\n `/_agent-native/slots/${encodeURIComponent(slotId)}/install/${encodeURIComponent(extensionId)}`,\n ),\n { method: \"DELETE\" },\n );\n } finally {\n queryClient.invalidateQueries({ queryKey: [\"slot-installs\", slotId] });\n }\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n <header className=\"flex items-center justify-between border-b px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n <Link\n to={isEdit ? `/extensions/${extensionId}` : \"/extensions\"}\n className=\"inline-flex cursor-pointer items-center justify-center rounded-md p-1 text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n aria-label=\"Back\"\n >\n <IconArrowLeft className=\"h-4 w-4\" />\n </Link>\n <h1 className=\"text-sm font-semibold\">\n {isEdit ? \"Edit Extension\" : \"New Extension\"}\n </h1>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || !name.trim()}\n className={cn(\n \"inline-flex cursor-pointer items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground hover:bg-primary/90\",\n (saving || !name.trim()) && \"opacity-60\",\n )}\n >\n <IconDeviceFloppy className=\"h-3.5 w-3.5\" />\n {saving ? \"Saving...\" : isEdit ? \"Save\" : \"Create\"}\n </button>\n {isEdit && (\n <Popover\n open={menuOpen}\n onOpenChange={(o) => {\n setMenuOpen(o);\n if (!o) setConfirmingDelete(false);\n }}\n >\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n className=\"inline-flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n title=\"More options\"\n aria-label=\"More options\"\n >\n <IconDots className=\"h-4 w-4\" />\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" sideOffset={4} className=\"w-72 p-0\">\n {!confirmingDelete ? (\n <>\n <div className=\"px-3 py-2 border-b border-border/40\">\n <p className=\"text-[12px] font-medium\">Appears in</p>\n {slots.length === 0 ? (\n <p className=\"text-[11px] text-muted-foreground/70 mt-0.5\">\n Not installed in any widget areas. Ask the agent to\n add it somewhere.\n </p>\n ) : (\n <p className=\"text-[11px] text-muted-foreground/70 mt-0.5\">\n This extension can render in {slots.length} widget\n area\n {slots.length === 1 ? \"\" : \"s\"}.\n </p>\n )}\n </div>\n {slots.length > 0 && (\n <div className=\"max-h-48 overflow-y-auto py-1\">\n {slots.map((s) => (\n <div\n key={s.id}\n className=\"flex items-center gap-2 px-3 py-1.5 text-[12px]\"\n >\n <span className=\"flex-1 truncate font-mono text-[11px] text-muted-foreground\">\n {s.slotId}\n </span>\n <button\n type=\"button\"\n onClick={() => handleRemoveFromSlot(s.slotId)}\n className=\"rounded p-1 text-muted-foreground/60 hover:bg-accent hover:text-foreground cursor-pointer\"\n title=\"Remove from this widget area (for me)\"\n aria-label=\"Remove from this widget area\"\n >\n <IconX className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n ))}\n </div>\n )}\n <div className=\"border-t border-border/40 p-1\">\n <button\n type=\"button\"\n onClick={() => setConfirmingDelete(true)}\n className=\"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-[12px] text-destructive hover:bg-destructive/10 cursor-pointer text-left\"\n >\n <IconTrash className=\"h-3.5 w-3.5\" />\n <span>Delete extension…</span>\n </button>\n </div>\n </>\n ) : (\n <div className=\"flex flex-col gap-2 p-3\">\n <p className=\"text-[12px]\">\n Delete <span className=\"font-medium\">{name}</span>? This\n removes the extension everywhere, for everyone it's shared\n with.\n </p>\n <div className=\"flex justify-end gap-1\">\n <button\n type=\"button\"\n onClick={() => setConfirmingDelete(false)}\n className=\"rounded-md px-2 py-1 text-[12px] hover:bg-accent cursor-pointer\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={deleting}\n className={cn(\n \"rounded-md bg-destructive px-2 py-1 text-[12px] text-destructive-foreground hover:bg-destructive/90 cursor-pointer\",\n deleting && \"opacity-60\",\n )}\n >\n {deleting ? \"Deleting…\" : \"Delete\"}\n </button>\n </div>\n </div>\n )}\n </PopoverContent>\n </Popover>\n )}\n </div>\n </header>\n\n <div className=\"flex flex-1 overflow-hidden\">\n <div className=\"flex w-1/2 flex-col gap-4 overflow-auto border-r p-4\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-foreground\">\n Name\n </label>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"My Extension\"\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background\"\n />\n </div>\n\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-foreground\">\n Description\n </label>\n <textarea\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"What does this extension do?\"\n rows={2}\n className=\"w-full resize-none rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background\"\n />\n </div>\n\n <div className=\"flex flex-1 flex-col\">\n <label className=\"mb-1.5 block text-sm font-medium text-foreground\">\n Content\n </label>\n <textarea\n value={content}\n onChange={(e) => setContent(e.target.value)}\n placeholder=\"<html>...</html>\"\n className=\"flex-1 resize-none rounded-md border border-input bg-background p-3 font-mono text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background\"\n spellCheck={false}\n />\n </div>\n </div>\n\n <div className=\"w-1/2\">\n {content ? (\n <iframe\n srcDoc={content}\n className=\"h-full w-full border-0\"\n sandbox=\"allow-scripts allow-forms\"\n title=\"Extension preview\"\n />\n ) : (\n <div className=\"flex h-full items-center justify-center text-sm text-muted-foreground\">\n Preview will appear here\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"ExtensionEditor.js","sourceRoot":"","sources":["../../../src/client/extensions/ExtensionEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { agentNativePath } from \"../api-path.js\";\nimport { useState, useEffect } from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { Link, useNavigate } from \"react-router\";\nimport {\n IconArrowLeft,\n IconDeviceFloppy,\n IconDots,\n IconTrash,\n IconX,\n} from \"@tabler/icons-react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../components/ui/popover.js\";\nimport { cn } from \"../utils.js\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"../components/ui/tooltip.js\";\n\ninterface SlotDeclaration {\n id: string;\n extensionId: string;\n slotId: string;\n}\n\ninterface Extension {\n id: string;\n name: string;\n description?: string;\n content?: string;\n}\n\nexport interface ExtensionEditorProps {\n extensionId?: string;\n}\n\nexport function ExtensionEditor({ extensionId }: ExtensionEditorProps) {\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const isEdit = !!extensionId;\n\n const [name, setName] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [content, setContent] = useState(\"\");\n const [saving, setSaving] = useState(false);\n const [deleting, setDeleting] = useState(false);\n const [menuOpen, setMenuOpen] = useState(false);\n const [confirmingDelete, setConfirmingDelete] = useState(false);\n\n const { data: slots = [] } = useQuery<SlotDeclaration[]>({\n queryKey: [\"extension-slots\", extensionId],\n queryFn: async () => {\n const res = await fetch(\n agentNativePath(`/_agent-native/slots/extension/${extensionId}`),\n );\n if (!res.ok) return [];\n return res.json();\n },\n enabled: isEdit && menuOpen,\n });\n\n const { data: existingTool } = useQuery<Extension>({\n queryKey: [\"extension\", extensionId],\n queryFn: async () => {\n const res = await fetch(\n agentNativePath(`/_agent-native/extensions/${extensionId}`),\n );\n if (!res.ok) throw new Error(\"Failed to fetch extension\");\n return res.json();\n },\n enabled: isEdit,\n });\n\n useEffect(() => {\n if (existingTool) {\n setName(existingTool.name ?? \"\");\n setDescription(existingTool.description ?? \"\");\n setContent(existingTool.content ?? \"\");\n }\n }, [existingTool]);\n\n const handleSave = async () => {\n if (!name.trim()) return;\n setSaving(true);\n try {\n const body = JSON.stringify({\n name: name.trim(),\n description: description.trim() || undefined,\n content,\n });\n\n if (isEdit) {\n const res = await fetch(\n agentNativePath(`/_agent-native/extensions/${extensionId}`),\n {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n },\n );\n if (!res.ok) throw new Error(\"Update failed\");\n queryClient.invalidateQueries({ queryKey: [\"extension\", extensionId] });\n queryClient.invalidateQueries({ queryKey: [\"extensions\"] });\n navigate(`/extensions/${extensionId}`);\n } else {\n const res = await fetch(agentNativePath(\"/_agent-native/extensions\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n });\n if (!res.ok) throw new Error(\"Create failed\");\n const created = await res.json();\n queryClient.invalidateQueries({ queryKey: [\"extensions\"] });\n navigate(`/extensions/${created.id}`);\n }\n } finally {\n setSaving(false);\n }\n };\n\n const handleDelete = async () => {\n if (!extensionId) return;\n setDeleting(true);\n try {\n const prev = queryClient.getQueryData<Extension[]>([\"extensions\"]);\n queryClient.setQueryData<Extension[]>([\"extensions\"], (old) =>\n (old ?? []).filter((t) => t.id !== extensionId),\n );\n\n const res = await fetch(\n agentNativePath(`/_agent-native/extensions/${extensionId}`),\n {\n method: \"DELETE\",\n },\n );\n if (!res.ok) {\n if (prev) queryClient.setQueryData([\"extensions\"], prev);\n throw new Error(\"Delete failed\");\n }\n\n queryClient.invalidateQueries({ queryKey: [\"extensions\"] });\n slots.forEach((s) =>\n queryClient.invalidateQueries({\n queryKey: [\"slot-installs\", s.slotId],\n }),\n );\n navigate(\"/extensions\");\n } finally {\n setDeleting(false);\n setConfirmingDelete(false);\n setMenuOpen(false);\n }\n };\n\n const handleRemoveFromSlot = async (slotId: string) => {\n if (!extensionId) return;\n try {\n await fetch(\n agentNativePath(\n `/_agent-native/slots/${encodeURIComponent(slotId)}/install/${encodeURIComponent(extensionId)}`,\n ),\n { method: \"DELETE\" },\n );\n } finally {\n queryClient.invalidateQueries({ queryKey: [\"slot-installs\", slotId] });\n }\n };\n\n return (\n <TooltipProvider delayDuration={200}>\n <div className=\"flex h-full flex-col\">\n <header className=\"flex items-center justify-between border-b px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n <Link\n to={isEdit ? `/extensions/${extensionId}` : \"/extensions\"}\n className=\"inline-flex cursor-pointer items-center justify-center rounded-md p-1 text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n aria-label=\"Back\"\n >\n <IconArrowLeft className=\"h-4 w-4\" />\n </Link>\n <h1 className=\"text-sm font-semibold\">\n {isEdit ? \"Edit Extension\" : \"New Extension\"}\n </h1>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || !name.trim()}\n className={cn(\n \"inline-flex cursor-pointer items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground hover:bg-primary/90\",\n (saving || !name.trim()) && \"opacity-60\",\n )}\n >\n <IconDeviceFloppy className=\"h-3.5 w-3.5\" />\n {saving ? \"Saving...\" : isEdit ? \"Save\" : \"Create\"}\n </button>\n {isEdit && (\n <Popover\n open={menuOpen}\n onOpenChange={(o) => {\n setMenuOpen(o);\n if (!o) setConfirmingDelete(false);\n }}\n >\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n className=\"inline-flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n aria-label=\"More options\"\n >\n <IconDots className=\"h-4 w-4\" />\n </button>\n </PopoverTrigger>\n </TooltipTrigger>\n <TooltipContent>More options</TooltipContent>\n </Tooltip>\n <PopoverContent align=\"end\" sideOffset={4} className=\"w-72 p-0\">\n {!confirmingDelete ? (\n <>\n <div className=\"px-3 py-2 border-b border-border/40\">\n <p className=\"text-[12px] font-medium\">Appears in</p>\n {slots.length === 0 ? (\n <p className=\"text-[11px] text-muted-foreground/70 mt-0.5\">\n Not installed in any widget areas. Ask the agent to\n add it somewhere.\n </p>\n ) : (\n <p className=\"text-[11px] text-muted-foreground/70 mt-0.5\">\n This extension can render in {slots.length} widget\n area\n {slots.length === 1 ? \"\" : \"s\"}.\n </p>\n )}\n </div>\n {slots.length > 0 && (\n <div className=\"max-h-48 overflow-y-auto py-1\">\n {slots.map((s) => (\n <div\n key={s.id}\n className=\"flex items-center gap-2 px-3 py-1.5 text-[12px]\"\n >\n <span className=\"flex-1 truncate font-mono text-[11px] text-muted-foreground\">\n {s.slotId}\n </span>\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n onClick={() =>\n handleRemoveFromSlot(s.slotId)\n }\n className=\"rounded p-1 text-muted-foreground/60 hover:bg-accent hover:text-foreground cursor-pointer\"\n aria-label=\"Remove from this widget area\"\n >\n <IconX className=\"h-3.5 w-3.5\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>\n Remove from this widget area (for me)\n </TooltipContent>\n </Tooltip>\n </div>\n ))}\n </div>\n )}\n <div className=\"border-t border-border/40 p-1\">\n <button\n type=\"button\"\n onClick={() => setConfirmingDelete(true)}\n className=\"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-[12px] text-destructive hover:bg-destructive/10 cursor-pointer text-left\"\n >\n <IconTrash className=\"h-3.5 w-3.5\" />\n <span>Delete extension…</span>\n </button>\n </div>\n </>\n ) : (\n <div className=\"flex flex-col gap-2 p-3\">\n <p className=\"text-[12px]\">\n Delete <span className=\"font-medium\">{name}</span>? This\n removes the extension everywhere, for everyone it's\n shared with.\n </p>\n <div className=\"flex justify-end gap-1\">\n <button\n type=\"button\"\n onClick={() => setConfirmingDelete(false)}\n className=\"rounded-md px-2 py-1 text-[12px] hover:bg-accent cursor-pointer\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={deleting}\n className={cn(\n \"rounded-md bg-destructive px-2 py-1 text-[12px] text-destructive-foreground hover:bg-destructive/90 cursor-pointer\",\n deleting && \"opacity-60\",\n )}\n >\n {deleting ? \"Deleting…\" : \"Delete\"}\n </button>\n </div>\n </div>\n )}\n </PopoverContent>\n </Popover>\n )}\n </div>\n </header>\n\n <div className=\"flex flex-1 overflow-hidden\">\n <div className=\"flex w-1/2 flex-col gap-4 overflow-auto border-r p-4\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-foreground\">\n Name\n </label>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"My Extension\"\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background\"\n />\n </div>\n\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-foreground\">\n Description\n </label>\n <textarea\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"What does this extension do?\"\n rows={2}\n className=\"w-full resize-none rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background\"\n />\n </div>\n\n <div className=\"flex flex-1 flex-col\">\n <label className=\"mb-1.5 block text-sm font-medium text-foreground\">\n Content\n </label>\n <textarea\n value={content}\n onChange={(e) => setContent(e.target.value)}\n placeholder=\"<html>...</html>\"\n className=\"flex-1 resize-none rounded-md border border-input bg-background p-3 font-mono text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background\"\n spellCheck={false}\n />\n </div>\n </div>\n\n <div className=\"w-1/2\">\n {content ? (\n <iframe\n srcDoc={content}\n className=\"h-full w-full border-0\"\n sandbox=\"allow-scripts allow-forms\"\n title=\"Extension preview\"\n />\n ) : (\n <div className=\"flex h-full items-center justify-center text-sm text-muted-foreground\">\n Preview will appear here\n </div>\n )}\n </div>\n </div>\n </div>\n </TooltipProvider>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ExtensionSlot.d.ts","sourceRoot":"","sources":["../../../src/client/extensions/ExtensionSlot.tsx"],"names":[],"mappings":"AA+BA,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzC,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,EAC5B,EAAE,EACF,OAAO,EACP,mBAAmB,EACnB,SAAS,EACT,aAAa,GACd,EAAE,kBAAkB,2CAwCpB"}
1
+ {"version":3,"file":"ExtensionSlot.d.ts","sourceRoot":"","sources":["../../../src/client/extensions/ExtensionSlot.tsx"],"names":[],"mappings":"AAqCA,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzC,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,EAC5B,EAAE,EACF,OAAO,EACP,mBAAmB,EACnB,SAAS,EACT,aAAa,GACd,EAAE,kBAAkB,2CAwCpB"}
@@ -6,6 +6,7 @@ import { IconPlus } from "@tabler/icons-react";
6
6
  import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
7
7
  import { sendToAgentChat } from "../agent-chat.js";
8
8
  import { EmbeddedExtension } from "./EmbeddedExtension.js";
9
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../components/ui/tooltip.js";
9
10
  /**
10
11
  * A named UI slot that user-installed extensions can render into. Apps drop this
11
12
  * component wherever they want to allow extensions; the framework handles
@@ -91,6 +92,6 @@ function SlotEmptyAffordance({ slotId }) {
91
92
  openSidebar: true,
92
93
  });
93
94
  };
94
- return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "flex w-full items-center gap-2 px-4 py-2 text-[11px] text-muted-foreground/60 hover:text-muted-foreground cursor-pointer", title: "Add a widget", children: [_jsx("div", { className: "h-5 w-5 rounded-md border border-dashed border-border/40 flex items-center justify-center shrink-0", children: _jsx(IconPlus, { className: "h-3 w-3" }) }), _jsx("span", { children: "Add widget" })] }) }), _jsxs(PopoverContent, { side: "left", align: "end", sideOffset: 8, className: "w-72 p-0 overflow-hidden", children: [_jsxs("div", { className: "px-3 py-2 border-b border-border/40", children: [_jsx("p", { className: "text-[12px] font-medium", children: "Add widget here" }), _jsx("p", { className: "text-[11px] text-muted-foreground/70", children: slotId })] }), _jsxs("div", { className: "max-h-72 overflow-y-auto py-1", children: [isLoading && (_jsx("div", { className: "px-3 py-3 text-[12px] text-muted-foreground/60", children: "Loading\u2026" })), !isLoading && available.length === 0 && (_jsx("div", { className: "px-3 py-3 text-[12px] text-muted-foreground/60", children: "No widgets available for this slot yet." })), available.map((extension) => (_jsx("button", { type: "button", onClick: () => install(extension.extensionId), className: "flex w-full items-start gap-2 px-3 py-2 text-left hover:bg-accent cursor-pointer", children: _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-[12px] font-medium truncate", children: extension.name }), extension.description && (_jsx("p", { className: "text-[11px] text-muted-foreground/70 truncate", children: extension.description }))] }) }, extension.extensionId)))] }), _jsx("div", { className: "border-t border-border/40 p-1", children: _jsxs("button", { type: "button", onClick: requestNew, className: "flex w-full items-center gap-2 rounded-md px-3 py-2 text-[12px] text-muted-foreground hover:bg-accent hover:text-foreground cursor-pointer", children: [_jsx(IconPlus, { className: "h-3.5 w-3.5" }), _jsx("span", { children: "Build a new widget" })] }) })] })] }));
95
+ return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(TooltipProvider, { delayDuration: 200, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "flex w-full items-center gap-2 px-4 py-2 text-[11px] text-muted-foreground/60 hover:text-muted-foreground cursor-pointer", children: [_jsx("div", { className: "h-5 w-5 rounded-md border border-dashed border-border/40 flex items-center justify-center shrink-0", children: _jsx(IconPlus, { className: "h-3 w-3" }) }), _jsx("span", { children: "Add widget" })] }) }) }), _jsx(TooltipContent, { children: "Add a widget" })] }) }), _jsxs(PopoverContent, { side: "left", align: "end", sideOffset: 8, className: "w-72 p-0 overflow-hidden", children: [_jsxs("div", { className: "px-3 py-2 border-b border-border/40", children: [_jsx("p", { className: "text-[12px] font-medium", children: "Add widget here" }), _jsx("p", { className: "text-[11px] text-muted-foreground/70", children: slotId })] }), _jsxs("div", { className: "max-h-72 overflow-y-auto py-1", children: [isLoading && (_jsx("div", { className: "px-3 py-3 text-[12px] text-muted-foreground/60", children: "Loading\u2026" })), !isLoading && available.length === 0 && (_jsx("div", { className: "px-3 py-3 text-[12px] text-muted-foreground/60", children: "No widgets available for this slot yet." })), available.map((extension) => (_jsx("button", { type: "button", onClick: () => install(extension.extensionId), className: "flex w-full items-start gap-2 px-3 py-2 text-left hover:bg-accent cursor-pointer", children: _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-[12px] font-medium truncate", children: extension.name }), extension.description && (_jsx("p", { className: "text-[11px] text-muted-foreground/70 truncate", children: extension.description }))] }) }, extension.extensionId)))] }), _jsx("div", { className: "border-t border-border/40 p-1", children: _jsxs("button", { type: "button", onClick: requestNew, className: "flex w-full items-center gap-2 rounded-md px-3 py-2 text-[12px] text-muted-foreground hover:bg-accent hover:text-foreground cursor-pointer", children: [_jsx(IconPlus, { className: "h-3.5 w-3.5" }), _jsx("span", { children: "Build a new widget" })] }) })] })] }));
95
96
  }
96
97
  //# sourceMappingURL=ExtensionSlot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExtensionSlot.js","sourceRoot":"","sources":["../../../src/client/extensions/ExtensionSlot.tsx"],"names":[],"mappings":"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","sourcesContent":["import { agentNativePath } from \"../api-path.js\";\nimport { useState } from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { IconPlus } from \"@tabler/icons-react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../components/ui/popover.js\";\nimport { sendToAgentChat } from \"../agent-chat.js\";\nimport { EmbeddedExtension } from \"./EmbeddedExtension.js\";\n\ninterface SlotInstall {\n installId: string;\n extensionId: string;\n name: string;\n description: string;\n icon: string | null;\n updatedAt: string;\n position: number;\n config: string | null;\n}\n\ninterface AvailableTool {\n extensionId: string;\n name: string;\n description: string;\n icon: string | null;\n config: string | null;\n}\n\nexport interface ExtensionSlotProps {\n /** Stable slot identifier — convention: `<app>.<area>.<position>`. */\n id: string;\n /** Object pushed to each embedded extension as `slotContext`. */\n context?: Record<string, unknown> | null;\n /** Show a small \"+\" affordance when the slot has no installs. Default: false. */\n showEmptyAffordance?: boolean;\n /** Optional className applied to the wrapper. */\n className?: string;\n /** Optional className applied to each EmbeddedExtension. */\n toolClassName?: string;\n}\n\n/**\n * A named UI slot that user-installed extensions can render into. Apps drop this\n * component wherever they want to allow extensions; the framework handles\n * fetching, sandboxing, context delivery, and lifecycle.\n *\n * Example:\n *\n * <ExtensionSlot\n * id=\"mail.contact-sidebar.bottom\"\n * context={{ contactEmail }}\n * showEmptyAffordance\n * />\n */\nexport function ExtensionSlot({\n id,\n context,\n showEmptyAffordance,\n className,\n toolClassName,\n}: ExtensionSlotProps) {\n const { data: installs = [], isLoading } = useQuery<SlotInstall[]>({\n queryKey: [\"slot-installs\", id],\n queryFn: async () => {\n const res = await fetch(\n agentNativePath(\n `/_agent-native/slots/${encodeURIComponent(id)}/installs`,\n ),\n );\n if (!res.ok) return [];\n return res.json();\n },\n });\n\n if (isLoading) {\n return null;\n }\n\n if (installs.length === 0) {\n if (!showEmptyAffordance) return null;\n return (\n <div className={className}>\n <SlotEmptyAffordance slotId={id} />\n </div>\n );\n }\n\n return (\n <div className={className}>\n {installs.map((install) => (\n <EmbeddedExtension\n key={install.installId}\n extensionId={install.extensionId}\n slotId={id}\n context={context}\n className={toolClassName}\n />\n ))}\n </div>\n );\n}\n\nfunction SlotEmptyAffordance({ slotId }: { slotId: string }) {\n const [open, setOpen] = useState(false);\n const { data: available = [], isLoading } = useQuery<AvailableTool[]>({\n queryKey: [\"slot-available\", slotId],\n queryFn: async () => {\n const res = await fetch(\n agentNativePath(\n `/_agent-native/slots/${encodeURIComponent(slotId)}/available`,\n ),\n );\n if (!res.ok) return [];\n return res.json();\n },\n enabled: open,\n });\n const queryClient = useQueryClient();\n\n const install = async (extensionId: string) => {\n queryClient.setQueryData<SlotInstall[]>(\n [\"slot-installs\", slotId],\n (old) => {\n const extension = available.find((t) => t.extensionId === extensionId);\n if (!extension || !old) return old;\n return [\n ...old,\n {\n installId: `optimistic-${extensionId}`,\n extensionId,\n name: extension.name,\n description: extension.description,\n icon: extension.icon,\n updatedAt: new Date().toISOString(),\n position: old.length,\n config: extension.config,\n },\n ];\n },\n );\n setOpen(false);\n try {\n await fetch(\n agentNativePath(\n `/_agent-native/slots/${encodeURIComponent(slotId)}/install`,\n ),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ extensionId }),\n },\n );\n } finally {\n queryClient.invalidateQueries({ queryKey: [\"slot-installs\", slotId] });\n }\n };\n\n const requestNew = () => {\n setOpen(false);\n sendToAgentChat({\n message: `Create a new widget that fits in slot \"${slotId}\". I'll describe what it should do next.`,\n submit: false,\n openSidebar: true,\n });\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n className=\"flex w-full items-center gap-2 px-4 py-2 text-[11px] text-muted-foreground/60 hover:text-muted-foreground cursor-pointer\"\n title=\"Add a widget\"\n >\n <div className=\"h-5 w-5 rounded-md border border-dashed border-border/40 flex items-center justify-center shrink-0\">\n <IconPlus className=\"h-3 w-3\" />\n </div>\n <span>Add widget</span>\n </button>\n </PopoverTrigger>\n <PopoverContent\n side=\"left\"\n align=\"end\"\n sideOffset={8}\n className=\"w-72 p-0 overflow-hidden\"\n >\n <div className=\"px-3 py-2 border-b border-border/40\">\n <p className=\"text-[12px] font-medium\">Add widget here</p>\n <p className=\"text-[11px] text-muted-foreground/70\">{slotId}</p>\n </div>\n <div className=\"max-h-72 overflow-y-auto py-1\">\n {isLoading && (\n <div className=\"px-3 py-3 text-[12px] text-muted-foreground/60\">\n Loading…\n </div>\n )}\n {!isLoading && available.length === 0 && (\n <div className=\"px-3 py-3 text-[12px] text-muted-foreground/60\">\n No widgets available for this slot yet.\n </div>\n )}\n {available.map((extension) => (\n <button\n key={extension.extensionId}\n type=\"button\"\n onClick={() => install(extension.extensionId)}\n className=\"flex w-full items-start gap-2 px-3 py-2 text-left hover:bg-accent cursor-pointer\"\n >\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-[12px] font-medium truncate\">\n {extension.name}\n </p>\n {extension.description && (\n <p className=\"text-[11px] text-muted-foreground/70 truncate\">\n {extension.description}\n </p>\n )}\n </div>\n </button>\n ))}\n </div>\n <div className=\"border-t border-border/40 p-1\">\n <button\n type=\"button\"\n onClick={requestNew}\n className=\"flex w-full items-center gap-2 rounded-md px-3 py-2 text-[12px] text-muted-foreground hover:bg-accent hover:text-foreground cursor-pointer\"\n >\n <IconPlus className=\"h-3.5 w-3.5\" />\n <span>Build a new widget</span>\n </button>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"]}
1
+ {"version":3,"file":"ExtensionSlot.js","sourceRoot":"","sources":["../../../src/client/extensions/ExtensionSlot.tsx"],"names":[],"mappings":"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","sourcesContent":["import { agentNativePath } from \"../api-path.js\";\nimport { useState } from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { IconPlus } from \"@tabler/icons-react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../components/ui/popover.js\";\nimport { sendToAgentChat } from \"../agent-chat.js\";\nimport { EmbeddedExtension } from \"./EmbeddedExtension.js\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"../components/ui/tooltip.js\";\n\ninterface SlotInstall {\n installId: string;\n extensionId: string;\n name: string;\n description: string;\n icon: string | null;\n updatedAt: string;\n position: number;\n config: string | null;\n}\n\ninterface AvailableTool {\n extensionId: string;\n name: string;\n description: string;\n icon: string | null;\n config: string | null;\n}\n\nexport interface ExtensionSlotProps {\n /** Stable slot identifier — convention: `<app>.<area>.<position>`. */\n id: string;\n /** Object pushed to each embedded extension as `slotContext`. */\n context?: Record<string, unknown> | null;\n /** Show a small \"+\" affordance when the slot has no installs. Default: false. */\n showEmptyAffordance?: boolean;\n /** Optional className applied to the wrapper. */\n className?: string;\n /** Optional className applied to each EmbeddedExtension. */\n toolClassName?: string;\n}\n\n/**\n * A named UI slot that user-installed extensions can render into. Apps drop this\n * component wherever they want to allow extensions; the framework handles\n * fetching, sandboxing, context delivery, and lifecycle.\n *\n * Example:\n *\n * <ExtensionSlot\n * id=\"mail.contact-sidebar.bottom\"\n * context={{ contactEmail }}\n * showEmptyAffordance\n * />\n */\nexport function ExtensionSlot({\n id,\n context,\n showEmptyAffordance,\n className,\n toolClassName,\n}: ExtensionSlotProps) {\n const { data: installs = [], isLoading } = useQuery<SlotInstall[]>({\n queryKey: [\"slot-installs\", id],\n queryFn: async () => {\n const res = await fetch(\n agentNativePath(\n `/_agent-native/slots/${encodeURIComponent(id)}/installs`,\n ),\n );\n if (!res.ok) return [];\n return res.json();\n },\n });\n\n if (isLoading) {\n return null;\n }\n\n if (installs.length === 0) {\n if (!showEmptyAffordance) return null;\n return (\n <div className={className}>\n <SlotEmptyAffordance slotId={id} />\n </div>\n );\n }\n\n return (\n <div className={className}>\n {installs.map((install) => (\n <EmbeddedExtension\n key={install.installId}\n extensionId={install.extensionId}\n slotId={id}\n context={context}\n className={toolClassName}\n />\n ))}\n </div>\n );\n}\n\nfunction SlotEmptyAffordance({ slotId }: { slotId: string }) {\n const [open, setOpen] = useState(false);\n const { data: available = [], isLoading } = useQuery<AvailableTool[]>({\n queryKey: [\"slot-available\", slotId],\n queryFn: async () => {\n const res = await fetch(\n agentNativePath(\n `/_agent-native/slots/${encodeURIComponent(slotId)}/available`,\n ),\n );\n if (!res.ok) return [];\n return res.json();\n },\n enabled: open,\n });\n const queryClient = useQueryClient();\n\n const install = async (extensionId: string) => {\n queryClient.setQueryData<SlotInstall[]>(\n [\"slot-installs\", slotId],\n (old) => {\n const extension = available.find((t) => t.extensionId === extensionId);\n if (!extension || !old) return old;\n return [\n ...old,\n {\n installId: `optimistic-${extensionId}`,\n extensionId,\n name: extension.name,\n description: extension.description,\n icon: extension.icon,\n updatedAt: new Date().toISOString(),\n position: old.length,\n config: extension.config,\n },\n ];\n },\n );\n setOpen(false);\n try {\n await fetch(\n agentNativePath(\n `/_agent-native/slots/${encodeURIComponent(slotId)}/install`,\n ),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ extensionId }),\n },\n );\n } finally {\n queryClient.invalidateQueries({ queryKey: [\"slot-installs\", slotId] });\n }\n };\n\n const requestNew = () => {\n setOpen(false);\n sendToAgentChat({\n message: `Create a new widget that fits in slot \"${slotId}\". I'll describe what it should do next.`,\n submit: false,\n openSidebar: true,\n });\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <TooltipProvider delayDuration={200}>\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n className=\"flex w-full items-center gap-2 px-4 py-2 text-[11px] text-muted-foreground/60 hover:text-muted-foreground cursor-pointer\"\n >\n <div className=\"h-5 w-5 rounded-md border border-dashed border-border/40 flex items-center justify-center shrink-0\">\n <IconPlus className=\"h-3 w-3\" />\n </div>\n <span>Add widget</span>\n </button>\n </PopoverTrigger>\n </TooltipTrigger>\n <TooltipContent>Add a widget</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <PopoverContent\n side=\"left\"\n align=\"end\"\n sideOffset={8}\n className=\"w-72 p-0 overflow-hidden\"\n >\n <div className=\"px-3 py-2 border-b border-border/40\">\n <p className=\"text-[12px] font-medium\">Add widget here</p>\n <p className=\"text-[11px] text-muted-foreground/70\">{slotId}</p>\n </div>\n <div className=\"max-h-72 overflow-y-auto py-1\">\n {isLoading && (\n <div className=\"px-3 py-3 text-[12px] text-muted-foreground/60\">\n Loading…\n </div>\n )}\n {!isLoading && available.length === 0 && (\n <div className=\"px-3 py-3 text-[12px] text-muted-foreground/60\">\n No widgets available for this slot yet.\n </div>\n )}\n {available.map((extension) => (\n <button\n key={extension.extensionId}\n type=\"button\"\n onClick={() => install(extension.extensionId)}\n className=\"flex w-full items-start gap-2 px-3 py-2 text-left hover:bg-accent cursor-pointer\"\n >\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-[12px] font-medium truncate\">\n {extension.name}\n </p>\n {extension.description && (\n <p className=\"text-[11px] text-muted-foreground/70 truncate\">\n {extension.description}\n </p>\n )}\n </div>\n </button>\n ))}\n </div>\n <div className=\"border-t border-border/40 p-1\">\n <button\n type=\"button\"\n onClick={requestNew}\n className=\"flex w-full items-center gap-2 rounded-md px-3 py-2 text-[12px] text-muted-foreground hover:bg-accent hover:text-foreground cursor-pointer\"\n >\n <IconPlus className=\"h-3.5 w-3.5\" />\n <span>Build a new widget</span>\n </button>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ExtensionViewer.d.ts","sourceRoot":"","sources":["../../../src/client/extensions/ExtensionViewer.tsx"],"names":[],"mappings":"AA8EA,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;CACrB;AA4DD,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,EAAE,oBAAoB,2CAkZpE"}
1
+ {"version":3,"file":"ExtensionViewer.d.ts","sourceRoot":"","sources":["../../../src/client/extensions/ExtensionViewer.tsx"],"names":[],"mappings":"AAsFA,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;CACrB;AA2ED,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,EAAE,oBAAoB,2CA4cpE"}
@@ -2,8 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { agentNativePath } from "../api-path.js";
3
3
  import { useState, useEffect, useRef, useCallback, useMemo } from "react";
4
4
  import { useQuery, useQueryClient } from "@tanstack/react-query";
5
- import { useNavigate } from "react-router";
6
- import { IconDots, IconLoader2, IconPencil, IconRefresh, IconTrash, IconX, } from "@tabler/icons-react";
5
+ import { Link, useNavigate } from "react-router";
6
+ import { IconArrowLeft, IconChevronRight, IconDots, IconLoader2, IconPencil, IconRefresh, IconTrash, IconX, } from "@tabler/icons-react";
7
7
  import { ShareButton } from "../sharing/ShareButton.js";
8
8
  import { AgentToggleButton } from "../AgentPanel.js";
9
9
  import { NotificationsBell } from "../notifications/NotificationsBell.js";
@@ -11,6 +11,7 @@ import { sendToAgentChat } from "../agent-chat.js";
11
11
  import { PromptComposer } from "../composer/PromptComposer.js";
12
12
  import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
13
13
  import { isAllowedExtensionPath, sanitizeExtensionRequestOptions, checkBridgePolicy, } from "./iframe-bridge.js";
14
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../components/ui/tooltip.js";
14
15
  const THEME_CSS_VARS = [
15
16
  "--background",
16
17
  "--foreground",
@@ -51,8 +52,12 @@ function getParentThemeVars() {
51
52
  }
52
53
  return vars;
53
54
  }
54
- function EditToolPopover({ extension }) {
55
+ function EditToolPopover({ extension, onOpenChange, }) {
55
56
  const [open, setOpen] = useState(false);
57
+ const setOpenAndNotify = (v) => {
58
+ setOpen(v);
59
+ onOpenChange?.(v);
60
+ };
56
61
  // Radix's outside-click detection runs in the parent document, so a click
57
62
  // inside the extension iframe (or any other iframe) never fires it. The browser
58
63
  // does shift focus to the iframe though, which blurs the parent window — we
@@ -65,7 +70,7 @@ function EditToolPopover({ extension }) {
65
70
  // reflects the iframe (or whatever the user clicked on).
66
71
  setTimeout(() => {
67
72
  if (document.activeElement?.tagName === "IFRAME")
68
- setOpen(false);
73
+ setOpenAndNotify(false);
69
74
  }, 0);
70
75
  };
71
76
  window.addEventListener("blur", handleBlur);
@@ -81,9 +86,9 @@ function EditToolPopover({ extension }) {
81
86
  submit: true,
82
87
  openSidebar: true,
83
88
  });
84
- setOpen(false);
89
+ setOpenAndNotify(false);
85
90
  };
86
- return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "inline-flex items-center justify-center rounded-md h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer", title: "Edit", children: _jsx(IconPencil, { className: "h-4 w-4" }) }) }), _jsxs(PopoverContent, { align: "end", sideOffset: 6, className: "w-[420px] p-3", children: [_jsx("p", { className: "px-1 pb-2 text-sm font-semibold text-foreground", children: "Edit extension" }), _jsx(PromptComposer, { autoFocus: true, placeholder: "What would you like to change?", draftScope: `extensions:edit:${extension.id}`, onSubmit: handleSubmit })] })] }));
91
+ return (_jsxs(Popover, { open: open, onOpenChange: setOpenAndNotify, children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "inline-flex items-center justify-center rounded-md h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer", children: _jsx(IconPencil, { className: "h-4 w-4" }) }) }) }), _jsx(TooltipContent, { children: "Edit" })] }), _jsxs(PopoverContent, { align: "end", sideOffset: 6, className: "w-[420px] p-3", children: [_jsx("p", { className: "px-1 pb-2 text-sm font-semibold text-foreground", children: "Edit extension" }), _jsx(PromptComposer, { autoFocus: true, placeholder: "What would you like to change?", draftScope: `extensions:edit:${extension.id}`, onSubmit: handleSubmit })] })] }));
87
92
  }
88
93
  export function ExtensionViewer({ extensionId }) {
89
94
  const [isDark, setIsDark] = useState(false);
@@ -94,6 +99,16 @@ export function ExtensionViewer({ extensionId }) {
94
99
  const [renameValue, setRenameValue] = useState("");
95
100
  const [refreshKey, setRefreshKey] = useState(0);
96
101
  const renameInputRef = useRef(null);
102
+ // Tracks how many toolbar popovers are open. Iframes capture pointer events
103
+ // from areas they visually overlap, so when a popover opens above the iframe,
104
+ // hover and click on the popover items get swallowed by the iframe. Disabling
105
+ // pointer-events on the iframe while any popover is open lets the popover
106
+ // receive its own events. Each popover increments on open / decrements on
107
+ // close, so concurrent popovers (rare) compose correctly.
108
+ const [openPopoverCount, setOpenPopoverCount] = useState(0);
109
+ const onPopoverOpenChange = useCallback((open) => {
110
+ setOpenPopoverCount((c) => Math.max(0, c + (open ? 1 : -1)));
111
+ }, []);
97
112
  const queryClient = useQueryClient();
98
113
  // (audit H4) Role plumbed through from the iframe's render binding. Until
99
114
  // the iframe announces its role we deny non-trivial helper calls — that
@@ -340,21 +355,27 @@ export function ExtensionViewer({ extensionId }) {
340
355
  if (!extension) {
341
356
  return (_jsx("div", { className: "flex h-full items-center justify-center text-sm text-muted-foreground", children: "Extension not found" }));
342
357
  }
343
- return (_jsxs("div", { className: "flex h-full w-full flex-col", children: [_jsxs("div", { className: "flex h-12 items-center justify-between border-b px-3 shrink-0", children: [_jsx("div", { className: "group/name flex items-center gap-1", children: isRenaming ? (_jsx("input", { ref: renameInputRef, value: renameValue, onChange: (e) => setRenameValue(e.target.value), onBlur: submitRename, onKeyDown: (e) => {
344
- if (e.key === "Enter")
345
- submitRename();
346
- if (e.key === "Escape")
347
- setIsRenaming(false);
348
- }, className: "text-sm font-medium bg-transparent border-b border-primary outline-none py-0 px-0" })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-sm font-medium", children: extension.name }), _jsx("button", { type: "button", onClick: startRename, className: "cursor-pointer rounded p-0.5 text-muted-foreground/40 opacity-0 group-hover/name:opacity-100 hover:text-foreground", title: "Rename", children: _jsx(IconPencil, { className: "h-3 w-3" }) })] })) }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => setRefreshKey((k) => k + 1), className: "inline-flex items-center justify-center rounded-md h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer", title: "Refresh", children: _jsx(IconRefresh, { className: "h-4 w-4" }) }), _jsx(EditToolPopover, { extension: extension }), _jsx(ShareButton, { resourceType: "extension", resourceId: extensionId, resourceTitle: extension.name }), _jsx(ToolMoreMenu, { extensionId: extensionId, toolName: extension.name }), _jsx(NotificationsBell, {}), _jsx(AgentToggleButton, { className: "h-8 w-8 rounded-md hover:bg-accent" })] })] }), _jsxs("div", { className: "relative flex-1 min-h-0", children: [!iframeReady && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-background z-10", children: _jsx(IconLoader2, { className: "size-5 animate-spin text-muted-foreground", role: "status", "aria-label": "Loading" }) })), _jsx("iframe", { ref: iframeRef, src: iframeSrc, className: "h-full w-full border-0", sandbox: "allow-scripts allow-forms", title: extension.name, onLoad: () => {
349
- sendThemeToIframe();
350
- setTimeout(() => setIframeReady(true), 150);
351
- } }, `${extension.updatedAt}-${refreshKey}`)] })] }));
358
+ return (_jsx(TooltipProvider, { delayDuration: 200, children: _jsxs("div", { className: "flex h-full w-full flex-col", children: [_jsxs("div", { className: "flex h-12 shrink-0 items-center justify-between gap-3 border-b px-3", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Link, { to: "/", className: "inline-flex h-8 shrink-0 items-center justify-center gap-1.5 rounded-md px-2 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-accent-foreground", "aria-label": "Back to app", children: [_jsx(IconArrowLeft, { className: "h-4 w-4" }), _jsx("span", { className: "hidden sm:inline", children: "Back to app" })] }) }), _jsx(TooltipContent, { children: "Back to app" })] }), _jsxs("nav", { "aria-label": "Extension breadcrumb", className: "group/name flex min-w-0 items-center gap-1 text-sm", children: [_jsx(Link, { to: "/extensions", className: "shrink-0 text-muted-foreground hover:text-foreground", children: "Extensions" }), _jsx(IconChevronRight, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground/60" }), isRenaming ? (_jsx("input", { ref: renameInputRef, value: renameValue, onChange: (e) => setRenameValue(e.target.value), onBlur: submitRename, onKeyDown: (e) => {
359
+ if (e.key === "Enter")
360
+ submitRename();
361
+ if (e.key === "Escape")
362
+ setIsRenaming(false);
363
+ }, className: "min-w-0 bg-transparent px-0 py-0 text-sm font-medium outline-none border-b border-primary" })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "truncate text-sm font-medium", children: extension.name }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: startRename, className: "shrink-0 cursor-pointer rounded p-0.5 text-muted-foreground/40 opacity-0 group-hover/name:opacity-100 hover:text-foreground", children: _jsx(IconPencil, { className: "h-3 w-3" }) }) }), _jsx(TooltipContent, { children: "Rename" })] })] }))] })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => setRefreshKey((k) => k + 1), className: "inline-flex items-center justify-center rounded-md h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer", children: _jsx(IconRefresh, { className: "h-4 w-4" }) }) }), _jsx(TooltipContent, { children: "Refresh" })] }), _jsx(EditToolPopover, { extension: extension, onOpenChange: onPopoverOpenChange }), _jsx(ShareButton, { resourceType: "extension", resourceId: extensionId, resourceTitle: extension.name, onOpenChange: onPopoverOpenChange }), _jsx(ToolMoreMenu, { extensionId: extensionId, toolName: extension.name, onOpenChange: onPopoverOpenChange }), _jsx(NotificationsBell, {}), _jsx(AgentToggleButton, { className: "h-8 w-8 rounded-md hover:bg-accent" })] })] }), _jsxs("div", { className: "relative flex-1 min-h-0", children: [!iframeReady && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-background z-10", children: _jsx(IconLoader2, { className: "size-5 animate-spin text-muted-foreground", role: "status", "aria-label": "Loading" }) })), _jsx("iframe", { ref: iframeRef, src: iframeSrc, className: "h-full w-full border-0", sandbox: "allow-scripts allow-forms", title: extension.name, style: {
364
+ pointerEvents: openPopoverCount > 0 ? "none" : "auto",
365
+ }, onLoad: () => {
366
+ sendThemeToIframe();
367
+ setTimeout(() => setIframeReady(true), 150);
368
+ } }, `${extension.updatedAt}-${refreshKey}`)] })] }) }));
352
369
  }
353
- function ToolMoreMenu({ extensionId, toolName, }) {
370
+ function ToolMoreMenu({ extensionId, toolName, onOpenChange, }) {
354
371
  const [open, setOpen] = useState(false);
355
372
  const [confirmingDelete, setConfirmingDelete] = useState(false);
356
373
  const queryClient = useQueryClient();
357
374
  const navigate = useNavigate();
375
+ const setOpenAndNotify = (v) => {
376
+ setOpen(v);
377
+ onOpenChange?.(v);
378
+ };
358
379
  const { data: slots = [] } = useQuery({
359
380
  queryKey: ["extension-slots", extensionId],
360
381
  queryFn: async () => {
@@ -366,7 +387,7 @@ function ToolMoreMenu({ extensionId, toolName, }) {
366
387
  enabled: open,
367
388
  });
368
389
  const closeMenu = () => {
369
- setOpen(false);
390
+ setOpenAndNotify(false);
370
391
  setConfirmingDelete(false);
371
392
  };
372
393
  const removeFromSlot = async (slotId) => {
@@ -394,9 +415,9 @@ function ToolMoreMenu({ extensionId, toolName, }) {
394
415
  }
395
416
  };
396
417
  return (_jsxs(Popover, { open: open, onOpenChange: (o) => {
397
- setOpen(o);
418
+ setOpenAndNotify(o);
398
419
  if (!o)
399
420
  setConfirmingDelete(false);
400
- }, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "inline-flex items-center justify-center rounded-md h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer", title: "More options", "aria-label": "More options", children: _jsx(IconDots, { className: "h-4 w-4" }) }) }), _jsx(PopoverContent, { align: "end", sideOffset: 4, className: "w-72 p-0", children: !confirmingDelete ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "px-3 py-2 border-b border-border/40", children: [_jsx("p", { className: "text-[12px] font-medium", children: "Appears in" }), slots.length === 0 ? (_jsx("p", { className: "text-[11px] text-muted-foreground/70 mt-0.5", children: "Not installed in any widget areas. Ask the agent to add it somewhere." })) : (_jsxs("p", { className: "text-[11px] text-muted-foreground/70 mt-0.5", children: ["This extension can render in ", slots.length, " widget area", slots.length === 1 ? "" : "s", "."] }))] }), slots.length > 0 && (_jsx("div", { className: "max-h-48 overflow-y-auto py-1", children: slots.map((s) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 text-[12px]", children: [_jsx("span", { className: "flex-1 truncate font-mono text-[11px] text-muted-foreground", children: s.slotId }), _jsx("button", { type: "button", onClick: () => removeFromSlot(s.slotId), className: "rounded p-1 text-muted-foreground/60 hover:bg-accent hover:text-foreground cursor-pointer", title: "Remove from this widget area (for me)", "aria-label": "Remove from this widget area", children: _jsx(IconX, { className: "h-3.5 w-3.5" }) })] }, s.id))) })), _jsx("div", { className: "border-t border-border/40 p-1", children: _jsxs("button", { type: "button", onClick: () => setConfirmingDelete(true), className: "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-[12px] text-destructive hover:bg-destructive/10 cursor-pointer text-left", children: [_jsx(IconTrash, { className: "h-3.5 w-3.5" }), _jsx("span", { children: "Delete extension\u2026" })] }) })] })) : (_jsxs("div", { className: "flex flex-col gap-2 p-3", children: [_jsxs("p", { className: "text-[12px]", children: ["Delete ", _jsx("span", { className: "font-medium", children: toolName }), "? This removes the extension everywhere, for everyone it's shared with."] }), _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx("button", { type: "button", onClick: () => setConfirmingDelete(false), className: "rounded-md px-2 py-1 text-[12px] hover:bg-accent cursor-pointer", children: "Cancel" }), _jsx("button", { type: "button", onClick: deleteExtension, className: "rounded-md bg-destructive px-2 py-1 text-[12px] text-destructive-foreground hover:bg-destructive/90 cursor-pointer", children: "Delete" })] })] })) })] }));
421
+ }, children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "inline-flex items-center justify-center rounded-md h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer", "aria-label": "More options", children: _jsx(IconDots, { className: "h-4 w-4" }) }) }) }), _jsx(TooltipContent, { children: "More options" })] }), _jsx(PopoverContent, { align: "end", sideOffset: 4, className: "w-72 p-0", children: !confirmingDelete ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "px-3 py-2 border-b border-border/40", children: [_jsx("p", { className: "text-[12px] font-medium", children: "Appears in" }), slots.length === 0 ? (_jsx("p", { className: "text-[11px] text-muted-foreground/70 mt-0.5", children: "Not installed in any widget areas. Ask the agent to add it somewhere." })) : (_jsxs("p", { className: "text-[11px] text-muted-foreground/70 mt-0.5", children: ["This extension can render in ", slots.length, " widget area", slots.length === 1 ? "" : "s", "."] }))] }), slots.length > 0 && (_jsx("div", { className: "max-h-48 overflow-y-auto py-1", children: slots.map((s) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 text-[12px]", children: [_jsx("span", { className: "flex-1 truncate font-mono text-[11px] text-muted-foreground", children: s.slotId }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => removeFromSlot(s.slotId), className: "rounded p-1 text-muted-foreground/60 hover:bg-accent hover:text-foreground cursor-pointer", "aria-label": "Remove from this widget area", children: _jsx(IconX, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Remove from this widget area (for me)" })] })] }, s.id))) })), _jsx("div", { className: "border-t border-border/40 p-1", children: _jsxs("button", { type: "button", onClick: () => setConfirmingDelete(true), className: "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-[12px] text-destructive hover:bg-destructive/10 cursor-pointer text-left", children: [_jsx(IconTrash, { className: "h-3.5 w-3.5" }), _jsx("span", { children: "Delete extension\u2026" })] }) })] })) : (_jsxs("div", { className: "flex flex-col gap-2 p-3", children: [_jsxs("p", { className: "text-[12px]", children: ["Delete ", _jsx("span", { className: "font-medium", children: toolName }), "? This removes the extension everywhere, for everyone it's shared with."] }), _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx("button", { type: "button", onClick: () => setConfirmingDelete(false), className: "rounded-md px-2 py-1 text-[12px] hover:bg-accent cursor-pointer", children: "Cancel" }), _jsx("button", { type: "button", onClick: deleteExtension, className: "rounded-md bg-destructive px-2 py-1 text-[12px] text-destructive-foreground hover:bg-destructive/90 cursor-pointer", children: "Delete" })] })] })) })] }));
401
422
  }
402
423
  //# sourceMappingURL=ExtensionViewer.js.map