@psnext/design-system 2.1.0 → 2.2.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 (225) hide show
  1. package/DESIGN.md +2 -2
  2. package/dist/Header.cjs +27 -27
  3. package/dist/Header.cjs.map +1 -1
  4. package/dist/Header.js +22 -28
  5. package/dist/Header.js.map +1 -1
  6. package/dist/index.d.cts +5 -5
  7. package/dist/index.d.ts +5 -5
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index10.d.cts +6 -6
  10. package/dist/index10.d.cts.map +1 -1
  11. package/dist/index10.d.ts +7 -7
  12. package/dist/index10.d.ts.map +1 -1
  13. package/dist/index11.d.cts +3 -3
  14. package/dist/index11.d.cts.map +1 -1
  15. package/dist/index11.d.ts +3 -3
  16. package/dist/index11.d.ts.map +1 -1
  17. package/dist/index12.d.cts +12 -12
  18. package/dist/index12.d.cts.map +1 -1
  19. package/dist/index12.d.ts +12 -12
  20. package/dist/index12.d.ts.map +1 -1
  21. package/dist/index13.d.cts +6 -6
  22. package/dist/index13.d.ts +6 -6
  23. package/dist/index14.d.cts +5 -5
  24. package/dist/index14.d.cts.map +1 -1
  25. package/dist/index14.d.ts +5 -5
  26. package/dist/index15.d.cts +3 -3
  27. package/dist/index15.d.cts.map +1 -1
  28. package/dist/index15.d.ts +3 -3
  29. package/dist/index16.d.cts +2 -2
  30. package/dist/index16.d.cts.map +1 -1
  31. package/dist/index16.d.ts +2 -2
  32. package/dist/index17.d.cts +4 -4
  33. package/dist/index17.d.cts.map +1 -1
  34. package/dist/index17.d.ts +4 -4
  35. package/dist/index18.d.cts +11 -11
  36. package/dist/index18.d.ts +11 -11
  37. package/dist/index19.d.cts +10 -10
  38. package/dist/index19.d.ts +10 -10
  39. package/dist/index2.d.cts +8 -8
  40. package/dist/index2.d.ts +8 -8
  41. package/dist/index2.d.ts.map +1 -1
  42. package/dist/index20.d.cts +16 -16
  43. package/dist/index20.d.ts +16 -16
  44. package/dist/index21.d.cts +4 -4
  45. package/dist/index21.d.ts +4 -4
  46. package/dist/index22.d.cts +11 -11
  47. package/dist/index22.d.ts +11 -11
  48. package/dist/index23.d.cts +16 -16
  49. package/dist/index23.d.ts +16 -16
  50. package/dist/index24.d.cts +7 -7
  51. package/dist/index24.d.ts +10 -10
  52. package/dist/index25.d.cts +2 -2
  53. package/dist/index25.d.cts.map +1 -1
  54. package/dist/index25.d.ts +2 -2
  55. package/dist/index26.d.cts +13 -13
  56. package/dist/index26.d.cts.map +1 -1
  57. package/dist/index26.d.ts +14 -14
  58. package/dist/index26.d.ts.map +1 -1
  59. package/dist/index27.d.cts +7 -7
  60. package/dist/index27.d.cts.map +1 -1
  61. package/dist/index27.d.ts +7 -7
  62. package/dist/index28.d.cts +5 -5
  63. package/dist/index28.d.ts +5 -5
  64. package/dist/index29.d.cts +4 -4
  65. package/dist/index29.d.ts +4 -4
  66. package/dist/index3.d.cts +13 -13
  67. package/dist/index3.d.cts.map +1 -1
  68. package/dist/index30.d.cts +5 -5
  69. package/dist/index30.d.cts.map +1 -1
  70. package/dist/index30.d.ts +6 -6
  71. package/dist/index31.d.cts +4 -4
  72. package/dist/index31.d.cts.map +1 -1
  73. package/dist/index31.d.ts +4 -4
  74. package/dist/index32.d.cts +6 -6
  75. package/dist/index32.d.ts +3 -3
  76. package/dist/index33.d.cts +11 -11
  77. package/dist/index33.d.cts.map +1 -1
  78. package/dist/index33.d.ts +11 -11
  79. package/dist/index34.d.cts +5 -5
  80. package/dist/index34.d.ts +5 -5
  81. package/dist/index35.d.cts +15 -15
  82. package/dist/index35.d.cts.map +1 -1
  83. package/dist/index35.d.ts +15 -15
  84. package/dist/index35.d.ts.map +1 -1
  85. package/dist/index36.d.cts +3 -3
  86. package/dist/index36.d.ts +3 -3
  87. package/dist/index37.d.cts +4 -4
  88. package/dist/index37.d.cts.map +1 -1
  89. package/dist/index37.d.ts +5 -5
  90. package/dist/index38.d.cts +2 -2
  91. package/dist/index38.d.ts +2 -2
  92. package/dist/index39.d.cts +17 -17
  93. package/dist/index39.d.cts.map +1 -1
  94. package/dist/index39.d.ts +17 -17
  95. package/dist/index4.d.cts +2 -2
  96. package/dist/index4.d.cts.map +1 -1
  97. package/dist/index4.d.ts +2 -2
  98. package/dist/index4.d.ts.map +1 -1
  99. package/dist/index40.d.cts +5 -5
  100. package/dist/index40.d.cts.map +1 -1
  101. package/dist/index40.d.ts +5 -5
  102. package/dist/index41.d.cts +11 -11
  103. package/dist/index41.d.ts +11 -11
  104. package/dist/index42.d.cts +8 -8
  105. package/dist/index42.d.cts.map +1 -1
  106. package/dist/index42.d.ts +8 -8
  107. package/dist/index43.d.cts +9 -9
  108. package/dist/index43.d.cts.map +1 -1
  109. package/dist/index43.d.ts +9 -9
  110. package/dist/index45.d.cts +8 -8
  111. package/dist/index45.d.cts.map +1 -1
  112. package/dist/index45.d.ts +8 -8
  113. package/dist/index46.d.cts +2 -2
  114. package/dist/index46.d.cts.map +1 -1
  115. package/dist/index46.d.ts +2 -2
  116. package/dist/index47.d.cts +4 -4
  117. package/dist/index47.d.ts +4 -4
  118. package/dist/index48.d.cts +4 -4
  119. package/dist/index48.d.cts.map +1 -1
  120. package/dist/index48.d.ts +4 -4
  121. package/dist/index49.d.cts +3 -3
  122. package/dist/index49.d.cts.map +1 -1
  123. package/dist/index49.d.ts +3 -3
  124. package/dist/index5.d.cts +7 -7
  125. package/dist/index5.d.ts +7 -7
  126. package/dist/index5.d.ts.map +1 -1
  127. package/dist/index50.d.cts +11 -11
  128. package/dist/index50.d.ts +11 -11
  129. package/dist/index51.d.cts +9 -9
  130. package/dist/index51.d.cts.map +1 -1
  131. package/dist/index51.d.ts +9 -9
  132. package/dist/index51.d.ts.map +1 -1
  133. package/dist/index52.d.cts +5 -5
  134. package/dist/index52.d.ts +5 -5
  135. package/dist/index53.d.cts +26 -26
  136. package/dist/index53.d.cts.map +1 -1
  137. package/dist/index53.d.ts +26 -26
  138. package/dist/index54.d.cts +5 -5
  139. package/dist/index54.d.cts.map +1 -1
  140. package/dist/index54.d.ts +5 -5
  141. package/dist/index55.d.cts +2 -2
  142. package/dist/index55.d.ts +2 -2
  143. package/dist/index55.d.ts.map +1 -1
  144. package/dist/index56.d.ts +2 -2
  145. package/dist/index57.d.cts +2 -2
  146. package/dist/index57.d.ts +2 -2
  147. package/dist/index57.d.ts.map +1 -1
  148. package/dist/index58.d.cts +4 -4
  149. package/dist/index58.d.ts +4 -4
  150. package/dist/index59.d.cts +9 -9
  151. package/dist/index59.d.ts +9 -9
  152. package/dist/index59.d.ts.map +1 -1
  153. package/dist/index6.d.cts +6 -6
  154. package/dist/index6.d.ts +5 -5
  155. package/dist/index6.d.ts.map +1 -1
  156. package/dist/index60.d.cts +6 -6
  157. package/dist/index60.d.ts +13 -13
  158. package/dist/index60.d.ts.map +1 -1
  159. package/dist/index61.d.cts +6 -6
  160. package/dist/index61.d.ts +5 -5
  161. package/dist/index61.d.ts.map +1 -1
  162. package/dist/index62.d.cts +5 -5
  163. package/dist/index62.d.ts +4 -4
  164. package/dist/index62.d.ts.map +1 -1
  165. package/dist/index63.d.cts +2 -2
  166. package/dist/index63.d.ts +2 -2
  167. package/dist/index63.d.ts.map +1 -1
  168. package/dist/index64.d.cts +6 -6
  169. package/dist/index64.d.cts.map +1 -1
  170. package/dist/index64.d.ts +6 -6
  171. package/dist/index64.d.ts.map +1 -1
  172. package/dist/index65.d.cts +3 -3
  173. package/dist/index65.d.ts +3 -3
  174. package/dist/index65.d.ts.map +1 -1
  175. package/dist/index66.d.cts +4 -4
  176. package/dist/index66.d.cts.map +1 -1
  177. package/dist/index66.d.ts +5 -5
  178. package/dist/index67.d.cts +2 -2
  179. package/dist/index67.d.cts.map +1 -1
  180. package/dist/index67.d.ts +2 -2
  181. package/dist/index68.d.cts +7 -7
  182. package/dist/index68.d.cts.map +1 -1
  183. package/dist/index68.d.ts +8 -8
  184. package/dist/index68.d.ts.map +1 -1
  185. package/dist/index69.d.cts +7 -7
  186. package/dist/index69.d.cts.map +1 -1
  187. package/dist/index69.d.ts +7 -7
  188. package/dist/index69.d.ts.map +1 -1
  189. package/dist/index7.d.cts +8 -8
  190. package/dist/index7.d.ts +8 -8
  191. package/dist/index7.d.ts.map +1 -1
  192. package/dist/index71.d.cts +26 -12
  193. package/dist/index71.d.cts.map +1 -1
  194. package/dist/index71.d.ts +26 -12
  195. package/dist/index71.d.ts.map +1 -1
  196. package/dist/index8.d.cts +5 -5
  197. package/dist/index8.d.ts +4 -4
  198. package/dist/index8.d.ts.map +1 -1
  199. package/dist/index9.d.cts +2 -2
  200. package/dist/index9.d.cts.map +1 -1
  201. package/dist/index9.d.ts +2 -2
  202. package/dist/patterns/DataTable/index.d.cts +3 -3
  203. package/dist/patterns/DataTable/index.d.ts +3 -3
  204. package/dist/patterns/DataTable/index.d.ts.map +1 -1
  205. package/dist/patterns/Footer/index.d.cts +7 -7
  206. package/dist/patterns/Footer/index.d.cts.map +1 -1
  207. package/dist/patterns/Footer/index.d.ts +7 -7
  208. package/dist/patterns/Header/index.cjs +1 -0
  209. package/dist/patterns/Header/index.d.cts +2 -2
  210. package/dist/patterns/Header/index.d.ts +2 -2
  211. package/dist/patterns/Header/index.js +2 -2
  212. package/dist/patterns/MediaObject/index.d.cts +12 -12
  213. package/dist/patterns/MediaObject/index.d.cts.map +1 -1
  214. package/dist/patterns/MediaObject/index.d.ts +13 -13
  215. package/dist/patterns/SectionCard/index.d.cts +2 -2
  216. package/dist/patterns/SectionCard/index.d.cts.map +1 -1
  217. package/dist/patterns/SectionCard/index.d.ts +2 -2
  218. package/dist/patterns/SectionHeading/index.d.cts +2 -2
  219. package/dist/patterns/SectionHeading/index.d.cts.map +1 -1
  220. package/dist/patterns/SectionHeading/index.d.ts +2 -2
  221. package/dist/patterns/index.cjs +1 -0
  222. package/dist/patterns/index.d.cts +2 -2
  223. package/dist/patterns/index.d.ts +2 -2
  224. package/dist/patterns/index.js +2 -2
  225. package/package.json +1 -1
package/DESIGN.md CHANGED
@@ -536,9 +536,9 @@ Stories in `src/<layer>/<Component>/<Component>.stories.tsx` are the canonical e
536
536
  - **Notes**: Responsive 3-region grid (start/center/end), stacks centered on mobile, spreads on md+. Compositional — assemble from the region parts + Text/Link/Logo.
537
537
 
538
538
  ### Header
