@agent-native/core 0.7.81 → 0.7.83

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 (257) hide show
  1. package/dist/action.d.ts +8 -0
  2. package/dist/action.d.ts.map +1 -1
  3. package/dist/action.js +4 -0
  4. package/dist/action.js.map +1 -1
  5. package/dist/agent/production-agent.d.ts +12 -2
  6. package/dist/agent/production-agent.d.ts.map +1 -1
  7. package/dist/agent/production-agent.js +58 -20
  8. package/dist/agent/production-agent.js.map +1 -1
  9. package/dist/agent/run-manager.d.ts +8 -1
  10. package/dist/agent/run-manager.d.ts.map +1 -1
  11. package/dist/agent/run-manager.js +11 -12
  12. package/dist/agent/run-manager.js.map +1 -1
  13. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  14. package/dist/agent/thread-data-builder.js +13 -17
  15. package/dist/agent/thread-data-builder.js.map +1 -1
  16. package/dist/agent/types.d.ts +4 -0
  17. package/dist/agent/types.d.ts.map +1 -1
  18. package/dist/agent/types.js.map +1 -1
  19. package/dist/application-state/handlers.d.ts.map +1 -1
  20. package/dist/application-state/handlers.js +3 -8
  21. package/dist/application-state/handlers.js.map +1 -1
  22. package/dist/application-state/script-helpers.d.ts +2 -4
  23. package/dist/application-state/script-helpers.d.ts.map +1 -1
  24. package/dist/application-state/script-helpers.js +10 -47
  25. package/dist/application-state/script-helpers.js.map +1 -1
  26. package/dist/cli/create.d.ts +1 -1
  27. package/dist/cli/create.d.ts.map +1 -1
  28. package/dist/cli/create.js +35 -10
  29. package/dist/cli/create.js.map +1 -1
  30. package/dist/cli/workspace-dev.js +78 -15
  31. package/dist/cli/workspace-dev.js.map +1 -1
  32. package/dist/client/AgentPanel.d.ts.map +1 -1
  33. package/dist/client/AgentPanel.js +6 -2
  34. package/dist/client/AgentPanel.js.map +1 -1
  35. package/dist/client/AssistantChat.d.ts +0 -15
  36. package/dist/client/AssistantChat.d.ts.map +1 -1
  37. package/dist/client/AssistantChat.js +69 -57
  38. package/dist/client/AssistantChat.js.map +1 -1
  39. package/dist/client/ConnectBuilderCard.d.ts +7 -1
  40. package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
  41. package/dist/client/ConnectBuilderCard.js +46 -5
  42. package/dist/client/ConnectBuilderCard.js.map +1 -1
  43. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  44. package/dist/client/ErrorBoundary.js +20 -5
  45. package/dist/client/ErrorBoundary.js.map +1 -1
  46. package/dist/client/FeedbackButton.d.ts +3 -2
  47. package/dist/client/FeedbackButton.d.ts.map +1 -1
  48. package/dist/client/FeedbackButton.js +23 -15
  49. package/dist/client/FeedbackButton.js.map +1 -1
  50. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  51. package/dist/client/agent-chat-adapter.js +303 -169
  52. package/dist/client/agent-chat-adapter.js.map +1 -1
  53. package/dist/client/builder-frame.d.ts +36 -0
  54. package/dist/client/builder-frame.d.ts.map +1 -1
  55. package/dist/client/builder-frame.js +80 -9
  56. package/dist/client/builder-frame.js.map +1 -1
  57. package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
  58. package/dist/client/composer/ComposerPlusMenu.js +7 -2
  59. package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
  60. package/dist/client/composer/PastedTextChip.d.ts +9 -0
  61. package/dist/client/composer/PastedTextChip.d.ts.map +1 -0
  62. package/dist/client/composer/PastedTextChip.js +47 -0
  63. package/dist/client/composer/PastedTextChip.js.map +1 -0
  64. package/dist/client/composer/PromptComposer.d.ts +4 -2
  65. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  66. package/dist/client/composer/PromptComposer.js +33 -5
  67. package/dist/client/composer/PromptComposer.js.map +1 -1
  68. package/dist/client/composer/TiptapComposer.d.ts +13 -1
  69. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  70. package/dist/client/composer/TiptapComposer.js +61 -16
  71. package/dist/client/composer/TiptapComposer.js.map +1 -1
  72. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  73. package/dist/client/composer/VoiceButton.js +5 -1
  74. package/dist/client/composer/VoiceButton.js.map +1 -1
  75. package/dist/client/composer/pasted-text.d.ts +6 -0
  76. package/dist/client/composer/pasted-text.d.ts.map +1 -0
  77. package/dist/client/composer/pasted-text.js +49 -0
  78. package/dist/client/composer/pasted-text.js.map +1 -0
  79. package/dist/client/composer/useVoiceDictation.d.ts +1 -0
  80. package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
  81. package/dist/client/composer/useVoiceDictation.js +18 -0
  82. package/dist/client/composer/useVoiceDictation.js.map +1 -1
  83. package/dist/client/index.d.ts +0 -1
  84. package/dist/client/index.d.ts.map +1 -1
  85. package/dist/client/index.js +0 -1
  86. package/dist/client/index.js.map +1 -1
  87. package/dist/client/integrations/IntegrationCard.d.ts.map +1 -1
  88. package/dist/client/integrations/IntegrationCard.js +14 -2
  89. package/dist/client/integrations/IntegrationCard.js.map +1 -1
  90. package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
  91. package/dist/client/integrations/IntegrationsPanel.js +23 -4
  92. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  93. package/dist/client/notifications/NotificationsBell.d.ts.map +1 -1
  94. package/dist/client/notifications/NotificationsBell.js +4 -42
  95. package/dist/client/notifications/NotificationsBell.js.map +1 -1
  96. package/dist/client/org/OrgSwitcher.d.ts +4 -6
  97. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  98. package/dist/client/org/OrgSwitcher.js +84 -74
  99. package/dist/client/org/OrgSwitcher.js.map +1 -1
  100. package/dist/client/org/TeamPage.d.ts.map +1 -1
  101. package/dist/client/org/TeamPage.js +3 -154
  102. package/dist/client/org/TeamPage.js.map +1 -1
  103. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  104. package/dist/client/resources/ResourcesPanel.js +13 -35
  105. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  106. package/dist/client/settings/SettingsPanel.js +1 -1
  107. package/dist/client/settings/SettingsPanel.js.map +1 -1
  108. package/dist/client/settings/useBuilderStatus.d.ts +6 -0
  109. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  110. package/dist/client/settings/useBuilderStatus.js +3 -0
  111. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  112. package/dist/client/sse-event-processor.d.ts +15 -1
  113. package/dist/client/sse-event-processor.d.ts.map +1 -1
  114. package/dist/client/sse-event-processor.js +58 -54
  115. package/dist/client/sse-event-processor.js.map +1 -1
  116. package/dist/client/tools/ToolEditor.d.ts.map +1 -1
  117. package/dist/client/tools/ToolEditor.js +34 -4
  118. package/dist/client/tools/ToolEditor.js.map +1 -1
  119. package/dist/client/tools/ToolViewer.d.ts.map +1 -1
  120. package/dist/client/tools/ToolViewer.js +20 -1
  121. package/dist/client/tools/ToolViewer.js.map +1 -1
  122. package/dist/client/tools/ToolsListPage.d.ts.map +1 -1
  123. package/dist/client/tools/ToolsListPage.js +2 -1
  124. package/dist/client/tools/ToolsListPage.js.map +1 -1
  125. package/dist/client/tools/ToolsSidebarSection.js +1 -1
  126. package/dist/client/tools/ToolsSidebarSection.js.map +1 -1
  127. package/dist/client/transcription/BuilderTranscriptionCta.js +1 -1
  128. package/dist/client/transcription/BuilderTranscriptionCta.js.map +1 -1
  129. package/dist/client/use-chat-threads.d.ts.map +1 -1
  130. package/dist/client/use-chat-threads.js +7 -2
  131. package/dist/client/use-chat-threads.js.map +1 -1
  132. package/dist/collab/client.d.ts.map +1 -1
  133. package/dist/collab/client.js +26 -7
  134. package/dist/collab/client.js.map +1 -1
  135. package/dist/jobs/scheduler.js +0 -4
  136. package/dist/jobs/scheduler.js.map +1 -1
  137. package/dist/oauth-tokens/store.d.ts +0 -4
  138. package/dist/oauth-tokens/store.d.ts.map +1 -1
  139. package/dist/oauth-tokens/store.js +3 -24
  140. package/dist/oauth-tokens/store.js.map +1 -1
  141. package/dist/observability/routes.d.ts.map +1 -1
  142. package/dist/observability/routes.js +1 -9
  143. package/dist/observability/routes.js.map +1 -1
  144. package/dist/onboarding/default-steps.js +1 -1
  145. package/dist/onboarding/default-steps.js.map +1 -1
  146. package/dist/onboarding/plugin.d.ts.map +1 -1
  147. package/dist/onboarding/plugin.js +1 -8
  148. package/dist/onboarding/plugin.js.map +1 -1
  149. package/dist/org/accept-pending.d.ts.map +1 -1
  150. package/dist/org/accept-pending.js +1 -2
  151. package/dist/org/accept-pending.js.map +1 -1
  152. package/dist/org/context.d.ts +0 -2
  153. package/dist/org/context.d.ts.map +1 -1
  154. package/dist/org/context.js +0 -5
  155. package/dist/org/context.js.map +1 -1
  156. package/dist/resources/script-helpers.d.ts +3 -4
  157. package/dist/resources/script-helpers.d.ts.map +1 -1
  158. package/dist/resources/script-helpers.js +8 -15
  159. package/dist/resources/script-helpers.js.map +1 -1
  160. package/dist/scripts/chat/search-chats.d.ts.map +1 -1
  161. package/dist/scripts/chat/search-chats.js +4 -4
  162. package/dist/scripts/chat/search-chats.js.map +1 -1
  163. package/dist/scripts/manage-agent-loop-settings.js +2 -2
  164. package/dist/scripts/manage-agent-loop-settings.js.map +1 -1
  165. package/dist/scripts/resources/delete-memory.d.ts.map +1 -1
  166. package/dist/scripts/resources/delete-memory.js +4 -2
  167. package/dist/scripts/resources/delete-memory.js.map +1 -1
  168. package/dist/scripts/resources/delete.d.ts.map +1 -1
  169. package/dist/scripts/resources/delete.js +11 -4
  170. package/dist/scripts/resources/delete.js.map +1 -1
  171. package/dist/scripts/resources/list.d.ts.map +1 -1
  172. package/dist/scripts/resources/list.js +5 -3
  173. package/dist/scripts/resources/list.js.map +1 -1
  174. package/dist/scripts/resources/migrate-learnings.d.ts.map +1 -1
  175. package/dist/scripts/resources/migrate-learnings.js +5 -2
  176. package/dist/scripts/resources/migrate-learnings.js.map +1 -1
  177. package/dist/scripts/resources/read.d.ts.map +1 -1
  178. package/dist/scripts/resources/read.js +4 -2
  179. package/dist/scripts/resources/read.js.map +1 -1
  180. package/dist/scripts/resources/save-memory.d.ts.map +1 -1
  181. package/dist/scripts/resources/save-memory.js +4 -2
  182. package/dist/scripts/resources/save-memory.js.map +1 -1
  183. package/dist/scripts/resources/write.d.ts.map +1 -1
  184. package/dist/scripts/resources/write.js +11 -4
  185. package/dist/scripts/resources/write.js.map +1 -1
  186. package/dist/secrets/onboarding.d.ts.map +1 -1
  187. package/dist/secrets/onboarding.js +1 -9
  188. package/dist/secrets/onboarding.js.map +1 -1
  189. package/dist/secrets/routes.d.ts.map +1 -1
  190. package/dist/secrets/routes.js +2 -7
  191. package/dist/secrets/routes.js.map +1 -1
  192. package/dist/server/action-discovery.d.ts +15 -0
  193. package/dist/server/action-discovery.d.ts.map +1 -1
  194. package/dist/server/action-discovery.js +49 -0
  195. package/dist/server/action-discovery.js.map +1 -1
  196. package/dist/server/agent-chat-plugin.d.ts +5 -0
  197. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  198. package/dist/server/agent-chat-plugin.js +81 -20
  199. package/dist/server/agent-chat-plugin.js.map +1 -1
  200. package/dist/server/agent-discovery.d.ts.map +1 -1
  201. package/dist/server/agent-discovery.js +5 -7
  202. package/dist/server/agent-discovery.js.map +1 -1
  203. package/dist/server/auth.d.ts +16 -20
  204. package/dist/server/auth.d.ts.map +1 -1
  205. package/dist/server/auth.js +115 -333
  206. package/dist/server/auth.js.map +1 -1
  207. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  208. package/dist/server/core-routes-plugin.js +23 -16
  209. package/dist/server/core-routes-plugin.js.map +1 -1
  210. package/dist/server/credential-provider.d.ts.map +1 -1
  211. package/dist/server/credential-provider.js +1 -2
  212. package/dist/server/credential-provider.js.map +1 -1
  213. package/dist/server/google-oauth.d.ts +14 -2
  214. package/dist/server/google-oauth.d.ts.map +1 -1
  215. package/dist/server/google-oauth.js +32 -10
  216. package/dist/server/google-oauth.js.map +1 -1
  217. package/dist/server/index.d.ts +3 -3
  218. package/dist/server/index.d.ts.map +1 -1
  219. package/dist/server/index.js +2 -2
  220. package/dist/server/index.js.map +1 -1
  221. package/dist/server/oauth-helpers.d.ts +2 -4
  222. package/dist/server/oauth-helpers.d.ts.map +1 -1
  223. package/dist/server/oauth-helpers.js +2 -4
  224. package/dist/server/oauth-helpers.js.map +1 -1
  225. package/dist/server/transcribe-voice.d.ts.map +1 -1
  226. package/dist/server/transcribe-voice.js +2 -4
  227. package/dist/server/transcribe-voice.js.map +1 -1
  228. package/dist/triggers/dispatcher.d.ts.map +1 -1
  229. package/dist/triggers/dispatcher.js +0 -3
  230. package/dist/triggers/dispatcher.js.map +1 -1
  231. package/dist/vite/client.d.ts.map +1 -1
  232. package/dist/vite/client.js +13 -0
  233. package/dist/vite/client.js.map +1 -1
  234. package/docs/content/actions.md +1 -0
  235. package/docs/content/authentication.md +3 -20
  236. package/docs/content/creating-templates.md +1 -1
  237. package/docs/content/deployment.md +0 -1
  238. package/docs/content/security.md +0 -1
  239. package/docs/content/template-analytics.md +10 -0
  240. package/docs/content/template-calendar.md +10 -0
  241. package/docs/content/template-clips.md +10 -0
  242. package/docs/content/template-content.md +11 -1
  243. package/docs/content/template-dispatch.md +10 -0
  244. package/docs/content/template-forms.md +10 -0
  245. package/docs/content/template-mail.md +10 -0
  246. package/docs/content/template-slides.md +11 -1
  247. package/docs/content/template-starter.md +11 -1
  248. package/docs/content/template-video.md +10 -0
  249. package/package.json +1 -1
  250. package/dist/client/dev-mode.d.ts +0 -14
  251. package/dist/client/dev-mode.d.ts.map +0 -1
  252. package/dist/client/dev-mode.js +0 -14
  253. package/dist/client/dev-mode.js.map +0 -1
  254. package/dist/server/local-migration.d.ts +0 -41
  255. package/dist/server/local-migration.d.ts.map +0 -1
  256. package/dist/server/local-migration.js +0 -235
  257. package/dist/server/local-migration.js.map +0 -1
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useRef, useState } from "react";
3
- import { IconBuilding, IconCheck, IconLoader2, IconPlus, IconSelector, IconUserPlus, } from "@tabler/icons-react";
2
+ import { useState } from "react";
3
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
4
+ import { IconBuilding, IconCheck, IconLoader2, IconLogout, IconPlus, IconSelector, IconUser, IconUserPlus, } from "@tabler/icons-react";
4
5
  import { useOrg, useSwitchOrg, useCreateOrg, useInviteMember, useAcceptInvitation, } from "./hooks.js";
