@the-portland-company/shell 0.15.0 → 0.16.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.
package/dist/chrome.css CHANGED
@@ -296,3 +296,56 @@
296
296
  --line: #333333;
297
297
  --shadow-soft: 0 4px 16px rgba(0, 0, 0, 0.5);
298
298
  }
299
+
300
+ /* ---------------------------------------------------------------------------
301
+ Top-bar widget slot — normalizes third-party launchers (e.g. DevNotes) to
302
+ the universal top-bar icon treatment (Global Rules: every icon the exact
303
+ same size, every badge the exact same style; no slot ships its own look).
304
+ Wrap the widget in `.topbar-widget-slot` (shell exports <TopBarWidgetSlot>).
305
+ --------------------------------------------------------------------------- */
306
+ .topbar-widget-slot > button,
307
+ .topbar-widget-slot > div > button:first-of-type,
308
+ .topbar-widget-slot > span > button:first-of-type {
309
+ width: var(--topbar-icon-button-size, 32px);
310
+ height: var(--topbar-icon-button-size, 32px);
311
+ display: inline-flex;
312
+ align-items: center;
313
+ justify-content: center;
314
+ border: 1px solid var(--line);
315
+ border-radius: 9999px;
316
+ background: transparent;
317
+ color: var(--topbar-icon-color, var(--muted));
318
+ opacity: 1;
319
+ position: relative;
320
+ transition: background-color 0.15s ease, color 0.15s ease;
321
+ }
322
+ .topbar-widget-slot > button:hover,
323
+ .topbar-widget-slot > div > button:first-of-type:hover,
324
+ .topbar-widget-slot > span > button:first-of-type:hover {
325
+ background: var(--topbar-icon-hover-bg, transparent);
326
+ color: var(--topbar-icon-hover-color, var(--foreground));
327
+ }
328
+ .topbar-widget-slot button svg {
329
+ width: var(--topbar-icon-size, 20px);
330
+ height: var(--topbar-icon-size, 20px);
331
+ opacity: 1;
332
+ }
333
+ /* Match <TopBarCountBadge>: same offset, size, fill and type as every count. */
334
+ .topbar-widget-slot button [class*="rounded-full"][class*="bg-"],
335
+ .topbar-widget-slot button .dn-badge {
336
+ position: absolute;
337
+ top: -4px;
338
+ right: -4px;
339
+ height: 1rem;
340
+ min-width: 1rem;
341
+ display: inline-flex;
342
+ align-items: center;
343
+ justify-content: center;
344
+ padding: 0 0.25rem;
345
+ border-radius: 9999px;
346
+ background: var(--color-race-red, #e63946);
347
+ color: #fff;
348
+ font-size: 10px;
349
+ font-weight: 400;
350
+ line-height: 1;
351
+ }
package/dist/native.cjs CHANGED
@@ -55,7 +55,8 @@ function resolveBrandGlyph(name) {
55
55
  function topBarIconButtonClass(tone = "default") {
56
56
  const toneClass = tone === "danger" || tone === "warning" ? "text-[color:var(--color-race-red)] hover:text-[color:var(--color-race-red)]" : "text-[color:var(--topbar-icon-color,var(--muted))] hover:text-[color:var(--topbar-icon-hover-color,var(--foreground))] hover:bg-[color:var(--topbar-icon-hover-bg,transparent)]";
57
57
  return [
58
- "relative flex items-center justify-center rounded-full border border-[color:var(--line)] transition",
58
+ // `group/tbtip` powers the shared <TopBarTooltip> rendered inside the trigger.
59
+ "group/tbtip relative flex items-center justify-center rounded-full border border-[color:var(--line)] transition",
59
60
  "h-[var(--topbar-icon-button-size,32px)] w-[var(--topbar-icon-button-size,32px)]",
60
61
  toneClass
61
62
  ].join(" ");
@@ -64,6 +65,75 @@ function TopBarCountBadge({ count }) {
64
65
  if (!(count > 0)) return null;
65
66
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-[color:var(--color-race-red)] px-1 text-[10px] text-white", children: count > 99 ? "99+" : count });
66
67
  }
68
+ function TopBarTooltip({
69
+ title,
70
+ description
71
+ }) {
72
+ return /* @__PURE__ */ jsxRuntime.jsxs(
73
+ "span",
74
+ {
75
+ role: "tooltip",
76
+ className: "pointer-events-none absolute left-1/2 top-full z-50 mt-2 hidden w-max max-w-56 -translate-x-1/2 flex-col rounded-lg border border-[color:var(--line)] bg-[color:var(--panel-elevated)] px-3 py-2 text-left shadow-[var(--shadow-soft)] group-hover/tbtip:flex",
77
+ children: [
78
+ /* @__PURE__ */ jsxRuntime.jsx(
79
+ "span",
80
+ {
81
+ "aria-hidden": true,
82
+ className: "absolute -top-[5px] left-1/2 h-2.5 w-2.5 -translate-x-1/2 rotate-45 border-l border-t border-[color:var(--line)] bg-[color:var(--panel-elevated)]"
83
+ }
84
+ ),
85
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-[color:var(--foreground)]", children: title }),
86
+ description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-0.5 text-[11px] leading-snug text-[color:var(--muted)]", children: description }) : null
87
+ ]
88
+ }
89
+ );
90
+ }
91
+ function TopBarWidgetSlot({
92
+ title,
93
+ description,
94
+ children
95
+ }) {
96
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "topbar-widget-slot group/tbtip relative inline-flex", children: [
97
+ children,
98
+ /* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title, description })
99
+ ] });
100
+ }
101
+ var THEME_OPTIONS = [
102
+ { value: "light", label: "Light" },
103
+ { value: "system", label: "System" },
104
+ { value: "dark", label: "Dark" }
105
+ ];
106
+ function NavThemeControl({
107
+ value,
108
+ onChange
109
+ }) {
110
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3", children: [
111
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Appearance" }),
112
+ /* @__PURE__ */ jsxRuntime.jsx(
113
+ "div",
114
+ {
115
+ role: "radiogroup",
116
+ "aria-label": "Appearance",
117
+ className: "flex overflow-hidden rounded-full border border-[color:var(--line)]",
118
+ children: THEME_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
119
+ "button",
120
+ {
121
+ type: "button",
122
+ role: "radio",
123
+ "aria-checked": value === opt.value,
124
+ onClick: () => onChange(opt.value),
125
+ className: [
126
+ "flex-1 px-3 py-1.5 text-xs font-semibold transition",
127
+ value === opt.value ? "bg-[color:var(--color-race-red)] text-white" : "text-[color:var(--muted)] hover:bg-[color:var(--panel)] hover:text-[color:var(--foreground)]"
128
+ ].join(" "),
129
+ children: opt.label
130
+ },
131
+ opt.value
132
+ ))
133
+ }
134
+ )
135
+ ] });
136
+ }
67
137
  var NavChromeContext = react.createContext(null);
