@psnext/design-system 2.1.0 → 2.3.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 (228) hide show
  1. package/DESIGN.md +3 -3
  2. package/dist/Header.cjs +90 -115
  3. package/dist/Header.cjs.map +1 -1
  4. package/dist/Header.js +86 -117
  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/index10.d.cts +6 -6
  9. package/dist/index10.d.cts.map +1 -1
  10. package/dist/index10.d.ts +2 -2
  11. package/dist/index11.d.cts +3 -3
  12. package/dist/index11.d.cts.map +1 -1
  13. package/dist/index11.d.ts +3 -3
  14. package/dist/index12.d.cts +12 -12
  15. package/dist/index12.d.ts +11 -11
  16. package/dist/index13.d.cts +6 -6
  17. package/dist/index13.d.ts +6 -6
  18. package/dist/index14.d.cts +5 -5
  19. package/dist/index14.d.ts +5 -5
  20. package/dist/index15.d.cts +3 -3
  21. package/dist/index15.d.ts +3 -3
  22. package/dist/index16.d.cts +2 -2
  23. package/dist/index16.d.ts +2 -2
  24. package/dist/index17.d.cts +4 -4
  25. package/dist/index17.d.ts +4 -4
  26. package/dist/index18.d.cts +11 -11
  27. package/dist/index18.d.cts.map +1 -1
  28. package/dist/index18.d.ts +11 -11
  29. package/dist/index19.d.cts +10 -10
  30. package/dist/index19.d.ts +10 -10
  31. package/dist/index2.d.cts +8 -8
  32. package/dist/index2.d.ts +8 -8
  33. package/dist/index20.d.cts +16 -16
  34. package/dist/index20.d.ts +16 -16
  35. package/dist/index21.d.cts +4 -4
  36. package/dist/index21.d.ts +4 -4
  37. package/dist/index22.d.cts +11 -11
  38. package/dist/index22.d.ts +11 -11
  39. package/dist/index23.d.cts +16 -16
  40. package/dist/index23.d.ts +16 -16
  41. package/dist/index24.d.cts +10 -10
  42. package/dist/index24.d.cts.map +1 -1
  43. package/dist/index24.d.ts +10 -10
  44. package/dist/index25.d.cts +2 -2
  45. package/dist/index25.d.ts +2 -2
  46. package/dist/index26.d.cts +13 -13
  47. package/dist/index26.d.cts.map +1 -1
  48. package/dist/index26.d.ts +13 -13
  49. package/dist/index27.d.cts +7 -7
  50. package/dist/index27.d.cts.map +1 -1
  51. package/dist/index27.d.ts +7 -7
  52. package/dist/index28.d.cts +5 -5
  53. package/dist/index28.d.cts.map +1 -1
  54. package/dist/index28.d.ts +4 -4
  55. package/dist/index29.d.cts +4 -4
  56. package/dist/index29.d.cts.map +1 -1
  57. package/dist/index29.d.ts +4 -4
  58. package/dist/index3.d.cts +13 -13
  59. package/dist/index3.d.ts +13 -13
  60. package/dist/index30.d.cts +6 -6
  61. package/dist/index30.d.cts.map +1 -1
  62. package/dist/index30.d.ts +5 -5
  63. package/dist/index31.d.cts +4 -4
  64. package/dist/index31.d.cts.map +1 -1
  65. package/dist/index31.d.ts +5 -5
  66. package/dist/index32.d.cts +5 -5
  67. package/dist/index32.d.cts.map +1 -1
  68. package/dist/index32.d.ts +5 -5
  69. package/dist/index33.d.cts +10 -10
  70. package/dist/index33.d.cts.map +1 -1
  71. package/dist/index33.d.ts +10 -10
  72. package/dist/index34.d.cts +5 -5
  73. package/dist/index34.d.cts.map +1 -1
  74. package/dist/index34.d.ts +5 -5
  75. package/dist/index35.d.cts +16 -16
  76. package/dist/index35.d.cts.map +1 -1
  77. package/dist/index35.d.ts +16 -16
  78. package/dist/index35.d.ts.map +1 -1
  79. package/dist/index36.d.cts +3 -3
  80. package/dist/index36.d.cts.map +1 -1
  81. package/dist/index36.d.ts +3 -3
  82. package/dist/index37.d.cts +4 -4
  83. package/dist/index37.d.cts.map +1 -1
  84. package/dist/index37.d.ts +4 -4
  85. package/dist/index38.d.cts +2 -2
  86. package/dist/index38.d.cts.map +1 -1
  87. package/dist/index38.d.ts +2 -2
  88. package/dist/index39.d.cts +17 -17
  89. package/dist/index39.d.ts +17 -17
  90. package/dist/index4.d.cts +2 -2
  91. package/dist/index4.d.cts.map +1 -1
  92. package/dist/index4.d.ts +2 -2
  93. package/dist/index40.d.cts +5 -5
  94. package/dist/index40.d.cts.map +1 -1
  95. package/dist/index40.d.ts +5 -5
  96. package/dist/index41.d.cts +11 -11
  97. package/dist/index41.d.cts.map +1 -1
  98. package/dist/index41.d.ts +11 -11
  99. package/dist/index41.d.ts.map +1 -1
  100. package/dist/index42.d.cts +8 -8
  101. package/dist/index42.d.cts.map +1 -1
  102. package/dist/index42.d.ts +8 -8
  103. package/dist/index42.d.ts.map +1 -1
  104. package/dist/index43.d.cts +9 -9
  105. package/dist/index43.d.cts.map +1 -1
  106. package/dist/index43.d.ts +9 -9
  107. package/dist/index43.d.ts.map +1 -1
  108. package/dist/index45.d.cts +8 -8
  109. package/dist/index45.d.cts.map +1 -1
  110. package/dist/index45.d.ts +8 -8
  111. package/dist/index45.d.ts.map +1 -1
  112. package/dist/index46.d.cts +2 -2
  113. package/dist/index46.d.cts.map +1 -1
  114. package/dist/index46.d.ts +2 -2
  115. package/dist/index47.d.cts +4 -4
  116. package/dist/index47.d.ts +4 -4
  117. package/dist/index48.d.cts +4 -4
  118. package/dist/index48.d.cts.map +1 -1
  119. package/dist/index48.d.ts +4 -4
  120. package/dist/index49.d.cts +3 -3
  121. package/dist/index49.d.cts.map +1 -1
  122. package/dist/index49.d.ts +3 -3
  123. package/dist/index5.d.cts +7 -7
  124. package/dist/index5.d.ts +7 -7
  125. package/dist/index50.d.cts +11 -11
  126. package/dist/index50.d.cts.map +1 -1
  127. package/dist/index50.d.ts +11 -11
  128. package/dist/index51.d.cts +9 -9
  129. package/dist/index51.d.cts.map +1 -1
  130. package/dist/index51.d.ts +9 -9
  131. package/dist/index51.d.ts.map +1 -1
  132. package/dist/index52.d.cts +5 -5
  133. package/dist/index52.d.ts +5 -5
  134. package/dist/index53.d.cts +27 -27
  135. package/dist/index53.d.cts.map +1 -1
  136. package/dist/index53.d.ts +27 -27
  137. package/dist/index53.d.ts.map +1 -1
  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/index56.d.cts +2 -2
  144. package/dist/index56.d.ts +2 -2
  145. package/dist/index57.d.cts +2 -2
  146. package/dist/index57.d.cts.map +1 -1
  147. package/dist/index57.d.ts +2 -2
  148. package/dist/index57.d.ts.map +1 -1
  149. package/dist/index58.d.cts +4 -4
  150. package/dist/index58.d.cts.map +1 -1
  151. package/dist/index58.d.ts +4 -4
  152. package/dist/index59.d.cts +9 -9
  153. package/dist/index59.d.ts +9 -9
  154. package/dist/index6.d.cts +5 -5
  155. package/dist/index6.d.ts +5 -5
  156. package/dist/index6.d.ts.map +1 -1
  157. package/dist/index60.d.cts +9 -9
  158. package/dist/index60.d.cts.map +1 -1
  159. package/dist/index60.d.ts +9 -9
  160. package/dist/index61.d.cts +7 -7
  161. package/dist/index61.d.cts.map +1 -1
  162. package/dist/index61.d.ts +4 -4
  163. package/dist/index62.d.cts +4 -4
  164. package/dist/index62.d.cts.map +1 -1
  165. package/dist/index62.d.ts +4 -4
  166. package/dist/index63.d.cts +2 -2
  167. package/dist/index63.d.cts.map +1 -1
  168. package/dist/index63.d.ts +2 -2
  169. package/dist/index64.d.cts +5 -5
  170. package/dist/index64.d.cts.map +1 -1
  171. package/dist/index64.d.ts +5 -5
  172. package/dist/index65.d.cts +3 -3
  173. package/dist/index65.d.ts +3 -3
  174. package/dist/index66.d.cts +4 -4
  175. package/dist/index66.d.cts.map +1 -1
  176. package/dist/index66.d.ts +2 -2
  177. package/dist/index66.d.ts.map +1 -1
  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/index67.d.ts.map +1 -1
  182. package/dist/index68.d.cts +7 -7
  183. package/dist/index68.d.cts.map +1 -1
  184. package/dist/index68.d.ts +4 -4
  185. package/dist/index68.d.ts.map +1 -1
  186. package/dist/index69.d.cts +7 -7
  187. package/dist/index69.d.cts.map +1 -1
  188. package/dist/index69.d.ts +4 -4
  189. package/dist/index7.d.cts +8 -8
  190. package/dist/index7.d.ts +8 -8
  191. package/dist/index71.d.cts +37 -21
  192. package/dist/index71.d.cts.map +1 -1
  193. package/dist/index71.d.ts +37 -21
  194. package/dist/index71.d.ts.map +1 -1
  195. package/dist/index8.d.cts +4 -4
  196. package/dist/index8.d.cts.map +1 -1
  197. package/dist/index8.d.ts +4 -4
  198. package/dist/index9.d.cts +2 -2
  199. package/dist/index9.d.cts.map +1 -1
  200. package/dist/index9.d.ts +2 -2
  201. package/dist/index9.d.ts.map +1 -1
  202. package/dist/patterns/DataTable/index.d.cts +3 -3
  203. package/dist/patterns/DataTable/index.d.cts.map +1 -1
  204. package/dist/patterns/DataTable/index.d.ts +3 -3
  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/Footer/index.d.ts.map +1 -1
  209. package/dist/patterns/Header/index.cjs +1 -0
  210. package/dist/patterns/Header/index.d.cts +2 -2
  211. package/dist/patterns/Header/index.d.ts +2 -2
  212. package/dist/patterns/Header/index.js +2 -2
  213. package/dist/patterns/MediaObject/index.d.cts +12 -12
  214. package/dist/patterns/MediaObject/index.d.cts.map +1 -1
  215. package/dist/patterns/MediaObject/index.d.ts +11 -11
  216. package/dist/patterns/SectionCard/index.d.cts +2 -2
  217. package/dist/patterns/SectionCard/index.d.cts.map +1 -1
  218. package/dist/patterns/SectionCard/index.d.ts +2 -2
  219. package/dist/patterns/SectionCard/index.d.ts.map +1 -1
  220. package/dist/patterns/SectionHeading/index.d.cts +2 -2
  221. package/dist/patterns/SectionHeading/index.d.cts.map +1 -1
  222. package/dist/patterns/SectionHeading/index.d.ts +2 -2
  223. package/dist/patterns/SectionHeading/index.d.ts.map +1 -1
  224. package/dist/patterns/index.cjs +1 -0
  225. package/dist/patterns/index.d.cts +2 -2
  226. package/dist/patterns/index.d.ts +2 -2
  227. package/dist/patterns/index.js +2 -2
  228. 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`
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? }`.
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
+ - **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` — fires on user pick or automatic default; `logoAddon` — node between logo and picker; `actionsStart`/`actionsEnd` — nodes at the start/end of the right-hand actions cluster; `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). 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? }`. Project selection is per-tab (sessionStorage); explicit picks are remembered cross-tab in localStorage `lastSelectedProject`, and a tab with no valid selection auto-defaults to that (else the first selectable project) — there is no selection overlay.
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
@@ -28,6 +28,14 @@ function findProjectName(tree, id) {
28
28
  if (found) return found;
29
29
  }
30
30
  }
31
+ /** First selectable node in the tree (depth-first, skipping context-only ancestors). */
32
+ function firstSelectableProject(tree) {
33
+ for (const node of tree) {
34
+ if (!node.isContextOnly) return node;
35
+ const found = firstSelectableProject(node.children);
36
+ if (found) return found;
37
+ }
38
+ }
31
39
  /** Filter the tree to nodes matching `term`, preserving ancestors of matches. */
32
40
  function filterTree(nodes, term) {
33
41
  if (!term) return nodes;
@@ -179,17 +187,14 @@ const DEFAULT_LABELS = {
179
187
  searchPlaceholder: "Search project",
180
188
  emptyStateText: "No projects found",
181
189
  profileMenuLabel: "Profile menu",
182
- logoutLabel: "Logout",
183
- overlayTitle: "Select a Project",
184
- overlayDescription: "Choose a project to get started. You can change it anytime from the header."
190
+ logoutLabel: "Logout"
185
191
  };
186
192
  //#endregion
187
193
  //#region src/patterns/Header/api.ts
188
194
  const PATHS = {
189
195
  token: "/api/v1/studio-auth/sessions/token",
190
- me: "/api/v1/onboarding/users/me",
191
196
  logout: "/api/v1/studio-auth/sessions/logout",
192
- projects: (clientId) => `/api/v1/onboarding/organization/${clientId}/projects`,
197
+ projects: (clientId) => `/api/v1/client-projects/getAllProjects?limit=50&page=1&sort=name&sortOrder=ASC&clientId=${encodeURIComponent(clientId)}&metaData=true`,
193
198
  credits: "/save/api/v1/users"
194
199
  };
195
200
  /** Join a base URL and path without doubling slashes. */
@@ -199,7 +204,6 @@ function join(baseUrl, path) {
199
204
  function buildEndpoints(apiBaseUrl, overrides) {
200
205
  return {
201
206
  token: overrides?.token ?? join(apiBaseUrl, PATHS.token),
202
- me: overrides?.me ?? join(apiBaseUrl, PATHS.me),
203
207
  logout: overrides?.logout ?? join(apiBaseUrl, PATHS.logout),
204
208
  projects: overrides?.projects ?? ((clientId) => join(apiBaseUrl, PATHS.projects(clientId))),
205
209
  credits: overrides?.credits ?? join(apiBaseUrl, PATHS.credits)
@@ -211,16 +215,6 @@ async function fetchToken(endpoints, fetcher) {
211
215
  const { accessToken } = await res.json();
212
216
  return accessToken;
213
217
  }
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
218
  async function fetchProjects(endpoints, clientId, accessToken, fetcher) {
225
219
  const res = await fetcher(endpoints.projects(clientId), {
226
220
  credentials: "include",
@@ -254,6 +248,7 @@ async function callLogout(endpoints, fetcher) {
254
248
  //#endregion
255
249
  //#region src/patterns/Header/storage.ts
256
250
  const PROJECT_KEY = "ds-header-project";
251
+ const LAST_PROJECT_KEY = "lastSelectedProject";
257
252
  function getStoredProject() {
258
253
  try {
259
254
  const raw = sessionStorage.getItem(PROJECT_KEY);
@@ -272,6 +267,20 @@ function clearStoredProject() {
272
267
  sessionStorage.removeItem(PROJECT_KEY);
273
268
  } catch {}
274
269
  }
270
+ /** The project the user last explicitly selected, in any tab (localStorage). */
271
+ function getLastSelectedProject() {
272
+ try {
273
+ const raw = localStorage.getItem(LAST_PROJECT_KEY);
274
+ return raw ? JSON.parse(raw) : null;
275
+ } catch {
276
+ return null;
277
+ }
278
+ }
279
+ function setLastSelectedProject(project) {
280
+ try {
281
+ localStorage.setItem(LAST_PROJECT_KEY, JSON.stringify(project));
282
+ } catch {}
283
+ }
275
284
  //#endregion
276
285
  //#region src/patterns/Header/header-store.ts
277
286
  function initialState() {
@@ -307,14 +316,22 @@ const headerStore = {
307
316
  getServerSnapshot() {
308
317
  return SERVER_SNAPSHOT;
309
318
  },
310
- /** Set the selected project. `name` is best-effort; the Header backfills it from the tree. */
311
- setProject(id, name = "", clientId) {
312
- if (id) setStoredProject({
313
- id,
314
- name,
315
- clientId: clientId ?? state.clientId
316
- });
317
- else clearStoredProject();
319
+ /**
320
+ * Set the selected project. `name` is best-effort; the Header backfills it from
321
+ * the tree. Explicit selections are also remembered in localStorage as the
322
+ * cross-tab default; pass `remember: false` for internal backfill/auto-select
323
+ * writes so they don't overwrite the user's last explicit choice.
324
+ */
325
+ setProject(id, name = "", clientId, { remember = true } = {}) {
326
+ if (id) {
327
+ const project = {
328
+ id,
329
+ name,
330
+ clientId: clientId ?? state.clientId
331
+ };
332
+ setStoredProject(project);
333
+ if (remember) setLastSelectedProject(project);
334
+ } else clearStoredProject();
318
335
  state = {
319
336
  ...state,
320
337
  selectedProjectId: id,
@@ -414,6 +431,24 @@ function decodeJwt(token) {
414
431
  return null;
415
432
  }
416
433
  }
434
+ /**
435
+ * Decode the user claims from the session cookie's JWT — no network call.
436
+ * Returns null when the cookie is absent or undecodable.
437
+ */
438
+ function getSessionClaims(environmentId) {
439
+ const token = getSessionToken(environmentId);
440
+ if (!token) return null;
441
+ const payload = decodeJwt(token);
442
+ if (!payload) return null;
443
+ const str = (v) => typeof v === "string" ? v : void 0;
444
+ return {
445
+ email: str(payload.email),
446
+ name: str(payload.name),
447
+ userId: str(payload.user_id),
448
+ clientId: str(payload.client_id),
449
+ exp: typeof payload.exp === "number" ? payload.exp : void 0
450
+ };
451
+ }
417
452
  /** True when a valid, unexpired session cookie exists for the environment. */
418
453
  function isSessionValid(environmentId) {
419
454
  const token = getSessionToken(environmentId);
@@ -433,6 +468,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
433
468
  const fetcher = react$1.useMemo(() => fetcherProp ?? ((input, init) => fetch(input, init)), [fetcherProp]);
434
469
  const endpoints = react$1.useMemo(() => buildEndpoints(apiBaseUrl, endpointOverrides), [apiBaseUrl, endpointOverrides]);
435
470
  const isAuthenticated = isSessionValid(environmentId);
471
+ const claims = getSessionClaims(environmentId);
436
472
  const tokenQuery = (0, _tanstack_react_query.useQuery)({
437
473
  queryKey: [
438
474
  "ds-header",
@@ -445,20 +481,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
445
481
  gcTime
446
482
  });
447
483
  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;
484
+ const effectiveClientId = clientId ?? claims?.clientId;
462
485
  const projectsQuery = (0, _tanstack_react_query.useQuery)({
463
486
  queryKey: [
464
487
  "ds-header",
@@ -484,7 +507,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
484
507
  gcTime
485
508
  });
486
509
  const projectsTree = projectsQuery.data?.projectsTree ?? [];
487
- const userEmail = showProfile ? meQuery.data?.email : void 0;
510
+ const userEmail = showProfile ? claims?.email : void 0;
488
511
  const creditsHref = react$1.useMemo(() => {
489
512
  try {
490
513
  return new URL("/user/activity", apiBaseUrl).toString();
@@ -493,6 +516,10 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
493
516
  }
494
517
  }, [apiBaseUrl]);
495
518
  const selection = useHeaderStore();
519
+ const onProjectChangeRef = react$1.useRef(onProjectChange);
520
+ react$1.useEffect(() => {
521
+ onProjectChangeRef.current = onProjectChange;
522
+ });
496
523
  react$1.useEffect(() => {
497
524
  if (effectiveClientId) headerStore.reconcileClient(effectiveClientId);
498
525
  }, [effectiveClientId]);
@@ -500,10 +527,19 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
500
527
  if (!projectsQuery.data) return;
501
528
  const tree = projectsQuery.data.projectsTree;
502
529
  const id = headerStore.getSnapshot().selectedProjectId;
503
- if (id && !nodeExistsInTree(tree, id)) headerStore.clearProject();
504
- else if (id) {
530
+ if (id && nodeExistsInTree(tree, id)) {
505
531
  const name = findProjectName(tree, id);
506
- if (name && name !== headerStore.getSnapshot().selectedProjectName) headerStore.setProject(id, name, effectiveClientId);
532
+ if (name && name !== headerStore.getSnapshot().selectedProjectName) headerStore.setProject(id, name, effectiveClientId, { remember: false });
533
+ } else {
534
+ const last = getLastSelectedProject();
535
+ const fallback = last && nodeExistsInTree(tree, last.id) ? {
536
+ id: last.id,
537
+ name: findProjectName(tree, last.id) ?? last.name
538
+ } : firstSelectableProject(tree);
539
+ if (fallback) {
540
+ headerStore.setProject(fallback.id, fallback.name, effectiveClientId, { remember: false });
541
+ onProjectChangeRef.current?.(fallback.id);
542
+ } else if (id) headerStore.clearProject();
507
543
  }
508
544
  headerStore.setOrganization(projectsQuery.data.organizationName, effectiveClientId);
509
545
  }, [projectsQuery.data, effectiveClientId]);
@@ -541,7 +577,6 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
541
577
  credits: selection.credits,
542
578
  creditsHref,
543
579
  projectDropdownVisible,
544
- showOverlay: isAuthenticated && projectDropdownVisible && !selection.selectedProjectId,
545
580
  selectProject,
546
581
  logout
547
582
  };
@@ -653,7 +688,7 @@ function Header(props) {
653
688
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeaderInner, { ...props })
654
689
  });
655
690
  }
656
- function HeaderInner({ logoType, logoHref, showOrg = false, showProjectDropdown = false, showProfile = false, showThemeSwitcher = false, showCredits = false, pageTitle, feedbackUrl, logoAddon, className, onCloseProjectOverlay, ...rest }) {
691
+ function HeaderInner({ logoType, logoHref, showOrg = false, showProjectDropdown = false, showProfile = false, showThemeSwitcher = false, showCredits = false, pageTitle, feedbackUrl, logoAddon, actionsStart, actionsEnd, className, ...rest }) {
657
692
  const { product } = require_primitives_ThemeProvider_index.useTheme();
658
693
  const brand = logoType ?? product;
659
694
  const state = useGlobalHeader({
@@ -667,8 +702,8 @@ function HeaderInner({ logoType, logoHref, showOrg = false, showProjectDropdown
667
702
  const showOrgUi = showOrg && state.isAuthenticated;
668
703
  const showProfileUi = showProfile && state.isAuthenticated;
669
704
  const showCreditsUi = showCredits && state.isAuthenticated && state.credits !== void 0;
670
- const showActions = showThemeSwitcher || showProfileUi || showCreditsUi || !!feedbackUrl;
671
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(HeaderBar, {
705
+ const showActions = showThemeSwitcher || showProfileUi || showCreditsUi || !!feedbackUrl || !!actionsStart || !!actionsEnd;
706
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(HeaderBar, {
672
707
  className,
673
708
  children: [
674
709
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeaderLogo, {
@@ -702,6 +737,7 @@ function HeaderInner({ logoType, logoHref, showOrg = false, showProjectDropdown
702
737
  showActions && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
703
738
  className: "ml-auto flex items-center gap-2",
704
739
  children: [
740
+ actionsStart,
705
741
  feedbackUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_primitives_Button_index.Button, {
706
742
  variant: "ghost",
707
743
  size: "icon",
@@ -724,78 +760,11 @@ function HeaderInner({ logoType, logoHref, showOrg = false, showProjectDropdown
724
760
  showProfileUi && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProfileMenu, {
725
761
  email: state.userEmail,
726
762
  onLogout: state.logout
727
- })
763
+ }),
764
+ actionsEnd
728
765
  ]
729
766
  })
730
767
  ]
731
- }), showProjectUi && state.showOverlay && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProjectOverlay, {
732
- organizationName: state.organizationName,
733
- projectsTree: state.projectsTree,
734
- selectedProjectId: state.selectedProjectId,
735
- onSelect: state.selectProject,
736
- isLoading: state.isProjectsLoading,
737
- onClose: onCloseProjectOverlay
738
- })] });
739
- }
740
- function ProjectOverlay({ organizationName, projectsTree, selectedProjectId, onSelect, isLoading, onClose }) {
741
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
742
- role: "dialog",
743
- "aria-modal": "true",
744
- "aria-label": DEFAULT_LABELS.overlayTitle,
745
- className: "fixed inset-0 z-50 flex items-center justify-center bg-primary-bg/80 backdrop-blur-sm",
746
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
747
- 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",
748
- children: [
749
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_primitives_Button_index.Button, {
750
- variant: "ghost",
751
- size: "icon",
752
- onClick: onClose ?? (() => window.history.back()),
753
- className: "absolute top-4 right-4",
754
- "aria-label": "Close",
755
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "size-5" })
756
- }),
757
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
758
- className: "flex flex-col items-center gap-3 text-center",
759
- children: [
760
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
761
- className: "rounded-full bg-primary/10 p-4",
762
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.FolderOpen, { className: "size-8 text-primary" })
763
- }),
764
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
765
- className: "font-semibold text-primary-txt text-xl",
766
- children: DEFAULT_LABELS.overlayTitle
767
- }),
768
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
769
- className: "text-muted-txt text-sm",
770
- children: DEFAULT_LABELS.overlayDescription
771
- })
772
- ]
773
- }),
774
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
775
- className: "flex w-full justify-center gap-2",
776
- children: [organizationName && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
777
- className: require_cn.cn(PILL, "select-none"),
778
- title: organizationName,
779
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
780
- className: "shrink-0 text-muted-txt",
781
- children: [DEFAULT_LABELS.orgLabel, ":"]
782
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
783
- className: "max-w-52 truncate font-medium",
784
- children: organizationName
785
- })]
786
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProjectPicker, {
787
- projectsTree,
788
- selectedProjectId,
789
- onSelect,
790
- projectLabel: DEFAULT_LABELS.projectLabel,
791
- projectPlaceholder: DEFAULT_LABELS.projectPlaceholder,
792
- searchPlaceholder: DEFAULT_LABELS.searchPlaceholder,
793
- emptyStateText: DEFAULT_LABELS.emptyStateText,
794
- isLoading
795
- })]
796
- })
797
- ]
798
- })
799
768
  });
800
769
  }
801
770
  //#endregion
@@ -859,6 +828,12 @@ Object.defineProperty(exports, "buildEndpoints", {
859
828
  return buildEndpoints;
860
829
  }
861
830
  });
831
+ Object.defineProperty(exports, "getSessionClaims", {
832
+ enumerable: true,
833
+ get: function() {
834
+ return getSessionClaims;
835
+ }
836
+ });
862
837
  Object.defineProperty(exports, "getSessionToken", {
863
838
  enumerable: true,
864
839
  get: function() {