@the-portland-company/shell 0.75.1 → 0.76.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.
@@ -188,6 +188,8 @@ type PolitogyFrameData = {
188
188
  devNotes?: ChromeDevNotesConfig;
189
189
  /** Controlled org state for apps that own active-org + switching. */
190
190
  org?: ControlledOrgState;
191
+ /** Auth-owned active org from verified token claims (v2). Wins over `org`. */
192
+ tokenOrg?: TokenOrgState;
191
193
  /**
192
194
  * App-owned children for a standard nav entry this app owns (Contacts).
193
195
  * `count` is the live contact total, pre-formatted ("31.9K"), drawn
@@ -393,6 +395,34 @@ type ControlledOrgState = {
393
395
  */
394
396
  onExternalSwitch?: (id: string) => void;
395
397
  };
398
+ /**
399
+ * Auth-owned active org (Global-Rules v38, UI-Platform v46). The app server
400
+ * passes the VERIFIED token claims; the shell never resolves the org itself.
401
+ */
402
+ type TokenOrgState = {
403
+ /** `org_id` claim from the app's verified token (full uuid). */
404
+ orgId: string | null;
405
+ /** `org_role` claim (raw VRM Core role). */
406
+ orgRole?: string | null;
407
+ /**
408
+ * The switcher list from the app server (names for the pill). When given,
409
+ * the chrome `/api/orgs` call is skipped. When absent it is the fallback.
410
+ */
411
+ orgs?: (NavOrgOption & {
412
+ rawRole?: string | null;
413
+ })[];
414
+ /**
415
+ * Server action that re-exchanges for tokens on `orgId`. Called after the
416
+ * IdP accepted the switch (shared) or for a per-tab override (`perTab`).
417
+ */
418
+ onReexchange: (orgId: string, opts: {
419
+ perTab: boolean;
420
+ }) => void | Promise<void>;
421
+ /** IdP base override (default: auth / auth-staging by host). */
422
+ authBaseUrl?: string;
423
+ /** Called when the IdP refuses a switch (403 not a member, 401 no session). */
424
+ onSwitchError?: (err: unknown) => void;
425
+ };
396
426
  type ChromeDevNotesConfig = {
397
427
  /** Override the DevNotes API base (default: `${proxyBasePath}/devnotes`). */
398
428
  basePath?: string;
@@ -417,7 +447,7 @@ type ChromeDevNotesConfig = {
417
447
  * <ShellFrame …/>
418
448
  * </PolitogyAppChrome>
419
449
  */
420
- declare function PolitogyAppChrome({ storagePrefix, proxyBasePath, fetchJson: fetchJsonOverride, devNotes, org, identity, children, }: {
450
+ declare function PolitogyAppChrome({ storagePrefix, proxyBasePath, fetchJson: fetchJsonOverride, devNotes, org, tokenOrg, identity, children, }: {
421
451
  storagePrefix: string;
422
452
  proxyBasePath?: string;
423
453
  /**
@@ -430,6 +460,8 @@ declare function PolitogyAppChrome({ storagePrefix, proxyBasePath, fetchJson: fe
430
460
  devNotes?: ChromeDevNotesConfig;
431
461
  /** Controlled org state — the app owns active-org + switching (Contacts). */
432
462
  org?: ControlledOrgState;
463
+ /** Auth-owned active org from verified token claims (v2). Wins over `org`. */
464
+ tokenOrg?: TokenOrgState;
433
465
  identity: ChromeIdentity;
434
466
  children: ReactNode;
435
467
  }): react_jsx_runtime.JSX.Element;
@@ -502,4 +534,4 @@ type UseChromeModeArg = ModeId | {
502
534
  */
503
535
  declare function useChromeMode(arg?: UseChromeModeArg): NavMode;
504
536
 
505
- export { type BootPhase, type BootProgress, type BootStageId, type ChromeDevNotesConfig, type ChromeIdentity, ChromeTopBarActions, type ControlledOrgState, LoadingScreen, POLITOGY_APPS, PolitogyAppChrome, PolitogyAppFrame, type PolitogyAppKey, type PolitogyAppMode, type PolitogyAppSession, type PolitogyBuildMeta, type PolitogyFrameData, type PolitogyFrameGrant, type PolitogyGrantedFrame, type ResolvedIdentity, deriveBreadcrumbs, looksLikeEmail, resolveIdentity, standardNavIcons, useChromeFooter, useChromeIdentity, useChromeMode, useChromeState, useChromeUserMenu, useIsSuperAdmin };
537
+ export { type BootPhase, type BootProgress, type BootStageId, type ChromeDevNotesConfig, type ChromeIdentity, ChromeTopBarActions, type ControlledOrgState, LoadingScreen, POLITOGY_APPS, PolitogyAppChrome, PolitogyAppFrame, type PolitogyAppKey, type PolitogyAppMode, type PolitogyAppSession, type PolitogyBuildMeta, type PolitogyFrameData, type PolitogyFrameGrant, type PolitogyGrantedFrame, type ResolvedIdentity, type TokenOrgState, deriveBreadcrumbs, looksLikeEmail, resolveIdentity, standardNavIcons, useChromeFooter, useChromeIdentity, useChromeMode, useChromeState, useChromeUserMenu, useIsSuperAdmin };
@@ -188,6 +188,8 @@ type PolitogyFrameData = {
188
188
  devNotes?: ChromeDevNotesConfig;
189
189
  /** Controlled org state for apps that own active-org + switching. */
190
190
  org?: ControlledOrgState;
191
+ /** Auth-owned active org from verified token claims (v2). Wins over `org`. */
192
+ tokenOrg?: TokenOrgState;
191
193
  /**
192
194
  * App-owned children for a standard nav entry this app owns (Contacts).
193
195
  * `count` is the live contact total, pre-formatted ("31.9K"), drawn
@@ -393,6 +395,34 @@ type ControlledOrgState = {
393
395
  */
394
396
  onExternalSwitch?: (id: string) => void;
395
397
  };
398
+ /**
399
+ * Auth-owned active org (Global-Rules v38, UI-Platform v46). The app server
400
+ * passes the VERIFIED token claims; the shell never resolves the org itself.
401
+ */
402
+ type TokenOrgState = {
403
+ /** `org_id` claim from the app's verified token (full uuid). */
404
+ orgId: string | null;
405
+ /** `org_role` claim (raw VRM Core role). */
406
+ orgRole?: string | null;
407
+ /**
408
+ * The switcher list from the app server (names for the pill). When given,
409
+ * the chrome `/api/orgs` call is skipped. When absent it is the fallback.
410
+ */
411
+ orgs?: (NavOrgOption & {
412
+ rawRole?: string | null;
413
+ })[];
414
+ /**
415
+ * Server action that re-exchanges for tokens on `orgId`. Called after the
416
+ * IdP accepted the switch (shared) or for a per-tab override (`perTab`).
417
+ */
418
+ onReexchange: (orgId: string, opts: {
419
+ perTab: boolean;
420
+ }) => void | Promise<void>;
421
+ /** IdP base override (default: auth / auth-staging by host). */
422
+ authBaseUrl?: string;
423
+ /** Called when the IdP refuses a switch (403 not a member, 401 no session). */
424
+ onSwitchError?: (err: unknown) => void;
425
+ };
396
426
  type ChromeDevNotesConfig = {
397
427
  /** Override the DevNotes API base (default: `${proxyBasePath}/devnotes`). */
398
428
  basePath?: string;
@@ -417,7 +447,7 @@ type ChromeDevNotesConfig = {
417
447
  * <ShellFrame …/>
418
448
  * </PolitogyAppChrome>
419
449
  */
420
- declare function PolitogyAppChrome({ storagePrefix, proxyBasePath, fetchJson: fetchJsonOverride, devNotes, org, identity, children, }: {
450
+ declare function PolitogyAppChrome({ storagePrefix, proxyBasePath, fetchJson: fetchJsonOverride, devNotes, org, tokenOrg, identity, children, }: {
421
451
  storagePrefix: string;
422
452
  proxyBasePath?: string;
423
453
  /**
@@ -430,6 +460,8 @@ declare function PolitogyAppChrome({ storagePrefix, proxyBasePath, fetchJson: fe
430
460
  devNotes?: ChromeDevNotesConfig;
431
461
  /** Controlled org state — the app owns active-org + switching (Contacts). */
432
462
  org?: ControlledOrgState;
463
+ /** Auth-owned active org from verified token claims (v2). Wins over `org`. */
464
+ tokenOrg?: TokenOrgState;
433
465
  identity: ChromeIdentity;
434
466
  children: ReactNode;
435
467
  }): react_jsx_runtime.JSX.Element;
@@ -502,4 +534,4 @@ type UseChromeModeArg = ModeId | {
502
534
  */
503
535
  declare function useChromeMode(arg?: UseChromeModeArg): NavMode;
504
536
 
505
- export { type BootPhase, type BootProgress, type BootStageId, type ChromeDevNotesConfig, type ChromeIdentity, ChromeTopBarActions, type ControlledOrgState, LoadingScreen, POLITOGY_APPS, PolitogyAppChrome, PolitogyAppFrame, type PolitogyAppKey, type PolitogyAppMode, type PolitogyAppSession, type PolitogyBuildMeta, type PolitogyFrameData, type PolitogyFrameGrant, type PolitogyGrantedFrame, type ResolvedIdentity, deriveBreadcrumbs, looksLikeEmail, resolveIdentity, standardNavIcons, useChromeFooter, useChromeIdentity, useChromeMode, useChromeState, useChromeUserMenu, useIsSuperAdmin };
537
+ export { type BootPhase, type BootProgress, type BootStageId, type ChromeDevNotesConfig, type ChromeIdentity, ChromeTopBarActions, type ControlledOrgState, LoadingScreen, POLITOGY_APPS, PolitogyAppChrome, PolitogyAppFrame, type PolitogyAppKey, type PolitogyAppMode, type PolitogyAppSession, type PolitogyBuildMeta, type PolitogyFrameData, type PolitogyFrameGrant, type PolitogyGrantedFrame, type ResolvedIdentity, type TokenOrgState, deriveBreadcrumbs, looksLikeEmail, resolveIdentity, standardNavIcons, useChromeFooter, useChromeIdentity, useChromeMode, useChromeState, useChromeUserMenu, useIsSuperAdmin };
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
  import { broadcastLogout, watchLogout } from './chunk-LL6LSJAH.js';
3
- import { clearBetaBannerDismissal, standardPolitogyNav, standardNavIcons, isNavItemActive, NavAppShell, NavChromeProvider, TopBarWidgetSlot, FuelAction, SpecOverlayAction, NotificationsAction, SupportAction, useChromeMe, useThemePreference, useNavChromeFetcher, useChromeOrgs } from './chunk-AQMSMAK2.js';
4
- export { standardNavIcons } from './chunk-AQMSMAK2.js';
3
+ import { clearBetaBannerDismissal, standardPolitogyNav, standardNavIcons, isNavItemActive, NavAppShell, NavChromeProvider, TopBarWidgetSlot, FuelAction, SpecOverlayAction, NotificationsAction, SupportAction, useChromeMe, useThemePreference, useNavChromeFetcher, useChromeOrgs } from './chunk-WHF6YW27.js';
4
+ export { standardNavIcons } from './chunk-WHF6YW27.js';
5
5
  import { warnDeprecated } from './chunk-MY6G3LMN.js';
6
6
  import './chunk-BHRVZPZ7.js';
7
- import { NAV_DISPLAY_DEFAULT, resolveNavDisplay, isNavDisplayPreference, writeNavDisplayCookie, writeNavDisplayLocal, NAV_DISPLAY_STORAGE_KEY, readNavDisplayCookie } from './chunk-7YVJ7HQG.js';
8
- import { resolveActiveOrg, isOrgId, writeActiveOrgCookie, ACTIVE_ORG_QUERY_PARAM, readActiveOrgCookie } from './chunk-W2HITMV5.js';
7
+ import { NAV_DISPLAY_DEFAULT, resolveNavDisplay, writeNavDisplayLocal, writeNavDisplayCookie, isNavDisplayPreference, NAV_DISPLAY_STORAGE_KEY, readNavDisplayCookie } from './chunk-7YVJ7HQG.js';
8
+ import { readOrgSelectorFromUrl, matchOrgSelector, isOrgId, switchActiveOrg, ACTIVE_ORG_QUERY_PARAM, ORG_ID_QUERY_PARAM, orgIdTail, writeActiveOrgCookie, resolveActiveOrg, readActiveOrgCookie } from './chunk-BWV52AT6.js';
9
9
  import { createContext, useMemo, useRef, useEffect, useState, useCallback, useContext } from 'react';
10
10
  import { DevNotesMenu, createDevNotesClient, DevNotesProvider, DevNotesTaskListModal, DevNotesOverlay } from '@the-portland-company/devnotes';
11
11
  import { LuNotebookPen, LuContactRound, LuMegaphone, LuFilePenLine } from 'react-icons/lu';
@@ -427,7 +427,9 @@ function LoadingScreen({
427
427
  padding: 0,
428
428
  border: "none",
429
429
  background: "transparent",
430
- color: "rgba(255,255,255,0.38)",
430
+ // 0.38 alpha was 3.51:1 on the ink-900 loading background —
431
+ // below WCAG 2.2 AA 4.5:1. 0.72 clears it.
432
+ color: "rgba(255,255,255,0.72)",
431
433
  fontFamily: "var(--font-family, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif)",
432
434
  fontSize: "12.5px",
433
435
  textDecoration: "underline",
@@ -454,7 +456,9 @@ function LoadingScreen({
454
456
  padding: "0 32px 30px",
455
457
  fontFamily: "var(--font-family, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif)",
456
458
  fontSize: "12.5px",
457
- color: "rgba(255,255,255,0.38)"
459
+ // 0.38 alpha was 3.51:1 on the ink-900 loading background — below
460
+ // WCAG 2.2 AA 4.5:1. 0.72 clears it.
461
+ color: "rgba(255,255,255,0.72)"
458
462
  },
459
463
  children: [
460
464
  /* @__PURE__ */ jsx("span", { children: "Politogy VRM. Built for the long game." }),
@@ -522,11 +526,17 @@ var POLITOGY_APPS = {
522
526
  storagePrefix: "contacts",
523
527
  pageTitle: false
524
528
  },
525
- forms: { title: "Forms", origin: "https://forms.politogyvrm.com", storagePrefix: "forms" },
526
- surveys: { title: "Surveys", origin: "https://surveys.politogyvrm.com", storagePrefix: "surveys" },
529
+ // 'title' for forms/surveys/polls/ctas for the same reason as petitions
530
+ // below: their pages have real names ("Volunteer Signup", "Q3 Satisfaction"),
531
+ // where the registry can only say the app's own name. Without the grant the
532
+ // page header falls back to the static registry title forever, so the
533
+ // designed "<mode> mode" eyebrow + H2 + subtitle block never gets the real
534
+ // per-page title an app passes it (UI-Platform → Page header).
535
+ forms: { title: "Forms", origin: "https://forms.politogyvrm.com", storagePrefix: "forms", grants: ["title"] },
536
+ surveys: { title: "Surveys", origin: "https://surveys.politogyvrm.com", storagePrefix: "surveys", grants: ["title"] },
527
537
  // Specs/Left-Navigation §9.2: Polling is a Campaign row (Research).
528
- polls: { title: "Polls", origin: "https://polls.politogyvrm.com", storagePrefix: "polls", mode: "campaign" },
529
- ctas: { title: "CTAs", origin: "https://ctas.politogyvrm.com", storagePrefix: "ctas" },
538
+ polls: { title: "Polls", origin: "https://polls.politogyvrm.com", storagePrefix: "polls", mode: "campaign", grants: ["title"] },
539
+ ctas: { title: "CTAs", origin: "https://ctas.politogyvrm.com", storagePrefix: "ctas", grants: ["title"] },
530
540
  // V2 apps that replace retired app.politogyvrm.com pages (Reference/
531
541
  // UI-Platform v42 "Retirement of app.politogyvrm.com"). Dashboard hosts the
532
542
  // Relationship Dashboard + Campaign Battleground/District data/Campaigns
@@ -550,7 +560,7 @@ var POLITOGY_APPS = {
550
560
  },
551
561
  // Relationship Mode Email Engine (Specs/Email-Engine) — the V2 standalone
552
562
  // app, running in parallel to VRM core's V1 Email Broadcast.
553
- email: { title: "Email", origin: "https://email.politogyvrm.com", storagePrefix: "email", mode: "relationship" },
563
+ email: { title: "Email", origin: "https://email.politogyvrm.com", storagePrefix: "email", mode: "relationship", grants: ["title"] },
554
564
  // Campaign Mode apps — each scoped to Campaign, so they render the Campaign
555
565
  // tree and a static Campaign pill. Not yet deployed; hidden from the Campaign
556
566
  // nav by CAMPAIGN_FEATURE_AVAILABILITY until their launch flips the flag.
@@ -563,11 +573,12 @@ var POLITOGY_APPS = {
563
573
  origin: "https://a2p.politogyvrm.com",
564
574
  storagePrefix: "a2p",
565
575
  mode: "campaign",
566
- dual: { modes: ["relationship", "campaign"], defaultMode: "campaign" }
576
+ dual: { modes: ["relationship", "campaign"], defaultMode: "campaign" },
577
+ grants: ["title"]
567
578
  },
568
- phonebank: { title: "Phone Banking", origin: "https://phonebank.politogyvrm.com", storagePrefix: "phonebank", mode: "campaign" },
569
- canvass: { title: "Field Canvassing", origin: "https://canvass.politogyvrm.com", storagePrefix: "canvass", mode: "campaign" },
570
- signs: { title: "Sign Mapping", origin: "https://signs.politogyvrm.com", storagePrefix: "signs", mode: "campaign" }
579
+ phonebank: { title: "Phone Banking", origin: "https://phonebank.politogyvrm.com", storagePrefix: "phonebank", mode: "campaign", grants: ["title"] },
580
+ canvass: { title: "Field Canvassing", origin: "https://canvass.politogyvrm.com", storagePrefix: "canvass", mode: "campaign", grants: ["title"] },
581
+ signs: { title: "Sign Mapping", origin: "https://signs.politogyvrm.com", storagePrefix: "signs", mode: "campaign", grants: ["title"] }
571
582
  };
572
583
  var DEFAULT_SEARCH_PLACEHOLDER = {
573
584
  contacts: "Search contacts, segments, forms"
@@ -594,6 +605,7 @@ function PolitogyAppFrame({
594
605
  fetchJson: data?.fetchJson,
595
606
  devNotes: data?.devNotes,
596
607
  org: data?.org,
608
+ tokenOrg: data?.tokenOrg,
597
609
  identity: {
598
610
  userId: session.userId,
599
611
  userName: session.userName ?? null,
@@ -874,6 +886,107 @@ var ChromeStateContext = createContext(null);
874
886
  function useChromeState() {
875
887
  return useContext(ChromeStateContext);
876
888
  }
889
+ function TokenChromeStateProvider({
890
+ tokenOrg,
891
+ children
892
+ }) {
893
+ const { orgs: fetchedOrgs, loading: orgsFetching, error: orgsError } = useChromeOrgs({
894
+ enabled: !tokenOrg.orgs
895
+ });
896
+ const [navDisplay, setNavDisplayState] = useState(NAV_DISPLAY_DEFAULT);
897
+ const [pendingOrg, setPendingOrg] = useState(null);
898
+ const tokenOrgRef = useRef(tokenOrg);
899
+ tokenOrgRef.current = tokenOrg;
900
+ useEffect(() => {
901
+ setNavDisplayState(resolveNavDisplay());
902
+ }, []);
903
+ const orgs = useMemo(
904
+ () => tokenOrg.orgs ?? (fetchedOrgs ?? []).map((o) => ({
905
+ id: o.id,
906
+ name: o.name,
907
+ logoUrl: o.logoUrl,
908
+ brandColor: o.brandColor,
909
+ role: o.role,
910
+ rawRole: o.rawRole ?? null
911
+ })),
912
+ [tokenOrg.orgs, fetchedOrgs]
913
+ );
914
+ const [urlSelector] = useState(
915
+ () => typeof window === "undefined" ? null : readOrgSelectorFromUrl(window.location.search)
916
+ );
917
+ const overrideOrg = useMemo(
918
+ () => urlSelector ? matchOrgSelector(urlSelector, orgs)?.id ?? null : null,
919
+ [urlSelector, orgs]
920
+ );
921
+ const orgId = pendingOrg ?? (tokenOrg.orgId && overrideOrg ? overrideOrg : tokenOrg.orgId);
922
+ const reexchangedFor = useRef(null);
923
+ useEffect(() => {
924
+ if (!overrideOrg || !tokenOrg.orgId || overrideOrg === tokenOrg.orgId) return;
925
+ if (reexchangedFor.current === overrideOrg) return;
926
+ reexchangedFor.current = overrideOrg;
927
+ void Promise.resolve(tokenOrgRef.current.onReexchange(overrideOrg, { perTab: true })).catch(
928
+ () => {
929
+ }
930
+ );
931
+ }, [overrideOrg, tokenOrg.orgId]);
932
+ useEffect(() => {
933
+ if (pendingOrg && tokenOrg.orgId === pendingOrg) setPendingOrg(null);
934
+ }, [pendingOrg, tokenOrg.orgId]);
935
+ const setOrgId = useCallback((id) => {
936
+ const current = tokenOrgRef.current;
937
+ if (!isOrgId(id) || id === (current.orgId ?? null)) return;
938
+ setPendingOrg(id);
939
+ void (async () => {
940
+ try {
941
+ const claims = await switchActiveOrg(id, { authBaseUrl: current.authBaseUrl });
942
+ try {
943
+ const url = new URL(window.location.href);
944
+ url.searchParams.delete(ACTIVE_ORG_QUERY_PARAM);
945
+ url.searchParams.set(ORG_ID_QUERY_PARAM, orgIdTail(claims.orgId));
946
+ window.history.replaceState(window.history.state, "", url.toString());
947
+ } catch {
948
+ }
949
+ await tokenOrgRef.current.onReexchange(claims.orgId, { perTab: false });
950
+ } catch (err) {
951
+ setPendingOrg(null);
952
+ tokenOrgRef.current.onSwitchError?.(err);
953
+ }
954
+ })();
955
+ }, []);
956
+ const setNavDisplay = useCallback((m) => {
957
+ setNavDisplayState(m);
958
+ writeNavDisplayLocal(m);
959
+ writeNavDisplayCookie(m);
960
+ }, []);
961
+ useEffect(() => {
962
+ if (!isOrgId(orgId)) return;
963
+ try {
964
+ const url = new URL(window.location.href);
965
+ const tail = orgIdTail(orgId);
966
+ if (url.searchParams.has(ACTIVE_ORG_QUERY_PARAM) || url.searchParams.get(ORG_ID_QUERY_PARAM) !== tail) {
967
+ url.searchParams.delete(ACTIVE_ORG_QUERY_PARAM);
968
+ url.searchParams.set(ORG_ID_QUERY_PARAM, tail);
969
+ window.history.replaceState(window.history.state, "", url.toString());
970
+ }
971
+ } catch {
972
+ }
973
+ }, [orgId]);
974
+ useEffect(() => {
975
+ if (isOrgId(tokenOrg.orgId) && !pendingOrg) writeActiveOrgCookie(tokenOrg.orgId);
976
+ }, [tokenOrg.orgId, pendingOrg]);
977
+ const value = useMemo(
978
+ () => ({
979
+ orgId,
980
+ setOrgId,
981
+ orgs,
982
+ orgsLoading: !tokenOrg.orgs && orgsFetching && !orgsError && !fetchedOrgs,
983
+ navDisplay,
984
+ setNavDisplay
985
+ }),
986
+ [orgId, setOrgId, orgs, tokenOrg.orgs, orgsFetching, orgsError, fetchedOrgs, navDisplay, setNavDisplay]
987
+ );
988
+ return /* @__PURE__ */ jsx(ChromeStateContext.Provider, { value, children });
989
+ }
877
990
  function ChromeStateProvider({
878
991
  controlledOrg,
879
992
  children
@@ -1177,6 +1290,7 @@ function PolitogyAppChrome({
1177
1290
  fetchJson: fetchJsonOverride,
1178
1291
  devNotes,
1179
1292
  org,
1293
+ tokenOrg,
1180
1294
  identity,
1181
1295
  children
1182
1296
  }) {
@@ -1195,7 +1309,7 @@ function PolitogyAppChrome({
1195
1309
  [proxyBasePath]
1196
1310
  );
1197
1311
  const fetchJson = fetchJsonOverride ?? proxyFetchJson;
1198
- return /* @__PURE__ */ jsx(NavChromeProvider, { fetchJson, children: /* @__PURE__ */ jsx(ChromeStateProvider, { controlledOrg: org, children: /* @__PURE__ */ jsx(
1312
+ const devNotesTree = /* @__PURE__ */ jsx(
1199
1313
  AppDevNotes,
1200
1314
  {
1201
1315
  storagePrefix,
@@ -1204,7 +1318,8 @@ function PolitogyAppChrome({
1204
1318
  identity,
1205
1319
  children
1206
1320
  }
1207
- ) }) });
1321
+ );
1322
+ return /* @__PURE__ */ jsx(NavChromeProvider, { fetchJson, children: tokenOrg ? /* @__PURE__ */ jsx(TokenChromeStateProvider, { tokenOrg, children: devNotesTree }) : /* @__PURE__ */ jsx(ChromeStateProvider, { controlledOrg: org, children: devNotesTree }) });
1208
1323
  }
1209
1324
  function ChromeTopBarActions() {
1210
1325
  const state = useChromeState();
@@ -1221,7 +1336,7 @@ function ChromeTopBarActions() {
1221
1336
  /* @__PURE__ */ jsx(FuelAction, { orgId: state.orgId }),
1222
1337
  /* @__PURE__ */ jsx(SpecOverlayAction, { orgId: state.orgId, orgName: state.orgs.find((o) => o.id === state.orgId)?.name ?? null }),
1223
1338
  /* @__PURE__ */ jsx(NotificationsAction, { orgId: state.orgId }),
1224
- /* @__PURE__ */ jsx(SupportAction, { orgId: state.orgId })
1339
+ /* @__PURE__ */ jsx(SupportAction, { orgId: state.orgId, orgs: state.orgs.length ? state.orgs : void 0 })
1225
1340
  ] });
1226
1341
  }
1227
1342
  function ChromeDevNotesMenu() {