5
- import { DEV_MODE_USER_EMAIL } from "../dev-mode.js";
6
+ import { agentNativePath } from "../api-path.js";
6
7
  function personalLabelFromEmail(email) {
7
8
  if (!email)
8
9
  return "Personal";
@@ -16,13 +17,14 @@ function personalLabelFromEmail(email) {
16
17
  .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
17
18
  .join(" ");
18
19
  }
20
+ const POPOVER_CONTENT_CLASS = "z-50 min-w-[14rem] rounded-md border border-border bg-popover py-1 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2";
21
+ const ITEM_CLASS = "flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-foreground hover:bg-accent focus-visible:bg-accent focus:outline-none disabled:opacity-50 disabled:pointer-events-none";
22
+ const SECTION_LABEL_CLASS = "px-2.5 pt-1 pb-0.5 text-[10px] uppercase tracking-wide text-muted-foreground";
19
23
  /**
20
- * Compact org switcher button. Shows the active org name; opens a dropdown
21
- * with the user's other orgs, pending invitations, and inline forms to
22
- * create a new org or invite a teammate. Renders nothing in solo / dev
23
- * mode or when the user has no orgs at all and no invites.
24
- *
25
- * Headless DOM (no shadcn deps) so it works in any template.
24
+ * Compact org switcher button. Shows the active org (or "Personal" when the
25
+ * user has none); opens a popover with the user's other orgs, pending
26
+ * invitations, inline forms to create a new org / invite a teammate, and a
27
+ * sign-out item. Renders nothing in dev / no-auth mode.
26
28
  */
27
29
  export function OrgSwitcher({ className, hideWhenSingle, reserveSpace, }) {
28
30
  const { data: org, isLoading } = useOrg();
@@ -34,31 +36,33 @@ export function OrgSwitcher({ className, hideWhenSingle, reserveSpace, }) {
34
36
  const [mode, setMode] = useState("list");
35
37
  const [newName, setNewName] = useState("");
36
38
  const [inviteEmail, setInviteEmail] = useState("");
37
- const ref = useRef(null);
38
- useEffect(() => {
39
- if (!open)
40
- return;
41
- const onClick = (e) => {
42
- if (ref.current && !ref.current.contains(e.target)) {
43
- setOpen(false);
44
- }
45
- };
46
- document.addEventListener("mousedown", onClick);
47
- return () => document.removeEventListener("mousedown", onClick);
48
- }, [open]);
49
- useEffect(() => {
50
- if (!open) {
39
+ const [signingOut, setSigningOut] = useState(false);
40
+ const handleOpenChange = (next) => {
41
+ setOpen(next);
42
+ if (!next) {
51
43
  setMode("list");
52
44
  setNewName("");
53
45
  setInviteEmail("");
54
46
  }
55
- }, [open]);
47
+ };
48
+ const handleSignOut = async () => {
49
+ if (signingOut)
50
+ return;
51
+ setSigningOut(true);
52
+ try {
53
+ await fetch(agentNativePath("/_agent-native/auth/logout"), {
54
+ method: "POST",
55
+ credentials: "include",
56
+ });
57
+ }
58
+ catch {
59
+ /* fall through to reload — server may already have cleared the cookie */
60
+ }
61
+ window.location.reload();
62
+ };
56
63
  if (!org) {
57
64
  return reserveSpace && isLoading ? (_jsx("div", { "aria-hidden": "true", className: `h-8 ${className ?? ""}` })) : null;
58
65
  }
59
- if (org.email === DEV_MODE_USER_EMAIL) {
60
- return reserveSpace ? (_jsx("div", { "aria-hidden": "true", className: `h-8 ${className ?? ""}` })) : null;
61
- }
62
66
  const orgs = org.orgs ?? [];
63
67
  const pendingInvitations = org.pendingInvitations ?? [];
64
68
  const orgCount = orgs.length;
@@ -71,53 +75,59 @@ export function OrgSwitcher({ className, hideWhenSingle, reserveSpace, }) {
71
75
  }
72
76
  const canInvite = !!org.orgId && (org.role === "owner" || org.role === "admin");
73
77
  const personalLabel = personalLabelFromEmail(org.email);
74
- const label = org.orgName ?? personalLabel;
75
- return (_jsxs("div", { ref: ref, className: `relative ${className ?? ""}`, children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent/50 hover:text-foreground border border-border/50", children: [_jsx(IconBuilding, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "truncate flex-1 text-left", children: label }), _jsx(IconSelector, { className: "h-3 w-3 shrink-0 opacity-50" })] }), open && (_jsxs("div", { className: "absolute left-0 right-0 bottom-full mb-1 z-50 rounded-md border border-border bg-popover shadow-md py-1 min-w-[14rem]", children: [mode === "list" && (_jsxs(_Fragment, { children: [(orgs.length > 0 || !org.orgId) && (_jsx("div", { className: "px-2.5 pt-1 pb-0.5 text-[10px] uppercase tracking-wide text-muted-foreground", children: "Organization" })), orgs.length === 0 && !org.orgId && (_jsxs("div", { className: "flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-muted-foreground", "aria-disabled": "true", children: [_jsx(IconBuilding, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "truncate flex-1 text-left", children: personalLabel }), _jsx(IconCheck, { className: "h-3.5 w-3.5 shrink-0 opacity-50" })] })), orgs.map((o) => (_jsxs("button", { type: "button", onClick: async () => {
76
- if (o.orgId === org.orgId) {
77
- setOpen(false);
78
- return;
79
- }
80
- try {
81
- await switchOrg.mutateAsync(o.orgId);
82
- setOpen(false);
83
- }
84
- catch {
85
- /* error surfaced via switchOrg.error */
86
- }
87
- }, disabled: switchOrg.isPending, className: "flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-foreground hover:bg-accent disabled:opacity-50", children: [_jsx(IconBuilding, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "truncate flex-1 text-left", children: o.orgName }), o.orgId === org.orgId && (_jsx(IconCheck, { className: "h-3.5 w-3.5 shrink-0 text-green-500" }))] }, o.orgId))), pendingInvitations.length > 0 && (_jsxs(_Fragment, { children: [orgs.length > 0 && _jsx("div", { className: "my-1 h-px bg-border" }), _jsx("div", { className: "px-2.5 pt-1 pb-0.5 text-[10px] uppercase tracking-wide text-muted-foreground", children: "Invitations" }), pendingInvitations.map((inv) => (_jsxs("div", { className: "flex items-center gap-2 px-2.5 py-1.5 text-xs", children: [_jsx(IconBuilding, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "truncate flex-1 text-foreground", children: inv.orgName }), _jsx("button", { type: "button", onClick: async () => {
88
- try {
89
- await acceptInvitation.mutateAsync(inv.id);
90
- setOpen(false);
91
- }
92
- catch {
93
- /* error surfaced via acceptInvitation.error */
94
- }
95
- }, disabled: acceptInvitation.isPending, className: "rounded px-1.5 py-0.5 text-[11px] font-medium text-green-600 hover:bg-green-500/10 disabled:opacity-50", children: acceptInvitation.isPending ? (_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" })) : ("Join") })] }, inv.id)))] })), _jsx("div", { className: "my-1 h-px bg-border" }), _jsxs("button", { type: "button", onClick: () => setMode("create"), className: "flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-foreground hover:bg-accent", children: [_jsx(IconPlus, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "flex-1 text-left", children: "Create organization" })] }), canInvite && (_jsxs("button", { type: "button", onClick: () => setMode("invite"), className: "flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-foreground hover:bg-accent", children: [_jsx(IconUserPlus, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "flex-1 text-left", children: "Invite member" })] })), (switchOrg.error || acceptInvitation.error) && (_jsx("div", { className: "px-2.5 pt-1 text-[11px] text-red-600", children: (switchOrg.error || acceptInvitation.error)
96
- .message }))] })), mode === "create" && (_jsxs("form", { onSubmit: async (e) => {
97
- e.preventDefault();
98
- const name = newName.trim();
99
- if (!name)
100
- return;
101
- try {
102
- await createOrg.mutateAsync(name);
103
- setOpen(false);
104
- }
105
- catch {
106
- /* error surfaced via createOrg.error */
107
- }
108
- }, className: "px-2 py-1.5", children: [_jsx("div", { className: "px-0.5 pb-1 text-[10px] uppercase tracking-wide text-muted-foreground", children: "New organization" }), _jsx("input", { autoFocus: true, value: newName, onChange: (e) => setNewName(e.target.value), placeholder: "Organization name", disabled: createOrg.isPending, className: "w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs outline-none focus:ring-2 focus:ring-ring disabled:opacity-50" }), createOrg.error && (_jsx("div", { className: "pt-1 text-[11px] text-red-600", children: createOrg.error.message })), _jsxs("div", { className: "flex items-center gap-1.5 pt-1.5", children: [_jsx("button", { type: "button", onClick: () => setMode("list"), disabled: createOrg.isPending, className: "flex-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent disabled:opacity-50", children: "Cancel" }), _jsx("button", { type: "submit", disabled: createOrg.isPending || !newName.trim(), className: "flex-1 rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50", children: createOrg.isPending ? (_jsx(IconLoader2, { className: "mx-auto h-3 w-3 animate-spin" })) : ("Create") })] })] })), mode === "invite" && (_jsxs("form", { onSubmit: async (e) => {
78
+ const inOrg = !!org.orgId;
79
+ const buttonLabel = org.orgName ?? "Personal";
80
+ const ButtonIcon = inOrg ? IconBuilding : IconUser;
81
+ return (_jsxs(PopoverPrimitive.Root, { open: open, onOpenChange: handleOpenChange, children: [_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsxs("button", { type: "button", className: `flex w-full items-center gap-2 rounded-md border border-border/50 px-2.5 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent/50 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring cursor-pointer ${className ?? ""}`, children: [_jsx(ButtonIcon, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "truncate flex-1 text-left", children: buttonLabel }), _jsx(IconSelector, { className: "h-3 w-3 shrink-0 opacity-50" })] }) }), _jsx(PopoverPrimitive.Portal, { children: _jsxs(PopoverPrimitive.Content, { side: "top", align: "start", sideOffset: 6, collisionPadding: 12, className: POPOVER_CONTENT_CLASS, onOpenAutoFocus: (e) => {
82
+ // Don't auto-focus the first item — feels heavy on a switcher.
83
+ if (mode === "list")
109
84
  e.preventDefault();
110
- const email = inviteEmail.trim();
111
- if (!email)
112
- return;
113
- try {
114
- await inviteMember.mutateAsync(email);
115
- setInviteEmail("");
116
- setMode("list");
117
- }
118
- catch {
119
- /* error surfaced via inviteMember.error */
120
- }
121
- }, className: "px-2 py-1.5", children: [_jsxs("div", { className: "px-0.5 pb-1 text-[10px] uppercase tracking-wide text-muted-foreground", children: ["Invite to ", org.orgName] }), _jsx("input", { autoFocus: true, type: "email", value: inviteEmail, onChange: (e) => setInviteEmail(e.target.value), placeholder: "teammate@company.com", disabled: inviteMember.isPending, className: "w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs outline-none focus:ring-2 focus:ring-ring disabled:opacity-50" }), inviteMember.error && (_jsx("div", { className: "pt-1 text-[11px] text-red-600", children: inviteMember.error.message })), _jsxs("div", { className: "flex items-center gap-1.5 pt-1.5", children: [_jsx("button", { type: "button", onClick: () => setMode("list"), disabled: inviteMember.isPending, className: "flex-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent disabled:opacity-50", children: "Cancel" }), _jsx("button", { type: "submit", disabled: inviteMember.isPending || !inviteEmail.trim(), className: "flex-1 rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50", children: inviteMember.isPending ? (_jsx(IconLoader2, { className: "mx-auto h-3 w-3 animate-spin" })) : ("Send invite") })] })] }))] }))] }));
85
+ }, children: [mode === "list" && (_jsxs(_Fragment, { children: [!inOrg && (_jsxs("div", { className: "flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-muted-foreground", "aria-disabled": "true", children: [_jsx(IconUser, { className: "h-3.5 w-3.5 shrink-0" }), _jsxs("span", { className: "truncate flex-1 text-left", children: ["Personal (", personalLabel, ")"] })] })), orgs.length > 0 && (_jsx("div", { className: SECTION_LABEL_CLASS, children: "Organizations" })), orgs.map((o) => (_jsxs("button", { type: "button", onClick: async () => {
86
+ if (o.orgId === org.orgId) {
87
+ setOpen(false);
88
+ return;
89
+ }
90
+ try {
91
+ await switchOrg.mutateAsync(o.orgId);
92
+ setOpen(false);
93
+ }
94
+ catch {
95
+ /* error surfaced via switchOrg.error */
96
+ }
97
+ }, disabled: switchOrg.isPending, className: `${ITEM_CLASS} cursor-pointer`, children: [_jsx(IconBuilding, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "truncate flex-1 text-left", children: o.orgName }), o.orgId === org.orgId && (_jsx(IconCheck, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }))] }, o.orgId))), pendingInvitations.length > 0 && (_jsxs(_Fragment, { children: [orgs.length > 0 && _jsx("div", { className: "my-1 h-px bg-border" }), _jsx("div", { className: SECTION_LABEL_CLASS, children: "Invitations" }), pendingInvitations.map((inv) => (_jsxs("div", { className: "flex items-center gap-2 px-2.5 py-1.5 text-xs", children: [_jsx(IconBuilding, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "truncate flex-1 text-foreground", children: inv.orgName }), _jsx("button", { type: "button", onClick: async () => {
98
+ try {
99
+ await acceptInvitation.mutateAsync(inv.id);
100
+ setOpen(false);
101
+ }
102
+ catch {
103
+ /* error surfaced via acceptInvitation.error */
104
+ }
105
+ }, disabled: acceptInvitation.isPending, className: "rounded px-1.5 py-0.5 text-[11px] font-medium text-primary hover:bg-primary/10 disabled:opacity-50 cursor-pointer", children: acceptInvitation.isPending ? (_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" })) : ("Join") })] }, inv.id)))] })), _jsx("div", { className: "my-1 h-px bg-border" }), _jsxs("button", { type: "button", onClick: () => setMode("create"), className: `${ITEM_CLASS} cursor-pointer`, children: [_jsx(IconPlus, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "flex-1 text-left", children: "Create organization" })] }), canInvite && (_jsxs("button", { type: "button", onClick: () => setMode("invite"), className: `${ITEM_CLASS} cursor-pointer`, children: [_jsx(IconUserPlus, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "flex-1 text-left", children: "Invite member" })] })), _jsx("div", { className: "my-1 h-px bg-border" }), _jsxs("button", { type: "button", onClick: handleSignOut, disabled: signingOut, className: `${ITEM_CLASS} cursor-pointer`, children: [signingOut ? (_jsx(IconLoader2, { className: "h-3.5 w-3.5 shrink-0 animate-spin text-muted-foreground" })) : (_jsx(IconLogout, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })), _jsxs("span", { className: "flex-1 text-left", children: ["Sign out", org.email ? (_jsxs("span", { className: "ml-1 text-muted-foreground", children: ["(", org.email, ")"] })) : null] })] }), (switchOrg.error || acceptInvitation.error) && (_jsx("div", { className: "px-2.5 pt-1 text-[11px] text-destructive", children: (switchOrg.error || acceptInvitation.error)
106
+ .message }))] })), mode === "create" && (_jsxs("form", { onSubmit: async (e) => {
107
+ e.preventDefault();
108
+ const name = newName.trim();
109
+ if (!name)
110
+ return;
111
+ try {
112
+ await createOrg.mutateAsync(name);
113
+ setOpen(false);
114
+ }
115
+ catch {
116
+ /* error surfaced via createOrg.error */
117
+ }
118
+ }, className: "px-2 py-1.5", children: [_jsx("div", { className: "px-0.5 pb-1 text-[10px] uppercase tracking-wide text-muted-foreground", children: "New organization" }), _jsx("input", { autoFocus: true, value: newName, onChange: (e) => setNewName(e.target.value), placeholder: "Organization name", disabled: createOrg.isPending, className: "w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs outline-none focus:ring-2 focus:ring-ring disabled:opacity-50" }), createOrg.error && (_jsx("div", { className: "pt-1 text-[11px] text-destructive", children: createOrg.error.message })), _jsxs("div", { className: "flex items-center gap-1.5 pt-1.5", children: [_jsx("button", { type: "button", onClick: () => setMode("list"), disabled: createOrg.isPending, className: "flex-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent disabled:opacity-50 cursor-pointer", children: "Cancel" }), _jsx("button", { type: "submit", disabled: createOrg.isPending || !newName.trim(), className: "flex-1 rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50 cursor-pointer", children: createOrg.isPending ? (_jsx(IconLoader2, { className: "mx-auto h-3 w-3 animate-spin" })) : ("Create") })] })] })), mode === "invite" && (_jsxs("form", { onSubmit: async (e) => {
119
+ e.preventDefault();
120
+ const email = inviteEmail.trim();
121
+ if (!email)
122
+ return;
123
+ try {
124
+ await inviteMember.mutateAsync(email);
125
+ setInviteEmail("");
126
+ setMode("list");
127
+ }
128
+ catch {
129
+ /* error surfaced via inviteMember.error */
130
+ }
131
+ }, className: "px-2 py-1.5", children: [_jsxs("div", { className: "px-0.5 pb-1 text-[10px] uppercase tracking-wide text-muted-foreground", children: ["Invite to ", org.orgName] }), _jsx("input", { autoFocus: true, type: "email", value: inviteEmail, onChange: (e) => setInviteEmail(e.target.value), placeholder: "teammate@company.com", disabled: inviteMember.isPending, className: "w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs outline-none focus:ring-2 focus:ring-ring disabled:opacity-50" }), inviteMember.error && (_jsx("div", { className: "pt-1 text-[11px] text-destructive", children: inviteMember.error.message })), _jsxs("div", { className: "flex items-center gap-1.5 pt-1.5", children: [_jsx("button", { type: "button", onClick: () => setMode("list"), disabled: inviteMember.isPending, className: "flex-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent disabled:opacity-50 cursor-pointer", children: "Cancel" }), _jsx("button", { type: "submit", disabled: inviteMember.isPending || !inviteEmail.trim(), className: "flex-1 rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50 cursor-pointer", children: inviteMember.isPending ? (_jsx(IconLoader2, { className: "mx-auto h-3 w-3 animate-spin" })) : ("Send invite") })] })] }))] }) })] }));
122
132
  }