68
138
  function NavChromeProvider({
69
139
  fetchJson,
@@ -128,6 +198,7 @@ function SlotGlyph({
128
198
  }
129
199
  function TopBarPopover({
130
200
  label,
201
+ description,
131
202
  slot,
132
203
  fallbackIcon,
133
204
  badge,
@@ -159,7 +230,6 @@ function TopBarPopover({
159
230
  {
160
231
  type: "button",
161
232
  "aria-label": label,
162
- title: label,
163
233
  "aria-expanded": open,
164
234
  className: topBarIconButtonClass(tone),
165
235
  onClick: () => {
@@ -171,7 +241,8 @@ function TopBarPopover({
171
241
  },
172
242
  children: [
173
243
  /* @__PURE__ */ jsxRuntime.jsx(SlotGlyph, { slot, fallback: fallbackIcon }),
174
- typeof badge === "number" ? /* @__PURE__ */ jsxRuntime.jsx(TopBarCountBadge, { count: badge }) : null
244
+ typeof badge === "number" ? /* @__PURE__ */ jsxRuntime.jsx(TopBarCountBadge, { count: badge }) : null,
245
+ !open ? /* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title: label, description }) : null
175
246
  ]
176
247
  }
177
248
  ),
@@ -285,6 +356,7 @@ function FuelAction({ orgId }) {
285
356
  TopBarPopover,
286
357
  {
287
358
  label: "Communications Fuel",
359
+ description: "Remaining SMS and email sending balance for the active organization.",
288
360
  slot: "comms-fuel",
289
361
  fallbackIcon: BatteryGlyph,
290
362
  tone: low ? "warning" : "default",
@@ -325,7 +397,8 @@ function NotificationsAction({ orgId }) {
325
397
  return /* @__PURE__ */ jsxRuntime.jsx(
326
398
  TopBarPopover,
327
399
  {
328
- label: "Notifications",
400
+ label: "Alerts & Notifications",
401
+ description: "System alerts and recent activity for the active organization.",
329
402
  slot: "notifications",
330
403
  fallbackIcon: BellGlyph,
331
404
  badge: loading ? 0 : count,
@@ -388,15 +461,23 @@ function SupportAction() {
388
461
  }
389
462
  };
390
463
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
391
- /* @__PURE__ */ jsxRuntime.jsx(
464
+ /* @__PURE__ */ jsxRuntime.jsxs(
392
465
  "button",
393
466
  {
394
467
  type: "button",
395
468
  "aria-label": "Support",
396
- title: "Support",
397
469
  className: topBarIconButtonClass(),
398
470
  onClick: () => setOpen(true),
399
- children: /* @__PURE__ */ jsxRuntime.jsx(SlotGlyph, { slot: "support", fallback: LifeBuoyGlyph })
471
+ children: [
472
+ /* @__PURE__ */ jsxRuntime.jsx(SlotGlyph, { slot: "support", fallback: LifeBuoyGlyph }),
473
+ /* @__PURE__ */ jsxRuntime.jsx(
474
+ TopBarTooltip,
475
+ {
476
+ title: "Support",
477
+ description: "Send a support request to the Politogy team."
478
+ }
479
+ )
480
+ ]
400
481
  }
401
482
  ),
402
483
  open ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -471,24 +552,33 @@ function NavDisplayAction({
471
552
  value,
472
553
  onChange
473
554
  }) {
474
- return /* @__PURE__ */ jsxRuntime.jsx(TopBarPopover, { label: "Navigation display", fallbackIcon: LayoutGlyph, panelClassName: "w-44", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", role: "radiogroup", "aria-label": "Navigation display", children: NAV_DISPLAY_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
475
- "button",
555
+ return /* @__PURE__ */ jsxRuntime.jsx(
556
+ TopBarPopover,
476
557
  {
477
- type: "button",
478
- role: "radio",
479
- "aria-checked": value === opt.value,
480
- onClick: () => onChange(opt.value),
481
- className: [
482
- "flex w-full items-center justify-between px-4 py-2 text-left text-sm font-medium",
483
- value === opt.value ? "text-[color:var(--color-race-red)]" : "text-[color:var(--foreground)] hover:bg-[color:var(--panel)]"
484
- ].join(" "),
485
- children: [
486
- opt.label,
487
- value === opt.value ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\u2713" }) : null
488
- ]
489
- },
490
- opt.value
491
- )) }) });
558
+ label: "Navigation Display",
559
+ description: "Show navigation as a sub-menu, tabs, or both.",
560
+ fallbackIcon: LayoutGlyph,
561
+ panelClassName: "w-44",
562
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", role: "radiogroup", "aria-label": "Navigation display", children: NAV_DISPLAY_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
563
+ "button",
564
+ {
565
+ type: "button",
566
+ role: "radio",
567
+ "aria-checked": value === opt.value,
568
+ onClick: () => onChange(opt.value),
569
+ className: [
570
+ "flex w-full items-center justify-between px-4 py-2 text-left text-sm font-medium",
571
+ value === opt.value ? "text-[color:var(--color-race-red)]" : "text-[color:var(--foreground)] hover:bg-[color:var(--panel)]"
572
+ ].join(" "),
573
+ children: [
574
+ opt.label,
575
+ value === opt.value ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\u2713" }) : null
576
+ ]
577
+ },
578
+ opt.value
579
+ )) })
580
+ }
581
+ );
492
582
  }
493
583
 
494
584
  // src/native/standardNav.ts
@@ -650,20 +740,20 @@ function NavSidebar({
650
740
  org.id ? /* @__PURE__ */ jsxRuntime.jsx(OrgIdCopyButton, { id: org.id }) : null
651
741
  ] }),
652
742
  org.organizations && org.organizations.length > 0 && org.onSwitch ? /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group relative", children: [
653
- /* @__PURE__ */ jsxRuntime.jsxs(
654
- "summary",
655
- {
656
- style: { backgroundColor: orgBg },
657
- className: "flex w-full cursor-pointer list-none items-center justify-between gap-2 rounded-md px-2.5 py-2 text-left text-white shadow-sm transition hover:opacity-95 [&::-webkit-details-marker]:hidden",
658
- children: [
659
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [
660
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-white/20 text-[11px] font-bold text-white", children: org.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: org.logoUrl, alt: "", className: "h-full w-full object-cover" }) : (org.name ?? "Org").slice(0, 2).toUpperCase() }),
661
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold", children: org.name ?? "No organizations" })
662
- ] }),
663
- /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
664
- ]
665
- }
666
- ),
743
+ /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex w-full cursor-pointer list-none items-center justify-between gap-2 rounded-md border border-[color:var(--line)] bg-[color:var(--panel-elevated)] px-2.5 py-2 text-left text-[color:var(--foreground)] shadow-sm transition hover:bg-[color:var(--panel)] [&::-webkit-details-marker]:hidden", children: [
744
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [
745
+ /* @__PURE__ */ jsxRuntime.jsx(
746
+ "span",
747
+ {
748
+ style: { backgroundColor: orgBg },
749
+ className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full text-[11px] font-bold text-white",
750
+ children: org.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: org.logoUrl, alt: "", className: "h-full w-full object-cover" }) : (org.name ?? "Org").slice(0, 2).toUpperCase()
751
+ }
752
+ ),
753
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold", children: org.name ?? "No organizations" })
754
+ ] }),
755
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
756
+ ] }),
667
757
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 right-0 z-40 mt-1 max-h-72 overflow-y-auto rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]", children: org.organizations.map((o) => {
668
758
  const selected = o.id === org.id;