539
- - **Exports**: `Header` (wired), `HeaderBar`, `HeaderLogo`, `OrgPill`, `ProfileMenu`, `ThemeToggle`, `ProjectPicker`; hooks/data layer `useGlobalHeader`, `useHeaderCredits`, `useHeaderProject`, `getSessionToken`, `isSessionValid`, `buildEndpoints`, `DEFAULT_LABELS`; consts/types `TOGGLE_PROJECT_DROPDOWN_EVENT`, `GlobalHeaderState`, `ToggleProjectDropdownDetail`, `HeaderSelection`, `UseHeaderProjectResult`, `Labels`, `Fetcher`, `HeaderEndpoints`, `HeaderProps`, `ProjectNode`
539
+ - **Exports**: `Header` (wired), `HeaderBar`, `HeaderLogo`, `OrgPill`, `ProfileMenu`, `ThemeToggle`, `ProjectPicker`; hooks/data layer `useGlobalHeader`, `useHeaderCredits`, `useHeaderProject`, `getSessionToken`, `getSessionClaims`, `isSessionValid`, `buildEndpoints`, `DEFAULT_LABELS`; consts/types `TOGGLE_PROJECT_DROPDOWN_EVENT`, `GlobalHeaderState`, `ToggleProjectDropdownDetail`, `HeaderSelection`, `UseHeaderProjectResult`, `Labels`, `Fetcher`, `HeaderEndpoints`, `HeaderProps`, `ProjectNode`, `SessionClaims`
540
540
  - **Custom props** (HeaderProps): `apiBaseUrl` — studio-auth API host (required); `environmentId` — reads `{environmentId}-sessionId` cookie (required); `logoType` — ThemeProduct, defaults to active product; `logoHref` — logo link destination (defaults to the product's public site); `showOrg`/`showProjectDropdown`/`showProfile`/`showThemeSwitcher`/`showCredits` — visibility toggles (all default `false`); `pageTitle`; `feedbackUrl`; `clientId` — active org id; `_dangerouslyInjectAccessToken` — escape hatch, avoid; `onProjectChange`; `onCloseProjectOverlay`; `logoAddon` — node between logo and picker; `endpoints` — per-path overrides; `fetcher` — custom fetch (default global); `staleTime` (default 5 min); `gcTime` (default 10 min).
541
- - **Notes**: Must render inside a `<ThemeProvider>`; owns an internal singleton QueryClient (no app setup). Logged-out users see no user content; `ProjectNode` shape = `{ id, name, children: ProjectNode[], isContextOnly? }`.
541
+ - **Notes**: Must render inside a `<ThemeProvider>`; owns an internal singleton QueryClient (no app setup). Auth state and user identity (email, client id) are decoded from the `{environmentId}-sessionId` JWT cookie — no `/me` call. Logged-out users see no user content; `ProjectNode` shape = `{ id, name, children: ProjectNode[], isContextOnly? }`.
542
542
 
543
543
  ### MediaObject
544
544
  - **Exports**: `MediaObject`, `MediaObjectImage`, `MediaObjectIcon`, `MediaObjectContent`, `MediaObjectTitle`, `MediaObjectText`, `MediaObjectActions`, `MediaObjectGroup`, `mediaObjectGroupVariants`, types `MediaObjectProps`, `MediaObjectImageProps`, `MediaObjectTitleProps`, `MediaObjectGroupProps`
package/dist/Header.cjs CHANGED
@@ -187,7 +187,6 @@ const DEFAULT_LABELS = {
187
187
  //#region src/patterns/Header/api.ts
188
188
  const PATHS = {
189
189
  token: "/api/v1/studio-auth/sessions/token",
190
- me: "/api/v1/onboarding/users/me",
191
190
  logout: "/api/v1/studio-auth/sessions/logout",
192
191
  projects: (clientId) => `/api/v1/onboarding/organization/${clientId}/projects`,
193
192
  credits: "/save/api/v1/users"
@@ -199,7 +198,6 @@ function join(baseUrl, path) {
199
198
  function buildEndpoints(apiBaseUrl, overrides) {
200
199
  return {
201
200
  token: overrides?.token ?? join(apiBaseUrl, PATHS.token),
202
- me: overrides?.me ?? join(apiBaseUrl, PATHS.me),
203
201
  logout: overrides?.logout ?? join(apiBaseUrl, PATHS.logout),
204
202
  projects: overrides?.projects ?? ((clientId) => join(apiBaseUrl, PATHS.projects(clientId))),
205
203
  credits: overrides?.credits ?? join(apiBaseUrl, PATHS.credits)
@@ -211,16 +209,6 @@ async function fetchToken(endpoints, fetcher) {
211
209
  const { accessToken } = await res.json();
212
210
  return accessToken;
213
211
  }
214
- async function fetchMe(endpoints, accessToken, fetcher) {
215
- const res = await fetcher(endpoints.me, {
216
- credentials: "include",
217
- headers: { Authorization: `Bearer ${accessToken}` }
218
- });
219
- if (!res.ok) throw new Error(`me request failed: ${res.status}`);
220
- const data = (await res.json()).response?.data;
221
- if (!data) throw new Error("me response missing data");
222
- return data;
223
- }
224
212
  async function fetchProjects(endpoints, clientId, accessToken, fetcher) {
225
213
  const res = await fetcher(endpoints.projects(clientId), {
226
214
  credentials: "include",
@@ -414,6 +402,24 @@ function decodeJwt(token) {
414
402
  return null;
415
403
  }
416
404
  }
405
+ /**
406
+ * Decode the user claims from the session cookie's JWT — no network call.
407
+ * Returns null when the cookie is absent or undecodable.
408
+ */
409
+ function getSessionClaims(environmentId) {
410
+ const token = getSessionToken(environmentId);
411
+ if (!token) return null;
412
+ const payload = decodeJwt(token);
413
+ if (!payload) return null;
414
+ const str = (v) => typeof v === "string" ? v : void 0;
415
+ return {
416
+ email: str(payload.email),
417
+ name: str(payload.name),
418
+ userId: str(payload.user_id),
419
+ clientId: str(payload.client_id),
420
+ exp: typeof payload.exp === "number" ? payload.exp : void 0
421
+ };
422
+ }
417
423
  /** True when a valid, unexpired session cookie exists for the environment. */
418
424
  function isSessionValid(environmentId) {
419
425
  const token = getSessionToken(environmentId);
@@ -433,6 +439,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
433
439
  const fetcher = react$1.useMemo(() => fetcherProp ?? ((input, init) => fetch(input, init)), [fetcherProp]);
434
440
  const endpoints = react$1.useMemo(() => buildEndpoints(apiBaseUrl, endpointOverrides), [apiBaseUrl, endpointOverrides]);
435
441
  const isAuthenticated = isSessionValid(environmentId);
442
+ const claims = getSessionClaims(environmentId);
436
443
  const tokenQuery = (0, _tanstack_react_query.useQuery)({
437
444
  queryKey: [
438
445
  "ds-header",
@@ -445,20 +452,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
445
452
  gcTime
446
453
  });
447
454
  const token = injectedToken ?? tokenQuery.data;
448
- const wantsUser = showProfile || showProjectDropdown || showOrg;
449
- const meQuery = (0, _tanstack_react_query.useQuery)({
450
- queryKey: [
451
- "ds-header",
452
- "me",
453
- endpoints.me,
454
- token
455
- ],
456
- queryFn: () => fetchMe(endpoints, token, fetcher),
457
- enabled: isAuthenticated && !!token && wantsUser,
458
- staleTime,
459
- gcTime
460
- });
461
- const effectiveClientId = clientId ?? meQuery.data?.clientId;
455
+ const effectiveClientId = clientId ?? claims?.clientId;
462
456
  const projectsQuery = (0, _tanstack_react_query.useQuery)({
463
457
  queryKey: [
464
458
  "ds-header",
@@ -484,7 +478,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
484
478
  gcTime
485
479
  });
486
480
  const projectsTree = projectsQuery.data?.projectsTree ?? [];
487
- const userEmail = showProfile ? meQuery.data?.email : void 0;
481
+ const userEmail = showProfile ? claims?.email : void 0;
488
482
  const creditsHref = react$1.useMemo(() => {
489
483
  try {
490
484
  return new URL("/user/activity", apiBaseUrl).toString();
@@ -859,6 +853,12 @@ Object.defineProperty(exports, "buildEndpoints", {
859
853
  return buildEndpoints;
860
854
  }
861
855
  });
856
+ Object.defineProperty(exports, "getSessionClaims", {
857
+ enumerable: true,
858
+ get: function() {
859
+ return getSessionClaims;
860
+ }
861
+ });
862
862
  Object.defineProperty(exports, "getSessionToken", {
863
863
  enumerable: true,
864
864
  get: function() {
@@ -1 +1 @@
1
- {"version":3,"file":"Header.cjs","names":["PILL","React","cn","Skeleton","ChevronDown","Popover","PopoverTrigger","PopoverContent","Search","Input","ChevronRight","Check","React","React","cn","Logo","Skeleton","useTheme","Button","Moon","Sun","DropdownMenu","DropdownMenuTrigger","Avatar","AvatarFallback","DropdownMenuContent","DropdownMenuLabel","DropdownMenuSeparator","DropdownMenuItem","LogOut","QueryClient","QueryClientProvider","MessageSquareShare","Credits","X","FolderOpen"],"sources":["../src/patterns/Header/project-tree.ts","../src/patterns/Header/ProjectPicker.tsx","../src/patterns/Header/labels.ts","../src/patterns/Header/api.ts","../src/patterns/Header/storage.ts","../src/patterns/Header/header-store.ts","../src/patterns/Header/session.ts","../src/patterns/Header/use-global-header.ts","../src/patterns/Header/Header.tsx"],"sourcesContent":["import type { ProjectNode } from \"./types\";\n\n/** True when a node with the given id exists anywhere in the tree. */\nexport function nodeExistsInTree(tree: ProjectNode[], id: string): boolean {\n return tree.some((node) => node.id === id || nodeExistsInTree(node.children, id));\n}\n\n/** Name of the node with the given id, or undefined. */\nexport function findProjectName(tree: ProjectNode[], id: string): string | undefined {\n for (const node of tree) {\n if (node.id === id) return node.name;\n const found = findProjectName(node.children, id);\n if (found) return found;\n }\n return undefined;\n}\n\n/** Filter the tree to nodes matching `term`, preserving ancestors of matches. */\nexport function filterTree(nodes: ProjectNode[], term: string): ProjectNode[] {\n if (!term) return nodes;\n const lower = term.toLowerCase();\n return nodes.flatMap((node) => {\n if (node.name.toLowerCase().includes(lower)) return [node];\n const children = filterTree(node.children, term);\n return children.length > 0 ? [{ ...node, children }] : [];\n });\n}\n","\"use client\";\n\nimport { Check, ChevronDown, ChevronRight, Search } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { Input } from \"@/primitives/Input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/primitives/Popover\";\nimport { Skeleton } from \"@/primitives/Skeleton\";\nimport { cn } from \"@/utils\";\nimport { filterTree, findProjectName } from \"./project-tree\";\nimport type { ProjectNode } from \"./types\";\n\ninterface ProjectPickerProps {\n projectsTree: ProjectNode[];\n selectedProjectId: string;\n selectedProjectName?: string;\n onSelect: (id: string) => void;\n projectLabel: string;\n projectPlaceholder: string;\n searchPlaceholder: string;\n emptyStateText: string;\n isLoading?: boolean;\n}\n\n// Pill shared by the trigger and the org pill — keeps the header lockup consistent.\nconst PILL =\n \"flex min-h-8 items-center gap-1 rounded-12 border border-primary-border bg-input-bg px-3 py-1 text-xs leading-4\";\n\nexport function ProjectPicker({\n projectsTree,\n selectedProjectId,\n selectedProjectName,\n onSelect,\n projectLabel,\n projectPlaceholder,\n searchPlaceholder,\n emptyStateText,\n isLoading = false,\n}: Readonly<ProjectPickerProps>) {\n const [open, setOpen] = React.useState(false);\n const [searchTerm, setSearchTerm] = React.useState(\"\");\n const [collapsedIds, setCollapsedIds] = React.useState<Set<string>>(new Set());\n\n const toggleCollapse = (id: string) =>\n setCollapsedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n\n const handleSelect = (id: string) => {\n onSelect(id);\n setOpen(false);\n };\n\n const selectedName = selectedProjectId\n ? (findProjectName(projectsTree, selectedProjectId) ?? selectedProjectName)\n : undefined;\n\n const filteredTree = filterTree(projectsTree, searchTerm);\n\n if (isLoading) {\n return (\n <div data-slot=\"header-project-picker\" className={cn(PILL, \"min-w-44\")}>\n <span className=\"shrink-0 text-muted-txt\">{projectLabel}:</span>\n <Skeleton className=\"h-3 flex-1\" />\n <ChevronDown className=\"size-4 shrink-0 text-muted-txt opacity-40\" />\n </div>\n );\n }\n\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setSearchTerm(\"\");\n }}\n >\n <PopoverTrigger\n data-slot=\"header-project-picker\"\n className={cn(PILL, \"text-left transition-colors hover:border-focus/60\")}\n >\n <span className=\"shrink-0 text-muted-txt\">{projectLabel}:</span>\n <span className={cn(\"max-w-36 truncate\", selectedName ? \"font-medium\" : \"text-muted-txt\")}>\n {selectedName ?? projectPlaceholder}\n </span>\n <ChevronDown className=\"ml-1 size-4 shrink-0 text-muted-txt\" />\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-64 p-0\" role=\"tree\" aria-label={projectLabel}>\n <div className=\"border-b p-2\">\n <div className=\"relative\">\n <Search className=\"absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-muted-txt\" />\n <Input\n size=\"sm\"\n className=\"pl-8\"\n placeholder={searchPlaceholder}\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n />\n </div>\n </div>\n <div className=\"max-h-64 overflow-y-auto py-1\">\n {filteredTree.length === 0 ? (\n <p className=\"px-1 py-0.5 text-sm text-muted-txt\">{emptyStateText}</p>\n ) : (\n filteredTree.map((node) => (\n <TreeNode\n key={node.id}\n node={node}\n depth={0}\n selectedId={selectedProjectId}\n onSelect={handleSelect}\n collapsedIds={collapsedIds}\n onToggleCollapse={toggleCollapse}\n />\n ))\n )}\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\ninterface TreeNodeProps {\n node: ProjectNode;\n depth: number;\n selectedId: string;\n onSelect: (id: string) => void;\n collapsedIds: Set<string>;\n onToggleCollapse: (id: string) => void;\n}\n\nfunction TreeNode({\n node,\n depth,\n selectedId,\n onSelect,\n collapsedIds,\n onToggleCollapse,\n}: Readonly<TreeNodeProps>) {\n const isSelected = !node.isContextOnly && selectedId === node.id;\n const isCollapsed = collapsedIds.has(node.id);\n const hasChildren = node.children.length > 0;\n\n return (\n <div\n role=\"treeitem\"\n aria-selected={node.isContextOnly ? undefined : isSelected}\n aria-expanded={hasChildren ? !isCollapsed : undefined}\n >\n <div\n style={{ paddingLeft: `${0.75 + depth}rem` }}\n className={cn(\n \"flex items-center gap-1 py-2 pr-3 text-sm transition-colors\",\n node.isContextOnly\n ? \"cursor-default text-muted-txt\"\n : \"cursor-pointer hover:bg-ghost-hover-bg\",\n isSelected && \"bg-secondary-bg font-medium\",\n )}\n >\n {hasChildren ? (\n <button\n type=\"button\"\n aria-label={isCollapsed ? `Expand ${node.name}` : `Collapse ${node.name}`}\n onClick={() => onToggleCollapse(node.id)}\n className=\"shrink-0 text-muted-txt hover:text-primary-txt\"\n >\n {isCollapsed ? (\n <ChevronRight className=\"size-3.5\" />\n ) : (\n <ChevronDown className=\"size-3.5\" />\n )}\n </button>\n ) : (\n <span className=\"inline-block w-3.5 shrink-0\" />\n )}\n\n {node.isContextOnly ? (\n <span className=\"flex-1 text-left\">{node.name}</span>\n ) : (\n <button type=\"button\" onClick={() => onSelect(node.id)} className=\"flex-1 text-left\">\n {node.name}\n </button>\n )}\n\n {isSelected && <Check className=\"ml-auto size-3.5 shrink-0 text-primary\" />}\n </div>\n {!isCollapsed && hasChildren && (\n <div role=\"group\">\n {node.children.map((child) => (\n <TreeNode\n key={child.id}\n node={child}\n depth={depth + 1}\n selectedId={selectedId}\n onSelect={onSelect}\n collapsedIds={collapsedIds}\n onToggleCollapse={onToggleCollapse}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n","/** Default UI strings. Spread-override via the Header `labels` prop if needed. */\nexport const DEFAULT_LABELS = {\n orgLabel: \"Org\",\n projectLabel: \"Project\",\n projectPlaceholder: \"Select project...\",\n searchPlaceholder: \"Search project\",\n emptyStateText: \"No projects found\",\n profileMenuLabel: \"Profile menu\",\n logoutLabel: \"Logout\",\n overlayTitle: \"Select a Project\",\n overlayDescription: \"Choose a project to get started. You can change it anytime from the header.\",\n} as const;\n\nexport type Labels = typeof DEFAULT_LABELS;\n","import type { Fetcher, HeaderEndpoints, HeaderProps, ProjectNode } from \"./types\";\n\n// Studio-auth API. Endpoints are built from the host-provided `apiBaseUrl` (full\n// domain) so the same header works across environments/subdomains. All requests\n// send cookies (`credentials: 'include'`); the parent-domain session cookie is\n// readable cross-subdomain.\n\nconst PATHS = {\n token: \"/api/v1/studio-auth/sessions/token\",\n me: \"/api/v1/onboarding/users/me\",\n logout: \"/api/v1/studio-auth/sessions/logout\",\n projects: (clientId: string) => `/api/v1/onboarding/organization/${clientId}/projects`,\n credits: \"/save/api/v1/users\",\n};\n\n/** Join a base URL and path without doubling slashes. */\nfunction join(baseUrl: string, path: string): string {\n return `${baseUrl.replace(/\\/$/, \"\")}${path}`;\n}\n\nexport function buildEndpoints(\n apiBaseUrl: string,\n overrides?: HeaderProps[\"endpoints\"],\n): HeaderEndpoints {\n return {\n token: overrides?.token ?? join(apiBaseUrl, PATHS.token),\n me: overrides?.me ?? join(apiBaseUrl, PATHS.me),\n logout: overrides?.logout ?? join(apiBaseUrl, PATHS.logout),\n projects:\n overrides?.projects ?? ((clientId: string) => join(apiBaseUrl, PATHS.projects(clientId))),\n credits: overrides?.credits ?? join(apiBaseUrl, PATHS.credits),\n };\n}\n\ntype MeResponse = { email: string; clientId: string };\ntype ApiEnvelope<T> = { response?: { data?: T } };\n\nexport async function fetchToken(endpoints: HeaderEndpoints, fetcher: Fetcher): Promise<string> {\n const res = await fetcher(endpoints.token, { credentials: \"include\" });\n if (!res.ok) throw new Error(`token request failed: ${res.status}`);\n const { accessToken } = (await res.json()) as { accessToken: string };\n return accessToken;\n}\n\nexport async function fetchMe(\n endpoints: HeaderEndpoints,\n accessToken: string,\n fetcher: Fetcher,\n): Promise<MeResponse> {\n const res = await fetcher(endpoints.me, {\n credentials: \"include\",\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) throw new Error(`me request failed: ${res.status}`);\n const body = (await res.json()) as ApiEnvelope<MeResponse>;\n const data = body.response?.data;\n if (!data) throw new Error(\"me response missing data\");\n return data;\n}\n\nexport async function fetchProjects(\n endpoints: HeaderEndpoints,\n clientId: string,\n accessToken: string,\n fetcher: Fetcher,\n): Promise<{ projectsTree: ProjectNode[]; organizationName: string | undefined }> {\n const res = await fetcher(endpoints.projects(clientId), {\n credentials: \"include\",\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) throw new Error(`projects request failed: ${res.status}`);\n const data = (await res.json()) as ApiEnvelope<{\n projectsTree?: ProjectNode[];\n organization?: { name?: string };\n }>;\n return {\n projectsTree: data.response?.data?.projectsTree ?? [],\n organizationName: data.response?.data?.organization?.name,\n };\n}\n\nexport async function fetchCredits(\n endpoints: HeaderEndpoints,\n accessToken: string,\n fetcher: Fetcher,\n): Promise<number | undefined> {\n const res = await fetcher(endpoints.credits, {\n credentials: \"include\",\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) throw new Error(`credits request failed: ${res.status}`);\n const data = (await res.json()) as { remainingCredits?: number };\n return data.remainingCredits;\n}\n\nexport async function callLogout(endpoints: HeaderEndpoints, fetcher: Fetcher): Promise<void> {\n try {\n await fetcher(endpoints.logout, { method: \"POST\", credentials: \"include\" });\n } finally {\n if (typeof window !== \"undefined\") window.location.href = \"/\";\n }\n}\n","// Persists the user's project selection across reloads (sessionStorage), tagged\n// with the client it belongs to so a stale selection from a different org is\n// discarded on re-login.\n\nconst PROJECT_KEY = \"ds-header-project\";\n\nexport interface StoredProject {\n id: string;\n name: string;\n clientId?: string;\n}\n\nexport function getStoredProject(): StoredProject | null {\n try {\n const raw = sessionStorage.getItem(PROJECT_KEY);\n return raw ? (JSON.parse(raw) as StoredProject) : null;\n } catch {\n return null;\n }\n}\n\nexport function setStoredProject(project: StoredProject): void {\n try {\n sessionStorage.setItem(PROJECT_KEY, JSON.stringify(project));\n } catch {\n // sessionStorage unavailable — degrade silently\n }\n}\n\nexport function clearStoredProject(): void {\n try {\n sessionStorage.removeItem(PROJECT_KEY);\n } catch {\n // noop\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport { clearStoredProject, getStoredProject, setStoredProject } from \"./storage\";\n\n// Module-level store for the current project/org selection. Module scope (not\n// React context) so a host app can read and change the selection from anywhere —\n// including components rendered outside the <Header> subtree. The Header is one\n// writer/reader; `useHeaderProject()` gives apps the same access.\n\nexport interface HeaderSelection {\n selectedProjectId: string;\n selectedProjectName: string;\n organizationName: string | undefined;\n /** Remaining credits for the logged-in user (read-only here; set by the Header). */\n credits: number | undefined;\n /** Client/org the current selection + org belong to (for cross-org invalidation). */\n clientId: string | undefined;\n}\n\nfunction initialState(): HeaderSelection {\n const stored = typeof window !== \"undefined\" ? getStoredProject() : null;\n return {\n selectedProjectId: stored?.id ?? \"\",\n selectedProjectName: stored?.name ?? \"\",\n organizationName: undefined,\n credits: undefined,\n clientId: stored?.clientId,\n };\n}\n\nlet state: HeaderSelection = initialState();\n// Stable reference for SSR / first server render.\nconst SERVER_SNAPSHOT: HeaderSelection = Object.freeze({\n selectedProjectId: \"\",\n selectedProjectName: \"\",\n organizationName: undefined,\n credits: undefined,\n clientId: undefined,\n});\n\nconst listeners = new Set<() => void>();\nfunction emit() {\n for (const listener of listeners) listener();\n}\n\nexport const headerStore = {\n subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n getSnapshot(): HeaderSelection {\n return state;\n },\n getServerSnapshot(): HeaderSelection {\n return SERVER_SNAPSHOT;\n },\n\n /** Set the selected project. `name` is best-effort; the Header backfills it from the tree. */\n setProject(id: string, name = \"\", clientId?: string) {\n if (id) setStoredProject({ id, name, clientId: clientId ?? state.clientId });\n else clearStoredProject();\n state = {\n ...state,\n selectedProjectId: id,\n selectedProjectName: name,\n clientId: clientId ?? state.clientId,\n };\n emit();\n },\n\n clearProject() {\n clearStoredProject();\n state = { ...state, selectedProjectId: \"\", selectedProjectName: \"\" };\n emit();\n },\n\n setOrganization(name: string | undefined, clientId?: string) {\n state = { ...state, organizationName: name, clientId: clientId ?? state.clientId };\n emit();\n },\n\n setCredits(credits: number | undefined) {\n if (state.credits === credits) return;\n state = { ...state, credits };\n emit();\n },\n\n /** When the active client changes, drop a selection/org from the previous one. */\n reconcileClient(clientId: string) {\n if (state.clientId === clientId) return;\n if (state.clientId && state.clientId !== clientId) {\n clearStoredProject();\n state = {\n selectedProjectId: \"\",\n selectedProjectName: \"\",\n organizationName: undefined,\n credits: state.credits, // user-level — survives org switches\n clientId,\n };\n } else {\n state = { ...state, clientId };\n }\n emit();\n },\n};\n\n/** Internal: subscribe a component to the selection store. */\nexport function useHeaderStore(): HeaderSelection {\n return React.useSyncExternalStore(\n headerStore.subscribe,\n headerStore.getSnapshot,\n headerStore.getServerSnapshot,\n );\n}\n\nexport interface UseHeaderProjectResult {\n selectedProjectId: string;\n selectedProjectName: string | undefined;\n organizationName: string | undefined;\n /** Set the active project from outside the Header (by id). */\n setProject: (id: string) => void;\n /** Clear the active project (e.g. to re-prompt selection). */\n clearProject: () => void;\n}\n\n/**\n * Read the current project/org and change the project from anywhere in the app —\n * no need to be inside the <Header>. Reflects user selections made in the header\n * and updates the header when changed here.\n */\nexport function useHeaderProject(): UseHeaderProjectResult {\n const snapshot = useHeaderStore();\n return {\n selectedProjectId: snapshot.selectedProjectId,\n selectedProjectName: snapshot.selectedProjectName || undefined,\n organizationName: snapshot.organizationName,\n setProject: React.useCallback((id: string) => headerStore.setProject(id), []),\n clearProject: React.useCallback(() => headerStore.clearProject(), []),\n };\n}\n\n/** Read the logged-in user's remaining credits (read-only; the Header owns it). */\nexport function useHeaderCredits(): number | undefined {\n return useHeaderStore().credits;\n}\n","// Client-side session detection. The auth system writes a JS-readable cookie\n// `{environmentId}-sessionId` (a JWT) on the shared parent domain. We decode it\n// and check its expiry to know whether the user is logged in — no network call.\n\ninterface JwtPayload {\n exp?: number;\n [key: string]: unknown;\n}\n\n/** Read the raw session JWT from the `{environmentId}-sessionId` cookie. */\nexport function getSessionToken(environmentId: string): string | null {\n if (typeof document === \"undefined\") return null;\n const name = `${environmentId}-sessionId`;\n for (const part of document.cookie.split(\";\")) {\n const [key, ...rest] = part.trim().split(\"=\");\n if (key === name) return decodeURIComponent(rest.join(\"=\"));\n }\n return null;\n}\n\n/** Decode a JWT payload without verifying the signature (display/expiry only). */\nexport function decodeJwt(token: string): JwtPayload | null {\n try {\n const payload = token.split(\".\")[1];\n if (!payload) return null;\n const base64 = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const json = atob(base64);\n return JSON.parse(json) as JwtPayload;\n } catch {\n return null;\n }\n}\n\n/** True when a valid, unexpired session cookie exists for the environment. */\nexport function isSessionValid(environmentId: string): boolean {\n const token = getSessionToken(environmentId);\n if (!token) return false;\n const payload = decodeJwt(token);\n if (!payload) return false;\n // No exp claim → treat as a non-expiring session.\n if (typeof payload.exp !== \"number\") return true;\n return payload.exp * 1000 > Date.now();\n}\n","\"use client\";\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport {\n buildEndpoints,\n callLogout,\n fetchCredits,\n fetchMe,\n fetchProjects,\n fetchToken,\n} from \"./api\";\nimport { headerStore, useHeaderStore } from \"./header-store\";\nimport { findProjectName, nodeExistsInTree } from \"./project-tree\";\nimport { isSessionValid } from \"./session\";\nimport type { HeaderProps, ProjectNode } from \"./types\";\n\n/** Window event other modules can dispatch to show/hide the project dropdown. */\nexport const TOGGLE_PROJECT_DROPDOWN_EVENT = \"toggle-project-dropdown\";\nexport interface ToggleProjectDropdownDetail {\n show: boolean;\n}\n\nconst DEFAULT_STALE_TIME = 5 * 60_000;\nconst DEFAULT_GC_TIME = 10 * 60_000;\n\nexport interface GlobalHeaderState {\n /** Whether a valid session cookie exists — when false, hide all user content. */\n isAuthenticated: boolean;\n userEmail: string | undefined;\n organizationName: string | undefined;\n projectsTree: ProjectNode[];\n selectedProjectId: string;\n selectedProjectName: string | undefined;\n isProjectsLoading: boolean;\n isOrgLoading: boolean;\n /** Remaining credits for the user (undefined until loaded / when disabled). */\n credits: number | undefined;\n /** Where the credits pill links to (studio-auth `/user/activity`). */\n creditsHref: string;\n projectDropdownVisible: boolean;\n /** True when the dropdown is on, the user is in, but no project is chosen yet. */\n showOverlay: boolean;\n selectProject: (id: string) => void;\n logout: () => void;\n}\n\ntype UseGlobalHeaderArgs = Pick<\n HeaderProps,\n | \"apiBaseUrl\"\n | \"environmentId\"\n | \"clientId\"\n | \"_dangerouslyInjectAccessToken\"\n | \"showProjectDropdown\"\n | \"showProfile\"\n | \"showOrg\"\n | \"showCredits\"\n | \"endpoints\"\n | \"fetcher\"\n | \"staleTime\"\n | \"gcTime\"\n | \"onProjectChange\"\n>;\n\nexport function useGlobalHeader({\n apiBaseUrl,\n environmentId,\n clientId,\n _dangerouslyInjectAccessToken: injectedToken,\n showProjectDropdown = false,\n showProfile = false,\n showOrg = false,\n showCredits = false,\n endpoints: endpointOverrides,\n fetcher: fetcherProp,\n staleTime = DEFAULT_STALE_TIME,\n gcTime = DEFAULT_GC_TIME,\n onProjectChange,\n}: UseGlobalHeaderArgs): GlobalHeaderState {\n const fetcher = React.useMemo(\n () => fetcherProp ?? ((input: RequestInfo | URL, init?: RequestInit) => fetch(input, init)),\n [fetcherProp],\n );\n const endpoints = React.useMemo(\n () => buildEndpoints(apiBaseUrl, endpointOverrides),\n [apiBaseUrl, endpointOverrides],\n );\n\n // Read the session cookie each render (cheap); gates every network call.\n const isAuthenticated = isSessionValid(environmentId);\n\n // token → me → projects, each enabled only when the previous resolved.\n const tokenQuery = useQuery({\n queryKey: [\"ds-header\", \"token\", endpoints.token],\n queryFn: () => fetchToken(endpoints, fetcher),\n enabled: isAuthenticated && !injectedToken,\n staleTime,\n gcTime,\n });\n const token = injectedToken ?? tokenQuery.data;\n\n const wantsUser = showProfile || showProjectDropdown || showOrg;\n const meQuery = useQuery({\n queryKey: [\"ds-header\", \"me\", endpoints.me, token],\n queryFn: () => fetchMe(endpoints, token as string, fetcher),\n enabled: isAuthenticated && !!token && wantsUser,\n staleTime,\n gcTime,\n });\n\n const effectiveClientId = clientId ?? meQuery.data?.clientId;\n const wantsProjects = showProjectDropdown || showOrg;\n const projectsQuery = useQuery({\n queryKey: [\"ds-header\", \"projects\", effectiveClientId, token],\n queryFn: () => fetchProjects(endpoints, effectiveClientId as string, token as string, fetcher),\n enabled: isAuthenticated && !!token && !!effectiveClientId && wantsProjects,\n staleTime,\n gcTime,\n });\n\n // Credits — only needs the token. Always refetched on mount (staleTime 0) so a\n // page refresh shows the current balance.\n const creditsQuery = useQuery({\n queryKey: [\"ds-header\", \"credits\", endpoints.credits, token],\n queryFn: () => fetchCredits(endpoints, token as string, fetcher),\n enabled: isAuthenticated && !!token && showCredits,\n staleTime: 0,\n gcTime,\n });\n\n const projectsTree = projectsQuery.data?.projectsTree ?? [];\n const userEmail = showProfile ? meQuery.data?.email : undefined;\n const creditsHref = React.useMemo(() => {\n try {\n return new URL(\"/user/activity\", apiBaseUrl).toString();\n } catch {\n return \"/user/activity\";\n }\n }, [apiBaseUrl]);\n\n // Selection + org live in a module store so host apps can read/change them\n // from outside the header (see useHeaderProject).\n const selection = useHeaderStore();\n\n // Once the client id resolves, drop a selection/org from a different client.\n React.useEffect(() => {\n if (effectiveClientId) headerStore.reconcileClient(effectiveClientId);\n }, [effectiveClientId]);\n\n // Once projects load: publish the org name and validate/backfill the selection\n // (drop one that no longer exists; fill in its name from the tree).\n React.useEffect(() => {\n if (!projectsQuery.data) return;\n const tree = projectsQuery.data.projectsTree;\n const id = headerStore.getSnapshot().selectedProjectId;\n if (id && !nodeExistsInTree(tree, id)) {\n headerStore.clearProject();\n } else if (id) {\n const name = findProjectName(tree, id);\n if (name && name !== headerStore.getSnapshot().selectedProjectName) {\n headerStore.setProject(id, name, effectiveClientId);\n }\n }\n headerStore.setOrganization(projectsQuery.data.organizationName, effectiveClientId);\n }, [projectsQuery.data, effectiveClientId]);\n\n // Publish credits to the store so host apps can read them via useHeaderCredits.\n React.useEffect(() => {\n if (creditsQuery.data !== undefined) headerStore.setCredits(creditsQuery.data);\n }, [creditsQuery.data]);\n\n const selectProject = (id: string) => {\n const name = findProjectName(projectsTree, id) ?? \"\";\n headerStore.setProject(id, name, effectiveClientId);\n onProjectChange?.(id);\n };\n\n const logout = () => {\n headerStore.clearProject();\n void callLogout(endpoints, fetcher);\n };\n\n // Visibility: declarative from the prop, overridable at runtime via the event.\n const [projectDropdownVisible, setProjectDropdownVisible] = React.useState(showProjectDropdown);\n React.useEffect(() => setProjectDropdownVisible(showProjectDropdown), [showProjectDropdown]);\n React.useEffect(() => {\n const handler = (e: Event) => {\n setProjectDropdownVisible((e as CustomEvent<ToggleProjectDropdownDetail>).detail.show);\n };\n window.addEventListener(TOGGLE_PROJECT_DROPDOWN_EVENT, handler);\n return () => window.removeEventListener(TOGGLE_PROJECT_DROPDOWN_EVENT, handler);\n }, []);\n\n const isProjectsLoading = projectsQuery.isFetching && !projectsQuery.data;\n\n return {\n isAuthenticated,\n userEmail,\n organizationName: selection.organizationName,\n projectsTree,\n selectedProjectId: selection.selectedProjectId,\n selectedProjectName: selection.selectedProjectName || undefined,\n isProjectsLoading,\n isOrgLoading: showOrg && isProjectsLoading,\n credits: selection.credits,\n creditsHref,\n projectDropdownVisible,\n showOverlay: isAuthenticated && projectDropdownVisible && !selection.selectedProjectId,\n selectProject,\n logout,\n };\n}\n","\"use client\";\n\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { FolderOpen, LogOut, MessageSquareShare, Moon, Sun, X } from \"lucide-react\";\nimport type * as React from \"react\";\n\nimport { Avatar, AvatarFallback } from \"@/primitives/Avatar\";\nimport { Button } from \"@/primitives/Button\";\nimport { Credits } from \"@/primitives/Credits\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/primitives/DropdownMenu\";\nimport { Logo } from \"@/primitives/Logo\";\nimport { Skeleton } from \"@/primitives/Skeleton\";\nimport { type ThemeProduct, useTheme } from \"@/primitives/ThemeProvider\";\nimport { cn } from \"@/utils\";\nimport { ProjectPicker } from \"./ProjectPicker\";\nimport { DEFAULT_LABELS } from \"./labels\";\nimport type { HeaderProps, ProjectNode } from \"./types\";\nimport { useGlobalHeader } from \"./use-global-header\";\n\n// Fallback logo destinations when the host doesn't pass `logoHref`.\nconst LOGO_HREF: Record<ThemeProduct, string> = {\n psai: \"https://sapientaiproducts.com/\",\n slingshot: \"https://sapientaiproducts.com/slingshot\",\n sustain: \"https://www.publicissapient.com/\",\n bodhi: \"https://sapientaiproducts.com/bodhi\",\n};\n\nconst PILL =\n \"flex min-h-8 items-center gap-1 rounded-12 border border-primary-border bg-input-bg px-3 py-1 text-xs leading-4\";\n\n// ── Presentational parts (props-driven; Storybook/VR-able) ──────────────────\n\n/** The header bar shell — a sticky glass strip. */\nfunction HeaderBar({ className, ...props }: React.ComponentProps<\"header\">) {\n return (\n <header\n data-slot=\"header\"\n className={cn(\n \"sticky top-1 z-10 mt-1 flex h-15 items-center gap-5 rounded-16 px-3 py-2\",\n \"bg-glass-60-bg backdrop-blur-glass\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Product logo linking to its home, with a Suspense-safe fallback. */\nfunction HeaderLogo({ brand, href }: { brand: ThemeProduct; href?: string }) {\n return (\n <a href={href ?? LOGO_HREF[brand]} aria-label=\"Go to product home\" data-slot=\"header-logo\">\n <Logo brand={brand} className=\"h-8 w-auto\" />\n </a>\n );\n}\n\n/** Read-only organisation pill. */\nfunction OrgPill({ name, loading }: { name?: string; loading?: boolean }) {\n if (loading && !name) {\n return (\n <div className={cn(PILL, \"ml-3\")} data-slot=\"header-org\">\n <span className=\"shrink-0 text-muted-txt\">{DEFAULT_LABELS.orgLabel}:</span>\n <Skeleton className=\"h-4 w-24\" />\n </div>\n );\n }\n if (!name) return null;\n return (\n <div className={cn(PILL, \"ml-3 select-none\")} title={name} data-slot=\"header-org\">\n <span className=\"shrink-0 text-muted-txt\">{DEFAULT_LABELS.orgLabel}:</span>\n <span className=\"max-w-36 truncate text-primary-txt\">{name}</span>\n </div>\n );\n}\n\n/** Light/dark toggle — defers to ThemeProvider. */\nfunction ThemeToggle() {\n const { theme, toggleTheme } = useTheme();\n return (\n <Button variant=\"ghost\" size=\"icon\" onClick={toggleTheme} aria-label=\"Toggle theme\">\n {theme === \"light\" ? <Moon className=\"size-4\" /> : <Sun className=\"size-4\" />}\n </Button>\n );\n}\n\n/** Profile menu — avatar trigger with email + logout. */\nfunction ProfileMenu({ email, onLogout }: { email?: string; onLogout: () => void }) {\n const initial = email?.trim().charAt(0).toUpperCase() || \"?\";\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={DEFAULT_LABELS.profileMenuLabel}>\n <Avatar className=\"size-7\">\n <AvatarFallback>{initial}</AvatarFallback>\n </Avatar>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" sideOffset={8} className=\"min-w-44\">\n {email && (\n <>\n <DropdownMenuLabel className=\"truncate font-normal text-muted-txt\">\n {email}\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n </>\n )}\n <DropdownMenuItem onSelect={onLogout} variant=\"destructive\">\n <LogOut />\n {DEFAULT_LABELS.logoutLabel}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nexport { HeaderBar, HeaderLogo, OrgPill, ThemeToggle, ProfileMenu };\n\n// ── Wired Header ─────────────────────────────────────────────────────────────\n\n// Internal singleton client so the header is drop-in (no app setup) while still\n// sharing its cache across mounts/navigation.\nlet sharedClient: QueryClient | undefined;\nfunction getQueryClient(): QueryClient {\n sharedClient ??= new QueryClient({\n defaultOptions: { queries: { retry: 1, refetchOnWindowFocus: false } },\n });\n return sharedClient;\n}\n\n/** Drop-in global header. Must be rendered inside a <ThemeProvider>. */\nexport function Header(props: HeaderProps) {\n return (\n <QueryClientProvider client={getQueryClient()}>\n <HeaderInner {...props} />\n </QueryClientProvider>\n );\n}\n\nfunction HeaderInner({\n logoType,\n logoHref,\n showOrg = false,\n showProjectDropdown = false,\n showProfile = false,\n showThemeSwitcher = false,\n showCredits = false,\n pageTitle,\n feedbackUrl,\n logoAddon,\n className,\n onCloseProjectOverlay,\n ...rest\n}: HeaderProps) {\n const { product } = useTheme();\n const brand = logoType ?? product;\n const state = useGlobalHeader({\n showOrg,\n showProjectDropdown,\n showProfile,\n showCredits,\n ...rest,\n });\n\n // Logged-out users see no user content (project/org/profile/credits).\n const showProjectUi = showProjectDropdown && state.isAuthenticated;\n const showOrgUi = showOrg && state.isAuthenticated;\n const showProfileUi = showProfile && state.isAuthenticated;\n const showCreditsUi = showCredits && state.isAuthenticated && state.credits !== undefined;\n const showActions = showThemeSwitcher || showProfileUi || showCreditsUi || !!feedbackUrl;\n\n return (\n <>\n <HeaderBar className={className}>\n <HeaderLogo brand={brand} href={logoHref} />\n {/* Page title — 20px (gap-5) from the logo. */}\n {pageTitle && (\n <span data-slot=\"header-title\" className=\"ml-5 font-semibold text-primary-txt text-sm\">\n {pageTitle}\n </span>\n )}\n {logoAddon}\n\n {showOrgUi && <OrgPill name={state.organizationName} loading={state.isOrgLoading} />}\n\n {showProjectUi && state.projectDropdownVisible && (\n <div className=\"ml-2\">\n <ProjectPicker\n projectsTree={state.projectsTree}\n selectedProjectId={state.selectedProjectId}\n selectedProjectName={state.selectedProjectName}\n onSelect={state.selectProject}\n projectLabel={DEFAULT_LABELS.projectLabel}\n projectPlaceholder={DEFAULT_LABELS.projectPlaceholder}\n searchPlaceholder={DEFAULT_LABELS.searchPlaceholder}\n emptyStateText={DEFAULT_LABELS.emptyStateText}\n isLoading={state.isProjectsLoading}\n />\n </div>\n )}\n\n {showActions && (\n <div className=\"ml-auto flex items-center gap-2\">\n {feedbackUrl && (\n <Button variant=\"ghost\" size=\"icon\" asChild>\n <a\n href={feedbackUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label=\"Share feedback\"\n >\n <MessageSquareShare className=\"size-4\" />\n </a>\n </Button>\n )}\n {showThemeSwitcher && <ThemeToggle />}\n {showCreditsUi && state.credits !== undefined && (\n <a\n href={state.creditsHref}\n aria-label=\"View credit activity\"\n className=\"rounded-full outline-none focus-visible:ring-3 focus-visible:ring-focus\"\n >\n <Credits value={state.credits} />\n </a>\n )}\n {showProfileUi && <ProfileMenu email={state.userEmail} onLogout={state.logout} />}\n </div>\n )}\n </HeaderBar>\n\n {showProjectUi && state.showOverlay && (\n <ProjectOverlay\n organizationName={state.organizationName}\n projectsTree={state.projectsTree}\n selectedProjectId={state.selectedProjectId}\n onSelect={state.selectProject}\n isLoading={state.isProjectsLoading}\n onClose={onCloseProjectOverlay}\n />\n )}\n </>\n );\n}\n\n// First-run blocking overlay prompting a project selection.\nfunction ProjectOverlay({\n organizationName,\n projectsTree,\n selectedProjectId,\n onSelect,\n isLoading,\n onClose,\n}: {\n organizationName?: string;\n projectsTree: ProjectNode[];\n selectedProjectId: string;\n onSelect: (id: string) => void;\n isLoading: boolean;\n onClose?: () => void;\n}) {\n return (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={DEFAULT_LABELS.overlayTitle}\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-primary-bg/80 backdrop-blur-sm\"\n >\n <div className=\"relative mx-1 flex w-full max-w-md flex-col items-center gap-6 rounded-16 border bg-glass-none-bg p-2 text-primary-txt shadow-xl\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={onClose ?? (() => window.history.back())}\n className=\"absolute top-4 right-4\"\n aria-label=\"Close\"\n >\n <X className=\"size-5\" />\n </Button>\n <div className=\"flex flex-col items-center gap-3 text-center\">\n <div className=\"rounded-full bg-primary/10 p-4\">\n <FolderOpen className=\"size-8 text-primary\" />\n </div>\n <h2 className=\"font-semibold text-primary-txt text-xl\">{DEFAULT_LABELS.overlayTitle}</h2>\n <p className=\"text-muted-txt text-sm\">{DEFAULT_LABELS.overlayDescription}</p>\n </div>\n <div className=\"flex w-full justify-center gap-2\">\n {organizationName && (\n <div className={cn(PILL, \"select-none\")} title={organizationName}>\n <span className=\"shrink-0 text-muted-txt\">{DEFAULT_LABELS.orgLabel}:</span>\n <span className=\"max-w-52 truncate font-medium\">{organizationName}</span>\n </div>\n )}\n <ProjectPicker\n projectsTree={projectsTree}\n selectedProjectId={selectedProjectId}\n onSelect={onSelect}\n projectLabel={DEFAULT_LABELS.projectLabel}\n projectPlaceholder={DEFAULT_LABELS.projectPlaceholder}\n searchPlaceholder={DEFAULT_LABELS.searchPlaceholder}\n emptyStateText={DEFAULT_LABELS.emptyStateText}\n isLoading={isLoading}\n />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Header.cjs","names":["PILL","React","cn","Skeleton","ChevronDown","Popover","PopoverTrigger","PopoverContent","Search","Input","ChevronRight","Check","React","React","cn","Logo","Skeleton","useTheme","Button","Moon","Sun","DropdownMenu","DropdownMenuTrigger","Avatar","AvatarFallback","DropdownMenuContent","DropdownMenuLabel","DropdownMenuSeparator","DropdownMenuItem","LogOut","QueryClient","QueryClientProvider","MessageSquareShare","Credits","X","FolderOpen"],"sources":["../src/patterns/Header/project-tree.ts","../src/patterns/Header/ProjectPicker.tsx","../src/patterns/Header/labels.ts","../src/patterns/Header/api.ts","../src/patterns/Header/storage.ts","../src/patterns/Header/header-store.ts","../src/patterns/Header/session.ts","../src/patterns/Header/use-global-header.ts","../src/patterns/Header/Header.tsx"],"sourcesContent":["import type { ProjectNode } from \"./types\";\n\n/** True when a node with the given id exists anywhere in the tree. */\nexport function nodeExistsInTree(tree: ProjectNode[], id: string): boolean {\n return tree.some((node) => node.id === id || nodeExistsInTree(node.children, id));\n}\n\n/** Name of the node with the given id, or undefined. */\nexport function findProjectName(tree: ProjectNode[], id: string): string | undefined {\n for (const node of tree) {\n if (node.id === id) return node.name;\n const found = findProjectName(node.children, id);\n if (found) return found;\n }\n return undefined;\n}\n\n/** Filter the tree to nodes matching `term`, preserving ancestors of matches. */\nexport function filterTree(nodes: ProjectNode[], term: string): ProjectNode[] {\n if (!term) return nodes;\n const lower = term.toLowerCase();\n return nodes.flatMap((node) => {\n if (node.name.toLowerCase().includes(lower)) return [node];\n const children = filterTree(node.children, term);\n return children.length > 0 ? [{ ...node, children }] : [];\n });\n}\n","\"use client\";\n\nimport { Check, ChevronDown, ChevronRight, Search } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { Input } from \"@/primitives/Input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/primitives/Popover\";\nimport { Skeleton } from \"@/primitives/Skeleton\";\nimport { cn } from \"@/utils\";\nimport { filterTree, findProjectName } from \"./project-tree\";\nimport type { ProjectNode } from \"./types\";\n\ninterface ProjectPickerProps {\n projectsTree: ProjectNode[];\n selectedProjectId: string;\n selectedProjectName?: string;\n onSelect: (id: string) => void;\n projectLabel: string;\n projectPlaceholder: string;\n searchPlaceholder: string;\n emptyStateText: string;\n isLoading?: boolean;\n}\n\n// Pill shared by the trigger and the org pill — keeps the header lockup consistent.\nconst PILL =\n \"flex min-h-8 items-center gap-1 rounded-12 border border-primary-border bg-input-bg px-3 py-1 text-xs leading-4\";\n\nexport function ProjectPicker({\n projectsTree,\n selectedProjectId,\n selectedProjectName,\n onSelect,\n projectLabel,\n projectPlaceholder,\n searchPlaceholder,\n emptyStateText,\n isLoading = false,\n}: Readonly<ProjectPickerProps>) {\n const [open, setOpen] = React.useState(false);\n const [searchTerm, setSearchTerm] = React.useState(\"\");\n const [collapsedIds, setCollapsedIds] = React.useState<Set<string>>(new Set());\n\n const toggleCollapse = (id: string) =>\n setCollapsedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n\n const handleSelect = (id: string) => {\n onSelect(id);\n setOpen(false);\n };\n\n const selectedName = selectedProjectId\n ? (findProjectName(projectsTree, selectedProjectId) ?? selectedProjectName)\n : undefined;\n\n const filteredTree = filterTree(projectsTree, searchTerm);\n\n if (isLoading) {\n return (\n <div data-slot=\"header-project-picker\" className={cn(PILL, \"min-w-44\")}>\n <span className=\"shrink-0 text-muted-txt\">{projectLabel}:</span>\n <Skeleton className=\"h-3 flex-1\" />\n <ChevronDown className=\"size-4 shrink-0 text-muted-txt opacity-40\" />\n </div>\n );\n }\n\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setSearchTerm(\"\");\n }}\n >\n <PopoverTrigger\n data-slot=\"header-project-picker\"\n className={cn(PILL, \"text-left transition-colors hover:border-focus/60\")}\n >\n <span className=\"shrink-0 text-muted-txt\">{projectLabel}:</span>\n <span className={cn(\"max-w-36 truncate\", selectedName ? \"font-medium\" : \"text-muted-txt\")}>\n {selectedName ?? projectPlaceholder}\n </span>\n <ChevronDown className=\"ml-1 size-4 shrink-0 text-muted-txt\" />\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-64 p-0\" role=\"tree\" aria-label={projectLabel}>\n <div className=\"border-b p-2\">\n <div className=\"relative\">\n <Search className=\"absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-muted-txt\" />\n <Input\n size=\"sm\"\n className=\"pl-8\"\n placeholder={searchPlaceholder}\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n />\n </div>\n </div>\n <div className=\"max-h-64 overflow-y-auto py-1\">\n {filteredTree.length === 0 ? (\n <p className=\"px-1 py-0.5 text-sm text-muted-txt\">{emptyStateText}</p>\n ) : (\n filteredTree.map((node) => (\n <TreeNode\n key={node.id}\n node={node}\n depth={0}\n selectedId={selectedProjectId}\n onSelect={handleSelect}\n collapsedIds={collapsedIds}\n onToggleCollapse={toggleCollapse}\n />\n ))\n )}\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\ninterface TreeNodeProps {\n node: ProjectNode;\n depth: number;\n selectedId: string;\n onSelect: (id: string) => void;\n collapsedIds: Set<string>;\n onToggleCollapse: (id: string) => void;\n}\n\nfunction TreeNode({\n node,\n depth,\n selectedId,\n onSelect,\n collapsedIds,\n onToggleCollapse,\n}: Readonly<TreeNodeProps>) {\n const isSelected = !node.isContextOnly && selectedId === node.id;\n const isCollapsed = collapsedIds.has(node.id);\n const hasChildren = node.children.length > 0;\n\n return (\n <div\n role=\"treeitem\"\n aria-selected={node.isContextOnly ? undefined : isSelected}\n aria-expanded={hasChildren ? !isCollapsed : undefined}\n >\n <div\n style={{ paddingLeft: `${0.75 + depth}rem` }}\n className={cn(\n \"flex items-center gap-1 py-2 pr-3 text-sm transition-colors\",\n node.isContextOnly\n ? \"cursor-default text-muted-txt\"\n : \"cursor-pointer hover:bg-ghost-hover-bg\",\n isSelected && \"bg-secondary-bg font-medium\",\n )}\n >\n {hasChildren ? (\n <button\n type=\"button\"\n aria-label={isCollapsed ? `Expand ${node.name}` : `Collapse ${node.name}`}\n onClick={() => onToggleCollapse(node.id)}\n className=\"shrink-0 text-muted-txt hover:text-primary-txt\"\n >\n {isCollapsed ? (\n <ChevronRight className=\"size-3.5\" />\n ) : (\n <ChevronDown className=\"size-3.5\" />\n )}\n </button>\n ) : (\n <span className=\"inline-block w-3.5 shrink-0\" />\n )}\n\n {node.isContextOnly ? (\n <span className=\"flex-1 text-left\">{node.name}</span>\n ) : (\n <button type=\"button\" onClick={() => onSelect(node.id)} className=\"flex-1 text-left\">\n {node.name}\n </button>\n )}\n\n {isSelected && <Check className=\"ml-auto size-3.5 shrink-0 text-primary\" />}\n </div>\n {!isCollapsed && hasChildren && (\n <div role=\"group\">\n {node.children.map((child) => (\n <TreeNode\n key={child.id}\n node={child}\n depth={depth + 1}\n selectedId={selectedId}\n onSelect={onSelect}\n collapsedIds={collapsedIds}\n onToggleCollapse={onToggleCollapse}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n","/** Default UI strings. Spread-override via the Header `labels` prop if needed. */\nexport const DEFAULT_LABELS = {\n orgLabel: \"Org\",\n projectLabel: \"Project\",\n projectPlaceholder: \"Select project...\",\n searchPlaceholder: \"Search project\",\n emptyStateText: \"No projects found\",\n profileMenuLabel: \"Profile menu\",\n logoutLabel: \"Logout\",\n overlayTitle: \"Select a Project\",\n overlayDescription: \"Choose a project to get started. You can change it anytime from the header.\",\n} as const;\n\nexport type Labels = typeof DEFAULT_LABELS;\n","import type { Fetcher, HeaderEndpoints, HeaderProps, ProjectNode } from \"./types\";\n\n// Studio-auth API. Endpoints are built from the host-provided `apiBaseUrl` (full\n// domain) so the same header works across environments/subdomains. All requests\n// send cookies (`credentials: 'include'`); the parent-domain session cookie is\n// readable cross-subdomain.\n\nconst PATHS = {\n token: \"/api/v1/studio-auth/sessions/token\",\n logout: \"/api/v1/studio-auth/sessions/logout\",\n projects: (clientId: string) => `/api/v1/onboarding/organization/${clientId}/projects`,\n credits: \"/save/api/v1/users\",\n};\n\n/** Join a base URL and path without doubling slashes. */\nfunction join(baseUrl: string, path: string): string {\n return `${baseUrl.replace(/\\/$/, \"\")}${path}`;\n}\n\nexport function buildEndpoints(\n apiBaseUrl: string,\n overrides?: HeaderProps[\"endpoints\"],\n): HeaderEndpoints {\n return {\n token: overrides?.token ?? join(apiBaseUrl, PATHS.token),\n logout: overrides?.logout ?? join(apiBaseUrl, PATHS.logout),\n projects:\n overrides?.projects ?? ((clientId: string) => join(apiBaseUrl, PATHS.projects(clientId))),\n credits: overrides?.credits ?? join(apiBaseUrl, PATHS.credits),\n };\n}\n\ntype ApiEnvelope<T> = { response?: { data?: T } };\n\nexport async function fetchToken(endpoints: HeaderEndpoints, fetcher: Fetcher): Promise<string> {\n const res = await fetcher(endpoints.token, { credentials: \"include\" });\n if (!res.ok) throw new Error(`token request failed: ${res.status}`);\n const { accessToken } = (await res.json()) as { accessToken: string };\n return accessToken;\n}\n\nexport async function fetchProjects(\n endpoints: HeaderEndpoints,\n clientId: string,\n accessToken: string,\n fetcher: Fetcher,\n): Promise<{ projectsTree: ProjectNode[]; organizationName: string | undefined }> {\n const res = await fetcher(endpoints.projects(clientId), {\n credentials: \"include\",\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) throw new Error(`projects request failed: ${res.status}`);\n const data = (await res.json()) as ApiEnvelope<{\n projectsTree?: ProjectNode[];\n organization?: { name?: string };\n }>;\n return {\n projectsTree: data.response?.data?.projectsTree ?? [],\n organizationName: data.response?.data?.organization?.name,\n };\n}\n\nexport async function fetchCredits(\n endpoints: HeaderEndpoints,\n accessToken: string,\n fetcher: Fetcher,\n): Promise<number | undefined> {\n const res = await fetcher(endpoints.credits, {\n credentials: \"include\",\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) throw new Error(`credits request failed: ${res.status}`);\n const data = (await res.json()) as { remainingCredits?: number };\n return data.remainingCredits;\n}\n\nexport async function callLogout(endpoints: HeaderEndpoints, fetcher: Fetcher): Promise<void> {\n try {\n await fetcher(endpoints.logout, { method: \"POST\", credentials: \"include\" });\n } finally {\n if (typeof window !== \"undefined\") window.location.href = \"/\";\n }\n}\n","// Persists the user's project selection across reloads (sessionStorage), tagged\n// with the client it belongs to so a stale selection from a different org is\n// discarded on re-login.\n\nconst PROJECT_KEY = \"ds-header-project\";\n\nexport interface StoredProject {\n id: string;\n name: string;\n clientId?: string;\n}\n\nexport function getStoredProject(): StoredProject | null {\n try {\n const raw = sessionStorage.getItem(PROJECT_KEY);\n return raw ? (JSON.parse(raw) as StoredProject) : null;\n } catch {\n return null;\n }\n}\n\nexport function setStoredProject(project: StoredProject): void {\n try {\n sessionStorage.setItem(PROJECT_KEY, JSON.stringify(project));\n } catch {\n // sessionStorage unavailable — degrade silently\n }\n}\n\nexport function clearStoredProject(): void {\n try {\n sessionStorage.removeItem(PROJECT_KEY);\n } catch {\n // noop\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport { clearStoredProject, getStoredProject, setStoredProject } from \"./storage\";\n\n// Module-level store for the current project/org selection. Module scope (not\n// React context) so a host app can read and change the selection from anywhere —\n// including components rendered outside the <Header> subtree. The Header is one\n// writer/reader; `useHeaderProject()` gives apps the same access.\n\nexport interface HeaderSelection {\n selectedProjectId: string;\n selectedProjectName: string;\n organizationName: string | undefined;\n /** Remaining credits for the logged-in user (read-only here; set by the Header). */\n credits: number | undefined;\n /** Client/org the current selection + org belong to (for cross-org invalidation). */\n clientId: string | undefined;\n}\n\nfunction initialState(): HeaderSelection {\n const stored = typeof window !== \"undefined\" ? getStoredProject() : null;\n return {\n selectedProjectId: stored?.id ?? \"\",\n selectedProjectName: stored?.name ?? \"\",\n organizationName: undefined,\n credits: undefined,\n clientId: stored?.clientId,\n };\n}\n\nlet state: HeaderSelection = initialState();\n// Stable reference for SSR / first server render.\nconst SERVER_SNAPSHOT: HeaderSelection = Object.freeze({\n selectedProjectId: \"\",\n selectedProjectName: \"\",\n organizationName: undefined,\n credits: undefined,\n clientId: undefined,\n});\n\nconst listeners = new Set<() => void>();\nfunction emit() {\n for (const listener of listeners) listener();\n}\n\nexport const headerStore = {\n subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n getSnapshot(): HeaderSelection {\n return state;\n },\n getServerSnapshot(): HeaderSelection {\n return SERVER_SNAPSHOT;\n },\n\n /** Set the selected project. `name` is best-effort; the Header backfills it from the tree. */\n setProject(id: string, name = \"\", clientId?: string) {\n if (id) setStoredProject({ id, name, clientId: clientId ?? state.clientId });\n else clearStoredProject();\n state = {\n ...state,\n selectedProjectId: id,\n selectedProjectName: name,\n clientId: clientId ?? state.clientId,\n };\n emit();\n },\n\n clearProject() {\n clearStoredProject();\n state = { ...state, selectedProjectId: \"\", selectedProjectName: \"\" };\n emit();\n },\n\n setOrganization(name: string | undefined, clientId?: string) {\n state = { ...state, organizationName: name, clientId: clientId ?? state.clientId };\n emit();\n },\n\n setCredits(credits: number | undefined) {\n if (state.credits === credits) return;\n state = { ...state, credits };\n emit();\n },\n\n /** When the active client changes, drop a selection/org from the previous one. */\n reconcileClient(clientId: string) {\n if (state.clientId === clientId) return;\n if (state.clientId && state.clientId !== clientId) {\n clearStoredProject();\n state = {\n selectedProjectId: \"\",\n selectedProjectName: \"\",\n organizationName: undefined,\n credits: state.credits, // user-level — survives org switches\n clientId,\n };\n } else {\n state = { ...state, clientId };\n }\n emit();\n },\n};\n\n/** Internal: subscribe a component to the selection store. */\nexport function useHeaderStore(): HeaderSelection {\n return React.useSyncExternalStore(\n headerStore.subscribe,\n headerStore.getSnapshot,\n headerStore.getServerSnapshot,\n );\n}\n\nexport interface UseHeaderProjectResult {\n selectedProjectId: string;\n selectedProjectName: string | undefined;\n organizationName: string | undefined;\n /** Set the active project from outside the Header (by id). */\n setProject: (id: string) => void;\n /** Clear the active project (e.g. to re-prompt selection). */\n clearProject: () => void;\n}\n\n/**\n * Read the current project/org and change the project from anywhere in the app —\n * no need to be inside the <Header>. Reflects user selections made in the header\n * and updates the header when changed here.\n */\nexport function useHeaderProject(): UseHeaderProjectResult {\n const snapshot = useHeaderStore();\n return {\n selectedProjectId: snapshot.selectedProjectId,\n selectedProjectName: snapshot.selectedProjectName || undefined,\n organizationName: snapshot.organizationName,\n setProject: React.useCallback((id: string) => headerStore.setProject(id), []),\n clearProject: React.useCallback(() => headerStore.clearProject(), []),\n };\n}\n\n/** Read the logged-in user's remaining credits (read-only; the Header owns it). */\nexport function useHeaderCredits(): number | undefined {\n return useHeaderStore().credits;\n}\n","// Client-side session detection. The auth system writes a JS-readable cookie\n// `{environmentId}-sessionId` (a JWT) on the shared parent domain. We decode it\n// and check its expiry to know whether the user is logged in — no network call.\n\ninterface JwtPayload {\n exp?: number;\n [key: string]: unknown;\n}\n\n/** Read the raw session JWT from the `{environmentId}-sessionId` cookie. */\nexport function getSessionToken(environmentId: string): string | null {\n if (typeof document === \"undefined\") return null;\n const name = `${environmentId}-sessionId`;\n for (const part of document.cookie.split(\";\")) {\n const [key, ...rest] = part.trim().split(\"=\");\n if (key === name) return decodeURIComponent(rest.join(\"=\"));\n }\n return null;\n}\n\n/** Decode a JWT payload without verifying the signature (display/expiry only). */\nexport function decodeJwt(token: string): JwtPayload | null {\n try {\n const payload = token.split(\".\")[1];\n if (!payload) return null;\n const base64 = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const json = atob(base64);\n return JSON.parse(json) as JwtPayload;\n } catch {\n return null;\n }\n}\n\n/** User/session claims carried in the session JWT (the subset the header uses). */\nexport interface SessionClaims {\n email?: string;\n name?: string;\n userId?: string;\n /** Active organisation id (`client_id` claim). */\n clientId?: string;\n exp?: number;\n}\n\n/**\n * Decode the user claims from the session cookie's JWT — no network call.\n * Returns null when the cookie is absent or undecodable.\n */\nexport function getSessionClaims(environmentId: string): SessionClaims | null {\n const token = getSessionToken(environmentId);\n if (!token) return null;\n const payload = decodeJwt(token);\n if (!payload) return null;\n const str = (v: unknown) => (typeof v === \"string\" ? v : undefined);\n return {\n email: str(payload.email),\n name: str(payload.name),\n userId: str(payload.user_id),\n clientId: str(payload.client_id),\n exp: typeof payload.exp === \"number\" ? payload.exp : undefined,\n };\n}\n\n/** True when a valid, unexpired session cookie exists for the environment. */\nexport function isSessionValid(environmentId: string): boolean {\n const token = getSessionToken(environmentId);\n if (!token) return false;\n const payload = decodeJwt(token);\n if (!payload) return false;\n // No exp claim → treat as a non-expiring session.\n if (typeof payload.exp !== \"number\") return true;\n return payload.exp * 1000 > Date.now();\n}\n","\"use client\";\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { buildEndpoints, callLogout, fetchCredits, fetchProjects, fetchToken } from \"./api\";\nimport { headerStore, useHeaderStore } from \"./header-store\";\nimport { findProjectName, nodeExistsInTree } from \"./project-tree\";\nimport { getSessionClaims, isSessionValid } from \"./session\";\nimport type { HeaderProps, ProjectNode } from \"./types\";\n\n/** Window event other modules can dispatch to show/hide the project dropdown. */\nexport const TOGGLE_PROJECT_DROPDOWN_EVENT = \"toggle-project-dropdown\";\nexport interface ToggleProjectDropdownDetail {\n show: boolean;\n}\n\nconst DEFAULT_STALE_TIME = 5 * 60_000;\nconst DEFAULT_GC_TIME = 10 * 60_000;\n\nexport interface GlobalHeaderState {\n /** Whether a valid session cookie exists — when false, hide all user content. */\n isAuthenticated: boolean;\n userEmail: string | undefined;\n organizationName: string | undefined;\n projectsTree: ProjectNode[];\n selectedProjectId: string;\n selectedProjectName: string | undefined;\n isProjectsLoading: boolean;\n isOrgLoading: boolean;\n /** Remaining credits for the user (undefined until loaded / when disabled). */\n credits: number | undefined;\n /** Where the credits pill links to (studio-auth `/user/activity`). */\n creditsHref: string;\n projectDropdownVisible: boolean;\n /** True when the dropdown is on, the user is in, but no project is chosen yet. */\n showOverlay: boolean;\n selectProject: (id: string) => void;\n logout: () => void;\n}\n\ntype UseGlobalHeaderArgs = Pick<\n HeaderProps,\n | \"apiBaseUrl\"\n | \"environmentId\"\n | \"clientId\"\n | \"_dangerouslyInjectAccessToken\"\n | \"showProjectDropdown\"\n | \"showProfile\"\n | \"showOrg\"\n | \"showCredits\"\n | \"endpoints\"\n | \"fetcher\"\n | \"staleTime\"\n | \"gcTime\"\n | \"onProjectChange\"\n>;\n\nexport function useGlobalHeader({\n apiBaseUrl,\n environmentId,\n clientId,\n _dangerouslyInjectAccessToken: injectedToken,\n showProjectDropdown = false,\n showProfile = false,\n showOrg = false,\n showCredits = false,\n endpoints: endpointOverrides,\n fetcher: fetcherProp,\n staleTime = DEFAULT_STALE_TIME,\n gcTime = DEFAULT_GC_TIME,\n onProjectChange,\n}: UseGlobalHeaderArgs): GlobalHeaderState {\n const fetcher = React.useMemo(\n () => fetcherProp ?? ((input: RequestInfo | URL, init?: RequestInit) => fetch(input, init)),\n [fetcherProp],\n );\n const endpoints = React.useMemo(\n () => buildEndpoints(apiBaseUrl, endpointOverrides),\n [apiBaseUrl, endpointOverrides],\n );\n\n // Read the session cookie each render (cheap); gates every network call.\n // User identity (email, client id) comes from the JWT claims — no /me call.\n const isAuthenticated = isSessionValid(environmentId);\n const claims = getSessionClaims(environmentId);\n\n // token → projects, enabled only when the previous resolved.\n const tokenQuery = useQuery({\n queryKey: [\"ds-header\", \"token\", endpoints.token],\n queryFn: () => fetchToken(endpoints, fetcher),\n enabled: isAuthenticated && !injectedToken,\n staleTime,\n gcTime,\n });\n const token = injectedToken ?? tokenQuery.data;\n\n const effectiveClientId = clientId ?? claims?.clientId;\n const wantsProjects = showProjectDropdown || showOrg;\n const projectsQuery = useQuery({\n queryKey: [\"ds-header\", \"projects\", effectiveClientId, token],\n queryFn: () => fetchProjects(endpoints, effectiveClientId as string, token as string, fetcher),\n enabled: isAuthenticated && !!token && !!effectiveClientId && wantsProjects,\n staleTime,\n gcTime,\n });\n\n // Credits — only needs the token. Always refetched on mount (staleTime 0) so a\n // page refresh shows the current balance.\n const creditsQuery = useQuery({\n queryKey: [\"ds-header\", \"credits\", endpoints.credits, token],\n queryFn: () => fetchCredits(endpoints, token as string, fetcher),\n enabled: isAuthenticated && !!token && showCredits,\n staleTime: 0,\n gcTime,\n });\n\n const projectsTree = projectsQuery.data?.projectsTree ?? [];\n const userEmail = showProfile ? claims?.email : undefined;\n const creditsHref = React.useMemo(() => {\n try {\n return new URL(\"/user/activity\", apiBaseUrl).toString();\n } catch {\n return \"/user/activity\";\n }\n }, [apiBaseUrl]);\n\n // Selection + org live in a module store so host apps can read/change them\n // from outside the header (see useHeaderProject).\n const selection = useHeaderStore();\n\n // Once the client id resolves, drop a selection/org from a different client.\n React.useEffect(() => {\n if (effectiveClientId) headerStore.reconcileClient(effectiveClientId);\n }, [effectiveClientId]);\n\n // Once projects load: publish the org name and validate/backfill the selection\n // (drop one that no longer exists; fill in its name from the tree).\n React.useEffect(() => {\n if (!projectsQuery.data) return;\n const tree = projectsQuery.data.projectsTree;\n const id = headerStore.getSnapshot().selectedProjectId;\n if (id && !nodeExistsInTree(tree, id)) {\n headerStore.clearProject();\n } else if (id) {\n const name = findProjectName(tree, id);\n if (name && name !== headerStore.getSnapshot().selectedProjectName) {\n headerStore.setProject(id, name, effectiveClientId);\n }\n }\n headerStore.setOrganization(projectsQuery.data.organizationName, effectiveClientId);\n }, [projectsQuery.data, effectiveClientId]);\n\n // Publish credits to the store so host apps can read them via useHeaderCredits.\n React.useEffect(() => {\n if (creditsQuery.data !== undefined) headerStore.setCredits(creditsQuery.data);\n }, [creditsQuery.data]);\n\n const selectProject = (id: string) => {\n const name = findProjectName(projectsTree, id) ?? \"\";\n headerStore.setProject(id, name, effectiveClientId);\n onProjectChange?.(id);\n };\n\n const logout = () => {\n headerStore.clearProject();\n void callLogout(endpoints, fetcher);\n };\n\n // Visibility: declarative from the prop, overridable at runtime via the event.\n const [projectDropdownVisible, setProjectDropdownVisible] = React.useState(showProjectDropdown);\n React.useEffect(() => setProjectDropdownVisible(showProjectDropdown), [showProjectDropdown]);\n React.useEffect(() => {\n const handler = (e: Event) => {\n setProjectDropdownVisible((e as CustomEvent<ToggleProjectDropdownDetail>).detail.show);\n };\n window.addEventListener(TOGGLE_PROJECT_DROPDOWN_EVENT, handler);\n return () => window.removeEventListener(TOGGLE_PROJECT_DROPDOWN_EVENT, handler);\n }, []);\n\n const isProjectsLoading = projectsQuery.isFetching && !projectsQuery.data;\n\n return {\n isAuthenticated,\n userEmail,\n organizationName: selection.organizationName,\n projectsTree,\n selectedProjectId: selection.selectedProjectId,\n selectedProjectName: selection.selectedProjectName || undefined,\n isProjectsLoading,\n isOrgLoading: showOrg && isProjectsLoading,\n credits: selection.credits,\n creditsHref,\n projectDropdownVisible,\n showOverlay: isAuthenticated && projectDropdownVisible && !selection.selectedProjectId,\n selectProject,\n logout,\n };\n}\n","\"use client\";\n\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { FolderOpen, LogOut, MessageSquareShare, Moon, Sun, X } from \"lucide-react\";\nimport type * as React from \"react\";\n\nimport { Avatar, AvatarFallback } from \"@/primitives/Avatar\";\nimport { Button } from \"@/primitives/Button\";\nimport { Credits } from \"@/primitives/Credits\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/primitives/DropdownMenu\";\nimport { Logo } from \"@/primitives/Logo\";\nimport { Skeleton } from \"@/primitives/Skeleton\";\nimport { type ThemeProduct, useTheme } from \"@/primitives/ThemeProvider\";\nimport { cn } from \"@/utils\";\nimport { ProjectPicker } from \"./ProjectPicker\";\nimport { DEFAULT_LABELS } from \"./labels\";\nimport type { HeaderProps, ProjectNode } from \"./types\";\nimport { useGlobalHeader } from \"./use-global-header\";\n\n// Fallback logo destinations when the host doesn't pass `logoHref`.\nconst LOGO_HREF: Record<ThemeProduct, string> = {\n psai: \"https://sapientaiproducts.com/\",\n slingshot: \"https://sapientaiproducts.com/slingshot\",\n sustain: \"https://www.publicissapient.com/\",\n bodhi: \"https://sapientaiproducts.com/bodhi\",\n};\n\nconst PILL =\n \"flex min-h-8 items-center gap-1 rounded-12 border border-primary-border bg-input-bg px-3 py-1 text-xs leading-4\";\n\n// ── Presentational parts (props-driven; Storybook/VR-able) ──────────────────\n\n/** The header bar shell — a sticky glass strip. */\nfunction HeaderBar({ className, ...props }: React.ComponentProps<\"header\">) {\n return (\n <header\n data-slot=\"header\"\n className={cn(\n \"sticky top-1 z-10 mt-1 flex h-15 items-center gap-5 rounded-16 px-3 py-2\",\n \"bg-glass-60-bg backdrop-blur-glass\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Product logo linking to its home, with a Suspense-safe fallback. */\nfunction HeaderLogo({ brand, href }: { brand: ThemeProduct; href?: string }) {\n return (\n <a href={href ?? LOGO_HREF[brand]} aria-label=\"Go to product home\" data-slot=\"header-logo\">\n <Logo brand={brand} className=\"h-8 w-auto\" />\n </a>\n );\n}\n\n/** Read-only organisation pill. */\nfunction OrgPill({ name, loading }: { name?: string; loading?: boolean }) {\n if (loading && !name) {\n return (\n <div className={cn(PILL, \"ml-3\")} data-slot=\"header-org\">\n <span className=\"shrink-0 text-muted-txt\">{DEFAULT_LABELS.orgLabel}:</span>\n <Skeleton className=\"h-4 w-24\" />\n </div>\n );\n }\n if (!name) return null;\n return (\n <div className={cn(PILL, \"ml-3 select-none\")} title={name} data-slot=\"header-org\">\n <span className=\"shrink-0 text-muted-txt\">{DEFAULT_LABELS.orgLabel}:</span>\n <span className=\"max-w-36 truncate text-primary-txt\">{name}</span>\n </div>\n );\n}\n\n/** Light/dark toggle — defers to ThemeProvider. */\nfunction ThemeToggle() {\n const { theme, toggleTheme } = useTheme();\n return (\n <Button variant=\"ghost\" size=\"icon\" onClick={toggleTheme} aria-label=\"Toggle theme\">\n {theme === \"light\" ? <Moon className=\"size-4\" /> : <Sun className=\"size-4\" />}\n </Button>\n );\n}\n\n/** Profile menu — avatar trigger with email + logout. */\nfunction ProfileMenu({ email, onLogout }: { email?: string; onLogout: () => void }) {\n const initial = email?.trim().charAt(0).toUpperCase() || \"?\";\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={DEFAULT_LABELS.profileMenuLabel}>\n <Avatar className=\"size-7\">\n <AvatarFallback>{initial}</AvatarFallback>\n </Avatar>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" sideOffset={8} className=\"min-w-44\">\n {email && (\n <>\n <DropdownMenuLabel className=\"truncate font-normal text-muted-txt\">\n {email}\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n </>\n )}\n <DropdownMenuItem onSelect={onLogout} variant=\"destructive\">\n <LogOut />\n {DEFAULT_LABELS.logoutLabel}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nexport { HeaderBar, HeaderLogo, OrgPill, ThemeToggle, ProfileMenu };\n\n// ── Wired Header ─────────────────────────────────────────────────────────────\n\n// Internal singleton client so the header is drop-in (no app setup) while still\n// sharing its cache across mounts/navigation.\nlet sharedClient: QueryClient | undefined;\nfunction getQueryClient(): QueryClient {\n sharedClient ??= new QueryClient({\n defaultOptions: { queries: { retry: 1, refetchOnWindowFocus: false } },\n });\n return sharedClient;\n}\n\n/** Drop-in global header. Must be rendered inside a <ThemeProvider>. */\nexport function Header(props: HeaderProps) {\n return (\n <QueryClientProvider client={getQueryClient()}>\n <HeaderInner {...props} />\n </QueryClientProvider>\n );\n}\n\nfunction HeaderInner({\n logoType,\n logoHref,\n showOrg = false,\n showProjectDropdown = false,\n showProfile = false,\n showThemeSwitcher = false,\n showCredits = false,\n pageTitle,\n feedbackUrl,\n logoAddon,\n className,\n onCloseProjectOverlay,\n ...rest\n}: HeaderProps) {\n const { product } = useTheme();\n const brand = logoType ?? product;\n const state = useGlobalHeader({\n showOrg,\n showProjectDropdown,\n showProfile,\n showCredits,\n ...rest,\n });\n\n // Logged-out users see no user content (project/org/profile/credits).\n const showProjectUi = showProjectDropdown && state.isAuthenticated;\n const showOrgUi = showOrg && state.isAuthenticated;\n const showProfileUi = showProfile && state.isAuthenticated;\n const showCreditsUi = showCredits && state.isAuthenticated && state.credits !== undefined;\n const showActions = showThemeSwitcher || showProfileUi || showCreditsUi || !!feedbackUrl;\n\n return (\n <>\n <HeaderBar className={className}>\n <HeaderLogo brand={brand} href={logoHref} />\n {/* Page title — 20px (gap-5) from the logo. */}\n {pageTitle && (\n <span data-slot=\"header-title\" className=\"ml-5 font-semibold text-primary-txt text-sm\">\n {pageTitle}\n </span>\n )}\n {logoAddon}\n\n {showOrgUi && <OrgPill name={state.organizationName} loading={state.isOrgLoading} />}\n\n {showProjectUi && state.projectDropdownVisible && (\n <div className=\"ml-2\">\n <ProjectPicker\n projectsTree={state.projectsTree}\n selectedProjectId={state.selectedProjectId}\n selectedProjectName={state.selectedProjectName}\n onSelect={state.selectProject}\n projectLabel={DEFAULT_LABELS.projectLabel}\n projectPlaceholder={DEFAULT_LABELS.projectPlaceholder}\n searchPlaceholder={DEFAULT_LABELS.searchPlaceholder}\n emptyStateText={DEFAULT_LABELS.emptyStateText}\n isLoading={state.isProjectsLoading}\n />\n </div>\n )}\n\n {showActions && (\n <div className=\"ml-auto flex items-center gap-2\">\n {feedbackUrl && (\n <Button variant=\"ghost\" size=\"icon\" asChild>\n <a\n href={feedbackUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label=\"Share feedback\"\n >\n <MessageSquareShare className=\"size-4\" />\n </a>\n </Button>\n )}\n {showThemeSwitcher && <ThemeToggle />}\n {showCreditsUi && state.credits !== undefined && (\n <a\n href={state.creditsHref}\n aria-label=\"View credit activity\"\n className=\"rounded-full outline-none focus-visible:ring-3 focus-visible:ring-focus\"\n >\n <Credits value={state.credits} />\n </a>\n )}\n {showProfileUi && <ProfileMenu email={state.userEmail} onLogout={state.logout} />}\n </div>\n )}\n </HeaderBar>\n\n {showProjectUi && state.showOverlay && (\n <ProjectOverlay\n organizationName={state.organizationName}\n projectsTree={state.projectsTree}\n selectedProjectId={state.selectedProjectId}\n onSelect={state.selectProject}\n isLoading={state.isProjectsLoading}\n onClose={onCloseProjectOverlay}\n />\n )}\n </>\n );\n}\n\n// First-run blocking overlay prompting a project selection.\nfunction ProjectOverlay({\n organizationName,\n projectsTree,\n selectedProjectId,\n onSelect,\n isLoading,\n onClose,\n}: {\n organizationName?: string;\n projectsTree: ProjectNode[];\n selectedProjectId: string;\n onSelect: (id: string) => void;\n isLoading: boolean;\n onClose?: () => void;\n}) {\n return (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={DEFAULT_LABELS.overlayTitle}\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-primary-bg/80 backdrop-blur-sm\"\n >\n <div className=\"relative mx-1 flex w-full max-w-md flex-col items-center gap-6 rounded-16 border bg-glass-none-bg p-2 text-primary-txt shadow-xl\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={onClose ?? (() => window.history.back())}\n className=\"absolute top-4 right-4\"\n aria-label=\"Close\"\n >\n <X className=\"size-5\" />\n </Button>\n <div className=\"flex flex-col items-center gap-3 text-center\">\n <div className=\"rounded-full bg-primary/10 p-4\">\n <FolderOpen className=\"size-8 text-primary\" />\n </div>\n <h2 className=\"font-semibold text-primary-txt text-xl\">{DEFAULT_LABELS.overlayTitle}</h2>\n <p className=\"text-muted-txt text-sm\">{DEFAULT_LABELS.overlayDescription}</p>\n </div>\n <div className=\"flex w-full justify-center gap-2\">\n {organizationName && (\n <div className={cn(PILL, \"select-none\")} title={organizationName}>\n <span className=\"shrink-0 text-muted-txt\">{DEFAULT_LABELS.orgLabel}:</span>\n <span className=\"max-w-52 truncate font-medium\">{organizationName}</span>\n </div>\n )}\n <ProjectPicker\n projectsTree={projectsTree}\n selectedProjectId={selectedProjectId}\n onSelect={onSelect}\n projectLabel={DEFAULT_LABELS.projectLabel}\n projectPlaceholder={DEFAULT_LABELS.projectPlaceholder}\n searchPlaceholder={DEFAULT_LABELS.searchPlaceholder}\n emptyStateText={DEFAULT_LABELS.emptyStateText}\n isLoading={isLoading}\n />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
package/dist/Header.js CHANGED
@@ -184,7 +184,6 @@ const DEFAULT_LABELS = {
184
184
  //#region src/patterns/Header/api.ts
185
185
  const PATHS = {
186
186
  token: "/api/v1/studio-auth/sessions/token",
187
- me: "/api/v1/onboarding/users/me",
188
187
  logout: "/api/v1/studio-auth/sessions/logout",
189
188
  projects: (clientId) => `/api/v1/onboarding/organization/${clientId}/projects`,
190
189
  credits: "/save/api/v1/users"
@@ -196,7 +195,6 @@ function join(baseUrl, path) {
196
195
  function buildEndpoints(apiBaseUrl, overrides) {
197
196
  return {
198
197
  token: overrides?.token ?? join(apiBaseUrl, PATHS.token),
199
- me: overrides?.me ?? join(apiBaseUrl, PATHS.me),
200
198
  logout: overrides?.logout ?? join(apiBaseUrl, PATHS.logout),
201
199
  projects: overrides?.projects ?? ((clientId) => join(apiBaseUrl, PATHS.projects(clientId))),
202
200
  credits: overrides?.credits ?? join(apiBaseUrl, PATHS.credits)
@@ -208,16 +206,6 @@ async function fetchToken(endpoints, fetcher) {
208
206
  const { accessToken } = await res.json();
209
207
  return accessToken;
210
208
  }
211
- async function fetchMe(endpoints, accessToken, fetcher) {
212
- const res = await fetcher(endpoints.me, {
213
- credentials: "include",
214
- headers: { Authorization: `Bearer ${accessToken}` }
215
- });
216
- if (!res.ok) throw new Error(`me request failed: ${res.status}`);
217
- const data = (await res.json()).response?.data;
218
- if (!data) throw new Error("me response missing data");
219
- return data;
220
- }
221
209
  async function fetchProjects(endpoints, clientId, accessToken, fetcher) {
222
210
  const res = await fetcher(endpoints.projects(clientId), {
223
211
  credentials: "include",
@@ -411,6 +399,24 @@ function decodeJwt(token) {
411
399
  return null;
412
400
  }
413
401
  }
402
+ /**
403
+ * Decode the user claims from the session cookie's JWT — no network call.
404
+ * Returns null when the cookie is absent or undecodable.
405
+ */
406
+ function getSessionClaims(environmentId) {
407
+ const token = getSessionToken(environmentId);
408
+ if (!token) return null;
409
+ const payload = decodeJwt(token);
410
+ if (!payload) return null;
411
+ const str = (v) => typeof v === "string" ? v : void 0;
412
+ return {
413
+ email: str(payload.email),
414
+ name: str(payload.name),
415
+ userId: str(payload.user_id),
416
+ clientId: str(payload.client_id),
417
+ exp: typeof payload.exp === "number" ? payload.exp : void 0
418
+ };
419
+ }
414
420
  /** True when a valid, unexpired session cookie exists for the environment. */
415
421
  function isSessionValid(environmentId) {
416
422
  const token = getSessionToken(environmentId);
@@ -430,6 +436,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
430
436
  const fetcher = React.useMemo(() => fetcherProp ?? ((input, init) => fetch(input, init)), [fetcherProp]);
431
437
  const endpoints = React.useMemo(() => buildEndpoints(apiBaseUrl, endpointOverrides), [apiBaseUrl, endpointOverrides]);
432
438
  const isAuthenticated = isSessionValid(environmentId);
439
+ const claims = getSessionClaims(environmentId);
433
440
  const tokenQuery = useQuery({
434
441
  queryKey: [
435
442
  "ds-header",
@@ -442,20 +449,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
442
449
  gcTime
443
450
  });
444
451
  const token = injectedToken ?? tokenQuery.data;
445
- const wantsUser = showProfile || showProjectDropdown || showOrg;
446
- const meQuery = useQuery({
447
- queryKey: [
448
- "ds-header",
449
- "me",
450
- endpoints.me,
451
- token
452
- ],
453
- queryFn: () => fetchMe(endpoints, token, fetcher),
454
- enabled: isAuthenticated && !!token && wantsUser,
455
- staleTime,
456
- gcTime
457
- });
458
- const effectiveClientId = clientId ?? meQuery.data?.clientId;
452
+ const effectiveClientId = clientId ?? claims?.clientId;
459
453
  const projectsQuery = useQuery({
460
454
  queryKey: [
461
455
  "ds-header",
@@ -481,7 +475,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
481
475
  gcTime
482
476
  });
483
477
  const projectsTree = projectsQuery.data?.projectsTree ?? [];
484
- const userEmail = showProfile ? meQuery.data?.email : void 0;
478
+ const userEmail = showProfile ? claims?.email : void 0;
485
479
  const creditsHref = React.useMemo(() => {
486
480
  try {
487
481
  return new URL("/user/activity", apiBaseUrl).toString();
@@ -796,6 +790,6 @@ function ProjectOverlay({ organizationName, projectsTree, selectedProjectId, onS
796
790
  });
797
791
  }
798
792
  //#endregion
799
- export { ProfileMenu as a, useGlobalHeader as c, useHeaderCredits as d, useHeaderProject as f, ProjectPicker as h, OrgPill as i, getSessionToken as l, DEFAULT_LABELS as m, HeaderBar as n, ThemeToggle as o, buildEndpoints as p, HeaderLogo as r, TOGGLE_PROJECT_DROPDOWN_EVENT as s, Header as t, isSessionValid as u };
793
+ export { ProfileMenu as a, useGlobalHeader as c, isSessionValid as d, useHeaderCredits as f, ProjectPicker as g, DEFAULT_LABELS as h, OrgPill as i, getSessionClaims as l, buildEndpoints as m, HeaderBar as n, ThemeToggle as o, useHeaderProject as p, HeaderLogo as r, TOGGLE_PROJECT_DROPDOWN_EVENT as s, Header as t, getSessionToken as u };
800
794
 
801
795
  //# sourceMappingURL=Header.js.map