123
133
  //# sourceMappingURL=OrgSwitcher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"OrgSwitcher.js","sourceRoot":"","sources":["../../../src/client/org/OrgSwitcher.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport {\n IconBuilding,\n IconCheck,\n IconLoader2,\n IconPlus,\n IconSelector,\n IconUserPlus,\n} from \"@tabler/icons-react\";\nimport {\n useOrg,\n useSwitchOrg,\n useCreateOrg,\n useInviteMember,\n useAcceptInvitation,\n} from \"./hooks.js\";\nimport { DEV_MODE_USER_EMAIL } from \"../dev-mode.js\";\n\nexport interface OrgSwitcherProps {\n className?: string;\n /** Hide entirely when the user only belongs to one org. Default: false. */\n hideWhenSingle?: boolean;\n /** Keep the switcher's button height reserved while org state is loading. */\n reserveSpace?: boolean;\n}\n\nfunction personalLabelFromEmail(email: string | null | undefined): string {\n if (!email) return \"Personal\";\n const local = email.split(\"@\")[0] ?? email;\n const cleaned = local.replace(/[._-]+/g, \" \").trim();\n if (!cleaned) return \"Personal\";\n return cleaned\n .split(\" \")\n .filter(Boolean)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n}\n\ntype Mode = \"list\" | \"create\" | \"invite\";\n\n/**\n * Compact org switcher button. Shows the active org name; opens a dropdown\n * with the user's other orgs, pending invitations, and inline forms to\n * create a new org or invite a teammate. Renders nothing in solo / dev\n * mode or when the user has no orgs at all and no invites.\n *\n * Headless DOM (no shadcn deps) so it works in any template.\n */\nexport function OrgSwitcher({\n className,\n hideWhenSingle,\n reserveSpace,\n}: OrgSwitcherProps) {\n const { data: org, isLoading } = useOrg();\n const switchOrg = useSwitchOrg();\n const createOrg = useCreateOrg();\n const inviteMember = useInviteMember();\n const acceptInvitation = useAcceptInvitation();\n const [open, setOpen] = useState(false);\n const [mode, setMode] = useState<Mode>(\"list\");\n const [newName, setNewName] = useState(\"\");\n const [inviteEmail, setInviteEmail] = useState(\"\");\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const onClick = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", onClick);\n return () => document.removeEventListener(\"mousedown\", onClick);\n }, [open]);\n\n useEffect(() => {\n if (!open) {\n setMode(\"list\");\n setNewName(\"\");\n setInviteEmail(\"\");\n }\n }, [open]);\n\n if (!org) {\n return reserveSpace && isLoading ? (\n <div aria-hidden=\"true\" className={`h-8 ${className ?? \"\"}`} />\n ) : null;\n }\n if (org.email === DEV_MODE_USER_EMAIL) {\n return reserveSpace ? (\n <div aria-hidden=\"true\" className={`h-8 ${className ?? \"\"}`} />\n ) : null;\n }\n\n const orgs = org.orgs ?? [];\n const pendingInvitations = org.pendingInvitations ?? [];\n const orgCount = orgs.length;\n const hasAny = orgCount > 0 || pendingInvitations.length > 0;\n if (!hasAny && !org.email) {\n return reserveSpace ? (\n <div aria-hidden=\"true\" className={`h-8 ${className ?? \"\"}`} />\n ) : null;\n }\n if (hideWhenSingle && orgCount < 2 && pendingInvitations.length === 0) {\n return reserveSpace ? (\n <div aria-hidden=\"true\" className={`h-8 ${className ?? \"\"}`} />\n ) : null;\n }\n\n const canInvite =\n !!org.orgId && (org.role === \"owner\" || org.role === \"admin\");\n\n const personalLabel = personalLabelFromEmail(org.email);\n const label = org.orgName ?? personalLabel;\n\n return (\n <div ref={ref} className={`relative ${className ?? \"\"}`}>\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className=\"flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent/50 hover:text-foreground border border-border/50\"\n >\n <IconBuilding className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"truncate flex-1 text-left\">{label}</span>\n <IconSelector className=\"h-3 w-3 shrink-0 opacity-50\" />\n </button>\n {open && (\n <div className=\"absolute left-0 right-0 bottom-full mb-1 z-50 rounded-md border border-border bg-popover shadow-md py-1 min-w-[14rem]\">\n {mode === \"list\" && (\n <>\n {(orgs.length > 0 || !org.orgId) && (\n <div className=\"px-2.5 pt-1 pb-0.5 text-[10px] uppercase tracking-wide text-muted-foreground\">\n Organization\n </div>\n )}\n {orgs.length === 0 && !org.orgId && (\n <div\n className=\"flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-muted-foreground\"\n aria-disabled=\"true\"\n >\n <IconBuilding className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"truncate flex-1 text-left\">\n {personalLabel}\n </span>\n <IconCheck className=\"h-3.5 w-3.5 shrink-0 opacity-50\" />\n </div>\n )}\n {orgs.map((o) => (\n <button\n key={o.orgId}\n type=\"button\"\n onClick={async () => {\n if (o.orgId === org.orgId) {\n setOpen(false);\n return;\n }\n try {\n await switchOrg.mutateAsync(o.orgId);\n setOpen(false);\n } catch {\n /* error surfaced via switchOrg.error */\n }\n }}\n disabled={switchOrg.isPending}\n className=\"flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-foreground hover:bg-accent disabled:opacity-50\"\n >\n <IconBuilding className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate flex-1 text-left\">{o.orgName}</span>\n {o.orgId === org.orgId && (\n <IconCheck className=\"h-3.5 w-3.5 shrink-0 text-green-500\" />\n )}\n </button>\n ))}\n\n {pendingInvitations.length > 0 && (\n <>\n {orgs.length > 0 && <div className=\"my-1 h-px bg-border\" />}\n <div className=\"px-2.5 pt-1 pb-0.5 text-[10px] uppercase tracking-wide text-muted-foreground\">\n Invitations\n </div>\n {pendingInvitations.map((inv) => (\n <div\n key={inv.id}\n className=\"flex items-center gap-2 px-2.5 py-1.5 text-xs\"\n >\n <IconBuilding className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate flex-1 text-foreground\">\n {inv.orgName}\n </span>\n <button\n type=\"button\"\n onClick={async () => {\n try {\n await acceptInvitation.mutateAsync(inv.id);\n setOpen(false);\n } catch {\n /* error surfaced via acceptInvitation.error */\n }\n }}\n disabled={acceptInvitation.isPending}\n className=\"rounded px-1.5 py-0.5 text-[11px] font-medium text-green-600 hover:bg-green-500/10 disabled:opacity-50\"\n >\n {acceptInvitation.isPending ? (\n <IconLoader2 className=\"h-3 w-3 animate-spin\" />\n ) : (\n \"Join\"\n )}\n </button>\n </div>\n ))}\n </>\n )}\n\n <div className=\"my-1 h-px bg-border\" />\n <button\n type=\"button\"\n onClick={() => setMode(\"create\")}\n className=\"flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-foreground hover:bg-accent\"\n >\n <IconPlus className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 text-left\">Create organization</span>\n </button>\n {canInvite && (\n <button\n type=\"button\"\n onClick={() => setMode(\"invite\")}\n className=\"flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-foreground hover:bg-accent\"\n >\n <IconUserPlus className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 text-left\">Invite member</span>\n </button>\n )}\n\n {(switchOrg.error || acceptInvitation.error) && (\n <div className=\"px-2.5 pt-1 text-[11px] text-red-600\">\n {\n ((switchOrg.error || acceptInvitation.error) as Error)\n .message\n }\n </div>\n )}\n </>\n )}\n\n {mode === \"create\" && (\n <form\n onSubmit={async (e) => {\n e.preventDefault();\n const name = newName.trim();\n if (!name) return;\n try {\n await createOrg.mutateAsync(name);\n setOpen(false);\n } catch {\n /* error surfaced via createOrg.error */\n }\n }}\n className=\"px-2 py-1.5\"\n >\n <div className=\"px-0.5 pb-1 text-[10px] uppercase tracking-wide text-muted-foreground\">\n New organization\n </div>\n <input\n autoFocus\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder=\"Organization name\"\n disabled={createOrg.isPending}\n className=\"w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs outline-none focus:ring-2 focus:ring-ring disabled:opacity-50\"\n />\n {createOrg.error && (\n <div className=\"pt-1 text-[11px] text-red-600\">\n {(createOrg.error as Error).message}\n </div>\n )}\n <div className=\"flex items-center gap-1.5 pt-1.5\">\n <button\n type=\"button\"\n onClick={() => setMode(\"list\")}\n disabled={createOrg.isPending}\n className=\"flex-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={createOrg.isPending || !newName.trim()}\n className=\"flex-1 rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50\"\n >\n {createOrg.isPending ? (\n <IconLoader2 className=\"mx-auto h-3 w-3 animate-spin\" />\n ) : (\n \"Create\"\n )}\n </button>\n </div>\n </form>\n )}\n\n {mode === \"invite\" && (\n <form\n onSubmit={async (e) => {\n e.preventDefault();\n const email = inviteEmail.trim();\n if (!email) return;\n try {\n await inviteMember.mutateAsync(email);\n setInviteEmail(\"\");\n setMode(\"list\");\n } catch {\n /* error surfaced via inviteMember.error */\n }\n }}\n className=\"px-2 py-1.5\"\n >\n <div className=\"px-0.5 pb-1 text-[10px] uppercase tracking-wide text-muted-foreground\">\n Invite to {org.orgName}\n </div>\n <input\n autoFocus\n type=\"email\"\n value={inviteEmail}\n onChange={(e) => setInviteEmail(e.target.value)}\n placeholder=\"teammate@company.com\"\n disabled={inviteMember.isPending}\n className=\"w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs outline-none focus:ring-2 focus:ring-ring disabled:opacity-50\"\n />\n {inviteMember.error && (\n <div className=\"pt-1 text-[11px] text-red-600\">\n {(inviteMember.error as Error).message}\n </div>\n )}\n <div className=\"flex items-center gap-1.5 pt-1.5\">\n <button\n type=\"button\"\n onClick={() => setMode(\"list\")}\n disabled={inviteMember.isPending}\n className=\"flex-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={inviteMember.isPending || !inviteEmail.trim()}\n className=\"flex-1 rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50\"\n >\n {inviteMember.isPending ? (\n <IconLoader2 className=\"mx-auto h-3 w-3 animate-spin\" />\n ) : (\n \"Send invite\"\n )}\n </button>\n </div>\n </form>\n )}\n </div>\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"OrgSwitcher.js","sourceRoot":"","sources":["../../../src/client/org/OrgSwitcher.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from \"react\";\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport {\n IconBuilding,\n IconCheck,\n IconLoader2,\n IconLogout,\n IconPlus,\n IconSelector,\n IconUser,\n IconUserPlus,\n} from \"@tabler/icons-react\";\nimport {\n useOrg,\n useSwitchOrg,\n useCreateOrg,\n useInviteMember,\n useAcceptInvitation,\n} from \"./hooks.js\";\nimport { agentNativePath } from \"../api-path.js\";\n\nexport interface OrgSwitcherProps {\n className?: string;\n /** Hide entirely when the user only belongs to one org. Default: false. */\n hideWhenSingle?: boolean;\n /** Keep the switcher's button height reserved while org state is loading. */\n reserveSpace?: boolean;\n}\n\nfunction personalLabelFromEmail(email: string | null | undefined): string {\n if (!email) return \"Personal\";\n const local = email.split(\"@\")[0] ?? email;\n const cleaned = local.replace(/[._-]+/g, \" \").trim();\n if (!cleaned) return \"Personal\";\n return cleaned\n .split(\" \")\n .filter(Boolean)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n}\n\ntype Mode = \"list\" | \"create\" | \"invite\";\n\nconst POPOVER_CONTENT_CLASS =\n \"z-50 min-w-[14rem] rounded-md border border-border bg-popover py-1 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\";\n\nconst ITEM_CLASS =\n \"flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-foreground hover:bg-accent focus-visible:bg-accent focus:outline-none disabled:opacity-50 disabled:pointer-events-none\";\n\nconst SECTION_LABEL_CLASS =\n \"px-2.5 pt-1 pb-0.5 text-[10px] uppercase tracking-wide text-muted-foreground\";\n\n/**\n * Compact org switcher button. Shows the active org (or \"Personal\" when the\n * user has none); opens a popover with the user's other orgs, pending\n * invitations, inline forms to create a new org / invite a teammate, and a\n * sign-out item. Renders nothing in dev / no-auth mode.\n */\nexport function OrgSwitcher({\n className,\n hideWhenSingle,\n reserveSpace,\n}: OrgSwitcherProps) {\n const { data: org, isLoading } = useOrg();\n const switchOrg = useSwitchOrg();\n const createOrg = useCreateOrg();\n const inviteMember = useInviteMember();\n const acceptInvitation = useAcceptInvitation();\n const [open, setOpen] = useState(false);\n const [mode, setMode] = useState<Mode>(\"list\");\n const [newName, setNewName] = useState(\"\");\n const [inviteEmail, setInviteEmail] = useState(\"\");\n const [signingOut, setSigningOut] = useState(false);\n\n const handleOpenChange = (next: boolean) => {\n setOpen(next);\n if (!next) {\n setMode(\"list\");\n setNewName(\"\");\n setInviteEmail(\"\");\n }\n };\n\n const handleSignOut = async () => {\n if (signingOut) return;\n setSigningOut(true);\n try {\n await fetch(agentNativePath(\"/_agent-native/auth/logout\"), {\n method: \"POST\",\n credentials: \"include\",\n });\n } catch {\n /* fall through to reload — server may already have cleared the cookie */\n }\n window.location.reload();\n };\n\n if (!org) {\n return reserveSpace && isLoading ? (\n <div aria-hidden=\"true\" className={`h-8 ${className ?? \"\"}`} />\n ) : null;\n }\n\n const orgs = org.orgs ?? [];\n const pendingInvitations = org.pendingInvitations ?? [];\n const orgCount = orgs.length;\n const hasAny = orgCount > 0 || pendingInvitations.length > 0;\n if (!hasAny && !org.email) {\n return reserveSpace ? (\n <div aria-hidden=\"true\" className={`h-8 ${className ?? \"\"}`} />\n ) : null;\n }\n if (hideWhenSingle && orgCount < 2 && pendingInvitations.length === 0) {\n return reserveSpace ? (\n <div aria-hidden=\"true\" className={`h-8 ${className ?? \"\"}`} />\n ) : null;\n }\n\n const canInvite =\n !!org.orgId && (org.role === \"owner\" || org.role === \"admin\");\n\n const personalLabel = personalLabelFromEmail(org.email);\n const inOrg = !!org.orgId;\n const buttonLabel = org.orgName ?? \"Personal\";\n const ButtonIcon = inOrg ? IconBuilding : IconUser;\n\n return (\n <PopoverPrimitive.Root open={open} onOpenChange={handleOpenChange}>\n <PopoverPrimitive.Trigger asChild>\n <button\n type=\"button\"\n className={`flex w-full items-center gap-2 rounded-md border border-border/50 px-2.5 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent/50 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring cursor-pointer ${className ?? \"\"}`}\n >\n <ButtonIcon className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"truncate flex-1 text-left\">{buttonLabel}</span>\n <IconSelector className=\"h-3 w-3 shrink-0 opacity-50\" />\n </button>\n </PopoverPrimitive.Trigger>\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n side=\"top\"\n align=\"start\"\n sideOffset={6}\n collisionPadding={12}\n className={POPOVER_CONTENT_CLASS}\n onOpenAutoFocus={(e) => {\n // Don't auto-focus the first item — feels heavy on a switcher.\n if (mode === \"list\") e.preventDefault();\n }}\n >\n {mode === \"list\" && (\n <>\n {!inOrg && (\n <div\n className=\"flex w-full items-center gap-2 px-2.5 py-1.5 text-xs text-muted-foreground\"\n aria-disabled=\"true\"\n >\n <IconUser className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"truncate flex-1 text-left\">\n Personal ({personalLabel})\n </span>\n </div>\n )}\n {orgs.length > 0 && (\n <div className={SECTION_LABEL_CLASS}>Organizations</div>\n )}\n {orgs.map((o) => (\n <button\n key={o.orgId}\n type=\"button\"\n onClick={async () => {\n if (o.orgId === org.orgId) {\n setOpen(false);\n return;\n }\n try {\n await switchOrg.mutateAsync(o.orgId);\n setOpen(false);\n } catch {\n /* error surfaced via switchOrg.error */\n }\n }}\n disabled={switchOrg.isPending}\n className={`${ITEM_CLASS} cursor-pointer`}\n >\n <IconBuilding className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate flex-1 text-left\">{o.orgName}</span>\n {o.orgId === org.orgId && (\n <IconCheck className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n )}\n </button>\n ))}\n\n {pendingInvitations.length > 0 && (\n <>\n {orgs.length > 0 && <div className=\"my-1 h-px bg-border\" />}\n <div className={SECTION_LABEL_CLASS}>Invitations</div>\n {pendingInvitations.map((inv) => (\n <div\n key={inv.id}\n className=\"flex items-center gap-2 px-2.5 py-1.5 text-xs\"\n >\n <IconBuilding className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate flex-1 text-foreground\">\n {inv.orgName}\n </span>\n <button\n type=\"button\"\n onClick={async () => {\n try {\n await acceptInvitation.mutateAsync(inv.id);\n setOpen(false);\n } catch {\n /* error surfaced via acceptInvitation.error */\n }\n }}\n disabled={acceptInvitation.isPending}\n className=\"rounded px-1.5 py-0.5 text-[11px] font-medium text-primary hover:bg-primary/10 disabled:opacity-50 cursor-pointer\"\n >\n {acceptInvitation.isPending ? (\n <IconLoader2 className=\"h-3 w-3 animate-spin\" />\n ) : (\n \"Join\"\n )}\n </button>\n </div>\n ))}\n </>\n )}\n\n <div className=\"my-1 h-px bg-border\" />\n <button\n type=\"button\"\n onClick={() => setMode(\"create\")}\n className={`${ITEM_CLASS} cursor-pointer`}\n >\n <IconPlus className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 text-left\">Create organization</span>\n </button>\n {canInvite && (\n <button\n type=\"button\"\n onClick={() => setMode(\"invite\")}\n className={`${ITEM_CLASS} cursor-pointer`}\n >\n <IconUserPlus className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"flex-1 text-left\">Invite member</span>\n </button>\n )}\n\n <div className=\"my-1 h-px bg-border\" />\n <button\n type=\"button\"\n onClick={handleSignOut}\n disabled={signingOut}\n className={`${ITEM_CLASS} cursor-pointer`}\n >\n {signingOut ? (\n <IconLoader2 className=\"h-3.5 w-3.5 shrink-0 animate-spin text-muted-foreground\" />\n ) : (\n <IconLogout className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n )}\n <span className=\"flex-1 text-left\">\n Sign out\n {org.email ? (\n <span className=\"ml-1 text-muted-foreground\">\n ({org.email})\n </span>\n ) : null}\n </span>\n </button>\n\n {(switchOrg.error || acceptInvitation.error) && (\n <div className=\"px-2.5 pt-1 text-[11px] text-destructive\">\n {\n ((switchOrg.error || acceptInvitation.error) as Error)\n .message\n }\n </div>\n )}\n </>\n )}\n\n {mode === \"create\" && (\n <form\n onSubmit={async (e) => {\n e.preventDefault();\n const name = newName.trim();\n if (!name) return;\n try {\n await createOrg.mutateAsync(name);\n setOpen(false);\n } catch {\n /* error surfaced via createOrg.error */\n }\n }}\n className=\"px-2 py-1.5\"\n >\n <div className=\"px-0.5 pb-1 text-[10px] uppercase tracking-wide text-muted-foreground\">\n New organization\n </div>\n <input\n autoFocus\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder=\"Organization name\"\n disabled={createOrg.isPending}\n className=\"w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs outline-none focus:ring-2 focus:ring-ring disabled:opacity-50\"\n />\n {createOrg.error && (\n <div className=\"pt-1 text-[11px] text-destructive\">\n {(createOrg.error as Error).message}\n </div>\n )}\n <div className=\"flex items-center gap-1.5 pt-1.5\">\n <button\n type=\"button\"\n onClick={() => setMode(\"list\")}\n disabled={createOrg.isPending}\n className=\"flex-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent disabled:opacity-50 cursor-pointer\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={createOrg.isPending || !newName.trim()}\n className=\"flex-1 rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50 cursor-pointer\"\n >\n {createOrg.isPending ? (\n <IconLoader2 className=\"mx-auto h-3 w-3 animate-spin\" />\n ) : (\n \"Create\"\n )}\n </button>\n </div>\n </form>\n )}\n\n {mode === \"invite\" && (\n <form\n onSubmit={async (e) => {\n e.preventDefault();\n const email = inviteEmail.trim();\n if (!email) return;\n try {\n await inviteMember.mutateAsync(email);\n setInviteEmail(\"\");\n setMode(\"list\");\n } catch {\n /* error surfaced via inviteMember.error */\n }\n }}\n className=\"px-2 py-1.5\"\n >\n <div className=\"px-0.5 pb-1 text-[10px] uppercase tracking-wide text-muted-foreground\">\n Invite to {org.orgName}\n </div>\n <input\n autoFocus\n type=\"email\"\n value={inviteEmail}\n onChange={(e) => setInviteEmail(e.target.value)}\n placeholder=\"teammate@company.com\"\n disabled={inviteMember.isPending}\n className=\"w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs outline-none focus:ring-2 focus:ring-ring disabled:opacity-50\"\n />\n {inviteMember.error && (\n <div className=\"pt-1 text-[11px] text-destructive\">\n {(inviteMember.error as Error).message}\n </div>\n )}\n <div className=\"flex items-center gap-1.5 pt-1.5\">\n <button\n type=\"button\"\n onClick={() => setMode(\"list\")}\n disabled={inviteMember.isPending}\n className=\"flex-1 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent disabled:opacity-50 cursor-pointer\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={inviteMember.isPending || !inviteEmail.trim()}\n className=\"flex-1 rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50 cursor-pointer\"\n >\n {inviteMember.isPending ? (\n <IconLoader2 className=\"mx-auto h-3 w-3 animate-spin\" />\n ) : (\n \"Send invite\"\n )}\n </button>\n </div>\n </form>\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"TeamPage.d.ts","sourceRoot":"","sources":["../../../src/client/org/TeamPage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAyC5D,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,SAAS,CAAC;IAC5C;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA29BD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,KAAc,EACd,oBAAoB,EACpB,SAAS,GACV,EAAE,aAAa,2CA0Cf"}