669
759
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -699,11 +789,17 @@ function NavSidebar({
699
789
  {
700
790
  type: "button",
701
791
  onClick: org.onClick,
702
- style: { backgroundColor: orgBg },
703
- className: "flex w-full items-center justify-between gap-2 rounded-md px-2.5 py-2 text-left text-white shadow-sm transition hover:opacity-95",
792
+ className: "flex w-full items-center justify-between gap-2 rounded-md border border-[color:var(--line)] bg-[color:var(--panel-elevated)] px-2.5 py-2 text-left text-[color:var(--foreground)] shadow-sm transition hover:bg-[color:var(--panel)]",
704
793
  children: [
705
794
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [
706
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-white/20 text-[11px] font-bold text-white", children: org.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: org.logoUrl, alt: "", className: "h-full w-full object-cover" }) : (org.name ?? "Org").slice(0, 2).toUpperCase() }),
795
+ /* @__PURE__ */ jsxRuntime.jsx(
796
+ "span",
797
+ {
798
+ style: { backgroundColor: orgBg },
799
+ className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full text-[11px] font-bold text-white",
800
+ children: org.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: org.logoUrl, alt: "", className: "h-full w-full object-cover" }) : (org.name ?? "Org").slice(0, 2).toUpperCase()
801
+ }
802
+ ),
707
803
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold", children: org.name ?? "No organizations" })
708
804
  ] }),
709
805
  /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
@@ -954,6 +1050,7 @@ function NavTopBar({
954
1050
  title,
955
1051
  subtitle,
956
1052
  user,
1053
+ userMenu,
957
1054
  onSignOut,
958
1055
  actionsList,
959
1056
  actions,
@@ -977,31 +1074,176 @@ function NavTopBar({
977
1074
  actionsList && actionsList.length > 0 ? actionsList.map((action) => /* @__PURE__ */ jsxRuntime.jsx(NavTopBarActionButton, { action }, action.key)) : null,
978
1075
  actions,
979
1076
  themeToggle,
980
- /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group relative", children: [
981
- /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex cursor-pointer list-none items-center gap-2.5 rounded-full py-1 pl-1 pr-2 transition hover:bg-[color:var(--panel-elevated)] [&::-webkit-details-marker]:hidden", children: [
1077
+ /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group/acct relative", children: [
1078
+ /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "group/tbtip relative flex cursor-pointer list-none items-center gap-2.5 rounded-full py-1 pl-1 pr-2 transition hover:bg-[color:var(--panel-elevated)] [&::-webkit-details-marker]:hidden", children: [
982
1079
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-[color:var(--color-race-red)] text-xs font-bold text-white", children: user?.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" }) : initials }),
983
1080
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "hidden flex-col leading-tight md:flex", children: [
984
1081
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: user?.name ?? "Signed in" }),
985
1082
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-[color:var(--muted)]", children: secondary })
986
1083
  ] }),