1
+ {"version":3,"file":"TeamPage.d.ts","sourceRoot":"","sources":["../../../src/client/org/TeamPage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAuC5D,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,SAAS,CAAC;IAC5C;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsuBD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,KAAc,EACd,oBAAoB,EACpB,SAAS,GACV,EAAE,aAAa,2CAgCf"}
@@ -1,8 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState } from "react";
3
- import { IconBuilding, IconUserPlus, IconTrash, IconCrown, IconShieldCheck, IconLoader2, IconMail, IconCheck, IconLogin, IconPencil, IconAt, IconX, IconKey, IconCopy, IconRefresh, IconEye, IconEyeOff, IconCloudUpload, } from "@tabler/icons-react";
4
- import { agentNativePath, appBasePath } from "../api-path.js";
5
- import { DEV_MODE_USER_EMAIL } from "../dev-mode.js";
2
+ import { useState } from "react";
3
+ import { IconBuilding, IconUserPlus, IconTrash, IconCrown, IconShieldCheck, IconLoader2, IconMail, IconCheck, IconPencil, IconAt, IconX, IconKey, IconCopy, IconRefresh, IconEye, IconEyeOff, IconCloudUpload, } from "@tabler/icons-react";
6
4
  import { useOrg, useOrgMembers, useOrgInvitations, useCreateOrg, useUpdateOrg, useInviteMember, useAcceptInvitation, useRemoveMember, useSwitchOrg, useSetOrgDomain, useSetA2ASecret, useSyncA2ASecret, useJoinByDomain, } from "./hooks.js";