987
- /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
1084
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {}),
1085
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "group-open/acct:hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
1086
+ TopBarTooltip,
1087
+ {
1088
+ title: "My Account",
1089
+ description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1090
+ user?.name ?? "Signed in",
1091
+ user?.email ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1092
+ /* @__PURE__ */ jsxRuntime.jsx("br", {}),
1093
+ user.email
1094
+ ] }) : null,
1095
+ /* @__PURE__ */ jsxRuntime.jsx("br", {}),
1096
+ "Profile, organizations, appearance and sign out."
1097
+ ] })
1098
+ }
1099
+ ) })
988
1100
  ] }),
989
- onSignOut ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 z-30 mt-2 w-44 overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]", children: /* @__PURE__ */ jsxRuntime.jsxs(
990
- "button",
991
- {
992
- type: "button",
993
- onClick: onSignOut,
994
- className: "flex w-full items-center gap-2.5 px-4 py-2 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)] hover:text-[color:var(--color-race-red)]",
995
- children: [
996
- /* @__PURE__ */ jsxRuntime.jsx(SignOutIcon, {}),
997
- "Sign out"
998
- ]
999
- }
1000
- ) }) : null
1101
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 z-30 mt-2 w-72 overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]", children: [
1102
+ userMenu?.myProfileHref ? /* @__PURE__ */ jsxRuntime.jsxs(
1103
+ "a",
1104
+ {
1105
+ href: safeHref(userMenu.myProfileHref),
1106
+ className: "flex w-full items-center gap-2.5 px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
1107
+ children: [
1108
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileIcon, {}),
1109
+ "My Profile"
1110
+ ]
1111
+ }
1112
+ ) : null,
1113
+ userMenu?.organizationSettingsHref ? /* @__PURE__ */ jsxRuntime.jsxs(
1114
+ "a",
1115
+ {
1116
+ href: safeHref(userMenu.organizationSettingsHref),
1117
+ className: "flex w-full items-center gap-2.5 border-t border-[color:var(--line)] px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
1118
+ children: [
1119
+ /* @__PURE__ */ jsxRuntime.jsx(BriefcaseIcon, {}),
1120
+ "Organization Settings"
1121
+ ]
1122
+ }
1123
+ ) : null,
1124
+ userMenu?.roles && userMenu.roles.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-[color:var(--line)] px-4 py-3", children: [
1125
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Roles & Organizations" }),
1126
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1.5", children: userMenu.roles.map((r, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex flex-wrap items-center gap-1.5 text-sm", children: [
1127
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-md bg-[color:var(--panel)] px-2 py-0.5 text-xs font-bold uppercase tracking-wide text-[color:var(--foreground)]", children: r.role }),
1128
+ r.orgName ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[color:var(--muted)]", children: [
1129
+ "in ",
1130
+ r.orgName
1131
+ ] }) : null
1132
+ ] }, `${r.role}-${i}`)) })
1133
+ ] }) : null,
1134
+ userMenu?.accounts && userMenu.accounts.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-[color:var(--line)] px-4 py-3", children: [
1135
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Accounts" }),
1136
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1", children: userMenu.accounts.map((a) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-2.5", children: [
1137
+ /* @__PURE__ */ jsxRuntime.jsxs(
1138
+ "button",
1139
+ {
1140
+ type: "button",
1141
+ onClick: () => {
1142
+ if (!a.active) userMenu.onSwitchAccount?.(a.id);
1143
+ },
1144
+ className: "flex min-w-0 flex-1 items-center gap-2.5 rounded-lg px-1 py-1 text-left hover:bg-[color:var(--panel)]",
1145
+ children: [
1146
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center overflow-hidden rounded-full bg-[color:var(--color-race-red)] text-[11px] font-bold text-white", children: a.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: a.avatarUrl, alt: "", className: "h-full w-full object-cover" }) : (a.name ?? a.email ?? "?").slice(0, 2).toUpperCase() }),
1147
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col leading-tight", children: [
1148
+ /* @__PURE__ */ jsxRuntime.jsx(
1149
+ "span",
1150
+ {
1151
+ className: [
1152
+ "truncate text-sm font-semibold",
1153
+ a.active ? "text-[color:var(--color-race-red)]" : "text-[color:var(--foreground)]"
1154
+ ].join(" "),
1155
+ children: a.name ?? a.email ?? a.id.slice(0, 8)
1156
+ }
1157
+ ),
1158
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-[color:var(--muted)]", children: a.active ? "Active" : a.email ?? "" })
1159
+ ] })
1160
+ ]
1161
+ }
1162
+ ),
1163
+ !a.active && userMenu.onRemoveAccount ? /* @__PURE__ */ jsxRuntime.jsx(
1164
+ "button",
1165
+ {
1166
+ type: "button",
1167
+ "aria-label": `Remove ${a.name ?? a.email ?? "account"}`,
1168
+ onClick: () => userMenu.onRemoveAccount?.(a.id),
1169
+ className: "shrink-0 rounded p-1 text-[color:var(--color-race-red)] hover:bg-[color:var(--panel)]",
1170
+ children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {})
1171
+ }
1172
+ ) : null
1173
+ ] }, a.id)) }),
1174
+ userMenu.addAccountHref || userMenu.onAddAccount ? userMenu.addAccountHref ? /* @__PURE__ */ jsxRuntime.jsxs(
1175
+ "a",
1176
+ {
1177
+ href: safeHref(userMenu.addAccountHref),
1178
+ className: "mt-1.5 flex items-center gap-2.5 rounded-lg px-1 py-1.5 text-sm font-semibold text-[color:var(--color-race-red)] hover:bg-[color:var(--panel)]",
1179
+ children: [
1180
+ /* @__PURE__ */ jsxRuntime.jsx(AddUserIcon, {}),
1181
+ "Add Account"
1182
+ ]
1183
+ }
1184
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
1185
+ "button",
1186
+ {
1187
+ type: "button",
1188
+ onClick: userMenu.onAddAccount,
1189
+ className: "mt-1.5 flex w-full items-center gap-2.5 rounded-lg px-1 py-1.5 text-left text-sm font-semibold text-[color:var(--color-race-red)] hover:bg-[color:var(--panel)]",
1190
+ children: [
1191
+ /* @__PURE__ */ jsxRuntime.jsx(AddUserIcon, {}),
1192
+ "Add Account"
1193
+ ]
1194
+ }
1195
+ ) : null
1196
+ ] }) : null,
1197
+ userMenu?.theme ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-[color:var(--line)]", children: /* @__PURE__ */ jsxRuntime.jsx(
1198
+ NavThemeControl,
1199
+ {
1200
+ value: userMenu.theme.value,
1201
+ onChange: userMenu.theme.onChange
1202
+ }
1203
+ ) }) : null,
1204
+ onSignOut ? /* @__PURE__ */ jsxRuntime.jsxs(
1205
+ "button",
1206
+ {
1207
+ type: "button",
1208
+ onClick: onSignOut,
1209
+ className: "flex w-full items-center gap-2.5 border-t border-[color:var(--line)] px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)] hover:text-[color:var(--color-race-red)]",
1210
+ children: [
1211
+ /* @__PURE__ */ jsxRuntime.jsx(SignOutIcon, {}),
1212
+ "Sign out"
1213
+ ]
1214
+ }
1215
+ ) : null
1216
+ ] })
1001
1217
  ] })