7
5
  function RoleIcon({ role }) {
8
6
  if (role === "owner")
@@ -195,162 +193,13 @@ function A2ASecretSection({ secret }) {
195
193
  setPasteValue("");
196
194
  }, className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground", children: "Cancel" })] })), _jsx(ErrorText, { error: setA2ASecret.error }), _jsx(ErrorText, { error: syncA2ASecret.error })] }));
197
195
  }
198
- const MIGRATE_FLAG_KEY = "an_migrate_from_local";
199
- function LocalModeSignInCard() {
200
- const [isSubmitting, setIsSubmitting] = useState(false);
201
- const [error, setError] = useState(null);
202
- async function upgradeToAccount() {
203
- setIsSubmitting(true);
204
- setError(null);
205
- try {
206
- // Remember that we want to migrate data once the user completes sign-in.
207
- try {
208
- localStorage.setItem(MIGRATE_FLAG_KEY, "1");
209
- }
210
- catch {
211
- // localStorage may be unavailable (private mode) — migration just
212
- // won't auto-run. The user can still sign in.
213
- }
214
- const res = await fetch(agentNativePath("/_agent-native/auth/exit-local-mode"), {
215
- method: "POST",
216
- });
217
- if (!res.ok) {
218
- const body = await res.json().catch(() => ({}));
219
- throw new Error(body?.error || "Failed to exit local mode");
220
- }
221
- // Reload with ?signin=1 → auth guard will serve the onboarding page so
222
- // the user can sign in with Google or create an email/password account.
223
- // The URL flag is used (rather than a cookie) because third-party iframe
224
- // contexts (e.g. the Builder.io editor) block SameSite=Lax cookies, so a
225
- // cookie-only signal would be lost on reload. The localStorage flag
226
- // survives the reload so TeamPage can migrate data automatically once
227
- // they're back.
228
- const url = new URL(window.location.href);
229
- url.searchParams.set("signin", "1");
230
- window.location.href = url.toString();
231
- }
232
- catch (e) {
233
- setError(e?.message || "Failed to start sign-in");
234
- setIsSubmitting(false);
235
- }
236
- }
237
- return (_jsxs("section", { className: "rounded-lg border border-border bg-card p-6 space-y-4", children: [_jsxs("p", { className: "text-sm text-muted-foreground", children: ["Signed in as ", _jsx("code", { children: "local@localhost" }), ". Create an account to:"] }), _jsxs("ul", { className: "text-sm text-muted-foreground list-disc pl-5 space-y-1", children: [_jsx("li", { children: "Sync data across devices" }), _jsx("li", { children: "Invite teammates" }), _jsx("li", { children: "Keep your local data \u2014 it migrates automatically" })] }), _jsxs("button", { type: "button", onClick: upgradeToAccount, disabled: isSubmitting, className: "inline-flex items-center gap-2 rounded-md bg-foreground px-3 py-2 text-xs font-medium text-background hover:opacity-90 disabled:opacity-50", children: [isSubmitting ? (_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(IconLogin, { className: "h-3.5 w-3.5" })), "Sign in or create account"] }), error && _jsx(ErrorText, { error: error })] }));
238
- }
239
- function MigrationStatusCard({ state }) {
240
- if (state.status === "idle")
241
- return null;
242
- const movedCore = Object.entries(state.coreTables);
243
- return (_jsxs("section", { className: "rounded-lg border border-border bg-card p-6 space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [state.status === "running" ? (_jsx(IconLoader2, { className: "h-4 w-4 animate-spin text-muted-foreground" })) : state.status === "done" ? (_jsx(IconCheck, { className: "h-4 w-4 text-green-500" })) : (_jsx(IconTrash, { className: "h-4 w-4 text-red-500" })), _jsx("h3", { className: "text-sm font-medium", children: state.status === "running"
244
- ? "Migrating your local workspace"
245
- : state.status === "done"
246
- ? "Local workspace migrated"
247
- : "Migration incomplete" })] }), state.status === "running" && (_jsx("p", { className: "text-sm text-muted-foreground", children: "Moving your local SQL data onto this real account. Keep this page open while the upgrade finishes." })), state.status === "done" && (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Your local workspace has been attached to this account." }), (movedCore.length > 0 || state.appKeys.length > 0) && (_jsxs("div", { className: "rounded-md border border-border p-3 text-xs text-muted-foreground space-y-1", children: [movedCore.map(([table, count]) => (_jsxs("div", { children: [table, ": ", count, " row", count === 1 ? "" : "s"] }, table))), state.appKeys.length > 0 && (_jsxs("div", { children: ["app settings: ", state.appKeys.length, " key", state.appKeys.length === 1 ? "" : "s"] }))] }))] })), state.status === "error" && (_jsx("p", { className: "text-sm text-red-500", children: state.error || "We signed you in, but moving local data failed." }))] }));
248
- }
249
- /**
250
- * After the user finishes signing in on the onboarding page and lands back on
251
- * the Team page, pull across any data that was previously scoped to
252
- * `local@localhost`. Triggered by a localStorage flag set from
253
- * `LocalModeSignInCard` so we only migrate when the user explicitly opted in.
254
- */
255
- function useMigrateLocalDataOnSignIn(email) {
256
- const [state, setState] = useState({
257
- status: "idle",
258
- coreTables: {},
259
- appKeys: [],
260
- error: null,
261
- });
262
- useEffect(() => {
263
- if (!email || email === DEV_MODE_USER_EMAIL)
264
- return;
265
- let flag = null;
266
- try {
267
- flag = localStorage.getItem(MIGRATE_FLAG_KEY);
268
- }
269
- catch {
270
- return;
271
- }
272
- if (!flag)
273
- return;
274
- // Clear the flag immediately so a failed request doesn't loop.
275
- try {
276
- localStorage.removeItem(MIGRATE_FLAG_KEY);
277
- }
278
- catch {
279
- // ignore
280
- }
281
- let cancelled = false;
282
- (async () => {
283
- setState({
284
- status: "running",
285
- coreTables: {},
286
- appKeys: [],
287
- error: null,
288
- });
289
- try {
290
- const coreRes = await fetch(agentNativePath("/_agent-native/auth/migrate-local-data"), {
291
- method: "POST",
292
- credentials: "include",
293
- });
294
- const coreBody = await coreRes.json().catch(() => ({}));
295
- if (!coreRes.ok) {
296
- throw new Error(coreBody?.error || "Failed to migrate local framework data");
297
- }
298
- let appKeys = [];
299
- try {
300
- const appRes = await fetch(`${appBasePath()}/api/local-migration`, {
301
- method: "POST",
302
- credentials: "include",
303
- });
304
- if (appRes.ok) {
305
- const appBody = await appRes.json().catch(() => ({}));
306
- appKeys = Array.isArray(appBody?.keys) ? appBody.keys : [];
307
- }
308
- else if (appRes.status !== 404) {
309
- const appBody = await appRes.json().catch(() => ({}));
310
- throw new Error(appBody?.error || "App-specific migration failed");
311
- }
312
- }
313
- catch (err) {
314
- if (!/404/.test(String(err?.message ?? "")))
315
- throw err;
316
- }
317
- if (!cancelled) {
318
- setState({
319
- status: "done",
320
- coreTables: typeof coreBody?.tables === "object" && coreBody.tables
321
- ? coreBody.tables
322
- : {},
323
- appKeys,
324
- error: null,
325
- });
326
- }
327
- }
328
- catch (err) {
329
- if (!cancelled) {
330
- setState({
331
- status: "error",
332
- coreTables: {},
333
- appKeys: [],
334
- error: err?.message || "Migration failed",
335
- });
336
- }
337
- }
338
- })();
339
- return () => {
340
- cancelled = true;
341
- };
342
- }, [email]);
343
- return state;
344
- }
345
196
  /**
346
197
  * Default Team management page. Templates can route directly to this component
347
198
  * or wrap it with their own Layout via the `layout` prop.
348
199
  */
349
200
  export function TeamPage({ layout, title = "Team", createOrgDescription, className, }) {
350
201
  const { data: org, isLoading } = useOrg();
351
- const migration = useMigrateLocalDataOnSignIn(org?.email);
352
- const isMigrating = migration.status === "running";
353
- const content = (_jsxs("div", { className: `space-y-6 max-w-2xl ${className ?? ""}`, children: [_jsx("h2", { className: "text-2xl font-bold tracking-tight", children: title }), isLoading && (_jsx("section", { className: "rounded-lg border border-border bg-card p-6", children: _jsx("div", { className: "text-sm text-muted-foreground", children: "Loading\u2026" }) })), !isLoading && org?.email === DEV_MODE_USER_EMAIL && (_jsx(LocalModeSignInCard, {})), !isLoading && org?.email !== DEV_MODE_USER_EMAIL && (_jsx(MigrationStatusCard, { state: migration })), !isLoading && org?.email !== DEV_MODE_USER_EMAIL && !isMigrating && (_jsxs(_Fragment, { children: [_jsx(PendingInvitationsCard, {}), !org?.orgId ? (_jsxs(_Fragment, { children: [org?.domainMatches && org.domainMatches.length > 0 && (_jsx(JoinByDomainCard, { matches: org.domainMatches })), _jsx(CreateOrgCard, { description: createOrgDescription })] })) : (_jsx(MembersCard, {}))] }))] }));
202
+ const content = (_jsxs("div", { className: `space-y-6 max-w-2xl ${className ?? ""}`, children: [_jsx("h2", { className: "text-2xl font-bold tracking-tight", children: title }), isLoading && (_jsx("section", { className: "rounded-lg border border-border bg-card p-6", children: _jsx("div", { className: "text-sm text-muted-foreground", children: "Loading\u2026" }) })), !isLoading && (_jsxs(_Fragment, { children: [_jsx(PendingInvitationsCard, {}), !org?.orgId ? (_jsxs(_Fragment, { children: [org?.domainMatches && org.domainMatches.length > 0 && (_jsx(JoinByDomainCard, { matches: org.domainMatches })), _jsx(CreateOrgCard, { description: createOrgDescription })] })) : (_jsx(MembersCard, {}))] }))] }));
354
203
  return layout ? _jsx(_Fragment, { children: layout(content) }) : content;
355
204
  }
356
205
  //# sourceMappingURL=TeamPage.js.map