1002
1218
  ] })
1003
1219
  ] });
1004
1220
  }
1221
+ function ProfileIcon() {
1222
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1223
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }),
1224
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "7", r: "4" })
1225
+ ] });
1226
+ }
1227
+ function BriefcaseIcon() {
1228
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1229
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "7", width: "20", height: "14", rx: "2", ry: "2" }),
1230
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" })
1231
+ ] });
1232
+ }
1233
+ function TrashIcon() {
1234
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1235
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "3 6 5 6 21 6" }),
1236
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
1237
+ ] });
1238
+ }
1239
+ function AddUserIcon() {
1240
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1241
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" }),
1242
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8.5", cy: "7", r: "4" }),
1243
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "20", y1: "8", x2: "20", y2: "14" }),
1244
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "23", y1: "11", x2: "17", y2: "11" })
1245
+ ] });
1246
+ }
1005
1247
  function SignOutIcon() {
1006
1248
  return /* @__PURE__ */ jsxRuntime.jsxs(
1007
1249
  "svg",
@@ -1031,7 +1273,8 @@ function NavTopBarActionButton({ action }) {
1031
1273
  const Glyph = BrandGlyph ?? Icon;
1032
1274
  const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1033
1275
  /* @__PURE__ */ jsxRuntime.jsx(Glyph, { size: 20, className: "text-current" }),
1034
- typeof action.badge === "number" && action.badge > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-[color:var(--color-race-red)] px-1 text-[10px] text-white", children: action.badge }) : null
1276
+ typeof action.badge === "number" && action.badge > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-[color:var(--color-race-red)] px-1 text-[10px] text-white", children: action.badge }) : null,
1277
+ /* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title: action.label, description: action.description })
1035
1278
  ] });
1036
1279
  const isExternal = action.href ? /^https?:\/\//.test(action.href) : false;
1037
1280
  if (action.href) {
@@ -1040,7 +1283,6 @@ function NavTopBarActionButton({ action }) {
1040
1283
  {
1041
1284
  href: safeHref(action.href),
1042
1285
  "aria-label": action.label,
1043
- title: action.label,
1044
1286
  className,
1045
1287
  ...isExternal ? { target: "_blank", rel: "noreferrer" } : {},
1046
1288
  children: content
@@ -1053,7 +1295,6 @@ function NavTopBarActionButton({ action }) {
1053
1295
  type: "button",
1054
1296
  onClick: action.onClick,
1055
1297
  "aria-label": action.label,
1056
- title: action.label,
1057
1298
  className,
1058
1299
  children: content
1059
1300
  }
@@ -1173,10 +1414,13 @@ exports.NavChromeProvider = NavChromeProvider;
1173
1414
  exports.NavDisplayAction = NavDisplayAction;
1174
1415
  exports.NavSidebar = NavSidebar;
1175
1416
  exports.NavTabStrip = NavTabStrip;
1417
+ exports.NavThemeControl = NavThemeControl;
1176
1418
  exports.NavTopBar = NavTopBar;
1177
1419
  exports.NotificationsAction = NotificationsAction;
1178
1420
  exports.SupportAction = SupportAction;
1179
1421
  exports.TopBarCountBadge = TopBarCountBadge;
1422
+ exports.TopBarTooltip = TopBarTooltip;
1423
+ exports.TopBarWidgetSlot = TopBarWidgetSlot;
1180
1424
  exports.envChipClasses = envChipClasses;
1181
1425
  exports.isNavItemActive = isNavItemActive;
1182
1426
  exports.standardPolitogyNav = standardPolitogyNav;