@flytedan/flytebot-design-system 0.14.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/index.cjs CHANGED
@@ -33,6 +33,8 @@ __export(index_exports, {
33
33
  AccountMenu: () => AccountMenu,
34
34
  AgentChatPanel: () => AgentChatPanel,
35
35
  ApiSpecBrowser: () => ApiSpecBrowser,
36
+ AppSwitcher: () => AppSwitcher,
37
+ AppearanceMenu: () => AppearanceMenu,
36
38
  Avatar: () => Avatar,
37
39
  AvatarStack: () => AvatarStack,
38
40
  Badge: () => Badge,
@@ -63,8 +65,11 @@ __export(index_exports, {
63
65
  CsiBadge: () => CsiBadge,
64
66
  CsiHero: () => CsiHero,
65
67
  CsiMeter: () => CsiMeter,
68
+ DEFAULT_PREFS: () => DEFAULT_PREFS,
69
+ DEFAULT_STORAGE_KEY: () => DEFAULT_STORAGE_KEY,
66
70
  DataTable: () => DataTable,
67
71
  DatePicker: () => DatePicker,
72
+ DemoSessionProvider: () => DemoSessionProvider,
68
73
  DiffBlock: () => DiffBlock,
69
74
  Drawer: () => Drawer,
70
75
  Dropzone: () => Dropzone,
@@ -134,6 +139,7 @@ __export(index_exports, {
134
139
  SegmentedMeter: () => SegmentedMeter,
135
140
  Select: () => Select,
136
141
  SessionKit: () => SessionKit,
142
+ SessionProvider: () => SessionProvider,
137
143
  SidebarNav: () => SidebarNav,
138
144
  Skeleton: () => Skeleton,
139
145
  SkeletonText: () => SkeletonText,
@@ -162,9 +168,11 @@ __export(index_exports, {
162
168
  VirtualList: () => VirtualList,
163
169
  acceptMatches: () => acceptMatches,
164
170
  anyOfFilter: () => anyOfFilter,
171
+ applyPrefs: () => applyPrefs,
165
172
  channelWeightOf: () => channelWeightOf,
166
173
  computeNeedMore: () => computeNeedMore,
167
174
  computeVirtualWindow: () => computeVirtualWindow,
175
+ createLocalStorageAppearanceStore: () => createLocalStorageAppearanceStore,
168
176
  createVersionStore: () => createVersionStore,
169
177
  eqFilter: () => eqFilter,
170
178
  extensionOf: () => extensionOf,
@@ -185,6 +193,7 @@ __export(index_exports, {
185
193
  isSystemMessage: () => isSystemMessage,
186
194
  jobBucket: () => jobBucket,
187
195
  languageLabel: () => languageLabel,
196
+ loadPrefs: () => loadPrefs,
188
197
  markdownToText: () => markdownToText,
189
198
  meterFormats: () => meterFormats,
190
199
  modifierLabel: () => modifierLabel,
@@ -194,14 +203,18 @@ __export(index_exports, {
194
203
  preferenceScore: () => preferenceScore,
195
204
  preferenceSegments: () => preferenceSegments,
196
205
  rangeFilter: () => rangeFilter,
206
+ resolveTheme: () => resolveTheme,
197
207
  revokeAttachment: () => revokeAttachment,
208
+ savePrefs: () => savePrefs,
198
209
  toAttachment: () => toAttachment,
199
210
  tokenize: () => tokenize,
211
+ useAppearancePrefs: () => useAppearancePrefs,
200
212
  useChatEngine: () => useChatEngine,
201
213
  useFeatureStatus: () => useFeatureStatus,
202
214
  usePopoverPosition: () => usePopoverPosition,
203
215
  useRuntimeMode: () => useRuntimeMode,
204
216
  useServerTable: () => useServerTable,
217
+ useSessionContext: () => useSessionContext,
205
218
  useStagedFiles: () => useStagedFiles,
206
219
  visibleMessages: () => visibleMessages
207
220
  });
@@ -5346,8 +5359,835 @@ var MarkdownEditor = React29.forwardRef(function MarkdownEditor2({
5346
5359
  // src/components/platform/AccountMenu.tsx
5347
5360
  var React31 = __toESM(require("react"), 1);
5348
5361
 
5349
- // src/kits/session.ts
5362
+ // src/kits/sessionContext.tsx
5350
5363
  var React30 = __toESM(require("react"), 1);
5364
+ var SessionContext = React30.createContext(null);
5365
+ function SessionProvider({ value, children }) {
5366
+ return React30.createElement(SessionContext.Provider, { value }, children);
5367
+ }
5368
+ function useSessionContext() {
5369
+ const ctx = React30.useContext(SessionContext);
5370
+ if (!ctx) {
5371
+ throw new Error(
5372
+ "useSessionContext() was called outside a <SessionProvider>. Wrap your app root in <SessionProvider value={...}> with your real session, or \u2014 in the docs site or a demo \u2014 in <DemoSessionProvider>, before rendering AccountMenu, Gate, ProfilePage or AppSwitcher."
5373
+ );
5374
+ }
5375
+ return ctx;
5376
+ }
5377
+
5378
+ // src/components/platform/AccountMenu.tsx
5379
+ var import_jsx_runtime58 = require("react/jsx-runtime");
5380
+ var DEFAULT_LINKS = [
5381
+ { id: "profile", label: "Your profile", icon: "user-circle", href: "../admin/index.html#profile" },
5382
+ { id: "preferences", label: "Preferences", icon: "sliders-horizontal", href: "../admin/index.html#preferences" }
5383
+ ];
5384
+ var DEFAULT_GROUPS = [
5385
+ {
5386
+ label: "Administration",
5387
+ links: [
5388
+ { id: "users", label: "Users", icon: "users-three", href: "../admin/index.html#users", perm: "users.view" },
5389
+ { id: "roles", label: "Roles & permissions", icon: "shield-check", href: "../admin/index.html#roles", perm: "roles.manage" },
5390
+ { id: "flags", label: "Feature flags", icon: "toggle-right", href: "../admin/index.html#flags", perm: "flags.manage" }
5391
+ ]
5392
+ }
5393
+ ];
5394
+ function Item({ item, onPick }) {
5395
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
5396
+ "button",
5397
+ {
5398
+ type: "button",
5399
+ role: "menuitem",
5400
+ className: "fd-acct-item",
5401
+ onClick: () => onPick(item),
5402
+ children: [
5403
+ item.icon ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-" + item.icon, "aria-hidden": "true" }) : null,
5404
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: item.label }),
5405
+ item.badge ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Badge, { tone: "neutral", children: item.badge }) : null
5406
+ ]
5407
+ }
5408
+ );
5409
+ }
5410
+ function AccountMenu({
5411
+ links = DEFAULT_LINKS,
5412
+ groups = DEFAULT_GROUPS,
5413
+ onNavigate,
5414
+ onSignOut,
5415
+ showRoles = true,
5416
+ allowUserSwitch = true,
5417
+ className = "",
5418
+ ...rest
5419
+ }) {
5420
+ const { user, roles, can: can2, signOut: ctxSignOut, demo } = useSessionContext();
5421
+ const [open, setOpen] = React31.useState(false);
5422
+ const [switching, setSwitching] = React31.useState(false);
5423
+ const [signingOut, setSigningOut] = React31.useState(false);
5424
+ const ref = React31.useRef(null);
5425
+ const visibleLinks = links.filter((l) => !l.perm || can2(l.perm));
5426
+ const visibleGroups = groups.map((g) => ({ label: g.label, links: g.links.filter((l) => !l.perm || can2(l.perm)) })).filter((g) => g.links.length);
5427
+ const pick = (item) => {
5428
+ setOpen(false);
5429
+ if (item.onSelect) return item.onSelect();
5430
+ if (onNavigate) return onNavigate(item.href, item);
5431
+ if (item.href) window.location.href = item.href;
5432
+ };
5433
+ const signOut = async () => {
5434
+ const handler = onSignOut ?? ctxSignOut;
5435
+ setSigningOut(true);
5436
+ try {
5437
+ await handler();
5438
+ } catch (err) {
5439
+ console.error("AccountMenu: sign-out handler failed", err);
5440
+ } finally {
5441
+ setSigningOut(false);
5442
+ setOpen(false);
5443
+ }
5444
+ };
5445
+ const showSwitcher = allowUserSwitch && !!demo;
5446
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_jsx_runtime58.Fragment, { children: [
5447
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
5448
+ "button",
5449
+ {
5450
+ type: "button",
5451
+ ref,
5452
+ className: ["fd-acct-trigger", className].filter(Boolean).join(" "),
5453
+ "aria-haspopup": "menu",
5454
+ "aria-expanded": open,
5455
+ "aria-label": "Account \u2014 " + user.name,
5456
+ onClick: () => setOpen((o) => !o),
5457
+ ...rest,
5458
+ children: [
5459
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: user.name, size: "sm" }),
5460
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true", style: { fontSize: 11, color: "var(--text-muted)" } })
5461
+ ]
5462
+ }
5463
+ ),
5464
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Popover, { open, anchorRef: ref, onClose: () => setOpen(false), placement: "bottom-end", width: 272, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { role: "menu", className: "fd-stack", style: { gap: 0 }, children: [
5465
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
5466
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: user.name }),
5467
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "fd-stack", style: { gap: 1, minWidth: 0, flex: 1 }, children: [
5468
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: user.name }),
5469
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-body-sm fd-muted fd-table-trunc", title: truncationTitle(user.email), children: user.email })
5470
+ ] })
5471
+ ] }),
5472
+ showRoles && roles.length ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-row", style: { gap: 6, padding: "10px 14px", flexWrap: "wrap", borderBottom: "1px solid var(--border)" }, children: roles.map((r) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)) }) : null,
5473
+ visibleLinks.length ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-acct-group", children: visibleLinks.map((l) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Item, { item: l, onPick: pick }, l.id)) }) : null,
5474
+ visibleGroups.map((g) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
5475
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-overline fd-muted", style: { padding: "8px 14px 4px", display: "block" }, children: g.label }),
5476
+ g.links.map((l) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Item, { item: l, onPick: pick }, l.id))
5477
+ ] }, g.label)),
5478
+ showSwitcher && demo ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
5479
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("button", { type: "button", role: "menuitem", className: "fd-acct-item", onClick: () => setSwitching((s) => !s), children: [
5480
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-user-switch", "aria-hidden": "true" }),
5481
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: "View as another member" }),
5482
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-caret-" + (switching ? "up" : "down"), "aria-hidden": "true", style: { fontSize: 11 } })
5483
+ ] }),
5484
+ switching ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-stack", style: { gap: 0 }, children: demo.allUsers.map((u) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
5485
+ "button",
5486
+ {
5487
+ type: "button",
5488
+ className: "fd-acct-item",
5489
+ "data-current": u.id === user.id ? "true" : "false",
5490
+ onClick: () => {
5491
+ demo.setActiveUser(u.id);
5492
+ setOpen(false);
5493
+ setSwitching(false);
5494
+ },
5495
+ children: [
5496
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: u.name, size: "sm" }),
5497
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "fd-stack", style: { gap: 0, flex: 1, minWidth: 0, alignItems: "flex-start" }, children: [
5498
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontWeight: u.id === user.id ? 700 : 500 }, children: u.name }),
5499
+ u.roles ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-muted", style: { fontSize: 11.5 }, children: u.roles.join(", ") }) : null
5500
+ ] }),
5501
+ u.id === user.id ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-check", "aria-hidden": "true", style: { color: "var(--ok-text)" } }) : null
5502
+ ]
5503
+ },
5504
+ u.id
5505
+ )) }) : null
5506
+ ] }) : null,
5507
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
5508
+ "button",
5509
+ {
5510
+ type: "button",
5511
+ role: "menuitem",
5512
+ className: "fd-acct-item",
5513
+ "data-danger": "true",
5514
+ disabled: signingOut,
5515
+ onClick: signOut,
5516
+ children: [
5517
+ signingOut ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Spinner, { size: "sm" }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-sign-out", "aria-hidden": "true" }),
5518
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: signingOut ? "Signing out\u2026" : "Sign out" })
5519
+ ]
5520
+ }
5521
+ ) })
5522
+ ] }) })
5523
+ ] });
5524
+ }
5525
+
5526
+ // src/components/platform/AppSwitcher.tsx
5527
+ var React32 = __toESM(require("react"), 1);
5528
+ var import_jsx_runtime59 = require("react/jsx-runtime");
5529
+ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
5530
+ const { apps: contextApps } = useSessionContext();
5531
+ const apps = appsProp ?? contextApps ?? [];
5532
+ const [open, setOpen] = React32.useState(false);
5533
+ const anchorRef = React32.useRef(null);
5534
+ if (!apps.length) return null;
5535
+ const items = apps.map((app) => ({
5536
+ id: app.key,
5537
+ label: app.name,
5538
+ icon: app.icon,
5539
+ meta: app.current ? "Current" : void 0,
5540
+ disabled: app.current,
5541
+ onSelect: () => {
5542
+ if (app.current) return;
5543
+ if (onNavigate) return onNavigate(app);
5544
+ window.location.href = app.url;
5545
+ }
5546
+ }));
5547
+ return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(React32.Fragment, { children: [
5548
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { ref: anchorRef, className: ["fd-appswitch-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
5549
+ IconButton,
5550
+ {
5551
+ icon: "squares-four",
5552
+ label: "All flytedesk apps",
5553
+ "aria-haspopup": "menu",
5554
+ "aria-expanded": open,
5555
+ onClick: () => setOpen((v) => !v)
5556
+ }
5557
+ ) }),
5558
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
5559
+ Popover,
5560
+ {
5561
+ open,
5562
+ anchorRef,
5563
+ onClose: () => setOpen(false),
5564
+ placement: "bottom-end",
5565
+ role: "menu",
5566
+ label: "All flytedesk apps",
5567
+ width: 240,
5568
+ maxHeight: 360,
5569
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Menu, { items, onClose: () => setOpen(false) })
5570
+ }
5571
+ )
5572
+ ] });
5573
+ }
5574
+
5575
+ // src/components/platform/AppearanceMenu.tsx
5576
+ var React34 = __toESM(require("react"), 1);
5577
+
5578
+ // src/kits/appearance.ts
5579
+ var React33 = __toESM(require("react"), 1);
5580
+ var DEFAULT_STORAGE_KEY = "fd-appearance";
5581
+ var DEFAULT_PREFS = {
5582
+ theme: "system",
5583
+ textScale: "md",
5584
+ highContrast: false,
5585
+ reduceMotion: false
5586
+ };
5587
+ function resolveDefaults(opts) {
5588
+ return { ...DEFAULT_PREFS, ...opts?.defaults };
5589
+ }
5590
+ function resolveStorage(opts) {
5591
+ if (opts?.storage) return opts.storage;
5592
+ return typeof window !== "undefined" ? window.localStorage : void 0;
5593
+ }
5594
+ function resolveTheme(mode2) {
5595
+ if (mode2 !== "system") return mode2;
5596
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
5597
+ }
5598
+ function loadPrefs(opts) {
5599
+ const defaults = resolveDefaults(opts);
5600
+ const storage = resolveStorage(opts);
5601
+ const key = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
5602
+ if (!storage) return defaults;
5603
+ let stored;
5604
+ try {
5605
+ const raw = storage.getItem(key);
5606
+ stored = raw ? JSON.parse(raw) : null;
5607
+ } catch {
5608
+ stored = null;
5609
+ }
5610
+ if (typeof stored !== "object" || stored === null) return defaults;
5611
+ const s = stored;
5612
+ return {
5613
+ theme: s.theme === "light" || s.theme === "dark" || s.theme === "system" ? s.theme : defaults.theme,
5614
+ textScale: s.textScale === "sm" || s.textScale === "md" || s.textScale === "lg" || s.textScale === "xl" ? s.textScale : defaults.textScale,
5615
+ highContrast: typeof s.highContrast === "boolean" ? s.highContrast : defaults.highContrast,
5616
+ reduceMotion: typeof s.reduceMotion === "boolean" ? s.reduceMotion : defaults.reduceMotion
5617
+ };
5618
+ }
5619
+ function savePrefs(prefs, opts) {
5620
+ const storage = resolveStorage(opts);
5621
+ if (!storage) return;
5622
+ const key = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
5623
+ try {
5624
+ storage.setItem(key, JSON.stringify(prefs));
5625
+ } catch {
5626
+ }
5627
+ }
5628
+ function createLocalStorageAppearanceStore(opts) {
5629
+ const key = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
5630
+ const storage = resolveStorage(opts);
5631
+ let snapshot = loadPrefs(opts);
5632
+ const listeners3 = /* @__PURE__ */ new Set();
5633
+ const notify = () => listeners3.forEach((l) => l());
5634
+ const onStorageEvent = (e) => {
5635
+ if (e.key !== null && e.key !== key) return;
5636
+ snapshot = loadPrefs(opts);
5637
+ notify();
5638
+ };
5639
+ return {
5640
+ getSnapshot: () => snapshot,
5641
+ write(prefs) {
5642
+ snapshot = prefs;
5643
+ if (storage) {
5644
+ try {
5645
+ storage.setItem(key, JSON.stringify(prefs));
5646
+ } catch {
5647
+ }
5648
+ }
5649
+ notify();
5650
+ },
5651
+ subscribe(onChange) {
5652
+ listeners3.add(onChange);
5653
+ if (listeners3.size === 1 && typeof window !== "undefined") {
5654
+ window.addEventListener("storage", onStorageEvent);
5655
+ }
5656
+ return () => {
5657
+ listeners3.delete(onChange);
5658
+ if (listeners3.size === 0 && typeof window !== "undefined") {
5659
+ window.removeEventListener("storage", onStorageEvent);
5660
+ }
5661
+ };
5662
+ }
5663
+ };
5664
+ }
5665
+ function applyPrefs(prefs, root = document.documentElement) {
5666
+ root.setAttribute("data-theme", resolveTheme(prefs.theme));
5667
+ root.setAttribute("data-text-scale", prefs.textScale);
5668
+ if (prefs.highContrast) root.setAttribute("data-contrast", "high");
5669
+ else root.removeAttribute("data-contrast");
5670
+ if (prefs.reduceMotion) root.setAttribute("data-motion", "reduce");
5671
+ else root.removeAttribute("data-motion");
5672
+ }
5673
+ function useAppearancePrefs(store) {
5674
+ const prefs = React33.useSyncExternalStore(store.subscribe, store.getSnapshot);
5675
+ React33.useEffect(() => {
5676
+ applyPrefs(prefs);
5677
+ }, [prefs]);
5678
+ React33.useEffect(() => {
5679
+ if (prefs.theme !== "system" || typeof window === "undefined" || typeof window.matchMedia !== "function") {
5680
+ return;
5681
+ }
5682
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
5683
+ const onChange = () => applyPrefs(prefs);
5684
+ mq.addEventListener("change", onChange);
5685
+ return () => mq.removeEventListener("change", onChange);
5686
+ }, [prefs]);
5687
+ const update = React33.useCallback(
5688
+ (patch) => {
5689
+ store.write({ ...store.getSnapshot(), ...patch });
5690
+ },
5691
+ [store]
5692
+ );
5693
+ return [prefs, update];
5694
+ }
5695
+
5696
+ // src/components/platform/AppearanceMenu.tsx
5697
+ var import_jsx_runtime60 = require("react/jsx-runtime");
5698
+ var THEME_OPTIONS = [
5699
+ { value: "light", label: "Light" },
5700
+ { value: "dark", label: "Dark" },
5701
+ { value: "system", label: "System" }
5702
+ ];
5703
+ var TEXT_SCALE_OPTIONS = [
5704
+ { value: "sm", label: "S" },
5705
+ { value: "md", label: "M" },
5706
+ { value: "lg", label: "L" },
5707
+ { value: "xl", label: "XL" }
5708
+ ];
5709
+ function AppearanceMenu({ className = "", store: storeProp, storageKey, storage, defaults }) {
5710
+ const store = React34.useMemo(
5711
+ () => storeProp ?? createLocalStorageAppearanceStore({ storageKey, storage, defaults }),
5712
+ [storeProp, storageKey, storage, defaults]
5713
+ );
5714
+ const [prefs, update] = useAppearancePrefs(store);
5715
+ const [open, setOpen] = React34.useState(false);
5716
+ const anchorRef = React34.useRef(null);
5717
+ const patch = (p) => update(p);
5718
+ return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(React34.Fragment, { children: [
5719
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { ref: anchorRef, className: ["fd-appearance-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
5720
+ IconButton,
5721
+ {
5722
+ icon: "circle-half",
5723
+ label: "Appearance settings",
5724
+ onClick: () => setOpen((v) => !v)
5725
+ }
5726
+ ) }),
5727
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
5728
+ Popover,
5729
+ {
5730
+ open,
5731
+ anchorRef,
5732
+ onClose: () => setOpen(false),
5733
+ placement: "bottom-end",
5734
+ padded: true,
5735
+ width: 260,
5736
+ label: "Appearance settings",
5737
+ children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-stack", style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
5738
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
5739
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-field-label", children: "Theme" }),
5740
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
5741
+ SegmentedControl,
5742
+ {
5743
+ options: THEME_OPTIONS,
5744
+ value: prefs.theme,
5745
+ onChange: (v) => patch({ theme: v }),
5746
+ "aria-label": "Theme"
5747
+ }
5748
+ )
5749
+ ] }),
5750
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
5751
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-field-label", children: "Text size" }),
5752
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
5753
+ SegmentedControl,
5754
+ {
5755
+ options: TEXT_SCALE_OPTIONS,
5756
+ value: prefs.textScale,
5757
+ onChange: (v) => patch({ textScale: v }),
5758
+ "aria-label": "Text size"
5759
+ }
5760
+ )
5761
+ ] }),
5762
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
5763
+ Switch,
5764
+ {
5765
+ label: "High contrast",
5766
+ description: "Stronger borders and text color",
5767
+ checked: prefs.highContrast,
5768
+ onChange: (e) => patch({ highContrast: e.target.checked })
5769
+ }
5770
+ ),
5771
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
5772
+ Switch,
5773
+ {
5774
+ label: "Reduce motion",
5775
+ description: "Minimize animations and transitions",
5776
+ checked: prefs.reduceMotion,
5777
+ onChange: (e) => patch({ reduceMotion: e.target.checked })
5778
+ }
5779
+ )
5780
+ ] })
5781
+ }
5782
+ )
5783
+ ] });
5784
+ }
5785
+
5786
+ // src/components/platform/ApiSpecBrowser.tsx
5787
+ var React35 = __toESM(require("react"), 1);
5788
+ var import_jsx_runtime61 = require("react/jsx-runtime");
5789
+ var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
5790
+ function Json({ obj }) {
5791
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
5792
+ }
5793
+ function Endpoint({ s, onRequest }) {
5794
+ const [tried, setTried] = React35.useState(null);
5795
+ const run = async () => {
5796
+ setTried("busy");
5797
+ const t0 = (window.performance || Date).now();
5798
+ try {
5799
+ const data = await onRequest(s.id, s.request || (s.isList ? { page: 1, pageSize: 5 } : {}));
5800
+ setTried({ ms: Math.round((window.performance || Date).now() - t0), data });
5801
+ } catch (e) {
5802
+ setTried({ ms: Math.round((window.performance || Date).now() - t0), error: String(e && e.message || e) });
5803
+ }
5804
+ };
5805
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Card, { elevation: "flat", padded: false, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 0 }, children: [
5806
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "14px 18px", flexWrap: "wrap" }, children: [
5807
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Badge, { tone: METHOD_TONE[s.method] || "neutral", children: s.method }),
5808
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("code", { className: "fd-mono", style: { fontSize: 12.5, fontWeight: 600, color: "var(--text)", wordBreak: "break-all" }, children: s.path }),
5809
+ s.isList ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Badge, { tone: "neutral", icon: "rows", children: "Paged list" }) : null,
5810
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { style: { flex: 1 } }),
5811
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-body-sm fd-muted fd-mono", children: [
5812
+ s.latency[0],
5813
+ "\u2013",
5814
+ s.latency[1],
5815
+ "ms"
5816
+ ] }),
5817
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Button, { size: "sm", variant: "secondary", icon: "play", loading: tried === "busy", onClick: run, children: "Try it" })
5818
+ ] }),
5819
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 14, padding: "0 18px 16px" }, children: [
5820
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
5821
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: s.title }),
5822
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: s.purpose }),
5823
+ s.usedBy && s.usedBy.length ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: s.usedBy.map((u) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Tag, { children: u }, u)) }) : null
5824
+ ] }),
5825
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(280px,1fr))", gap: 12, alignItems: "start" }, children: [
5826
+ s.request ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
5827
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Request body" }),
5828
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Json, { obj: s.request })
5829
+ ] }) : null,
5830
+ s.query ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
5831
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Query" }),
5832
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Json, { obj: s.query })
5833
+ ] }) : null,
5834
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
5835
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Response 200" }),
5836
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Json, { obj: s.response })
5837
+ ] })
5838
+ ] }),
5839
+ s.notes ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Flag, { tone: "info", statement: "Implementation note", cost: s.notes, actions: null }) : null,
5840
+ tried && tried !== "busy" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
5841
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-row", style: { gap: 8 }, children: [
5842
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Simulated response" }),
5843
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Badge, { tone: tried.error ? "danger" : "success", icon: "timer", children: [
5844
+ tried.ms,
5845
+ "ms"
5846
+ ] })
5847
+ ] }),
5848
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Json, { obj: tried.error ? { error: tried.error } : tried.data })
5849
+ ] }) : null
5850
+ ] })
5851
+ ] }) });
5852
+ }
5853
+ function ApiSpecBrowser({
5854
+ spec = [],
5855
+ onRequest,
5856
+ modules,
5857
+ groups,
5858
+ title = "API specification",
5859
+ kicker = "Architecture",
5860
+ lede,
5861
+ sourceNote,
5862
+ conventions,
5863
+ openQuestions,
5864
+ className = "",
5865
+ ...rest
5866
+ }) {
5867
+ const [q, setQ] = React35.useState("");
5868
+ const [method, setMethod] = React35.useState(null);
5869
+ const [mod, setMod] = React35.useState(null);
5870
+ const [listOnly, setListOnly] = React35.useState(false);
5871
+ const activeModule = modules && modules.find((m) => m.id === mod);
5872
+ const hits = spec.filter((s) => (!method || s.method === method) && (!listOnly || s.isList) && (!activeModule || activeModule.endpoints.indexOf(s.id) >= 0) && (!q || (s.path + " " + s.title + " " + s.purpose + " " + (s.usedBy || []).join(" ")).toLowerCase().includes(q.toLowerCase())));
5873
+ const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
5874
+ const methods = [...new Set(spec.map((s) => s.method))];
5875
+ const listCount = spec.filter((s) => s.isList).length;
5876
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 1100 }, ...rest, children: [
5877
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
5878
+ kicker ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: kicker }) : null,
5879
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
5880
+ lede ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: lede }) : null
5881
+ ] }),
5882
+ modules && modules.length ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 8 }, children: [
5883
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Filter by screen \u2014 every endpoint that screen depends on" }),
5884
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
5885
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Tag, { icon: "stack", selected: !mod, onClick: () => setMod(null), children: [
5886
+ "All screens ",
5887
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-mono", children: spec.length })
5888
+ ] }),
5889
+ modules.map((m) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Tag, { icon: m.icon, selected: mod === m.id, onClick: () => setMod(mod === m.id ? null : m.id), children: [
5890
+ m.label,
5891
+ " ",
5892
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-mono", children: m.endpoints.length })
5893
+ ] }, m.id))
5894
+ ] }),
5895
+ activeModule ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
5896
+ Flag,
5897
+ {
5898
+ tone: "info",
5899
+ statement: activeModule.label + " calls " + activeModule.endpoints.length + " endpoints.",
5900
+ cost: "Integration checklist for this screen: " + activeModule.endpoints.join(", ") + ". Wire these and the screen is done.",
5901
+ actions: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Button, { size: "sm", variant: "ghost", icon: "x", onClick: () => setMod(null), children: "Show all screens" })
5902
+ }
5903
+ ) : null
5904
+ ] }) : null,
5905
+ sourceNote ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Flag, { tone: "info", statement: "Design-first: this page is the spec.", cost: sourceNote, actions: null }) : null,
5906
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
5907
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Input, { icon: "magnifying-glass", placeholder: "Find an endpoint, screen, or behavior", value: q, onChange: (e) => setQ(e.target.value), style: { width: 300 } }),
5908
+ methods.map((m) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Tag, { selected: method === m, onClick: () => setMethod(method === m ? null : m), children: [
5909
+ m,
5910
+ " ",
5911
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-mono", children: spec.filter((s) => s.method === m).length })
5912
+ ] }, m)),
5913
+ listCount ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Tag, { icon: "rows", selected: listOnly, onClick: () => setListOnly(!listOnly), children: [
5914
+ "Paged lists ",
5915
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-mono", children: listCount })
5916
+ ] }) : null,
5917
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { style: { flex: 1 } }),
5918
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
5919
+ hits.length,
5920
+ " of ",
5921
+ spec.length,
5922
+ " endpoints",
5923
+ listCount ? " \xB7 " + listCount + " paged" : ""
5924
+ ] })
5925
+ ] }),
5926
+ hits.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: "No endpoint matches those filters." }) }) : effGroups.map(([g, ids]) => {
5927
+ const items = hits.filter((s) => ids.indexOf(s.id) >= 0);
5928
+ if (!items.length) return null;
5929
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 12 }, children: [
5930
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-label-lg", children: g }),
5931
+ items.map((s) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Endpoint, { s, onRequest }, s.id))
5932
+ ] }, g);
5933
+ }),
5934
+ conventions && conventions.length ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Card, { title: "Cross-cutting conventions", elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "fd-stack", style: { gap: 10 }, children: conventions.map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 12, alignItems: "flex-start" }, children: [
5935
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", style: { width: 110, flex: "none", paddingTop: 2 }, children: k }),
5936
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: v })
5937
+ ] }, k)) }) }) : null,
5938
+ openQuestions && openQuestions.length ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Collapsible, { icon: "list-checks", title: "Open questions before implementation", subtitle: openQuestions.length + " unresolved", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "fd-stack", style: { gap: 8 }, children: openQuestions.map(([id, question, why]) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start", padding: "8px 0", borderTop: "1px solid var(--border)" }, children: [
5939
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Badge, { tone: "danger", children: id }),
5940
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-stack", style: { gap: 2, flex: 1 }, children: [
5941
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: question }),
5942
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-body-sm fd-muted", children: why })
5943
+ ] })
5944
+ ] }, id)) }) }) : null
5945
+ ] });
5946
+ }
5947
+
5948
+ // src/components/platform/ProfilePage.tsx
5949
+ var React36 = __toESM(require("react"), 1);
5950
+ var import_jsx_runtime62 = require("react/jsx-runtime");
5951
+ function fieldInitialValue(field, user) {
5952
+ if (field.value) return field.value(user);
5953
+ const raw = user[field.key];
5954
+ return raw == null ? "" : String(raw);
5955
+ }
5956
+ function groupFields(fields) {
5957
+ const order = [];
5958
+ const byGroup = /* @__PURE__ */ new Map();
5959
+ for (const field of fields) {
5960
+ const group = field.group ?? "Identity";
5961
+ if (!byGroup.has(group)) {
5962
+ byGroup.set(group, { group, description: field.groupDescription, fields: [] });
5963
+ order.push(group);
5964
+ }
5965
+ const entry = byGroup.get(group);
5966
+ if (entry.description === void 0) entry.description = field.groupDescription;
5967
+ entry.fields.push(field);
5968
+ }
5969
+ return order.map((g) => byGroup.get(g));
5970
+ }
5971
+ function ProfilePage({
5972
+ user: userProp,
5973
+ fields = [],
5974
+ notifications = [],
5975
+ extra,
5976
+ onChangePhoto,
5977
+ onSave,
5978
+ onNavigate,
5979
+ sessions,
5980
+ onSignOutEverywhere,
5981
+ className = ""
5982
+ }) {
5983
+ const { user: sessionUser, roles, permissions, saveProfile: saveProfile2 } = useSessionContext();
5984
+ const user = userProp || sessionUser;
5985
+ const initialFieldValues = React36.useMemo(() => {
5986
+ const values2 = {};
5987
+ for (const field of fields) values2[field.key] = fieldInitialValue(field, user);
5988
+ return values2;
5989
+ }, [user.id, fields]);
5990
+ const initialNotify = React36.useMemo(() => {
5991
+ const values2 = {};
5992
+ for (const n of notifications) values2[n.key] = !!user.notify?.[n.key];
5993
+ return values2;
5994
+ }, [user.id, notifications]);
5995
+ const [values, setValues] = React36.useState(initialFieldValues);
5996
+ const [notify, setNotify] = React36.useState(initialNotify);
5997
+ const [saving, setSaving] = React36.useState(false);
5998
+ const [saved, setSaved] = React36.useState(false);
5999
+ const [prevUserId, setPrevUserId] = React36.useState(user.id);
6000
+ if (user.id !== prevUserId) {
6001
+ setPrevUserId(user.id);
6002
+ setValues(initialFieldValues);
6003
+ setNotify(initialNotify);
6004
+ setSaved(false);
6005
+ }
6006
+ const setField = (key, v) => {
6007
+ setValues((d) => ({ ...d, [key]: v }));
6008
+ setSaved(false);
6009
+ };
6010
+ const toggleNotify = (key) => {
6011
+ setNotify((d) => ({ ...d, [key]: !d[key] }));
6012
+ setSaved(false);
6013
+ };
6014
+ const dirty = fields.some((f) => values[f.key] !== initialFieldValues[f.key]) || notifications.some((n) => notify[n.key] !== initialNotify[n.key]);
6015
+ const canSave = !!(onSave || saveProfile2) && (fields.length > 0 || notifications.length > 0);
6016
+ const save = async () => {
6017
+ if (!canSave) return;
6018
+ setSaving(true);
6019
+ const draft = { ...values };
6020
+ if (notifications.length > 0) draft.notify = notify;
6021
+ try {
6022
+ if (onSave) await onSave(draft);
6023
+ else await saveProfile2(draft);
6024
+ setSaved(true);
6025
+ } finally {
6026
+ setSaving(false);
6027
+ }
6028
+ };
6029
+ const discard = () => {
6030
+ setValues(initialFieldValues);
6031
+ setNotify(initialNotify);
6032
+ setSaved(false);
6033
+ };
6034
+ const displayName = fields.some((f) => f.key === "name") ? values.name || user.name : user.name;
6035
+ const fieldGroups = groupFields(fields);
6036
+ return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 860 }, children: [
6037
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
6038
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-overline fd-muted", children: "Account" }),
6039
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: "Your profile" }),
6040
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: "How you appear to colleagues and clients across every flytedesk app, and what we send you." })
6041
+ ] }),
6042
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-row", style: { gap: 16, flexWrap: "wrap", alignItems: "flex-start" }, children: [
6043
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Avatar, { name: displayName, size: "lg" }),
6044
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-stack", style: { gap: 4, flex: 1, minWidth: 200 }, children: [
6045
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-h3", style: { margin: 0 }, children: displayName }),
6046
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 4 }, children: [
6047
+ roles.map((r) => /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)),
6048
+ user.sso ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Badge, { tone: "info", icon: "shield-check", children: "SSO" }) : null
6049
+ ] })
6050
+ ] }),
6051
+ onChangePhoto ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { variant: "secondary", size: "sm", icon: "image", onClick: onChangePhoto, children: "Change photo" }) : null
6052
+ ] }) }),
6053
+ fieldGroups.map(({ group, description, fields: groupFieldDefs }) => /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
6054
+ Card,
6055
+ {
6056
+ title: description ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-stack", style: { gap: 2 }, children: [
6057
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: group }),
6058
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: description })
6059
+ ] }) : group,
6060
+ elevation: "flat",
6061
+ children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-stack", style: { gap: 14 }, children: [
6062
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: groupFieldDefs.filter((f) => f.type !== "textarea").map((field) => {
6063
+ const help = typeof field.help === "function" ? field.help(user) : field.help;
6064
+ const readOnly = field.readOnly || field.type === "readonly";
6065
+ if (field.type === "select") {
6066
+ return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
6067
+ Select,
6068
+ {
6069
+ label: field.label,
6070
+ value: values[field.key],
6071
+ onChange: readOnly ? void 0 : (e) => setField(field.key, e.target.value),
6072
+ options: field.options ?? [],
6073
+ placeholder: field.placeholder,
6074
+ help,
6075
+ disabled: readOnly
6076
+ },
6077
+ field.key
6078
+ );
6079
+ }
6080
+ return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
6081
+ Input,
6082
+ {
6083
+ label: field.label,
6084
+ value: values[field.key],
6085
+ onChange: readOnly ? void 0 : (e) => setField(field.key, e.target.value),
6086
+ readOnly,
6087
+ disabled: readOnly,
6088
+ placeholder: field.placeholder,
6089
+ help
6090
+ },
6091
+ field.key
6092
+ );
6093
+ }) }),
6094
+ groupFieldDefs.filter((f) => f.type === "textarea").map((field) => {
6095
+ const help = typeof field.help === "function" ? field.help(user) : field.help;
6096
+ return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
6097
+ Textarea,
6098
+ {
6099
+ label: field.label,
6100
+ value: values[field.key],
6101
+ onChange: field.readOnly ? void 0 : (e) => setField(field.key, e.target.value),
6102
+ readOnly: field.readOnly,
6103
+ disabled: field.readOnly,
6104
+ rows: field.rows ?? 3,
6105
+ placeholder: field.placeholder,
6106
+ help
6107
+ },
6108
+ field.key
6109
+ );
6110
+ })
6111
+ ] })
6112
+ },
6113
+ group
6114
+ )),
6115
+ extra,
6116
+ notifications.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Card, { title: "Notifications", elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "fd-stack", style: { gap: 14 }, children: notifications.map((n) => /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Switch, { checked: !!notify[n.key], onChange: () => toggleNotify(n.key), label: n.label, description: n.description }, n.key)) }) }) : null,
6117
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
6118
+ Card,
6119
+ {
6120
+ title: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-stack", style: { gap: 2 }, children: [
6121
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: "Access" }),
6122
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "What your roles grant you" })
6123
+ ] }),
6124
+ elevation: "flat",
6125
+ action: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
6126
+ Button,
6127
+ {
6128
+ size: "sm",
6129
+ variant: "ghost",
6130
+ icon: "arrow-right",
6131
+ onClick: () => onNavigate && onNavigate("#roles"),
6132
+ children: "See role definitions"
6133
+ }
6134
+ ),
6135
+ children: [
6136
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-stack", style: { gap: 0 }, children: [
6137
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(CardRow, { label: "Roles", children: roles.map((r) => r.name).join(", ") || "None" }),
6138
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(CardRow, { label: "Permissions", children: [
6139
+ permissions.length,
6140
+ " granted"
6141
+ ] }),
6142
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(CardRow, { label: "Member since", children: user.joined || "\u2014" })
6143
+ ] }),
6144
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "fd-body-sm fd-muted", style: { margin: "12px 0 0" }, children: "You cannot change your own roles \u2014 that is the point of them. An administrator manages roles from Admin \u2192 Users." })
6145
+ ]
6146
+ }
6147
+ ),
6148
+ sessions ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
6149
+ Card,
6150
+ {
6151
+ title: "Signed-in devices",
6152
+ elevation: "flat",
6153
+ action: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { size: "sm", variant: "ghost", icon: "sign-out", onClick: onSignOutEverywhere, children: "Sign out everywhere" }),
6154
+ children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "fd-stack", style: { gap: 0 }, children: sessions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: "No other signed-in devices." }) : sessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-row", style: { gap: 12, padding: "10px 0", borderTop: "1px solid var(--border)", flexWrap: "wrap" }, children: [
6155
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-device-mobile", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
6156
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-stack", style: { gap: 1, flex: 1, minWidth: 160 }, children: [
6157
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: s.device }),
6158
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
6159
+ s.where,
6160
+ " \xB7 ",
6161
+ s.when
6162
+ ] })
6163
+ ] }),
6164
+ s.current ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Badge, { tone: "success", dot: true, children: "This device" }) : /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { size: "sm", variant: "ghost", children: "Revoke" })
6165
+ ] }, s.id)) })
6166
+ }
6167
+ ) : null,
6168
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-row", style: {
6169
+ gap: 10,
6170
+ flexWrap: "wrap",
6171
+ position: "sticky",
6172
+ bottom: 0,
6173
+ padding: "12px 14px",
6174
+ borderRadius: 10,
6175
+ background: "var(--surface)",
6176
+ border: "1px solid var(--border)",
6177
+ boxShadow: "0 -4px 16px rgba(11,13,17,.06)"
6178
+ }, children: [
6179
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600, flex: 1 }, children: saved ? "Saved." : dirty ? "Unsaved changes" : "No pending changes" }),
6180
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { size: "sm", variant: "ghost", disabled: !dirty || saving, onClick: discard, children: "Discard" }),
6181
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { size: "sm", icon: "check", disabled: !dirty || !canSave, loading: saving, onClick: save, children: "Save changes" })
6182
+ ] })
6183
+ ] });
6184
+ }
6185
+
6186
+ // src/components/platform/RoadmapTimeline.tsx
6187
+ var React39 = __toESM(require("react"), 1);
6188
+
6189
+ // src/kits/session.ts
6190
+ var React37 = __toESM(require("react"), 1);
5351
6191
  var PERMISSION_CATALOG = [
5352
6192
  { group: "Plans", items: [
5353
6193
  { key: "plan.view", label: "View plans", detail: "Read any plan in the workspace." },
@@ -6845,611 +7685,130 @@ function can(permission) {
6845
7685
  const list = Array.isArray(permission) ? permission : [permission];
6846
7686
  return list.every((p) => s.permissions.indexOf(p) >= 0);
6847
7687
  }
6848
- function canAny(permissions) {
6849
- const s = getSession();
6850
- return (permissions || []).some((p) => s.permissions.indexOf(p) >= 0);
6851
- }
6852
- function hasRole(roleId) {
6853
- return getSession().user.roles.indexOf(roleId) >= 0;
6854
- }
6855
- function flagOn(key) {
6856
- return !!getSession().flags[key];
6857
- }
6858
- function findFlag(key) {
6859
- return (getSession().allFlags || []).find((f) => f.key === key) || null;
6860
- }
6861
- function flagState(key) {
6862
- if (!key) return "on";
6863
- const f = findFlag(key);
6864
- if (!f) return "on";
6865
- return flagOn(key) ? "on" : "off";
6866
- }
6867
- function isEnabled(key) {
6868
- return flagState(key) !== "off";
6869
- }
6870
- function flagVisible(key) {
6871
- return isEnabled(key);
6872
- }
6873
- function flagLive(key) {
6874
- return isEnabled(key);
6875
- }
6876
- function roadmap(overrides) {
6877
- const flags = overrides && overrides.flags || (getSession().allFlags || []);
6878
- const isComplete = overrides && overrides.isComplete || (() => false);
6879
- const items = flags.map((f) => {
6880
- const done = !!isComplete(f.key);
6881
- const date = done ? f.shippedAt || f.eta : f.eta || f.shippedAt;
6882
- const phaseInfo = (f.phase != null ? PHASES[f.phase] : void 0) || {};
6883
- return {
6884
- key: f.key,
6885
- label: f.label,
6886
- project: f.project,
6887
- description: f.description,
6888
- backend: f.backend,
6889
- effort: f.effort,
6890
- owner: f.owner,
6891
- dependsOn: f.dependsOn || [],
6892
- screen: f.screen || null,
6893
- enabled: f.enabled,
6894
- /* Which build phase this sits in, and why that phase comes where it does. */
6895
- phase: f.phase,
6896
- phaseName: phaseInfo.name || "Unscheduled",
6897
- phaseWhy: phaseInfo.why || null,
6898
- phaseStart: phaseInfo.start || null,
6899
- /* Derived, not stored. */
6900
- implemented: done,
6901
- date: date || null,
6902
- /* True when the only date we have is when the UI was designed, not when it ships. */
6903
- dateIsDesign: !done && !f.eta && !!f.shippedAt,
6904
- status: done ? "shipped" : f.enabled ? "next" : "planned"
6905
- };
6906
- }).filter((i) => i.date);
6907
- items.sort((a, b) => (a.phase ?? 0) - (b.phase ?? 0) || String(a.date).localeCompare(String(b.date)) || a.label.localeCompare(b.label));
6908
- const nextIndex = items.findIndex((i) => !i.implemented);
6909
- return {
6910
- items,
6911
- nextIndex: nextIndex < 0 ? items.length : nextIndex,
6912
- shipped: items.filter((i) => i.implemented).length,
6913
- remaining: items.filter((i) => !i.implemented).length
6914
- };
6915
- }
6916
- function useSession() {
6917
- const [s, setS] = React30.useState(getSession);
6918
- React30.useEffect(() => subscribe(setS), []);
6919
- return s;
7688
+ function canAny(permissions) {
7689
+ const s = getSession();
7690
+ return (permissions || []).some((p) => s.permissions.indexOf(p) >= 0);
6920
7691
  }
6921
- var SessionKit = {
6922
- PERMISSION_CATALOG,
6923
- ALL_PERMISSIONS,
6924
- ROLES,
6925
- FLAGS,
6926
- USERS,
6927
- loadSession,
6928
- getSession,
6929
- setActiveUser,
6930
- saveProfile,
6931
- refresh,
6932
- subscribe,
6933
- can,
6934
- canAny,
6935
- hasRole,
6936
- flagOn,
6937
- useSession,
6938
- findFlag,
6939
- flagState,
6940
- isEnabled,
6941
- flagVisible,
6942
- flagLive,
6943
- roadmap,
6944
- evaluateFlag,
6945
- explainFlag,
6946
- bucket,
6947
- buildSession
6948
- };
6949
-
6950
- // src/components/platform/AccountMenu.tsx
6951
- var import_jsx_runtime58 = require("react/jsx-runtime");
6952
- var DEFAULT_LINKS = [
6953
- { id: "profile", label: "Your profile", icon: "user-circle", href: "../admin/index.html#profile" },
6954
- { id: "preferences", label: "Preferences", icon: "sliders-horizontal", href: "../admin/index.html#preferences" }
6955
- ];
6956
- var DEFAULT_ADMIN_LINKS = [
6957
- { id: "users", label: "Users", icon: "users-three", href: "../admin/index.html#users", perm: "users.view" },
6958
- { id: "roles", label: "Roles & permissions", icon: "shield-check", href: "../admin/index.html#roles", perm: "roles.manage" },
6959
- { id: "flags", label: "Feature flags", icon: "toggle-right", href: "../admin/index.html#flags", perm: "flags.manage" }
6960
- ];
6961
- function Item({ item, onPick }) {
6962
- return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
6963
- "button",
6964
- {
6965
- type: "button",
6966
- role: "menuitem",
6967
- className: "fd-acct-item",
6968
- onClick: () => onPick(item),
6969
- children: [
6970
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-" + item.icon, "aria-hidden": "true" }),
6971
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: item.label }),
6972
- item.badge ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Badge, { tone: "neutral", children: item.badge }) : null
6973
- ]
6974
- }
6975
- );
7692
+ function hasRole(roleId) {
7693
+ return getSession().user.roles.indexOf(roleId) >= 0;
6976
7694
  }
6977
- function AccountMenu({
6978
- links = DEFAULT_LINKS,
6979
- adminLinks = DEFAULT_ADMIN_LINKS,
6980
- onNavigate,
6981
- onSignOut,
6982
- showRoles = true,
6983
- allowUserSwitch = true,
6984
- className = "",
6985
- ...rest
6986
- }) {
6987
- const session = SessionKit.useSession();
6988
- const [open, setOpen] = React31.useState(false);
6989
- const [switching, setSwitching] = React31.useState(false);
6990
- const ref = React31.useRef(null);
6991
- const user = session.user;
6992
- const visibleAdmin = adminLinks.filter((l) => !l.perm || SessionKit.can(l.perm));
6993
- const pick = (item) => {
6994
- setOpen(false);
6995
- if (item.onSelect) return item.onSelect();
6996
- if (onNavigate) return onNavigate(item.href, item);
6997
- if (item.href) window.location.href = item.href;
6998
- };
6999
- const signOut = () => {
7000
- setOpen(false);
7001
- if (onSignOut) return onSignOut();
7002
- window.alert("Signed out. (Simulated \u2014 no auth provider is wired up.)");
7003
- };
7004
- return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_jsx_runtime58.Fragment, { children: [
7005
- /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
7006
- "button",
7007
- {
7008
- type: "button",
7009
- ref,
7010
- className: ["fd-acct-trigger", className].filter(Boolean).join(" "),
7011
- "aria-haspopup": "menu",
7012
- "aria-expanded": open,
7013
- "aria-label": "Account \u2014 " + user.name,
7014
- onClick: () => setOpen((o) => !o),
7015
- ...rest,
7016
- children: [
7017
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: user.name, size: "sm" }),
7018
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true", style: { fontSize: 11, color: "var(--text-muted)" } })
7019
- ]
7020
- }
7021
- ),
7022
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Popover, { open, anchorRef: ref, onClose: () => setOpen(false), placement: "bottom-end", width: 272, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { role: "menu", className: "fd-stack", style: { gap: 0 }, children: [
7023
- /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
7024
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: user.name }),
7025
- /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "fd-stack", style: { gap: 1, minWidth: 0, flex: 1 }, children: [
7026
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: user.name }),
7027
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-body-sm fd-muted fd-table-trunc", title: truncationTitle(user.email), children: user.email })
7028
- ] })
7029
- ] }),
7030
- showRoles && session.roles.length ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-row", style: { gap: 6, padding: "10px 14px", flexWrap: "wrap", borderBottom: "1px solid var(--border)" }, children: session.roles.map((r) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)) }) : null,
7031
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-acct-group", children: links.map((l) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Item, { item: l, onPick: pick }, l.id)) }),
7032
- visibleAdmin.length ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
7033
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-overline fd-muted", style: { padding: "8px 14px 4px", display: "block" }, children: "Administration" }),
7034
- visibleAdmin.map((l) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Item, { item: l, onPick: pick }, l.id))
7035
- ] }) : null,
7036
- allowUserSwitch ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
7037
- /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("button", { type: "button", role: "menuitem", className: "fd-acct-item", onClick: () => setSwitching((s) => !s), children: [
7038
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-user-switch", "aria-hidden": "true" }),
7039
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: "View as another member" }),
7040
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-caret-" + (switching ? "up" : "down"), "aria-hidden": "true", style: { fontSize: 11 } })
7041
- ] }),
7042
- switching ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-stack", style: { gap: 0 }, children: session.allUsers.filter((u) => u.status === "active").map((u) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
7043
- "button",
7044
- {
7045
- type: "button",
7046
- className: "fd-acct-item",
7047
- "data-current": u.id === user.id ? "true" : "false",
7048
- onClick: () => {
7049
- SessionKit.setActiveUser(u.id);
7050
- setOpen(false);
7051
- setSwitching(false);
7052
- },
7053
- children: [
7054
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: u.name, size: "sm" }),
7055
- /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "fd-stack", style: { gap: 0, flex: 1, minWidth: 0, alignItems: "flex-start" }, children: [
7056
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontWeight: u.id === user.id ? 700 : 500 }, children: u.name }),
7057
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-muted", style: { fontSize: 11.5 }, children: u.roles.join(", ") })
7058
- ] }),
7059
- u.id === user.id ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-check", "aria-hidden": "true", style: { color: "var(--ok-text)" } }) : null
7060
- ]
7061
- },
7062
- u.id
7063
- )) }) : null
7064
- ] }) : null,
7065
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("button", { type: "button", role: "menuitem", className: "fd-acct-item", "data-danger": "true", onClick: signOut, children: [
7066
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-sign-out", "aria-hidden": "true" }),
7067
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: "Sign out" })
7068
- ] }) })
7069
- ] }) })
7070
- ] });
7695
+ function flagOn(key) {
7696
+ return !!getSession().flags[key];
7071
7697
  }
7072
-
7073
- // src/components/platform/ApiSpecBrowser.tsx
7074
- var React32 = __toESM(require("react"), 1);
7075
- var import_jsx_runtime59 = require("react/jsx-runtime");
7076
- var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
7077
- function Json({ obj }) {
7078
- return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
7698
+ function findFlag(key) {
7699
+ return (getSession().allFlags || []).find((f) => f.key === key) || null;
7079
7700
  }
7080
- function Endpoint({ s, onRequest }) {
7081
- const [tried, setTried] = React32.useState(null);
7082
- const run = async () => {
7083
- setTried("busy");
7084
- const t0 = (window.performance || Date).now();
7085
- try {
7086
- const data = await onRequest(s.id, s.request || (s.isList ? { page: 1, pageSize: 5 } : {}));
7087
- setTried({ ms: Math.round((window.performance || Date).now() - t0), data });
7088
- } catch (e) {
7089
- setTried({ ms: Math.round((window.performance || Date).now() - t0), error: String(e && e.message || e) });
7090
- }
7091
- };
7092
- return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Card, { elevation: "flat", padded: false, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 0 }, children: [
7093
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "14px 18px", flexWrap: "wrap" }, children: [
7094
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Badge, { tone: METHOD_TONE[s.method] || "neutral", children: s.method }),
7095
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("code", { className: "fd-mono", style: { fontSize: 12.5, fontWeight: 600, color: "var(--text)", wordBreak: "break-all" }, children: s.path }),
7096
- s.isList ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Badge, { tone: "neutral", icon: "rows", children: "Paged list" }) : null,
7097
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { style: { flex: 1 } }),
7098
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("span", { className: "fd-body-sm fd-muted fd-mono", children: [
7099
- s.latency[0],
7100
- "\u2013",
7101
- s.latency[1],
7102
- "ms"
7103
- ] }),
7104
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Button, { size: "sm", variant: "secondary", icon: "play", loading: tried === "busy", onClick: run, children: "Try it" })
7105
- ] }),
7106
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 14, padding: "0 18px 16px" }, children: [
7107
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
7108
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: s.title }),
7109
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: s.purpose }),
7110
- s.usedBy && s.usedBy.length ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: s.usedBy.map((u) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Tag, { children: u }, u)) }) : null
7111
- ] }),
7112
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(280px,1fr))", gap: 12, alignItems: "start" }, children: [
7113
- s.request ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
7114
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: "Request body" }),
7115
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Json, { obj: s.request })
7116
- ] }) : null,
7117
- s.query ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
7118
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: "Query" }),
7119
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Json, { obj: s.query })
7120
- ] }) : null,
7121
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
7122
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: "Response 200" }),
7123
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Json, { obj: s.response })
7124
- ] })
7125
- ] }),
7126
- s.notes ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Flag, { tone: "info", statement: "Implementation note", cost: s.notes, actions: null }) : null,
7127
- tried && tried !== "busy" ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
7128
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("span", { className: "fd-row", style: { gap: 8 }, children: [
7129
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: "Simulated response" }),
7130
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Badge, { tone: tried.error ? "danger" : "success", icon: "timer", children: [
7131
- tried.ms,
7132
- "ms"
7133
- ] })
7134
- ] }),
7135
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Json, { obj: tried.error ? { error: tried.error } : tried.data })
7136
- ] }) : null
7137
- ] })
7138
- ] }) });
7701
+ function flagState(key) {
7702
+ if (!key) return "on";
7703
+ const f = findFlag(key);
7704
+ if (!f) return "on";
7705
+ return flagOn(key) ? "on" : "off";
7139
7706
  }
7140
- function ApiSpecBrowser({
7141
- spec = [],
7142
- onRequest,
7143
- modules,
7144
- groups,
7145
- title = "API specification",
7146
- kicker = "Architecture",
7147
- lede,
7148
- sourceNote,
7149
- conventions,
7150
- openQuestions,
7151
- className = "",
7152
- ...rest
7153
- }) {
7154
- const [q, setQ] = React32.useState("");
7155
- const [method, setMethod] = React32.useState(null);
7156
- const [mod, setMod] = React32.useState(null);
7157
- const [listOnly, setListOnly] = React32.useState(false);
7158
- const activeModule = modules && modules.find((m) => m.id === mod);
7159
- const hits = spec.filter((s) => (!method || s.method === method) && (!listOnly || s.isList) && (!activeModule || activeModule.endpoints.indexOf(s.id) >= 0) && (!q || (s.path + " " + s.title + " " + s.purpose + " " + (s.usedBy || []).join(" ")).toLowerCase().includes(q.toLowerCase())));
7160
- const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
7161
- const methods = [...new Set(spec.map((s) => s.method))];
7162
- const listCount = spec.filter((s) => s.isList).length;
7163
- return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 1100 }, ...rest, children: [
7164
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
7165
- kicker ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: kicker }) : null,
7166
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
7167
- lede ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: lede }) : null
7168
- ] }),
7169
- modules && modules.length ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 8 }, children: [
7170
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: "Filter by screen \u2014 every endpoint that screen depends on" }),
7171
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
7172
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Tag, { icon: "stack", selected: !mod, onClick: () => setMod(null), children: [
7173
- "All screens ",
7174
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-mono", children: spec.length })
7175
- ] }),
7176
- modules.map((m) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Tag, { icon: m.icon, selected: mod === m.id, onClick: () => setMod(mod === m.id ? null : m.id), children: [
7177
- m.label,
7178
- " ",
7179
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-mono", children: m.endpoints.length })
7180
- ] }, m.id))
7181
- ] }),
7182
- activeModule ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
7183
- Flag,
7184
- {
7185
- tone: "info",
7186
- statement: activeModule.label + " calls " + activeModule.endpoints.length + " endpoints.",
7187
- cost: "Integration checklist for this screen: " + activeModule.endpoints.join(", ") + ". Wire these and the screen is done.",
7188
- actions: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Button, { size: "sm", variant: "ghost", icon: "x", onClick: () => setMod(null), children: "Show all screens" })
7189
- }
7190
- ) : null
7191
- ] }) : null,
7192
- sourceNote ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Flag, { tone: "info", statement: "Design-first: this page is the spec.", cost: sourceNote, actions: null }) : null,
7193
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
7194
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Input, { icon: "magnifying-glass", placeholder: "Find an endpoint, screen, or behavior", value: q, onChange: (e) => setQ(e.target.value), style: { width: 300 } }),
7195
- methods.map((m) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Tag, { selected: method === m, onClick: () => setMethod(method === m ? null : m), children: [
7196
- m,
7197
- " ",
7198
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-mono", children: spec.filter((s) => s.method === m).length })
7199
- ] }, m)),
7200
- listCount ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Tag, { icon: "rows", selected: listOnly, onClick: () => setListOnly(!listOnly), children: [
7201
- "Paged lists ",
7202
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-mono", children: listCount })
7203
- ] }) : null,
7204
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { style: { flex: 1 } }),
7205
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
7206
- hits.length,
7207
- " of ",
7208
- spec.length,
7209
- " endpoints",
7210
- listCount ? " \xB7 " + listCount + " paged" : ""
7211
- ] })
7212
- ] }),
7213
- hits.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: "No endpoint matches those filters." }) }) : effGroups.map(([g, ids]) => {
7214
- const items = hits.filter((s) => ids.indexOf(s.id) >= 0);
7215
- if (!items.length) return null;
7216
- return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 12 }, children: [
7217
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-label-lg", children: g }),
7218
- items.map((s) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Endpoint, { s, onRequest }, s.id))
7219
- ] }, g);
7220
- }),
7221
- conventions && conventions.length ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Card, { title: "Cross-cutting conventions", elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("div", { className: "fd-stack", style: { gap: 10 }, children: conventions.map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-row", style: { gap: 12, alignItems: "flex-start" }, children: [
7222
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", style: { width: 110, flex: "none", paddingTop: 2 }, children: k }),
7223
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: v })
7224
- ] }, k)) }) }) : null,
7225
- openQuestions && openQuestions.length ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Collapsible, { icon: "list-checks", title: "Open questions before implementation", subtitle: openQuestions.length + " unresolved", children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("div", { className: "fd-stack", style: { gap: 8 }, children: openQuestions.map(([id, question, why]) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start", padding: "8px 0", borderTop: "1px solid var(--border)" }, children: [
7226
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Badge, { tone: "danger", children: id }),
7227
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("span", { className: "fd-stack", style: { gap: 2, flex: 1 }, children: [
7228
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: question }),
7229
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-body-sm fd-muted", children: why })
7230
- ] })
7231
- ] }, id)) }) }) : null
7232
- ] });
7707
+ function isEnabled(key) {
7708
+ return flagState(key) !== "off";
7233
7709
  }
7234
-
7235
- // src/components/platform/ProfilePage.tsx
7236
- var React33 = __toESM(require("react"), 1);
7237
- var import_jsx_runtime60 = require("react/jsx-runtime");
7238
- var TIMEZONES = ["America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles", "America/Anchorage", "Pacific/Honolulu", "Europe/London", "Europe/Berlin"];
7239
- var NOTIFY = [
7240
- { key: "planShared", label: "A plan is shared with me", detail: "Someone sends you a plan or a client link." },
7241
- { key: "planChanged", label: "A plan I own changes", detail: "Budget moves, campuses added or removed." },
7242
- { key: "goalMissed", label: "The reviewer flags a missed criterion", detail: "After every solve, if a criterion is short." },
7243
- { key: "flagChanged", label: "A feature flag affecting me changes", detail: "Someone toggles a feature you can see." },
7244
- { key: "weekly", label: "Weekly digest", detail: "Monday summary of plan activity." }
7245
- ];
7246
- function ProfilePage({ user: userProp, onSave, onNavigate, sessions, showSessions = true, className = "", ...rest }) {
7247
- const session = SessionKit.useSession();
7248
- const user = userProp || session.user;
7249
- const [draft, setDraft] = React33.useState(() => ({
7250
- name: user.name || "",
7251
- title: user.title || "",
7252
- email: user.email || "",
7253
- phone: user.phone || "",
7254
- timezone: user.timezone || TIMEZONES[2],
7255
- bio: user.bio || "",
7256
- notify: user.notify || { planShared: true, planChanged: true, goalMissed: true, flagChanged: false, weekly: true }
7257
- }));
7258
- const [saving, setSaving] = React33.useState(false);
7259
- const [saved, setSaved] = React33.useState(false);
7260
- const set = (k, v) => {
7261
- setDraft((d) => Object.assign({}, d, { [k]: v }));
7262
- setSaved(false);
7263
- };
7264
- const setNotify = (k) => {
7265
- setDraft((d) => Object.assign({}, d, { notify: Object.assign({}, d.notify, { [k]: !d.notify[k] }) }));
7266
- setSaved(false);
7267
- };
7268
- const dirty = draft.name !== (user.name || "") || draft.title !== (user.title || "") || draft.phone !== (user.phone || "") || draft.timezone !== (user.timezone || TIMEZONES[2]) || draft.bio !== (user.bio || "") || JSON.stringify(draft.notify) !== JSON.stringify(user.notify || { planShared: true, planChanged: true, goalMissed: true, flagChanged: false, weekly: true });
7269
- const save = async () => {
7270
- setSaving(true);
7271
- if (onSave) await onSave(draft);
7272
- else SessionKit.saveProfile(draft);
7273
- setSaving(false);
7274
- setSaved(true);
7710
+ function flagVisible(key) {
7711
+ return isEnabled(key);
7712
+ }
7713
+ function flagLive(key) {
7714
+ return isEnabled(key);
7715
+ }
7716
+ function roadmap(overrides) {
7717
+ const flags = overrides && overrides.flags || (getSession().allFlags || []);
7718
+ const isComplete = overrides && overrides.isComplete || (() => false);
7719
+ const items = flags.map((f) => {
7720
+ const done = !!isComplete(f.key);
7721
+ const date = done ? f.shippedAt || f.eta : f.eta || f.shippedAt;
7722
+ const phaseInfo = (f.phase != null ? PHASES[f.phase] : void 0) || {};
7723
+ return {
7724
+ key: f.key,
7725
+ label: f.label,
7726
+ project: f.project,
7727
+ description: f.description,
7728
+ backend: f.backend,
7729
+ effort: f.effort,
7730
+ owner: f.owner,
7731
+ dependsOn: f.dependsOn || [],
7732
+ screen: f.screen || null,
7733
+ enabled: f.enabled,
7734
+ /* Which build phase this sits in, and why that phase comes where it does. */
7735
+ phase: f.phase,
7736
+ phaseName: phaseInfo.name || "Unscheduled",
7737
+ phaseWhy: phaseInfo.why || null,
7738
+ phaseStart: phaseInfo.start || null,
7739
+ /* Derived, not stored. */
7740
+ implemented: done,
7741
+ date: date || null,
7742
+ /* True when the only date we have is when the UI was designed, not when it ships. */
7743
+ dateIsDesign: !done && !f.eta && !!f.shippedAt,
7744
+ status: done ? "shipped" : f.enabled ? "next" : "planned"
7745
+ };
7746
+ }).filter((i) => i.date);
7747
+ items.sort((a, b) => (a.phase ?? 0) - (b.phase ?? 0) || String(a.date).localeCompare(String(b.date)) || a.label.localeCompare(b.label));
7748
+ const nextIndex = items.findIndex((i) => !i.implemented);
7749
+ return {
7750
+ items,
7751
+ nextIndex: nextIndex < 0 ? items.length : nextIndex,
7752
+ shipped: items.filter((i) => i.implemented).length,
7753
+ remaining: items.filter((i) => !i.implemented).length
7275
7754
  };
7276
- const liveSessions = sessions || [
7277
- { id: "s1", device: "MacBook Pro \xB7 Chrome", where: "Denver, CO", when: "Active now", current: true },
7278
- { id: "s2", device: "iPhone 15 \xB7 Safari", where: "Denver, CO", when: "2 hours ago" },
7279
- { id: "s3", device: "Windows \xB7 Edge", where: "Chicago, IL", when: "Aug 12" }
7280
- ];
7281
- return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 860 }, ...rest, children: [
7282
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
7283
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-overline fd-muted", children: "Account" }),
7284
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: "Your profile" }),
7285
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: "How you appear to colleagues and clients across every flytedesk app, and what we send you." })
7286
- ] }),
7287
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-row", style: { gap: 16, flexWrap: "wrap", alignItems: "flex-start" }, children: [
7288
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Avatar, { name: draft.name || user.name, size: "lg" }),
7289
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-stack", style: { gap: 4, flex: 1, minWidth: 200 }, children: [
7290
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-h3", style: { margin: 0 }, children: draft.name || user.name }),
7291
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
7292
- draft.title || "No title set",
7293
- " \xB7 ",
7294
- user.team || "No team"
7295
- ] }),
7296
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 4 }, children: [
7297
- session.roles.map((r) => /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)),
7298
- user.sso ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Badge, { tone: "info", icon: "shield-check", children: "SSO" }) : null
7299
- ] })
7300
- ] }),
7301
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Button, { variant: "secondary", size: "sm", icon: "image", children: "Change photo" })
7302
- ] }) }),
7303
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
7304
- Card,
7305
- {
7306
- title: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-stack", style: { gap: 2 }, children: [
7307
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: "Identity" }),
7308
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "Name and title appear on plans you share" })
7309
- ] }),
7310
- elevation: "flat",
7311
- children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-stack", style: { gap: 14 }, children: [
7312
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: [
7313
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Input, { label: "Full name", value: draft.name, onChange: (e) => set("name", e.target.value) }),
7314
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Input, { label: "Job title", value: draft.title, onChange: (e) => set("title", e.target.value), placeholder: "e.g. Senior media planner" })
7315
- ] }),
7316
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: [
7317
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
7318
- Input,
7319
- {
7320
- label: "Work email",
7321
- value: draft.email,
7322
- readOnly: true,
7323
- disabled: true,
7324
- help: user.sso ? "Managed by your identity provider \u2014 change it there." : "Contact an administrator to change this."
7325
- }
7326
- ),
7327
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Input, { label: "Phone", value: draft.phone, onChange: (e) => set("phone", e.target.value), placeholder: "Optional" })
7328
- ] }),
7329
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
7330
- Textarea,
7331
- {
7332
- label: "Short bio",
7333
- value: draft.bio,
7334
- onChange: (e) => set("bio", e.target.value),
7335
- rows: 3,
7336
- placeholder: "Optional \u2014 shown to colleagues on shared plans.",
7337
- help: "Never shown to clients."
7338
- }
7339
- )
7340
- ] })
7341
- }
7342
- ),
7343
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Card, { title: "Working preferences", elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: [
7344
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
7345
- Select,
7346
- {
7347
- label: "Time zone",
7348
- value: draft.timezone,
7349
- onChange: (e) => set("timezone", e.target.value),
7350
- options: TIMEZONES,
7351
- help: "Flight dates and schedules render in this zone."
7352
- }
7353
- ),
7354
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Select, { label: "Start of week", options: ["Monday", "Sunday"], placeholder: "Monday" })
7355
- ] }) }),
7356
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
7357
- Card,
7358
- {
7359
- title: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-stack", style: { gap: 2 }, children: [
7360
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: "Notifications" }),
7361
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "Email only for now \u2014 in-app notifications are on the roadmap" })
7362
- ] }),
7363
- elevation: "flat",
7364
- children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "fd-stack", style: { gap: 14 }, children: NOTIFY.map((n) => /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Switch, { checked: !!draft.notify[n.key], onChange: () => setNotify(n.key), label: n.label, description: n.detail }, n.key)) })
7365
- }
7366
- ),
7367
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
7368
- Card,
7369
- {
7370
- title: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-stack", style: { gap: 2 }, children: [
7371
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: "Access" }),
7372
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "What your roles grant you" })
7373
- ] }),
7374
- elevation: "flat",
7375
- action: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
7376
- Button,
7377
- {
7378
- size: "sm",
7379
- variant: "ghost",
7380
- icon: "arrow-right",
7381
- onClick: () => onNavigate && onNavigate("#roles"),
7382
- children: "See role definitions"
7383
- }
7384
- ),
7385
- children: [
7386
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-stack", style: { gap: 0 }, children: [
7387
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(CardRow, { label: "Roles", children: session.roles.map((r) => r.name).join(", ") || "None" }),
7388
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(CardRow, { label: "Permissions", children: [
7389
- session.permissions.length,
7390
- " granted"
7391
- ] }),
7392
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(CardRow, { label: "Member since", children: user.joined || "\u2014" })
7393
- ] }),
7394
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("p", { className: "fd-body-sm fd-muted", style: { margin: "12px 0 0" }, children: "You cannot change your own roles \u2014 that is the point of them. An administrator manages roles from Admin \u2192 Users." })
7395
- ]
7396
- }
7397
- ),
7398
- showSessions ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
7399
- Card,
7400
- {
7401
- title: "Signed-in devices",
7402
- elevation: "flat",
7403
- action: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Button, { size: "sm", variant: "ghost", icon: "sign-out", children: "Sign out everywhere" }),
7404
- children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "fd-stack", style: { gap: 0 }, children: liveSessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-row", style: { gap: 12, padding: "10px 0", borderTop: "1px solid var(--border)", flexWrap: "wrap" }, children: [
7405
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("i", { className: "ph ph-device-mobile", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
7406
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-stack", style: { gap: 1, flex: 1, minWidth: 160 }, children: [
7407
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: s.device }),
7408
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
7409
- s.where,
7410
- " \xB7 ",
7411
- s.when
7412
- ] })
7413
- ] }),
7414
- s.current ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Badge, { tone: "success", dot: true, children: "This device" }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Button, { size: "sm", variant: "ghost", children: "Revoke" })
7415
- ] }, s.id)) })
7416
- }
7417
- ) : null,
7418
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-row", style: {
7419
- gap: 10,
7420
- flexWrap: "wrap",
7421
- position: "sticky",
7422
- bottom: 0,
7423
- padding: "12px 14px",
7424
- borderRadius: 10,
7425
- background: "var(--surface)",
7426
- border: "1px solid var(--border)",
7427
- boxShadow: "0 -4px 16px rgba(11,13,17,.06)"
7428
- }, children: [
7429
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600, flex: 1 }, children: saved ? "Saved." : dirty ? "Unsaved changes" : "No pending changes" }),
7430
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
7431
- Button,
7432
- {
7433
- size: "sm",
7434
- variant: "ghost",
7435
- disabled: !dirty || saving,
7436
- onClick: () => {
7437
- setDraft({ name: user.name || "", title: user.title || "", email: user.email || "", phone: user.phone || "", timezone: user.timezone || TIMEZONES[2], bio: user.bio || "", notify: user.notify || { planShared: true, planChanged: true, goalMissed: true, flagChanged: false, weekly: true } });
7438
- setSaved(false);
7439
- },
7440
- children: "Discard"
7441
- }
7442
- ),
7443
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Button, { size: "sm", icon: "check", disabled: !dirty, loading: saving, onClick: save, children: "Save changes" })
7444
- ] })
7445
- ] });
7446
7755
  }
7447
-
7448
- // src/components/platform/RoadmapTimeline.tsx
7449
- var React35 = __toESM(require("react"), 1);
7756
+ function useSession() {
7757
+ const [s, setS] = React37.useState(getSession);
7758
+ React37.useEffect(() => subscribe(setS), []);
7759
+ return s;
7760
+ }
7761
+ var SessionKit = {
7762
+ PERMISSION_CATALOG,
7763
+ ALL_PERMISSIONS,
7764
+ ROLES,
7765
+ FLAGS,
7766
+ USERS,
7767
+ loadSession,
7768
+ getSession,
7769
+ setActiveUser,
7770
+ saveProfile,
7771
+ refresh,
7772
+ subscribe,
7773
+ can,
7774
+ canAny,
7775
+ hasRole,
7776
+ flagOn,
7777
+ useSession,
7778
+ findFlag,
7779
+ flagState,
7780
+ isEnabled,
7781
+ flagVisible,
7782
+ flagLive,
7783
+ roadmap,
7784
+ evaluateFlag,
7785
+ explainFlag,
7786
+ bucket,
7787
+ buildSession
7788
+ };
7789
+ function DemoSessionProvider({ children }) {
7790
+ const session = useSession();
7791
+ const value = React37.useMemo(
7792
+ () => ({
7793
+ user: session.user,
7794
+ roles: session.roles,
7795
+ permissions: session.permissions,
7796
+ can,
7797
+ canAny,
7798
+ hasRole,
7799
+ signOut: () => window.alert("Signed out. (Simulated \u2014 no auth provider is wired up.)"),
7800
+ saveProfile: (patch) => {
7801
+ saveProfile(patch);
7802
+ },
7803
+ demo: { allUsers: session.allUsers.filter((u) => u.status === "active"), setActiveUser }
7804
+ }),
7805
+ [session]
7806
+ );
7807
+ return React37.createElement(SessionProvider, { value }, children);
7808
+ }
7450
7809
 
7451
7810
  // src/kits/runtime.ts
7452
- var React34 = __toESM(require("react"), 1);
7811
+ var React38 = __toESM(require("react"), 1);
7453
7812
  var WIRED_ENDPOINTS = [
7454
7813
  // Nothing yet. Every id below would come from a real service:
7455
7814
  // "plan.get", "placements.list", …
@@ -7620,8 +7979,8 @@ var RuntimeKit = {
7620
7979
  };
7621
7980
  RuntimeKit.declare(FEATURE_NEEDS);
7622
7981
  function useRuntimeMode() {
7623
- const [m, setM] = React34.useState(RuntimeKit.getMode());
7624
- React34.useEffect(() => RuntimeKit.subscribe(setM), []);
7982
+ const [m, setM] = React38.useState(RuntimeKit.getMode());
7983
+ React38.useEffect(() => RuntimeKit.subscribe(setM), []);
7625
7984
  return m;
7626
7985
  }
7627
7986
  function useFeatureStatus(key) {
@@ -7640,7 +7999,7 @@ var UseRuntimeMode = useRuntimeMode;
7640
7999
  var UseFeatureStatus = useFeatureStatus;
7641
8000
 
7642
8001
  // src/components/platform/RoadmapTimeline.tsx
7643
- var import_jsx_runtime61 = require("react/jsx-runtime");
8002
+ var import_jsx_runtime63 = require("react/jsx-runtime");
7644
8003
  var STATUS = {
7645
8004
  shipped: { tone: "success", icon: "check-circle", label: "Wired" },
7646
8005
  next: { tone: "warning", icon: "traffic-cone", label: "Not wired" },
@@ -7655,45 +8014,45 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
7655
8014
  const s = STATUS[item.status] || STATUS.planned;
7656
8015
  const deps = (item.dependsOn || []).map((k) => byKey[k]).filter(Boolean);
7657
8016
  const blocking = deps.filter((d) => !d.implemented);
7658
- return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
7659
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
7660
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-body", style: { fontWeight: 700, flex: 1, minWidth: 140 }, children: item.label }),
7661
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Badge, { tone: "neutral", icon: PROJECT_ICON[item.project] || "squares-four", children: item.project }),
7662
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Badge, { tone: s.tone, icon: s.icon, children: s.label })
8017
+ return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
8018
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
8019
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-body", style: { fontWeight: 700, flex: 1, minWidth: 140 }, children: item.label }),
8020
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Badge, { tone: "neutral", icon: PROJECT_ICON[item.project] || "squares-four", children: item.project }),
8021
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Badge, { tone: s.tone, icon: s.icon, children: s.label })
7663
8022
  ] }),
7664
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: item.description }),
7665
- item.backend ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start" }, children: [
7666
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", style: { width: 62, flex: "none", paddingTop: 2 }, children: "Backend" }),
7667
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-body-sm", style: { flex: 1, textWrap: "pretty" }, children: item.backend })
8023
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: item.description }),
8024
+ item.backend ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start" }, children: [
8025
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-overline fd-muted", style: { width: 62, flex: "none", paddingTop: 2 }, children: "Backend" }),
8026
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-body-sm", style: { flex: 1, textWrap: "pretty" }, children: item.backend })
7668
8027
  ] }) : null,
7669
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
7670
- item.effort ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
7671
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("i", { className: "ph ph-hourglass-medium" }),
8028
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
8029
+ item.effort ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
8030
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("i", { className: "ph ph-hourglass-medium" }),
7672
8031
  " ",
7673
8032
  item.effort
7674
8033
  ] }) : null,
7675
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
7676
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("i", { className: "ph ph-user" }),
8034
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
8035
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("i", { className: "ph ph-user" }),
7677
8036
  " ",
7678
8037
  item.owner
7679
8038
  ] }),
7680
- item.screen ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Badge, { tone: "neutral", icon: "browser", children: "screen" }) : null,
7681
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { style: { flex: 1 } }),
7682
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8039
+ item.screen ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Badge, { tone: "neutral", icon: "browser", children: "screen" }) : null,
8040
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { style: { flex: 1 } }),
8041
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
7683
8042
  "button",
7684
8043
  {
7685
8044
  type: "button",
7686
8045
  onClick: () => onOpenFlag && onOpenFlag(item.key),
7687
8046
  style: { all: "unset", cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 5 },
7688
- children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("code", { className: "fd-mono", style: { fontSize: 11, color: "var(--brand)" }, children: item.key })
8047
+ children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("code", { className: "fd-mono", style: { fontSize: 11, color: "var(--brand)" }, children: item.key })
7689
8048
  }
7690
8049
  )
7691
8050
  ] }),
7692
- deps.length ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 6, borderTop: "1px solid var(--border)" }, children: [
7693
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", style: { paddingTop: 3 }, children: "After" }),
7694
- deps.map((d) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Tag, { icon: d.implemented ? "check" : "clock", children: d.label }, d.key))
8051
+ deps.length ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 6, borderTop: "1px solid var(--border)" }, children: [
8052
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-overline fd-muted", style: { paddingTop: 3 }, children: "After" }),
8053
+ deps.map((d) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Tag, { icon: d.implemented ? "check" : "clock", children: d.label }, d.key))
7695
8054
  ] }) : null,
7696
- blocking.length && !item.implemented ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-body-sm", style: { color: "var(--warn-text)" }, children: [
8055
+ blocking.length && !item.implemented ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-body-sm", style: { color: "var(--warn-text)" }, children: [
7697
8056
  "Blocked until ",
7698
8057
  blocking.map((d) => d.label).join(" and "),
7699
8058
  " ",
@@ -7704,12 +8063,12 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
7704
8063
  }
7705
8064
  function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7706
8065
  const session = SessionKit.useSession();
7707
- const [project, setProject] = React35.useState("");
7708
- const [q, setQ] = React35.useState("");
7709
- const scrollRef = React35.useRef(null);
7710
- const nowRef = React35.useRef(null);
7711
- const rm = React35.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
7712
- const byKey = React35.useMemo(() => {
8066
+ const [project, setProject] = React39.useState("");
8067
+ const [q, setQ] = React39.useState("");
8068
+ const scrollRef = React39.useRef(null);
8069
+ const nowRef = React39.useRef(null);
8070
+ const rm = React39.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
8071
+ const byKey = React39.useMemo(() => {
7713
8072
  const m = {};
7714
8073
  rm.items.forEach((i) => {
7715
8074
  m[i.key] = i;
@@ -7718,7 +8077,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7718
8077
  }, [rm]);
7719
8078
  const items = rm.items.filter((i) => (!project || i.project === project) && (!q || (i.label + " " + i.description + " " + (i.backend || "") + " " + i.key).toLowerCase().includes(q.toLowerCase())));
7720
8079
  const projects = [...new Set(rm.items.map((i) => i.project))];
7721
- React35.useEffect(() => {
8080
+ React39.useEffect(() => {
7722
8081
  let raf1 = 0, raf2 = 0;
7723
8082
  const place = () => {
7724
8083
  const box = scrollRef.current, mark = nowRef.current;
@@ -7746,23 +8105,23 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7746
8105
  }, 0);
7747
8106
  const nextPhase = items.find((i) => !i.implemented);
7748
8107
  let lastPhase = null;
7749
- return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 16, maxWidth: 1e3 }, children: [
7750
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
7751
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Architecture" }),
7752
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
7753
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: lede || "Every screen and feature in these tools is already designed and working against a simulated backend. This is the plan for connecting them to the real one \u2014 so the only work described here is server work." })
8108
+ return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-stack", style: { gap: 16, maxWidth: 1e3 }, children: [
8109
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
8110
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-overline fd-muted", children: "Architecture" }),
8111
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
8112
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: lede || "Every screen and feature in these tools is already designed and working against a simulated backend. This is the plan for connecting them to the real one \u2014 so the only work described here is server work." })
7754
8113
  ] }),
7755
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-grid-stats is-thin", children: [
7756
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(StatTile, { compact: true, label: "Wired", value: String(rm.shipped), sub: "of " + rm.items.length + " features" }),
7757
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(StatTile, { compact: true, label: "Remaining", value: String(rm.remaining), sub: "backend work" }),
7758
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(StatTile, { compact: true, label: "Est. effort", value: days ? days + " days" : "\u2014", sub: "sum of estimates, not calendar" }),
7759
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(StatTile, { compact: true, label: "Up next", value: nextPhase ? "Phase " + nextPhase.phase : "\u2014", sub: nextPhase ? nextPhase.phaseName : "all wired" })
8114
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-grid-stats is-thin", children: [
8115
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(StatTile, { compact: true, label: "Wired", value: String(rm.shipped), sub: "of " + rm.items.length + " features" }),
8116
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(StatTile, { compact: true, label: "Remaining", value: String(rm.remaining), sub: "backend work" }),
8117
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(StatTile, { compact: true, label: "Est. effort", value: days ? days + " days" : "\u2014", sub: "sum of estimates, not calendar" }),
8118
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(StatTile, { compact: true, label: "Up next", value: nextPhase ? "Phase " + nextPhase.phase : "\u2014", sub: nextPhase ? nextPhase.phaseName : "all wired" })
7760
8119
  ] }),
7761
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
7762
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Input, { icon: "magnifying-glass", placeholder: "Find a feature or a piece of backend work", value: q, onChange: (e) => setQ(e.target.value), style: { width: 300 } }),
7763
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Select, { placeholder: "All projects", value: project, onChange: (e) => setProject(e.target.value), options: projects, style: { width: 190 } }),
7764
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { style: { flex: 1 } }),
7765
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8120
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
8121
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Input, { icon: "magnifying-glass", placeholder: "Find a feature or a piece of backend work", value: q, onChange: (e) => setQ(e.target.value), style: { width: 300 } }),
8122
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Select, { placeholder: "All projects", value: project, onChange: (e) => setProject(e.target.value), options: projects, style: { width: 190 } }),
8123
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { style: { flex: 1 } }),
8124
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
7766
8125
  Button,
7767
8126
  {
7768
8127
  size: "sm",
@@ -7776,7 +8135,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7776
8135
  }
7777
8136
  )
7778
8137
  ] }),
7779
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8138
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
7780
8139
  Flag,
7781
8140
  {
7782
8141
  tone: "info",
@@ -7785,60 +8144,60 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7785
8144
  actions: null
7786
8145
  }
7787
8146
  ),
7788
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "fd-rm", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "fd-rm-scroll", ref: scrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-rm-track", children: [
7789
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-rm-spine", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-rm-spine-done", style: { height: items.length ? 100 * shippedShown / items.length + "%" : "0%" } }) }),
8147
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "fd-rm", children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "fd-rm-scroll", ref: scrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-rm-track", children: [
8148
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-rm-spine", children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-rm-spine-done", style: { height: items.length ? 100 * shippedShown / items.length + "%" : "0%" } }) }),
7790
8149
  items.map((item, n) => {
7791
8150
  const showPhase = item.phase !== lastPhase;
7792
8151
  lastPhase = item.phase;
7793
8152
  const inPhase = items.filter((i) => i.phase === item.phase).length;
7794
8153
  const isBoundary = n === firstPending;
7795
- return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(React35.Fragment, { children: [
7796
- showPhase ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-rm-era", children: [
7797
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
7798
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
7799
- item.phase === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-mono", style: { opacity: 0.7, fontWeight: 400 }, children: [
8154
+ return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(React39.Fragment, { children: [
8155
+ showPhase ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-rm-era", children: [
8156
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
8157
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
8158
+ item.phase === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-mono", style: { opacity: 0.7, fontWeight: 400 }, children: [
7800
8159
  fmtDate(item.phaseStart),
7801
8160
  " \u2013 ",
7802
8161
  fmtDate(item.date)
7803
8162
  ] }),
7804
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { style: { opacity: 0.7, fontWeight: 400 }, children: [
8163
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { style: { opacity: 0.7, fontWeight: 400 }, children: [
7805
8164
  "\xB7 ",
7806
8165
  inPhase,
7807
8166
  " feature",
7808
8167
  inPhase === 1 ? "" : "s"
7809
8168
  ] })
7810
8169
  ] }),
7811
- item.phaseWhy ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "fd-rm-era-why", children: item.phaseWhy }) : null
8170
+ item.phaseWhy ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { className: "fd-rm-era-why", children: item.phaseWhy }) : null
7812
8171
  ] }) : null,
7813
- isBoundary ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "fd-rm-now", ref: nowRef, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-rm-now-pill", children: [
7814
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("i", { className: "ph ph-map-pin" }),
8172
+ isBoundary ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "fd-rm-now", ref: nowRef, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-rm-now-pill", children: [
8173
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("i", { className: "ph ph-map-pin" }),
7815
8174
  " You are here \u2014 everything above is wired"
7816
8175
  ] }) }) : null,
7817
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-rm-row", children: [
7818
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-rm-dot " + (item.implemented ? "is-shipped" : item.status === "next" ? "is-next" : "") }),
7819
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-rm-date", children: [
8176
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-rm-row", children: [
8177
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-rm-dot " + (item.implemented ? "is-shipped" : item.status === "next" ? "is-next" : "") }),
8178
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-rm-date", children: [
7820
8179
  fmtDate(item.date),
7821
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("br", {}),
7822
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { style: { opacity: 0.75 }, children: item.implemented ? "shipped" : "phase " + item.phase })
8180
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("br", {}),
8181
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { style: { opacity: 0.75 }, children: item.implemented ? "shipped" : "phase " + item.phase })
7823
8182
  ] }),
7824
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(RoadmapCard, { item, byKey, onOpenFlag })
8183
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(RoadmapCard, { item, byKey, onOpenFlag })
7825
8184
  ] })
7826
8185
  ] }, item.key);
7827
8186
  }),
7828
- !items.length ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "fd-body-sm fd-muted", children: "Nothing matches that filter." }) : null
8187
+ !items.length ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { className: "fd-body-sm fd-muted", children: "Nothing matches that filter." }) : null
7829
8188
  ] }) }) }),
7830
- /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: [
8189
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: [
7831
8190
  "Derived from the feature-flag registry \u2014 each entry's status is its flag's ",
7832
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("code", { className: "fd-mono", children: "implemented" }),
8191
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("code", { className: "fd-mono", children: "implemented" }),
7833
8192
  " field, so this page cannot drift from what the apps actually do."
7834
8193
  ] })
7835
8194
  ] });
7836
8195
  }
7837
8196
 
7838
8197
  // src/components/platform/ComingSoon.tsx
7839
- var React36 = __toESM(require("react"), 1);
8198
+ var React40 = __toESM(require("react"), 1);
7840
8199
  var import_react_dom5 = require("react-dom");
7841
- var import_jsx_runtime62 = require("react/jsx-runtime");
8200
+ var import_jsx_runtime64 = require("react/jsx-runtime");
7842
8201
  var BYPASS_STORE = "fd.soon.bypass.v1";
7843
8202
  function readBypassed() {
7844
8203
  try {
@@ -7854,8 +8213,8 @@ function writeBypassed(list) {
7854
8213
  }
7855
8214
  }
7856
8215
  function useBypass(key) {
7857
- const [on, setOn] = React36.useState(() => !!key && readBypassed().indexOf(key) >= 0);
7858
- React36.useEffect(() => {
8216
+ const [on, setOn] = React40.useState(() => !!key && readBypassed().indexOf(key) >= 0);
8217
+ React40.useEffect(() => {
7859
8218
  setOn(!!key && readBypassed().indexOf(key) >= 0);
7860
8219
  }, [key]);
7861
8220
  const set = (next) => {
@@ -7868,43 +8227,43 @@ function useBypass(key) {
7868
8227
  return [on, set];
7869
8228
  }
7870
8229
  function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass }) {
7871
- return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(import_jsx_runtime62.Fragment, { children: [
7872
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-soon-badge", children: [
7873
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
8230
+ return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(import_jsx_runtime64.Fragment, { children: [
8231
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-soon-badge", children: [
8232
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
7874
8233
  label
7875
8234
  ] }),
7876
- detail ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-soon-detail", children: detail }) : null,
7877
- backend ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-soon-backend", children: [
7878
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-overline", children: "Needs" }),
8235
+ detail ? /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-soon-detail", children: detail }) : null,
8236
+ backend ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-soon-backend", children: [
8237
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-overline", children: "Needs" }),
7879
8238
  " ",
7880
8239
  backend
7881
8240
  ] }) : null,
7882
- eta || effort || onRoadmap ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-soon-meta", children: [
7883
- eta ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { children: [
7884
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-calendar-blank", "aria-hidden": "true" }),
8241
+ eta || effort || onRoadmap ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-soon-meta", children: [
8242
+ eta ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { children: [
8243
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-calendar-blank", "aria-hidden": "true" }),
7885
8244
  " ",
7886
8245
  eta
7887
8246
  ] }) : null,
7888
- effort ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { children: [
7889
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-hourglass-medium", "aria-hidden": "true" }),
8247
+ effort ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { children: [
8248
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-hourglass-medium", "aria-hidden": "true" }),
7890
8249
  " ",
7891
8250
  effort
7892
8251
  ] }) : null,
7893
- onRoadmap ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: onRoadmap, children: [
8252
+ onRoadmap ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: onRoadmap, children: [
7894
8253
  "See the roadmap ",
7895
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
8254
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
7896
8255
  ] }) : null
7897
8256
  ] }) : null,
7898
- allowed ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("button", { type: "button", className: "fd-soon-view", onClick: onBypass, children: [
7899
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-eye", "aria-hidden": "true" }),
8257
+ allowed ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-soon-view", onClick: onBypass, children: [
8258
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-eye", "aria-hidden": "true" }),
7900
8259
  " View and use it anyway"
7901
8260
  ] }) : null
7902
8261
  ] });
7903
8262
  }
7904
8263
  function useHoverCard(open) {
7905
- const anchor = React36.useRef(null);
7906
- const [pos, setPos] = React36.useState(null);
7907
- React36.useLayoutEffect(() => {
8264
+ const anchor = React40.useRef(null);
8265
+ const [pos, setPos] = React40.useState(null);
8266
+ React40.useLayoutEffect(() => {
7908
8267
  if (!open || !anchor.current) {
7909
8268
  setPos(null);
7910
8269
  return;
@@ -7956,9 +8315,9 @@ function ComingSoon({
7956
8315
  const tip = [label, detail, backend ? "Needs " + backend : null, eta ? "ETA " + eta : null, effort].filter(Boolean).join(" \xB7 ");
7957
8316
  if (inline) {
7958
8317
  if (allowed && bypassed) {
7959
- return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: ["fd-soon-inline-on", className].filter(Boolean).join(" "), ...rest, children: [
8318
+ return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: ["fd-soon-inline-on", className].filter(Boolean).join(" "), ...rest, children: [
7960
8319
  children,
7961
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
8320
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
7962
8321
  "button",
7963
8322
  {
7964
8323
  type: "button",
@@ -7966,12 +8325,12 @@ function ComingSoon({
7966
8325
  title: "Unwired \u2014 writes go to the simulated backend. " + tip + " Click to re-blur.",
7967
8326
  onClick: () => setBypassed(false),
7968
8327
  "aria-label": "Re-blur this unwired feature",
7969
- children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
8328
+ children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
7970
8329
  }
7971
8330
  )
7972
8331
  ] });
7973
8332
  }
7974
- return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
8333
+ return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
7975
8334
  InlineSoon,
7976
8335
  {
7977
8336
  label,
@@ -7991,20 +8350,20 @@ function ComingSoon({
7991
8350
  );
7992
8351
  }
7993
8352
  if (allowed && bypassed) {
7994
- return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 14 }, ...rest, children: [
7995
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-soon-bar", role: "status", children: [
7996
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-soon-badge", children: [
7997
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
8353
+ return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 14 }, ...rest, children: [
8354
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: "fd-soon-bar", role: "status", children: [
8355
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-soon-badge", children: [
8356
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
7998
8357
  "Unwired feature \u2014 you are using it anyway"
7999
8358
  ] }),
8000
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-soon-bar-detail", children: "Every action here writes to the simulated backend, so nothing you do persists beyond this session." }),
8001
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-soon-bar-actions", children: [
8002
- onRoadmap ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: onRoadmap, children: [
8359
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-soon-bar-detail", children: "Every action here writes to the simulated backend, so nothing you do persists beyond this session." }),
8360
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-soon-bar-actions", children: [
8361
+ onRoadmap ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: onRoadmap, children: [
8003
8362
  "Roadmap ",
8004
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
8363
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
8005
8364
  ] }) : null,
8006
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: () => setBypassed(false), children: [
8007
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-eye-slash", "aria-hidden": "true" }),
8365
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: () => setBypassed(false), children: [
8366
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-eye-slash", "aria-hidden": "true" }),
8008
8367
  " Re-blur"
8009
8368
  ] })
8010
8369
  ] })
@@ -8012,10 +8371,10 @@ function ComingSoon({
8012
8371
  children
8013
8372
  ] });
8014
8373
  }
8015
- return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: ["fd-soon", className].filter(Boolean).join(" "), style: minHeight ? { minHeight } : void 0, ...rest, children: [
8016
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "fd-soon-under", style: { filter: "blur(" + blur + "px) saturate(.62)" }, ...{ inert: "" }, "aria-hidden": "true", children }),
8017
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "fd-soon-veil" }),
8018
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "fd-soon-note", role: "note", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
8374
+ return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: ["fd-soon", className].filter(Boolean).join(" "), style: minHeight ? { minHeight } : void 0, ...rest, children: [
8375
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("div", { className: "fd-soon-under", style: { filter: "blur(" + blur + "px) saturate(.62)" }, ...{ inert: "" }, "aria-hidden": "true", children }),
8376
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("div", { className: "fd-soon-veil" }),
8377
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("div", { className: "fd-soon-note", role: "note", children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
8019
8378
  SoonCard,
8020
8379
  {
8021
8380
  label,
@@ -8031,10 +8390,10 @@ function ComingSoon({
8031
8390
  ] });
8032
8391
  }
8033
8392
  function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass, blur, tip, className, rest, children }) {
8034
- const [open, setOpen] = React36.useState(false);
8393
+ const [open, setOpen] = React40.useState(false);
8035
8394
  const [anchor, pos] = useHoverCard(open);
8036
8395
  const zIndex = useOverlayLayer(open);
8037
- const close = React36.useRef(null);
8396
+ const close = React40.useRef(null);
8038
8397
  const show = () => {
8039
8398
  if (close.current) {
8040
8399
  clearTimeout(close.current);
@@ -8050,10 +8409,10 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
8050
8409
  setOpen(false);
8051
8410
  }, 140);
8052
8411
  };
8053
- React36.useEffect(() => () => {
8412
+ React40.useEffect(() => () => {
8054
8413
  if (close.current) clearTimeout(close.current);
8055
8414
  }, []);
8056
- return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
8415
+ return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(
8057
8416
  "span",
8058
8417
  {
8059
8418
  ref: anchor,
@@ -8064,8 +8423,8 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
8064
8423
  onBlur: hide,
8065
8424
  ...rest,
8066
8425
  children: [
8067
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-soon-under", style: { filter: "blur(" + Math.min(blur, 1.1) + "px) saturate(.66)" }, ...{ inert: "" }, "aria-hidden": "true", children }),
8068
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
8426
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-soon-under", style: { filter: "blur(" + Math.min(blur, 1.1) + "px) saturate(.66)" }, ...{ inert: "" }, "aria-hidden": "true", children }),
8427
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
8069
8428
  "button",
8070
8429
  {
8071
8430
  type: "button",
@@ -8076,11 +8435,11 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
8076
8435
  onFocus: show,
8077
8436
  onBlur: hide,
8078
8437
  onClick: () => open ? setOpen(false) : show(),
8079
- children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
8438
+ children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
8080
8439
  }
8081
8440
  ),
8082
8441
  open && pos ? (0, import_react_dom5.createPortal)(
8083
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
8442
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
8084
8443
  "div",
8085
8444
  {
8086
8445
  className: "fd-soon-note fd-soon-hovercard",
@@ -8088,7 +8447,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
8088
8447
  style: { position: "fixed", left: pos.left, top: pos.top, bottom: pos.bottom, width: pos.width, zIndex },
8089
8448
  onMouseEnter: show,
8090
8449
  onMouseLeave: hide,
8091
- children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
8450
+ children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
8092
8451
  SoonCard,
8093
8452
  {
8094
8453
  label,
@@ -8118,10 +8477,10 @@ function formatEta(iso2) {
8118
8477
  var FormatEta = formatEta;
8119
8478
 
8120
8479
  // src/components/platform/Gate.tsx
8121
- var import_jsx_runtime63 = require("react/jsx-runtime");
8480
+ var import_jsx_runtime65 = require("react/jsx-runtime");
8122
8481
  function PermissionDenied({ permission, title, detail, compact: compact3 = false, className = "", ...rest }) {
8123
8482
  const need = Array.isArray(permission) ? permission : [permission].filter(Boolean);
8124
- return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(
8483
+ return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(
8125
8484
  "div",
8126
8485
  {
8127
8486
  className: ["fd-stack", className].filter(Boolean).join(" "),
@@ -8137,14 +8496,14 @@ function PermissionDenied({ permission, title, detail, compact: compact3 = false
8137
8496
  },
8138
8497
  ...rest,
8139
8498
  children: [
8140
- /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-row", style: { gap: 8 }, children: [
8141
- /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("i", { className: "ph ph-lock-simple", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
8142
- /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: compact3 ? "fd-label-lg" : "fd-h3", children: title || "You do not have access to this" })
8499
+ /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("span", { className: "fd-row", style: { gap: 8 }, children: [
8500
+ /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("i", { className: "ph ph-lock-simple", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
8501
+ /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: compact3 ? "fd-label-lg" : "fd-h3", children: title || "You do not have access to this" })
8143
8502
  ] }),
8144
- /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: detail || "Your roles do not grant this. An administrator can change that from Admin \u2192 Users." }),
8145
- need.length ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: [
8146
- /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-overline fd-muted", children: "Requires" }),
8147
- need.map((p) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("code", { className: "fd-mono", style: {
8503
+ /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: detail || "Your roles do not grant this. An administrator can change that from Admin \u2192 Users." }),
8504
+ need.length ? /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: [
8505
+ /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: "fd-overline fd-muted", children: "Requires" }),
8506
+ need.map((p) => /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("code", { className: "fd-mono", style: {
8148
8507
  fontSize: 11.5,
8149
8508
  padding: "2px 7px",
8150
8509
  borderRadius: 5,
@@ -8157,15 +8516,15 @@ function PermissionDenied({ permission, title, detail, compact: compact3 = false
8157
8516
  );
8158
8517
  }
8159
8518
  function Gate({ perm, anyOf, role, silent = false, fallback, compact: compact3 = false, children }) {
8160
- SessionKit.useSession();
8519
+ const { can: can2, canAny: canAny2, hasRole: hasRole2 } = useSessionContext();
8161
8520
  let ok = true;
8162
- if (perm) ok = ok && SessionKit.can(perm);
8163
- if (anyOf) ok = ok && SessionKit.canAny(anyOf);
8164
- if (role) ok = ok && (Array.isArray(role) ? role.some(SessionKit.hasRole) : SessionKit.hasRole(role));
8521
+ if (perm) ok = ok && can2(perm);
8522
+ if (anyOf) ok = ok && canAny2(anyOf);
8523
+ if (role) ok = ok && (Array.isArray(role) ? role.some(hasRole2) : hasRole2(role));
8165
8524
  if (ok) return children;
8166
8525
  if (fallback !== void 0) return fallback;
8167
8526
  if (silent) return null;
8168
- return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(PermissionDenied, { permission: perm || anyOf, compact: compact3 });
8527
+ return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(PermissionDenied, { permission: perm || anyOf, compact: compact3 });
8169
8528
  }
8170
8529
  function FeatureGate({
8171
8530
  flag,
@@ -8180,14 +8539,15 @@ function FeatureGate({
8180
8539
  bypassPerm = "flags.manage"
8181
8540
  }) {
8182
8541
  SessionKit.useSession();
8542
+ const { can: can2 } = useSessionContext();
8183
8543
  const rt = useFeatureStatus(flag);
8184
8544
  if (!SessionKit.isEnabled(flag)) return fallback;
8185
8545
  if (rt.complete || rt.mode === "test") return children;
8186
8546
  if (!preview) return fallback;
8187
8547
  const f = SessionKit.findFlag(flag) || {};
8188
8548
  const missing = rt.missing || [];
8189
- const canBypass = bypassPerm === null ? false : !bypassPerm || SessionKit.can(bypassPerm);
8190
- return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
8549
+ const canBypass = bypassPerm === null ? false : !bypassPerm || can2(bypassPerm);
8550
+ return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
8191
8551
  ComingSoon,
8192
8552
  {
8193
8553
  label: label || (variant === "inline" ? f.label || "Not wired yet" : "Designed \u2014 backend not wired yet"),
@@ -8206,28 +8566,28 @@ function FeatureGate({
8206
8566
  );
8207
8567
  }
8208
8568
  function PermissionHint({ perm, children }) {
8209
- SessionKit.useSession();
8210
- if (SessionKit.can(perm)) return children;
8211
- return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
8569
+ const { can: can2 } = useSessionContext();
8570
+ if (can2(perm)) return children;
8571
+ return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
8212
8572
  "span",
8213
8573
  {
8214
8574
  title: "Requires " + (Array.isArray(perm) ? perm.join(", ") : perm),
8215
8575
  style: { display: "inline-flex", opacity: 0.45, cursor: "not-allowed" },
8216
8576
  "aria-disabled": "true",
8217
- children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { style: { pointerEvents: "none" }, children })
8577
+ children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { style: { pointerEvents: "none" }, children })
8218
8578
  }
8219
8579
  );
8220
8580
  }
8221
8581
 
8222
8582
  // src/components/platform/ModeSwitch.tsx
8223
- var React37 = __toESM(require("react"), 1);
8224
- var import_jsx_runtime64 = require("react/jsx-runtime");
8583
+ var React41 = __toESM(require("react"), 1);
8584
+ var import_jsx_runtime66 = require("react/jsx-runtime");
8225
8585
  function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
8226
8586
  const mode2 = useRuntimeMode();
8227
- const [open, setOpen] = React37.useState(false);
8587
+ const [open, setOpen] = React41.useState(false);
8228
8588
  const zIndex = useOverlayLayer(open);
8229
- const ref = React37.useRef(null);
8230
- React37.useEffect(() => {
8589
+ const ref = React41.useRef(null);
8590
+ React41.useEffect(() => {
8231
8591
  if (!open) return;
8232
8592
  const away = (e) => {
8233
8593
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -8249,8 +8609,8 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
8249
8609
  RuntimeKit.setMode(next);
8250
8610
  setOpen(false);
8251
8611
  };
8252
- return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { style: { position: "relative", display: "inline-flex" }, ref, children: [
8253
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(
8612
+ return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { style: { position: "relative", display: "inline-flex" }, ref, children: [
8613
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(
8254
8614
  "button",
8255
8615
  {
8256
8616
  type: "button",
@@ -8260,29 +8620,29 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
8260
8620
  title: test ? "Test mode \u2014 every response is simulated. Click to inspect requests or return to live mode." : "Live mode \u2014 incomplete features are shown under construction. Click for test mode.",
8261
8621
  className: "fd-mode-btn" + (test ? " is-test" : "") + (open ? " is-open" : ""),
8262
8622
  children: [
8263
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph " + (test ? "ph-flask" : "ph-lightning"), style: { fontSize: 17 } }),
8264
- test && requestCount ? /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-mono fd-mode-count", children: requestCount > 99 ? "99+" : requestCount }, requestCount) : null
8623
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph " + (test ? "ph-flask" : "ph-lightning"), style: { fontSize: 17 } }),
8624
+ test && requestCount ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-mono fd-mode-count", children: requestCount > 99 ? "99+" : requestCount }, requestCount) : null
8265
8625
  ]
8266
8626
  }
8267
8627
  ),
8268
- open ? /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-view-enter fd-mode-pop", style: { zIndex }, children: [
8269
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
8270
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-badge " + (test ? "fd-badge-warning" : "fd-badge-neutral"), children: [
8271
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph " + (test ? "ph-flask" : "ph-lightning"), "aria-hidden": "true" }),
8628
+ open ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-view-enter fd-mode-pop", style: { zIndex }, children: [
8629
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
8630
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-badge " + (test ? "fd-badge-warning" : "fd-badge-neutral"), children: [
8631
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph " + (test ? "ph-flask" : "ph-lightning"), "aria-hidden": "true" }),
8272
8632
  test ? "Test mode" : "Live mode"
8273
8633
  ] }),
8274
- test ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-body-sm fd-muted fd-mono", children: [
8634
+ test ? /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-body-sm fd-muted fd-mono", children: [
8275
8635
  requestCount,
8276
8636
  " simulated request",
8277
8637
  requestCount === 1 ? "" : "s"
8278
8638
  ] }) : null,
8279
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { style: { flex: 1 } }),
8280
- test && onClearLog ? /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("button", { type: "button", className: "fd-soon-link", onClick: onClearLog, children: "Clear" }) : null
8639
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { style: { flex: 1 } }),
8640
+ test && onClearLog ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("button", { type: "button", className: "fd-soon-link", onClick: onClearLog, children: "Clear" }) : null
8281
8641
  ] }),
8282
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { style: { display: "block", padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
8283
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-body-sm fd-secondary", style: { display: "block", textWrap: "pretty" }, children: test ? "Every feature is usable and nothing is obstructed, but no response comes from a real service \u2014 nothing you do here persists. Use this to review and demo the design." : "This is what a user would see today. " + s.incomplete + " of " + s.total + " features depend on backend work that is not wired yet, so they render under construction." }),
8284
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-row", style: { gap: 8, marginTop: 10, flexWrap: "wrap" }, children: [
8285
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-body-sm fd-muted fd-mono", children: [
8642
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { style: { display: "block", padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
8643
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-body-sm fd-secondary", style: { display: "block", textWrap: "pretty" }, children: test ? "Every feature is usable and nothing is obstructed, but no response comes from a real service \u2014 nothing you do here persists. Use this to review and demo the design." : "This is what a user would see today. " + s.incomplete + " of " + s.total + " features depend on backend work that is not wired yet, so they render under construction." }),
8644
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-row", style: { gap: 8, marginTop: 10, flexWrap: "wrap" }, children: [
8645
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-body-sm fd-muted fd-mono", children: [
8286
8646
  s.endpointsWired,
8287
8647
  " endpoint",
8288
8648
  s.endpointsWired === 1 ? "" : "s",
@@ -8292,58 +8652,58 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
8292
8652
  s.total,
8293
8653
  " features complete"
8294
8654
  ] }),
8295
- onOpenSpec ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(import_jsx_runtime64.Fragment, { children: [
8296
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { style: { flex: 1 } }),
8297
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: () => {
8655
+ onOpenSpec ? /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(import_jsx_runtime66.Fragment, { children: [
8656
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { style: { flex: 1 } }),
8657
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: () => {
8298
8658
  setOpen(false);
8299
8659
  onOpenSpec();
8300
8660
  }, children: [
8301
8661
  "API spec ",
8302
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
8662
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
8303
8663
  ] })
8304
8664
  ] }) : null
8305
8665
  ] })
8306
8666
  ] }),
8307
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-mode-choice", children: [
8308
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-mode-opt" + (!test ? " is-on" : ""), onClick: () => go("live"), children: [
8309
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-lightning", "aria-hidden": "true" }),
8310
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-mode-opt-text", children: [
8311
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-mode-opt-title", children: "Live mode" }),
8312
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-mode-opt-desc", children: "Incomplete features under construction" })
8667
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-mode-choice", children: [
8668
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("button", { type: "button", className: "fd-mode-opt" + (!test ? " is-on" : ""), onClick: () => go("live"), children: [
8669
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-lightning", "aria-hidden": "true" }),
8670
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-mode-opt-text", children: [
8671
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-mode-opt-title", children: "Live mode" }),
8672
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-mode-opt-desc", children: "Incomplete features under construction" })
8313
8673
  ] }),
8314
- !test ? /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-check", "aria-hidden": "true" }) : null
8674
+ !test ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-check", "aria-hidden": "true" }) : null
8315
8675
  ] }),
8316
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-mode-opt" + (test ? " is-on" : ""), onClick: () => go("test"), children: [
8317
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-flask", "aria-hidden": "true" }),
8318
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-mode-opt-text", children: [
8319
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-mode-opt-title", children: "Test mode" }),
8320
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-mode-opt-desc", children: "Everything usable, all data simulated" })
8676
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("button", { type: "button", className: "fd-mode-opt" + (test ? " is-on" : ""), onClick: () => go("test"), children: [
8677
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-flask", "aria-hidden": "true" }),
8678
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-mode-opt-text", children: [
8679
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-mode-opt-title", children: "Test mode" }),
8680
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-mode-opt-desc", children: "Everything usable, all data simulated" })
8321
8681
  ] }),
8322
- test ? /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-check", "aria-hidden": "true" }) : null
8682
+ test ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-check", "aria-hidden": "true" }) : null
8323
8683
  ] })
8324
8684
  ] }),
8325
- test && renderLog ? /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { style: { display: "block", maxHeight: 340, overflowY: "auto", borderTop: "1px solid var(--border)" }, children: renderLog() }) : null
8685
+ test && renderLog ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { style: { display: "block", maxHeight: 340, overflowY: "auto", borderTop: "1px solid var(--border)" }, children: renderLog() }) : null
8326
8686
  ] }) }) : null
8327
8687
  ] });
8328
8688
  }
8329
8689
  function TestModeBar({ onExit }) {
8330
8690
  const mode2 = useRuntimeMode();
8331
8691
  if (mode2 !== "test") return null;
8332
- return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: "fd-testbar", role: "status", children: [
8333
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-badge fd-badge-warning", children: [
8334
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-flask", "aria-hidden": "true" }),
8692
+ return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("div", { className: "fd-testbar", role: "status", children: [
8693
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-badge fd-badge-warning", children: [
8694
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-flask", "aria-hidden": "true" }),
8335
8695
  "Test mode"
8336
8696
  ] }),
8337
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-testbar-detail", children: "Every feature is unlocked and every response is simulated \u2014 nothing here persists." }),
8338
- /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: () => onExit ? onExit() : RuntimeKit.setMode("live"), children: [
8339
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-lightning", "aria-hidden": "true" }),
8697
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-testbar-detail", children: "Every feature is unlocked and every response is simulated \u2014 nothing here persists." }),
8698
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: () => onExit ? onExit() : RuntimeKit.setMode("live"), children: [
8699
+ /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-lightning", "aria-hidden": "true" }),
8340
8700
  " Back to live mode"
8341
8701
  ] })
8342
8702
  ] });
8343
8703
  }
8344
8704
 
8345
8705
  // src/components/planner/ChannelMeta.tsx
8346
- var import_jsx_runtime65 = require("react/jsx-runtime");
8706
+ var import_jsx_runtime67 = require("react/jsx-runtime");
8347
8707
  var CHANNEL_WEIGHTS = {
8348
8708
  "OOH": 50,
8349
8709
  "DOOH": 50,
@@ -8396,9 +8756,9 @@ function ChannelTag({ channel, size = "md", showLabel = true, dot = false, weigh
8396
8756
  const m = ChannelMeta(channel);
8397
8757
  const wt = channelWeightOf(channel || "");
8398
8758
  if (dot) {
8399
- return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: ["fd-chan-dot", className].filter(Boolean).join(" "), title: m.name, style: { background: m.color }, ...rest });
8759
+ return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: ["fd-chan-dot", className].filter(Boolean).join(" "), title: m.name, style: { background: m.color }, ...rest });
8400
8760
  }
8401
- return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(
8761
+ return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
8402
8762
  "span",
8403
8763
  {
8404
8764
  className: ["fd-chan", size === "sm" ? "fd-chan-sm" : "", className].filter(Boolean).join(" "),
@@ -8406,16 +8766,16 @@ function ChannelTag({ channel, size = "md", showLabel = true, dot = false, weigh
8406
8766
  style: { "--chan": m.color },
8407
8767
  ...rest,
8408
8768
  children: [
8409
- /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }),
8410
- showLabel ? /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: "fd-chan-label", children: m.label }) : null,
8411
- weight ? /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: "fd-chan-weight", children: wt || 0 }) : null
8769
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }),
8770
+ showLabel ? /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "fd-chan-label", children: m.label }) : null,
8771
+ weight ? /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "fd-chan-weight", children: wt || 0 }) : null
8412
8772
  ]
8413
8773
  }
8414
8774
  );
8415
8775
  }
8416
8776
 
8417
8777
  // src/components/planner/SaturationDistribution.tsx
8418
- var import_jsx_runtime66 = require("react/jsx-runtime");
8778
+ var import_jsx_runtime68 = require("react/jsx-runtime");
8419
8779
  var BANDS2 = [
8420
8780
  { key: "weak", label: "Weak", n: 1, range: "< 50" },
8421
8781
  { key: "adequate", label: "Adequate", n: 2, range: "50\u2013100" },
@@ -8426,24 +8786,24 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8426
8786
  const grouped = BANDS2.map((b) => ({ ...b, items: campuses.filter((c) => c.band === b.key) }));
8427
8787
  const tallest = Math.max(1, ...grouped.map((g) => g.items.length));
8428
8788
  const floor = BANDS2.find((b) => b.key === floorBand) || BANDS2[0];
8429
- return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 16 }, children: [
8430
- /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("div", { style: { display: "grid", gridTemplateColumns: "repeat(4,minmax(0,1fr))", gap: 12, alignItems: "end" }, children: grouped.map((g) => {
8789
+ return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 16 }, children: [
8790
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { style: { display: "grid", gridTemplateColumns: "repeat(4,minmax(0,1fr))", gap: 12, alignItems: "end" }, children: grouped.map((g) => {
8431
8791
  const mark = "var(--csi-" + g.n + "-mark)";
8432
8792
  const active = selectedBand === g.key;
8433
- return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(
8793
+ return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)(
8434
8794
  "button",
8435
8795
  {
8436
8796
  type: "button",
8437
8797
  onClick: onSelectBand ? () => onSelectBand(active ? null : g.key) : void 0,
8438
8798
  style: { display: "flex", flexDirection: "column", justifyContent: "flex-end", gap: 10, padding: 12, border: "1px solid " + (active ? "var(--border-strong)" : "var(--border)"), borderRadius: "var(--r-lg)", background: active ? "var(--surface-2)" : "var(--surface)", cursor: onSelectBand ? "pointer" : "default", textAlign: "left", minHeight: 190, minWidth: 0, transition: "background var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)" },
8439
8799
  children: [
8440
- /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { style: { display: "flex", flexDirection: "column-reverse", gap: 4, minHeight: tallest * 24, minWidth: 0 }, children: loading ? Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-skel", style: { height: 16, borderRadius: 4 } }, i)) : g.items.map((c) => (
8800
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { style: { display: "flex", flexDirection: "column-reverse", gap: 4, minHeight: tallest * 24, minWidth: 0 }, children: loading ? Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-skel", style: { height: 16, borderRadius: 4 } }, i)) : g.items.map((c) => (
8441
8801
  // `overflow: hidden` + `white-space: nowrap` only clips once the span
8442
8802
  // actually HAS a bounded width to clip against (see the grid/flex
8443
8803
  // `minWidth: 0` chain above) — `textOverflow: ellipsis` then makes
8444
8804
  // that clip readable instead of a hard cut mid-letter; the full name
8445
8805
  // is still one hover away via `title`.
8446
- /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
8806
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
8447
8807
  "span",
8448
8808
  {
8449
8809
  title: c.name + " \xB7 " + c.crp,
@@ -8453,13 +8813,13 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8453
8813
  c.name
8454
8814
  )
8455
8815
  )) }),
8456
- /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-row", style: { gap: 8, alignItems: "baseline" }, children: [
8457
- /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-num-hero", style: { fontSize: 30, color: g.items.length ? "var(--text)" : "var(--text-disabled)" }, children: loading ? "\u2013" : g.items.length }),
8458
- /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-meter", style: { gap: 2, color: mark }, "aria-hidden": "true", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-meter-seg" + (i <= g.n ? " is-on" : ""), style: { width: 4, height: 9 } }, i)) })
8816
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "fd-row", style: { gap: 8, alignItems: "baseline" }, children: [
8817
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-num-hero", style: { fontSize: 30, color: g.items.length ? "var(--text)" : "var(--text-disabled)" }, children: loading ? "\u2013" : g.items.length }),
8818
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-meter", style: { gap: 2, color: mark }, "aria-hidden": "true", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-meter-seg" + (i <= g.n ? " is-on" : ""), style: { width: 4, height: 9 } }, i)) })
8459
8819
  ] }),
8460
- /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-stack", style: { gap: 1 }, children: [
8461
- /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-label-lg", children: g.label }),
8462
- /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-body-sm fd-muted", style: { fontSize: 11.5 }, children: [
8820
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "fd-stack", style: { gap: 1 }, children: [
8821
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-label-lg", children: g.label }),
8822
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "fd-body-sm fd-muted", style: { fontSize: 11.5 }, children: [
8463
8823
  "CRP ",
8464
8824
  g.range
8465
8825
  ] })
@@ -8469,10 +8829,10 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8469
8829
  g.key
8470
8830
  );
8471
8831
  }) }),
8472
- floorBand ? /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderRadius: "var(--r-md)", background: "var(--surface-2)", border: "1px solid var(--border)", flexWrap: "wrap" }, children: [
8473
- /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-meter", style: { gap: 2, color: "var(--csi-" + floor.n + "-mark)" }, "aria-hidden": "true", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-meter-seg" + (i <= floor.n ? " is-on" : ""), style: { width: 5, height: 12 } }, i)) }),
8474
- /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-body-sm fd-secondary", style: { flex: 1, minWidth: 240 }, children: [
8475
- /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("strong", { style: { color: "var(--text)" }, children: [
8832
+ floorBand ? /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderRadius: "var(--r-md)", background: "var(--surface-2)", border: "1px solid var(--border)", flexWrap: "wrap" }, children: [
8833
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-meter", style: { gap: 2, color: "var(--csi-" + floor.n + "-mark)" }, "aria-hidden": "true", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-meter-seg" + (i <= floor.n ? " is-on" : ""), style: { width: 5, height: 12 } }, i)) }),
8834
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "fd-body-sm fd-secondary", style: { flex: 1, minWidth: 240 }, children: [
8835
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("strong", { style: { color: "var(--text)" }, children: [
8476
8836
  "Plan floor ",
8477
8837
  floorScore
8478
8838
  ] }),
@@ -8483,8 +8843,8 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8483
8843
  }
8484
8844
 
8485
8845
  // src/components/planner/MixGap.tsx
8486
- var React38 = __toESM(require("react"), 1);
8487
- var import_jsx_runtime67 = require("react/jsx-runtime");
8846
+ var React42 = __toESM(require("react"), 1);
8847
+ var import_jsx_runtime69 = require("react/jsx-runtime");
8488
8848
  var MIX_GAP_LAYOUT = {
8489
8849
  trackHeight: 30,
8490
8850
  barHeight: 20,
@@ -8498,19 +8858,19 @@ function mixGapOffset(value, max) {
8498
8858
  }
8499
8859
  function MixGap({ rows = [], loading = false, className = "" }) {
8500
8860
  const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
8501
- const [hover, setHover] = React38.useState(null);
8861
+ const [hover, setHover] = React42.useState(null);
8502
8862
  const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
8503
8863
  const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
8504
8864
  const { trackHeight, barHeight, targetWidth, labelGap } = MIX_GAP_LAYOUT;
8505
8865
  const barTop = (trackHeight - barHeight) / 2;
8506
- return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 4 }, children: [
8507
- /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "fd-row", style: { gap: 16, justifyContent: "flex-end", paddingBottom: 6, flexWrap: "wrap" }, children: [
8508
- [["On target", TONE.ok], ["Close", TONE.warn], ["Short", TONE.danger]].map(([l, c]) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
8509
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { style: { width: 14, height: 9, borderRadius: 2, background: c } }),
8866
+ return /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 4 }, children: [
8867
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "fd-row", style: { gap: 16, justifyContent: "flex-end", paddingBottom: 6, flexWrap: "wrap" }, children: [
8868
+ [["On target", TONE.ok], ["Close", TONE.warn], ["Short", TONE.danger]].map(([l, c]) => /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
8869
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { style: { width: 14, height: 9, borderRadius: 2, background: c } }),
8510
8870
  l
8511
8871
  ] }, l)),
8512
- /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
8513
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { "data-mixgap-legend-target": "", style: { width: targetWidth, height: 16, borderRadius: 1, background: MIX_GAP_TARGET_STRIPE } }),
8872
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
8873
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { "data-mixgap-legend-target": "", style: { width: targetWidth, height: 16, borderRadius: 1, background: MIX_GAP_TARGET_STRIPE } }),
8514
8874
  "Target"
8515
8875
  ] })
8516
8876
  ] }),
@@ -8519,7 +8879,7 @@ function MixGap({ rows = [], loading = false, className = "" }) {
8519
8879
  const tone = toneOf(gap);
8520
8880
  const tip = "Realized " + r.realized + "% vs " + r.target + "% target" + (gap < -1 ? " \xB7 " + Math.abs(gap).toFixed(0) + " pts short" : gap > 1 ? " \xB7 " + gap.toFixed(0) + " pts over" : " \xB7 on target") + (r.note ? " \u2014 " + r.note : "");
8521
8881
  const labelLeft = r.realized > r.target ? `calc(${mixGapOffset(r.realized, max)} + ${labelGap}px)` : `calc(${mixGapOffset(r.target, max)} + ${targetWidth / 2 + labelGap}px)`;
8522
- return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
8882
+ return /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)(
8523
8883
  "div",
8524
8884
  {
8525
8885
  className: "fd-row",
@@ -8527,15 +8887,15 @@ function MixGap({ rows = [], loading = false, className = "" }) {
8527
8887
  onMouseEnter: () => setHover(r.channel),
8528
8888
  onMouseLeave: () => setHover(null),
8529
8889
  children: [
8530
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { style: { width: 128, flex: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(ChannelTag, { channel: r.channel, size: "sm" }) }),
8531
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { style: { position: "relative", flex: 1, height: trackHeight, minWidth: 120 }, children: loading ? /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "fd-skel", style: { position: "absolute", left: 0, right: 0, top: barTop, height: barHeight, borderRadius: 3 } }) : /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(import_jsx_runtime67.Fragment, { children: [
8532
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { "data-mixgap-bar": "", style: { position: "absolute", left: 0, top: barTop, height: barHeight, width: mixGapOffset(r.realized, max), background: TONE[tone], borderRadius: "2px 3px 3px 2px", transition: "width var(--dur-slow) var(--ease), background var(--dur-base) var(--ease)" } }),
8533
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { "data-mixgap-target": "", style: { position: "absolute", left: `calc(${mixGapOffset(r.target, max)} - ${targetWidth / 2}px)`, top: 0, width: targetWidth, height: trackHeight, background: MIX_GAP_TARGET_STRIPE, borderRadius: 1 } }),
8534
- /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("span", { "data-mixgap-label": "", className: "fd-num", style: { position: "absolute", left: labelLeft, top: "50%", transform: "translateY(-50%)", fontSize: 12, color: "var(--text-muted)", whiteSpace: "nowrap" }, children: [
8890
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { style: { width: 128, flex: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(ChannelTag, { channel: r.channel, size: "sm" }) }),
8891
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { style: { position: "relative", flex: 1, height: trackHeight, minWidth: 120 }, children: loading ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "fd-skel", style: { position: "absolute", left: 0, right: 0, top: barTop, height: barHeight, borderRadius: 3 } }) : /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)(import_jsx_runtime69.Fragment, { children: [
8892
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { "data-mixgap-bar": "", style: { position: "absolute", left: 0, top: barTop, height: barHeight, width: mixGapOffset(r.realized, max), background: TONE[tone], borderRadius: "2px 3px 3px 2px", transition: "width var(--dur-slow) var(--ease), background var(--dur-base) var(--ease)" } }),
8893
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { "data-mixgap-target": "", style: { position: "absolute", left: `calc(${mixGapOffset(r.target, max)} - ${targetWidth / 2}px)`, top: 0, width: targetWidth, height: trackHeight, background: MIX_GAP_TARGET_STRIPE, borderRadius: 1 } }),
8894
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("span", { "data-mixgap-label": "", className: "fd-num", style: { position: "absolute", left: labelLeft, top: "50%", transform: "translateY(-50%)", fontSize: 12, color: "var(--text-muted)", whiteSpace: "nowrap" }, children: [
8535
8895
  r.realized,
8536
8896
  "%"
8537
8897
  ] }),
8538
- hover === r.channel ? /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "fd-tooltip", role: "tooltip", style: { position: "absolute", left: 0, right: "auto", bottom: trackHeight + 4, maxWidth: "min(340px, 100%)", whiteSpace: "normal", textAlign: "left", width: "max-content" }, children: tip }) : null
8898
+ hover === r.channel ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "fd-tooltip", role: "tooltip", style: { position: "absolute", left: 0, right: "auto", bottom: trackHeight + 4, maxWidth: "min(340px, 100%)", whiteSpace: "normal", textAlign: "left", width: "max-content" }, children: tip }) : null
8539
8899
  ] }) })
8540
8900
  ]
8541
8901
  },
@@ -8546,7 +8906,7 @@ function MixGap({ rows = [], loading = false, className = "" }) {
8546
8906
  }
8547
8907
 
8548
8908
  // src/components/planner/ChannelContribution.tsx
8549
- var import_jsx_runtime68 = require("react/jsx-runtime");
8909
+ var import_jsx_runtime70 = require("react/jsx-runtime");
8550
8910
  var money = (n) => "$" + Math.round(n).toLocaleString();
8551
8911
  function ChannelContribution({
8552
8912
  channels = [],
@@ -8562,9 +8922,9 @@ function ChannelContribution({
8562
8922
  const grand = total !== void 0 ? total : base + bonus;
8563
8923
  const pct = (v) => grand ? v / grand * 100 : 0;
8564
8924
  const spendTotal = channels.reduce((s, c) => s + Number(String(c.spend || 0).replace(/[^0-9.]/g, "")), 0);
8565
- return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 16 }, children: [
8566
- loading ? /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-skel", style: { height: 34, borderRadius: "var(--r-sm)" } }) : /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("div", { className: "fd-row", style: { height: 34, borderRadius: "var(--r-sm)", overflow: "hidden", gap: 2, background: "var(--surface-3)" }, children: [
8567
- channels.map((c) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
8925
+ return /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 16 }, children: [
8926
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("span", { className: "fd-skel", style: { height: 34, borderRadius: "var(--r-sm)" } }) : /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { className: "fd-row", style: { height: 34, borderRadius: "var(--r-sm)", overflow: "hidden", gap: 2, background: "var(--surface-3)" }, children: [
8927
+ channels.map((c) => /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(
8568
8928
  "span",
8569
8929
  {
8570
8930
  title: c.name + " \xB7 " + c.crp.toFixed(1) + " CRP",
@@ -8573,7 +8933,7 @@ function ChannelContribution({
8573
8933
  },
8574
8934
  c.name
8575
8935
  )),
8576
- bonus > 0 ? /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
8936
+ bonus > 0 ? /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(
8577
8937
  "span",
8578
8938
  {
8579
8939
  title: "Surround-sound bonus +" + bonusPct + "%",
@@ -8582,18 +8942,18 @@ function ChannelContribution({
8582
8942
  }
8583
8943
  ) : null
8584
8944
  ] }),
8585
- showTable ? /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("table", { className: "fd-table", style: { fontSize: "var(--body-sm-size)" }, children: [
8586
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("tr", { children: [
8587
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("th", { style: { width: "34%" }, children: "Channel" }),
8588
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("th", { style: { width: "16%" }, children: "Weight" }),
8589
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("th", { className: "is-num", style: { width: "16%" }, children: "Spend" }),
8590
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("th", { className: "is-num", style: { width: "17%" }, children: "CRP" }),
8591
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("th", { className: "is-num", style: { width: "17%" }, children: "Share" })
8945
+ showTable ? /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("table", { className: "fd-table", style: { fontSize: "var(--body-sm-size)" }, children: [
8946
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("tr", { children: [
8947
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("th", { style: { width: "34%" }, children: "Channel" }),
8948
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("th", { style: { width: "16%" }, children: "Weight" }),
8949
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("th", { className: "is-num", style: { width: "16%" }, children: "Spend" }),
8950
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("th", { className: "is-num", style: { width: "17%" }, children: "CRP" }),
8951
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("th", { className: "is-num", style: { width: "17%" }, children: "Share" })
8592
8952
  ] }) }),
8593
- /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("tbody", { children: [
8594
- channels.map((c) => /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("tr", { children: [
8595
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(ChannelTag, { channel: c.name, size: "sm" }) }),
8596
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
8953
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("tbody", { children: [
8954
+ channels.map((c) => /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("tr", { children: [
8955
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(ChannelTag, { channel: c.name, size: "sm" }) }),
8956
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(
8597
8957
  "span",
8598
8958
  {
8599
8959
  className: "fd-badge fd-badge-neutral",
@@ -8602,38 +8962,38 @@ function ChannelContribution({
8602
8962
  children: "weight " + (channelWeightOf(c.name) || 0)
8603
8963
  }
8604
8964
  ) }),
8605
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { className: "is-num", children: c.spend }),
8606
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { className: "is-num", children: c.crp.toFixed(1) }),
8607
- /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("td", { className: "is-num", children: [
8965
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", children: c.spend }),
8966
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", children: c.crp.toFixed(1) }),
8967
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("td", { className: "is-num", children: [
8608
8968
  pct(c.crp).toFixed(0),
8609
8969
  "%"
8610
8970
  ] })
8611
8971
  ] }, c.name)),
8612
- bonus > 0 ? /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("tr", { children: [
8613
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "fd-row", style: { gap: 9, fontWeight: 600 }, children: [
8614
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { style: { width: 9, height: 9, borderRadius: 2, background: "repeating-linear-gradient(135deg,var(--csi-3-mark) 0 3px,var(--csi-2-mark) 3px 6px)", flex: "none" } }),
8972
+ bonus > 0 ? /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("tr", { children: [
8973
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { className: "fd-row", style: { gap: 9, fontWeight: 600 }, children: [
8974
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("span", { style: { width: 9, height: 9, borderRadius: 2, background: "repeating-linear-gradient(135deg,var(--csi-3-mark) 0 3px,var(--csi-2-mark) 3px 6px)", flex: "none" } }),
8615
8975
  "Surround-sound bonus"
8616
8976
  ] }) }),
8617
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "fd-badge fd-badge-success", style: { height: 20, fontSize: 11 }, children: [
8977
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { className: "fd-badge fd-badge-success", style: { height: 20, fontSize: 11 }, children: [
8618
8978
  "+",
8619
8979
  bonusPct,
8620
8980
  "% of +",
8621
8981
  bonusMax,
8622
8982
  "%"
8623
8983
  ] }) }),
8624
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { className: "is-num fd-muted", children: "\u2014" }),
8625
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { className: "is-num", children: bonus.toFixed(1) }),
8626
- /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("td", { className: "is-num", children: [
8984
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num fd-muted", children: "\u2014" }),
8985
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", children: bonus.toFixed(1) }),
8986
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("td", { className: "is-num", children: [
8627
8987
  pct(bonus).toFixed(0),
8628
8988
  "%"
8629
8989
  ] })
8630
8990
  ] }) : null,
8631
- /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("tr", { style: { background: "var(--surface-2)" }, children: [
8632
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { style: { fontWeight: 700 }, children: "Campus total" }),
8633
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", {}),
8634
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { className: "is-num", style: { fontWeight: 700 }, children: money(spendTotal) }),
8635
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { className: "is-num", style: { fontWeight: 700 }, children: grand.toFixed(1) }),
8636
- /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("td", { className: "is-num", style: { fontWeight: 700 }, children: "100%" })
8991
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("tr", { style: { background: "var(--surface-2)" }, children: [
8992
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { style: { fontWeight: 700 }, children: "Campus total" }),
8993
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", {}),
8994
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", style: { fontWeight: 700 }, children: money(spendTotal) }),
8995
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", style: { fontWeight: 700 }, children: grand.toFixed(1) }),
8996
+ /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", style: { fontWeight: 700 }, children: "100%" })
8637
8997
  ] })
8638
8998
  ] })
8639
8999
  ] }) : null
@@ -8641,8 +9001,8 @@ function ChannelContribution({
8641
9001
  }
8642
9002
 
8643
9003
  // src/components/planner/BudgetReallocator.tsx
8644
- var React39 = __toESM(require("react"), 1);
8645
- var import_jsx_runtime69 = require("react/jsx-runtime");
9004
+ var React43 = __toESM(require("react"), 1);
9005
+ var import_jsx_runtime71 = require("react/jsx-runtime");
8646
9006
  var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
8647
9007
  function BudgetReallocator({
8648
9008
  campus,
@@ -8657,8 +9017,8 @@ function BudgetReallocator({
8657
9017
  onCancel,
8658
9018
  className = ""
8659
9019
  }) {
8660
- const [draft, setDraft] = React39.useState(spend);
8661
- React39.useEffect(() => setDraft(spend), [spend]);
9020
+ const [draft, setDraft] = React43.useState(spend);
9021
+ React43.useEffect(() => setDraft(spend), [spend]);
8662
9022
  const dirty = draft !== spend;
8663
9023
  const nextCrp = scoreFor ? scoreFor(draft) : crp;
8664
9024
  const nextBand = bandFor(nextCrp);
@@ -8673,10 +9033,10 @@ function BudgetReallocator({
8673
9033
  setDraft(spend);
8674
9034
  if (onCancel) onCancel();
8675
9035
  };
8676
- const trackRef = React39.useRef(null);
8677
- const chipRef = React39.useRef(null);
8678
- const [chipLeft, setChipLeft] = React39.useState(null);
8679
- React39.useLayoutEffect(() => {
9036
+ const trackRef = React43.useRef(null);
9037
+ const chipRef = React43.useRef(null);
9038
+ const [chipLeft, setChipLeft] = React43.useState(null);
9039
+ React43.useLayoutEffect(() => {
8680
9040
  const track = trackRef.current;
8681
9041
  if (!dirty || !track) {
8682
9042
  setChipLeft(null);
@@ -8697,23 +9057,23 @@ function BudgetReallocator({
8697
9057
  ro.observe(track);
8698
9058
  return () => ro.disconnect();
8699
9059
  }, [dirty, pct]);
8700
- return /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)(
9060
+ return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(
8701
9061
  "div",
8702
9062
  {
8703
9063
  className: ["fd-stack", className].filter(Boolean).join(" "),
8704
9064
  style: { gap: 14, padding: 18, border: "1px solid " + (dirty ? "var(--border-brand)" : "var(--border)"), borderRadius: "var(--r-lg)", background: dirty ? "var(--surface-brand)" : "var(--surface)", color: "var(--text)", transition: "background var(--dur-base) var(--ease),border-color var(--dur-base) var(--ease)" },
8705
9065
  children: [
8706
- /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
8707
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "fd-h4", style: { flex: 1, minWidth: 140 }, children: campus }),
8708
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(CsiBadge, { band: nowBand, crp, size: "medium" }),
8709
- dirty ? /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)(import_jsx_runtime69.Fragment, { children: [
8710
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("i", { className: "ph ph-arrow-right fd-muted", "aria-hidden": "true" }),
8711
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(CsiBadge, { band: nextBand, crp: nextCrp, size: "medium", preview: true })
9066
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
9067
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "fd-h4", style: { flex: 1, minWidth: 140 }, children: campus }),
9068
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(CsiBadge, { band: nowBand, crp, size: "medium" }),
9069
+ dirty ? /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(import_jsx_runtime71.Fragment, { children: [
9070
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("i", { className: "ph ph-arrow-right fd-muted", "aria-hidden": "true" }),
9071
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(CsiBadge, { band: nextBand, crp: nextCrp, size: "medium", preview: true })
8712
9072
  ] }) : null
8713
9073
  ] }),
8714
- /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "fd-slider", ref: trackRef, children: [
8715
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "fd-slider-fill", style: { width: pct + "%" } }),
8716
- dirty ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
9074
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "fd-slider", ref: trackRef, children: [
9075
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "fd-slider-fill", style: { width: pct + "%" } }),
9076
+ dirty ? /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
8717
9077
  "span",
8718
9078
  {
8719
9079
  ref: chipRef,
@@ -8725,7 +9085,7 @@ function BudgetReallocator({
8725
9085
  children: (delta < 0 ? "\u2212$" : "+$") + Math.abs(delta).toLocaleString()
8726
9086
  }
8727
9087
  ) : null,
8728
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
9088
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
8729
9089
  "input",
8730
9090
  {
8731
9091
  type: "range",
@@ -8741,13 +9101,13 @@ function BudgetReallocator({
8741
9101
  }
8742
9102
  )
8743
9103
  ] }),
8744
- /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
8745
- /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("span", { className: "fd-stack", style: { gap: 2, flex: 1, minWidth: 150 }, children: [
8746
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "fd-num", style: { fontSize: 19, fontWeight: 700 }, children: "$" + draft.toLocaleString() }),
8747
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "fd-body-sm fd-muted", children: "Arrow keys step $100, shift+arrow $1,000. Escape reverts." })
9104
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
9105
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("span", { className: "fd-stack", style: { gap: 2, flex: 1, minWidth: 150 }, children: [
9106
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "fd-num", style: { fontSize: 19, fontWeight: 700 }, children: "$" + draft.toLocaleString() }),
9107
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "fd-body-sm fd-muted", children: "Arrow keys step $100, shift+arrow $1,000. Escape reverts." })
8748
9108
  ] }),
8749
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("button", { type: "button", className: "fd-btn fd-btn-ghost", disabled: !dirty, onClick: revert, children: "Cancel" }),
8750
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
9109
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("button", { type: "button", className: "fd-btn fd-btn-ghost", disabled: !dirty, onClick: revert, children: "Cancel" }),
9110
+ /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
8751
9111
  "button",
8752
9112
  {
8753
9113
  type: "button",
@@ -8766,7 +9126,7 @@ function BudgetReallocator({
8766
9126
  }
8767
9127
 
8768
9128
  // src/components/planner/SurroundSound.tsx
8769
- var import_jsx_runtime70 = require("react/jsx-runtime");
9129
+ var import_jsx_runtime72 = require("react/jsx-runtime");
8770
9130
  var CATEGORIES = [
8771
9131
  { key: "ooh", label: "OOH", icon: "flag-banner", color: "var(--ch-ooh)" },
8772
9132
  { key: "transit", label: "Transit", icon: "bus", color: "var(--ch-transit)" },
@@ -8778,26 +9138,26 @@ var CATEGORIES = [
8778
9138
  function SurroundSound({ present = [], bonusPct = 0, bonusMax = 40, missedCost, className = "" }) {
8779
9139
  const earned = Math.max(0, Math.min(1, bonusPct / bonusMax));
8780
9140
  const single = present.length <= 2;
8781
- return /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 14 }, children: [
8782
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: CATEGORIES.map((c) => {
9141
+ return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 14 }, children: [
9142
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: CATEGORIES.map((c) => {
8783
9143
  const on = present.includes(c.key);
8784
- return /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)(
9144
+ return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(
8785
9145
  "span",
8786
9146
  {
8787
9147
  title: c.label + (on ? " \u2014 present" : " \u2014 not bought"),
8788
9148
  style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 5, width: 62, padding: "10px 0", borderRadius: "var(--r-md)", border: "1px solid " + (on ? "var(--csi-2-edge)" : "var(--border)"), background: on ? "var(--csi-2-fill)" : "var(--surface-2)", color: on ? "var(--csi-2-text)" : "var(--text-disabled)" },
8789
9149
  children: [
8790
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("i", { className: "ph ph-" + c.icon, style: { fontSize: 19, color: on ? c.color : "var(--text-muted)" }, "aria-hidden": "true" }),
8791
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("span", { style: { fontSize: 10.5, fontWeight: 700 }, children: c.label })
9150
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("i", { className: "ph ph-" + c.icon, style: { fontSize: 19, color: on ? c.color : "var(--text-muted)" }, "aria-hidden": "true" }),
9151
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { style: { fontSize: 10.5, fontWeight: 700 }, children: c.label })
8792
9152
  ]
8793
9153
  },
8794
9154
  c.key
8795
9155
  );
8796
9156
  }) }),
8797
- /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { className: "fd-stack", style: { gap: 8 }, children: [
8798
- /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { className: "fd-row", style: { justifyContent: "space-between", gap: 12 }, children: [
8799
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("span", { className: "fd-label-lg", children: "Surround-sound bonus earned" }),
8800
- /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { className: "fd-num", style: { color: single ? "var(--warn-text)" : "var(--csi-3-mark)", fontWeight: 700 }, children: [
9157
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: "fd-stack", style: { gap: 8 }, children: [
9158
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: "fd-row", style: { justifyContent: "space-between", gap: 12 }, children: [
9159
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { className: "fd-label-lg", children: "Surround-sound bonus earned" }),
9160
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("span", { className: "fd-num", style: { color: single ? "var(--warn-text)" : "var(--csi-3-mark)", fontWeight: 700 }, children: [
8801
9161
  "+",
8802
9162
  bonusPct,
8803
9163
  "% of +",
@@ -8805,8 +9165,8 @@ function SurroundSound({ present = [], bonusPct = 0, bonusMax = 40, missedCost,
8805
9165
  "%"
8806
9166
  ] })
8807
9167
  ] }),
8808
- /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("div", { style: { height: 8, borderRadius: "var(--r-xs)", background: "var(--surface-3)", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("div", { style: { height: "100%", width: earned * 100 + "%", background: "var(--csi-3-mark)", borderRadius: "inherit", transition: "width var(--dur-slow) var(--ease)" } }) }),
8809
- /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: [
9168
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("div", { style: { height: 8, borderRadius: "var(--r-xs)", background: "var(--surface-3)", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("div", { style: { height: "100%", width: earned * 100 + "%", background: "var(--csi-3-mark)", borderRadius: "inherit", transition: "width var(--dur-slow) var(--ease)" } }) }),
9169
+ /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: [
8810
9170
  present.length,
8811
9171
  " of 6 channel categories present.",
8812
9172
  " ",
@@ -8818,7 +9178,7 @@ function SurroundSound({ present = [], bonusPct = 0, bonusMax = 40, missedCost,
8818
9178
  }
8819
9179
 
8820
9180
  // src/components/planner/Importance.tsx
8821
- var import_jsx_runtime71 = require("react/jsx-runtime");
9181
+ var import_jsx_runtime73 = require("react/jsx-runtime");
8822
9182
  var IMPORTANCE_LEVELS = [
8823
9183
  { id: "nudge", rank: 1, label: "Nudge", icon: "feather", description: "Breaks a tie, nothing more." },
8824
9184
  { id: "lean", rank: 2, label: "Lean", icon: "arrow-bend-up-right", description: "A mild preference between otherwise equal options." },
@@ -8848,23 +9208,23 @@ function importanceTokens(id) {
8848
9208
  function ImportanceTag({ level, showLabel = true, size = "sm", className = "" }) {
8849
9209
  const lvl = importanceLevel(level);
8850
9210
  const t = importanceTokens(level);
8851
- return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(
9211
+ return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(
8852
9212
  "span",
8853
9213
  {
8854
9214
  className: ["fd-imp-tag", size === "md" ? "is-md" : "", className].filter(Boolean).join(" "),
8855
9215
  style: { background: t.fill, borderColor: t.edge, color: t.text },
8856
9216
  title: lvl.label + " \u2014 " + lvl.description,
8857
9217
  children: [
8858
- /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("i", { className: "ph ph-" + lvl.icon, "aria-hidden": "true" }),
8859
- showLabel ? lvl.label : /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "fd-sr", children: lvl.label })
9218
+ /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-" + lvl.icon, "aria-hidden": "true" }),
9219
+ showLabel ? lvl.label : /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fd-sr", children: lvl.label })
8860
9220
  ]
8861
9221
  }
8862
9222
  );
8863
9223
  }
8864
9224
 
8865
9225
  // src/components/planner/PreferenceMeter.tsx
8866
- var React40 = __toESM(require("react"), 1);
8867
- var import_jsx_runtime72 = require("react/jsx-runtime");
9226
+ var React44 = __toESM(require("react"), 1);
9227
+ var import_jsx_runtime74 = require("react/jsx-runtime");
8868
9228
  function preferenceScore(criteria) {
8869
9229
  const earned = criteria.reduce((n, c) => n + (Number(c.earned) || 0), 0);
8870
9230
  const weight = criteria.reduce((n, c) => n + (Number(c.weight) || 0), 0);
@@ -8893,8 +9253,8 @@ function PreferenceMeter({
8893
9253
  className = ""
8894
9254
  }) {
8895
9255
  const value = score == null ? preferenceScore(criteria) : score;
8896
- const segments = React40.useMemo(() => preferenceSegments(criteria), [criteria]);
8897
- return /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(
9256
+ const segments = React44.useMemo(() => preferenceSegments(criteria), [criteria]);
9257
+ return /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
8898
9258
  ScoreMeter,
8899
9259
  {
8900
9260
  score: value,
@@ -8906,22 +9266,22 @@ function PreferenceMeter({
8906
9266
  placement,
8907
9267
  className,
8908
9268
  breakdownLabel: breakdownTitle,
8909
- breakdown: /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: "fd-prefbreak", children: [
8910
- /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: "fd-prefbreak-head", children: [
8911
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { className: "fd-label-lg", children: breakdownTitle }),
8912
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { className: "fd-prefbreak-score fd-tabular", children: Math.round(value) })
9269
+ breakdown: /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fd-prefbreak", children: [
9270
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fd-prefbreak-head", children: [
9271
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fd-label-lg", children: breakdownTitle }),
9272
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fd-prefbreak-score fd-tabular", children: Math.round(value) })
8913
9273
  ] }),
8914
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("ul", { className: "fd-prefbreak-list", children: criteria.map((c) => {
9274
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("ul", { className: "fd-prefbreak-list", children: criteria.map((c) => {
8915
9275
  const pct = c.weight > 0 ? Math.round((Number(c.earned) || 0) / c.weight * 100) : 0;
8916
- return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("li", { className: pct === 0 ? "fd-prefbreak-item is-unmet" : "fd-prefbreak-item", children: [
8917
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(ImportanceTag, { level: c.importance }),
8918
- /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("span", { className: "fd-prefbreak-text", children: [
8919
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { className: "fd-prefbreak-label", children: c.label }),
8920
- c.note ? /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { className: "fd-prefbreak-note", children: c.note }) : null
9276
+ return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("li", { className: pct === 0 ? "fd-prefbreak-item is-unmet" : "fd-prefbreak-item", children: [
9277
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(ImportanceTag, { level: c.importance }),
9278
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("span", { className: "fd-prefbreak-text", children: [
9279
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fd-prefbreak-label", children: c.label }),
9280
+ c.note ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fd-prefbreak-note", children: c.note }) : null
8921
9281
  ] }),
8922
- /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("span", { className: "fd-prefbreak-val fd-tabular", children: [
9282
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("span", { className: "fd-prefbreak-val fd-tabular", children: [
8923
9283
  Math.round(Number(c.earned) || 0),
8924
- /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { className: "fd-prefbreak-sep", children: "/" }),
9284
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fd-prefbreak-sep", children: "/" }),
8925
9285
  Math.round(c.weight)
8926
9286
  ] })
8927
9287
  ] }, c.id);
@@ -8932,10 +9292,10 @@ function PreferenceMeter({
8932
9292
  }
8933
9293
 
8934
9294
  // src/components/chat/AgentChatPanel.tsx
8935
- var React46 = __toESM(require("react"), 1);
9295
+ var React50 = __toESM(require("react"), 1);
8936
9296
 
8937
9297
  // src/components/chat/chatEngine.ts
8938
- var React41 = __toESM(require("react"), 1);
9298
+ var React45 = __toESM(require("react"), 1);
8939
9299
  var CHAT_UNAVAILABLE = "chat_unavailable";
8940
9300
  var JOB_PENDING = ["queued", "running"];
8941
9301
  var JOB_SUCCESS = ["completed", "recovered"];
@@ -8989,22 +9349,22 @@ function useChatEngine(opts) {
8989
9349
  onClear,
8990
9350
  onFeedback
8991
9351
  } = opts || {};
8992
- const [status, setStatus] = React41.useState("idle");
8993
- const [threadId, setThreadId] = React41.useState(null);
8994
- const [messages, setMessages] = React41.useState([]);
8995
- const [queue, setQueue] = React41.useState([]);
8996
- const [fatal, setFatal] = React41.useState(null);
8997
- const [busy, setBusy] = React41.useState(false);
8998
- const [turnStartedAt, setTurnStartedAt] = React41.useState(null);
8999
- const listRef = React41.useRef([]);
9000
- const queueRef = React41.useRef([]);
9001
- const busyRef = React41.useRef(false);
9002
- const stoppedRef = React41.useRef(false);
9003
- const abortRef = React41.useRef(null);
9004
- const serverCount = React41.useRef(0);
9005
- const threadRef = React41.useRef(null);
9006
- const mounted = React41.useRef(true);
9007
- React41.useEffect(() => {
9352
+ const [status, setStatus] = React45.useState("idle");
9353
+ const [threadId, setThreadId] = React45.useState(null);
9354
+ const [messages, setMessages] = React45.useState([]);
9355
+ const [queue, setQueue] = React45.useState([]);
9356
+ const [fatal, setFatal] = React45.useState(null);
9357
+ const [busy, setBusy] = React45.useState(false);
9358
+ const [turnStartedAt, setTurnStartedAt] = React45.useState(null);
9359
+ const listRef = React45.useRef([]);
9360
+ const queueRef = React45.useRef([]);
9361
+ const busyRef = React45.useRef(false);
9362
+ const stoppedRef = React45.useRef(false);
9363
+ const abortRef = React45.useRef(null);
9364
+ const serverCount = React45.useRef(0);
9365
+ const threadRef = React45.useRef(null);
9366
+ const mounted = React45.useRef(true);
9367
+ React45.useEffect(() => {
9008
9368
  mounted.current = true;
9009
9369
  return () => {
9010
9370
  mounted.current = false;
@@ -9026,14 +9386,14 @@ function useChatEngine(opts) {
9026
9386
  }
9027
9387
  return false;
9028
9388
  };
9029
- const loadThread = React41.useCallback(async (id) => {
9389
+ const loadThread = React45.useCallback(async (id) => {
9030
9390
  const data = await apiAdapter.getThread(id);
9031
9391
  const list = [...data && data.messages || []];
9032
9392
  serverCount.current = list.length;
9033
9393
  commit(list);
9034
9394
  return list;
9035
9395
  }, [apiAdapter]);
9036
- React41.useEffect(() => {
9396
+ React45.useEffect(() => {
9037
9397
  if (!apiAdapter) {
9038
9398
  setStatus("idle");
9039
9399
  setFatal(null);
@@ -9246,7 +9606,7 @@ function useChatEngine(opts) {
9246
9606
  await dispatchTurn(turn);
9247
9607
  }
9248
9608
  }
9249
- const send = React41.useCallback((text, attachments) => {
9609
+ const send = React45.useCallback((text, attachments) => {
9250
9610
  const body = (text || "").trim();
9251
9611
  if (!body && !(attachments && attachments.length)) return;
9252
9612
  if (status === "disconnected") return;
@@ -9256,7 +9616,7 @@ function useChatEngine(opts) {
9256
9616
  stoppedRef.current = false;
9257
9617
  drain();
9258
9618
  }, [status]);
9259
- const stop = React41.useCallback(() => {
9619
+ const stop = React45.useCallback(() => {
9260
9620
  stoppedRef.current = true;
9261
9621
  const ac = abortRef.current;
9262
9622
  if (ac) {
@@ -9275,11 +9635,11 @@ function useChatEngine(opts) {
9275
9635
  store.del(STORAGE_PREFIX + threadRef.current);
9276
9636
  }
9277
9637
  }, [apiAdapter]);
9278
- const removeQueued = React41.useCallback((id) => {
9638
+ const removeQueued = React45.useCallback((id) => {
9279
9639
  queueRef.current = queueRef.current.filter((t) => t.id !== id);
9280
9640
  setQueue(queueRef.current.slice());
9281
9641
  }, []);
9282
- const retry = React41.useCallback(() => {
9642
+ const retry = React45.useCallback(() => {
9283
9643
  const list = listRef.current;
9284
9644
  let at = -1;
9285
9645
  for (let i = list.length - 1; i >= 0; i--) if (list[i].role === "user") {
@@ -9295,19 +9655,19 @@ function useChatEngine(opts) {
9295
9655
  setQueue(queueRef.current.slice());
9296
9656
  drain();
9297
9657
  }, []);
9298
- const clear = React41.useCallback(() => {
9658
+ const clear = React45.useCallback(() => {
9299
9659
  commit([]);
9300
9660
  serverCount.current = 0;
9301
9661
  queueRef.current = [];
9302
9662
  setQueue([]);
9303
9663
  onClear && onClear();
9304
9664
  }, [onClear]);
9305
- const setFeedback = React41.useCallback((id, value) => {
9665
+ const setFeedback = React45.useCallback((id, value) => {
9306
9666
  patch(id, (m) => ({ feedback: m.feedback === value ? null : value }));
9307
9667
  const msg = listRef.current.find((m) => m.id === id);
9308
9668
  onFeedback && onFeedback({ message: msg, feedback: msg ? msg.feedback : value });
9309
9669
  }, [onFeedback]);
9310
- const reload = React41.useCallback(async () => {
9670
+ const reload = React45.useCallback(async () => {
9311
9671
  if (!threadRef.current) return;
9312
9672
  setStatus("loading");
9313
9673
  try {
@@ -9345,11 +9705,11 @@ var ChatKit = {
9345
9705
  };
9346
9706
 
9347
9707
  // src/components/chat/ChatTranscript.tsx
9348
- var React43 = __toESM(require("react"), 1);
9708
+ var React47 = __toESM(require("react"), 1);
9349
9709
 
9350
9710
  // src/components/chat/ChatTurn.tsx
9351
- var React42 = __toESM(require("react"), 1);
9352
- var import_jsx_runtime73 = require("react/jsx-runtime");
9711
+ var React46 = __toESM(require("react"), 1);
9712
+ var import_jsx_runtime75 = require("react/jsx-runtime");
9353
9713
  function JsonView({ value }) {
9354
9714
  let text;
9355
9715
  try {
@@ -9357,67 +9717,67 @@ function JsonView({ value }) {
9357
9717
  } catch (e) {
9358
9718
  text = String(value);
9359
9719
  }
9360
- return /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(CodeBlock, { code: text, language: "json", collapseAfter: 18 });
9720
+ return /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(CodeBlock, { code: text, language: "json", collapseAfter: 18 });
9361
9721
  }
9362
9722
  function PacketCard({ packet, schema, render, onApply, applied }) {
9363
9723
  if (!packet) return null;
9364
9724
  const s = schema || {};
9365
9725
  const invalid = packet.valid === false;
9366
9726
  const title = s.heading || packet.type;
9367
- return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("section", { className: "fdc-packet" + (invalid ? " is-invalid" : ""), "aria-label": title, children: [
9368
- /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("header", { className: "fdc-packet-head", children: [
9369
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-" + (s.icon || "package"), "aria-hidden": "true" }),
9370
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-packet-title", title: truncationTitle(title), children: title }),
9371
- packet.repaired ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-packet-badge", children: "Repaired" }) : null,
9372
- invalid ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-packet-badge is-danger", children: "Invalid" }) : null
9727
+ return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("section", { className: "fdc-packet" + (invalid ? " is-invalid" : ""), "aria-label": title, children: [
9728
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("header", { className: "fdc-packet-head", children: [
9729
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-" + (s.icon || "package"), "aria-hidden": "true" }),
9730
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-packet-title", title: truncationTitle(title), children: title }),
9731
+ packet.repaired ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-packet-badge", children: "Repaired" }) : null,
9732
+ invalid ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-packet-badge is-danger", children: "Invalid" }) : null
9373
9733
  ] }),
9374
- invalid ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-packet-alert", role: "alert", children: [
9375
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
9376
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { children: packet.error || "This result failed validation and can\u2019t be applied." })
9377
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("div", { className: "fdc-packet-body", children: render ? render(packet) : /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(JsonView, { value: packet.payload }) }),
9378
- !invalid && onApply ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("footer", { className: "fdc-packet-foot", children: [
9379
- packet.repaired ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-packet-note", children: "Corrected by the backend after a first attempt." }) : /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", {}),
9380
- /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fd-btn fd-btn-primary fd-btn-sm", disabled: applied, onClick: () => onApply(packet), children: [
9381
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-" + (applied ? "check" : "arrow-square-in"), "aria-hidden": "true" }),
9734
+ invalid ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-packet-alert", role: "alert", children: [
9735
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
9736
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { children: packet.error || "This result failed validation and can\u2019t be applied." })
9737
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-packet-body", children: render ? render(packet) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(JsonView, { value: packet.payload }) }),
9738
+ !invalid && onApply ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("footer", { className: "fdc-packet-foot", children: [
9739
+ packet.repaired ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-packet-note", children: "Corrected by the backend after a first attempt." }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {}),
9740
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fd-btn fd-btn-primary fd-btn-sm", disabled: applied, onClick: () => onApply(packet), children: [
9741
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-" + (applied ? "check" : "arrow-square-in"), "aria-hidden": "true" }),
9382
9742
  applied ? "Applied" : s.applyLabel || "Apply"
9383
9743
  ] })
9384
9744
  ] }) : null
9385
9745
  ] });
9386
9746
  }
9387
9747
  function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
9388
- const [open, setOpen] = React42.useState(defaultOpen);
9748
+ const [open, setOpen] = React46.useState(defaultOpen);
9389
9749
  if (!text) return null;
9390
- return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
9391
- /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
9392
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-brain", "aria-hidden": "true" }),
9393
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { children: streaming ? "Thinking" : durationMs ? "Thought for " + formatDuration(durationMs) : "Thought process" }),
9394
- streaming ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("span", { className: "fdc-dots", "aria-hidden": "true", children: [
9395
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", {}),
9396
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", {}),
9397
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", {})
9750
+ return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
9751
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
9752
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-brain", "aria-hidden": "true" }),
9753
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { children: streaming ? "Thinking" : durationMs ? "Thought for " + formatDuration(durationMs) : "Thought process" }),
9754
+ streaming ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-dots", "aria-hidden": "true", children: [
9755
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {}),
9756
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {}),
9757
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {})
9398
9758
  ] }) : null,
9399
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-caret-" + (open ? "up" : "down"), "aria-hidden": "true" })
9759
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-caret-" + (open ? "up" : "down"), "aria-hidden": "true" })
9400
9760
  ] }),
9401
- open ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("div", { className: "fdc-think-body", children: text }) : null
9761
+ open ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-think-body", children: text }) : null
9402
9762
  ] });
9403
9763
  }
9404
9764
  function Citations({ items = [], onOpen }) {
9405
- const [open, setOpen] = React42.useState(false);
9765
+ const [open, setOpen] = React46.useState(false);
9406
9766
  if (!items.length) return null;
9407
- return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-cites", children: [
9408
- /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
9409
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-quotes", "aria-hidden": "true" }),
9410
- /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("span", { children: [
9767
+ return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-cites", children: [
9768
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
9769
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-quotes", "aria-hidden": "true" }),
9770
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { children: [
9411
9771
  items.length,
9412
9772
  " source",
9413
9773
  items.length === 1 ? "" : "s"
9414
9774
  ] }),
9415
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-caret-" + (open ? "up" : "down"), "aria-hidden": "true" })
9775
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-caret-" + (open ? "up" : "down"), "aria-hidden": "true" })
9416
9776
  ] }),
9417
- open ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("ol", { className: "fdc-cites-list", children: items.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("li", { children: [
9418
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-cite-n fd-tabular", children: c.marker || i + 1 }),
9419
- c.url ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("a", { href: c.url, target: "_blank", rel: "noopener noreferrer", onClick: onOpen ? (e) => onOpen(c, e) : void 0, children: c.title || c.url }) : /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { children: c.title || "Source" }),
9420
- c.detail ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-cite-detail", children: c.detail }) : null
9777
+ open ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("ol", { className: "fdc-cites-list", children: items.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("li", { children: [
9778
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-cite-n fd-tabular", children: c.marker || i + 1 }),
9779
+ c.url ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("a", { href: c.url, target: "_blank", rel: "noopener noreferrer", onClick: onOpen ? (e) => onOpen(c, e) : void 0, children: c.title || c.url }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { children: c.title || "Source" }),
9780
+ c.detail ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-cite-detail", children: c.detail }) : null
9421
9781
  ] }, c.id || i)) }) : null
9422
9782
  ] });
9423
9783
  }
@@ -9428,29 +9788,29 @@ function clampText(text, max) {
9428
9788
  return (at > max * 0.6 ? cut.slice(0, at) : cut).trimEnd() + "\u2026";
9429
9789
  }
9430
9790
  function MessageBody({ message: m, ctx }) {
9431
- const [expanded, setExpanded] = React42.useState(false);
9791
+ const [expanded, setExpanded] = React46.useState(false);
9432
9792
  const isUser = m.role === "user";
9433
9793
  const raw = m.text || "";
9434
9794
  const clamped = !expanded && !m.streaming ? clampText(raw, ctx.maxVisibleChars) : null;
9435
9795
  const body = clamped != null ? clamped : raw;
9436
9796
  const showMd = ctx.markdown && !isUser;
9437
- return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-body", children: [
9438
- m.thinking && ctx.showThinking ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(ThinkingBlock, { text: m.thinking, durationMs: m.thinkingMs, streaming: m.streaming && !m.text }) : null,
9439
- m.steps && m.steps.length && ctx.showSteps ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(StepList, { steps: m.steps, defaultOpen: m.steps.some((s) => s.status === "running"), dense: true }) : null,
9440
- body ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-text" + (isUser ? " is-user" : ""), children: [
9441
- showMd ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Markdown, { source: body, headingOffset: 2, codeProps: { collapseAfter: 22 }, renderCitation: ctx.renderCitation }) : /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("div", { className: "fdc-plain", children: body }),
9442
- m.streaming ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-caret", "aria-hidden": "true" }) : null
9797
+ return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-body", children: [
9798
+ m.thinking && ctx.showThinking ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(ThinkingBlock, { text: m.thinking, durationMs: m.thinkingMs, streaming: m.streaming && !m.text }) : null,
9799
+ m.steps && m.steps.length && ctx.showSteps ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(StepList, { steps: m.steps, defaultOpen: m.steps.some((s) => s.status === "running"), dense: true }) : null,
9800
+ body ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-text" + (isUser ? " is-user" : ""), children: [
9801
+ showMd ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(Markdown, { source: body, headingOffset: 2, codeProps: { collapseAfter: 22 }, renderCitation: ctx.renderCitation }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-plain", children: body }),
9802
+ m.streaming ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-caret", "aria-hidden": "true" }) : null
9443
9803
  ] }) : null,
9444
- clamped != null ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fdc-more", onClick: () => setExpanded(true), children: [
9445
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true" }),
9804
+ clamped != null ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-more", onClick: () => setExpanded(true), children: [
9805
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true" }),
9446
9806
  "Show more",
9447
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-more-len fd-tabular", children: raw.length < 2e3 ? raw.length.toLocaleString() + " characters" : Math.round(raw.length / 100) / 10 + "k characters" })
9448
- ] }) : expanded && ctx.maxVisibleChars && raw.length > ctx.maxVisibleChars ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fdc-more", onClick: () => setExpanded(false), children: [
9449
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-caret-up", "aria-hidden": "true" }),
9807
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-more-len fd-tabular", children: raw.length < 2e3 ? raw.length.toLocaleString() + " characters" : Math.round(raw.length / 100) / 10 + "k characters" })
9808
+ ] }) : expanded && ctx.maxVisibleChars && raw.length > ctx.maxVisibleChars ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-more", onClick: () => setExpanded(false), children: [
9809
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-caret-up", "aria-hidden": "true" }),
9450
9810
  "Show less"
9451
9811
  ] }) : null,
9452
- m.attachments && m.attachments.length ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(FileGrid, { files: m.attachments, onOpen: ctx.onOpenAttachment, tiles: true, maxHeight: ctx.attachmentHeight || 220, compact: ctx.narrow }) : null,
9453
- m.packet ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(
9812
+ m.attachments && m.attachments.length ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(FileGrid, { files: m.attachments, onOpen: ctx.onOpenAttachment, tiles: true, maxHeight: ctx.attachmentHeight || 220, compact: ctx.narrow }) : null,
9813
+ m.packet ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
9454
9814
  PacketCard,
9455
9815
  {
9456
9816
  packet: m.packet,
@@ -9460,44 +9820,44 @@ function MessageBody({ message: m, ctx }) {
9460
9820
  applied: ctx.appliedPackets && ctx.appliedPackets[m.id]
9461
9821
  }
9462
9822
  ) : null,
9463
- m.citations && m.citations.length ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Citations, { items: m.citations, onOpen: ctx.onOpenCitation }) : null,
9464
- m.working ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-working", role: "status", children: [
9465
- /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("span", { className: "fdc-dots", "aria-hidden": "true", children: [
9466
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", {}),
9467
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", {}),
9468
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", {})
9823
+ m.citations && m.citations.length ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(Citations, { items: m.citations, onOpen: ctx.onOpenCitation }) : null,
9824
+ m.working ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-working", role: "status", children: [
9825
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-dots", "aria-hidden": "true", children: [
9826
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {}),
9827
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {}),
9828
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {})
9469
9829
  ] }),
9470
- /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("span", { className: "fdc-working-label", children: [
9830
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-working-label", children: [
9471
9831
  m.resumed ? "Resuming" : "Working",
9472
- m.job && m.job.status ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("span", { className: "fdc-working-job", children: [
9832
+ m.job && m.job.status ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-working-job", children: [
9473
9833
  " \xB7 ",
9474
9834
  m.job.status
9475
9835
  ] }) : null,
9476
- m.job && m.job.detail ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("span", { className: "fdc-working-job", children: [
9836
+ m.job && m.job.detail ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-working-job", children: [
9477
9837
  " \xB7 ",
9478
9838
  m.job.detail
9479
9839
  ] }) : null
9480
9840
  ] }),
9481
- m.jobId ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-working-id fd-mono", children: String(m.jobId).slice(0, 12) }) : null
9841
+ m.jobId ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-working-id fd-mono", children: String(m.jobId).slice(0, 12) }) : null
9482
9842
  ] }) : null,
9483
- m.stopped ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-stopped", children: [
9484
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-stop-circle", "aria-hidden": "true" }),
9843
+ m.stopped ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-stopped", children: [
9844
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-stop-circle", "aria-hidden": "true" }),
9485
9845
  "Stopped"
9486
9846
  ] }) : null,
9487
- m.error ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-error", role: "alert", children: [
9488
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
9489
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-error-text", children: ctx.errorCopy ? ctx.errorCopy(m.error) : m.error }),
9490
- m.retryable && ctx.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fdc-error-retry", onClick: ctx.onRetry, children: [
9491
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }),
9847
+ m.error ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-error", role: "alert", children: [
9848
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
9849
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-error-text", children: ctx.errorCopy ? ctx.errorCopy(m.error) : m.error }),
9850
+ m.retryable && ctx.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-error-retry", onClick: ctx.onRetry, children: [
9851
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }),
9492
9852
  "Retry"
9493
9853
  ] }) : null
9494
9854
  ] }) : null
9495
9855
  ] });
9496
9856
  }
9497
9857
  function useCopyRun() {
9498
- const [done, setDone] = React42.useState(false);
9499
- const t = React42.useRef(null);
9500
- React42.useEffect(() => () => {
9858
+ const [done, setDone] = React46.useState(false);
9859
+ const t = React46.useRef(null);
9860
+ React46.useEffect(() => () => {
9501
9861
  if (t.current) clearTimeout(t.current);
9502
9862
  }, []);
9503
9863
  return [done, (text) => {
@@ -9517,16 +9877,16 @@ function RunActions({ group, ctx }) {
9517
9877
  const isAssistant = group.role === "assistant";
9518
9878
  const fb = last.feedback;
9519
9879
  if (!ctx.messageActions) return null;
9520
- return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-actions", role: "group", "aria-label": "Message actions", children: [
9521
- /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fdc-act" + (copied ? " is-done" : ""), onClick: () => copy(markdownToText(text)), "aria-label": "Copy message", children: [
9522
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-" + (copied ? "check" : "copy"), "aria-hidden": "true" }),
9523
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-act-label", children: copied ? "Copied" : "Copy" })
9880
+ return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-actions", role: "group", "aria-label": "Message actions", children: [
9881
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-act" + (copied ? " is-done" : ""), onClick: () => copy(markdownToText(text)), "aria-label": "Copy message", children: [
9882
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-" + (copied ? "check" : "copy"), "aria-hidden": "true" }),
9883
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-act-label", children: copied ? "Copied" : "Copy" })
9524
9884
  ] }),
9525
- isAssistant && ctx.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("button", { type: "button", className: "fdc-act", onClick: ctx.onRetry, "aria-label": "Retry this turn", children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }) }) : null,
9526
- group.role === "user" && ctx.onEdit ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("button", { type: "button", className: "fdc-act", onClick: () => ctx.onEdit(last), "aria-label": "Edit and resend", children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-pencil-simple", "aria-hidden": "true" }) }) : null,
9527
- isAssistant && ctx.onFeedback ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(React42.Fragment, { children: [
9528
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("button", { type: "button", className: "fdc-act" + (fb === "up" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "up"), "aria-pressed": fb === "up", "aria-label": "Good response", children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-thumbs-up", "aria-hidden": "true" }) }),
9529
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("button", { type: "button", className: "fdc-act" + (fb === "down" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "down"), "aria-pressed": fb === "down", "aria-label": "Bad response", children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-thumbs-down", "aria-hidden": "true" }) })
9885
+ isAssistant && ctx.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("button", { type: "button", className: "fdc-act", onClick: ctx.onRetry, "aria-label": "Retry this turn", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }) }) : null,
9886
+ group.role === "user" && ctx.onEdit ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("button", { type: "button", className: "fdc-act", onClick: () => ctx.onEdit(last), "aria-label": "Edit and resend", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-pencil-simple", "aria-hidden": "true" }) }) : null,
9887
+ isAssistant && ctx.onFeedback ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)(React46.Fragment, { children: [
9888
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("button", { type: "button", className: "fdc-act" + (fb === "up" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "up"), "aria-pressed": fb === "up", "aria-label": "Good response", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-thumbs-up", "aria-hidden": "true" }) }),
9889
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("button", { type: "button", className: "fdc-act" + (fb === "down" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "down"), "aria-pressed": fb === "down", "aria-label": "Bad response", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-thumbs-down", "aria-hidden": "true" }) })
9530
9890
  ] }) : null,
9531
9891
  ctx.extraActions ? ctx.extraActions(group) : null
9532
9892
  ] });
@@ -9536,22 +9896,22 @@ function ChatTurn({ group, ctx }) {
9536
9896
  const name = isUser ? ctx.userName : group.author || ctx.assistantName;
9537
9897
  const avatar = isUser ? ctx.userAvatar : ctx.assistantAvatar;
9538
9898
  const stamp = group.messages[0].timestamp;
9539
- return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("article", { className: "fdc-turn is-" + group.role, "aria-label": String(name) + (stamp ? " at " + formatClock(stamp) : ""), children: [
9540
- /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-turn-head", children: [
9541
- ctx.showAvatars ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-avatar is-" + group.role, "aria-hidden": "true", children: avatar ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("img", { src: avatar, alt: "" }) : isUser ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-avatar-txt", children: (name || "You").slice(0, 1).toUpperCase() }) : /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-" + (ctx.assistantIcon || "sparkle") }) }) : null,
9542
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-who", children: name }),
9543
- stamp ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(RelativeTime, { className: "fdc-when", value: stamp }) : null,
9544
- group.messages.some((m) => m.model) ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fdc-turn-model", children: group.messages.find((m) => m.model).model }) : null
9899
+ return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("article", { className: "fdc-turn is-" + group.role, "aria-label": String(name) + (stamp ? " at " + formatClock(stamp) : ""), children: [
9900
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-turn-head", children: [
9901
+ ctx.showAvatars ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-avatar is-" + group.role, "aria-hidden": "true", children: avatar ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("img", { src: avatar, alt: "" }) : isUser ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-avatar-txt", children: (name || "You").slice(0, 1).toUpperCase() }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-" + (ctx.assistantIcon || "sparkle") }) }) : null,
9902
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-who", children: name }),
9903
+ stamp ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(RelativeTime, { className: "fdc-when", value: stamp }) : null,
9904
+ group.messages.some((m) => m.model) ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-turn-model", children: group.messages.find((m) => m.model).model }) : null
9545
9905
  ] }),
9546
- /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-turn-body", children: [
9547
- group.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("div", { className: "fdc-msg" + (m.pending ? " is-pending" : ""), children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(MessageBody, { message: m, ctx }) }, m.id)),
9548
- /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(RunActions, { group, ctx })
9906
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-turn-body", children: [
9907
+ group.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-msg" + (m.pending ? " is-pending" : ""), children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(MessageBody, { message: m, ctx }) }, m.id)),
9908
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(RunActions, { group, ctx })
9549
9909
  ] })
9550
9910
  ] });
9551
9911
  }
9552
9912
 
9553
9913
  // src/components/chat/ChatTranscript.tsx
9554
- var import_jsx_runtime74 = require("react/jsx-runtime");
9914
+ var import_jsx_runtime76 = require("react/jsx-runtime");
9555
9915
  var GROUP_WINDOW = 6e4;
9556
9916
  var STICK_PX = 100;
9557
9917
  function groupMessages(list) {
@@ -9584,25 +9944,25 @@ function dayLabel(key) {
9584
9944
  }
9585
9945
  function Suggestions({ items = [], onPick }) {
9586
9946
  if (!items.length) return null;
9587
- return /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "fdc-suggest", children: items.map((s, i) => {
9947
+ return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fdc-suggest", children: items.map((s, i) => {
9588
9948
  const it = typeof s === "string" ? { label: s, text: s } : s;
9589
- return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("button", { type: "button", className: "fdc-suggest-item", onClick: () => onPick && onPick(it.text || it.label), children: [
9590
- it.icon ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("i", { className: "ph ph-" + it.icon, "aria-hidden": "true" }) : null,
9591
- /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("span", { className: "fdc-suggest-text", children: [
9592
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fdc-suggest-label", children: it.label }),
9593
- it.description ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fdc-suggest-desc", children: it.description }) : null
9949
+ return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "fdc-suggest-item", onClick: () => onPick && onPick(it.text || it.label), children: [
9950
+ it.icon ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-" + it.icon, "aria-hidden": "true" }) : null,
9951
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("span", { className: "fdc-suggest-text", children: [
9952
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fdc-suggest-label", children: it.label }),
9953
+ it.description ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fdc-suggest-desc", children: it.description }) : null
9594
9954
  ] }),
9595
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("i", { className: "ph ph-arrow-up-right fdc-suggest-go", "aria-hidden": "true" })
9955
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-arrow-up-right fdc-suggest-go", "aria-hidden": "true" })
9596
9956
  ] }, it.id || i);
9597
9957
  }) });
9598
9958
  }
9599
9959
  function LoadingTurns() {
9600
- return /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "fdc-skel", "aria-hidden": "true", children: [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fdc-skel-turn", children: [
9601
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "fd-skel fd-skel-circle", style: { width: 22, height: 22 } }),
9602
- /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fdc-skel-lines", children: [
9603
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "fd-skel", style: { width: i ? "62%" : "44%", height: 11 } }),
9604
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "fd-skel", style: { width: i ? "94%" : "78%", height: 11 } }),
9605
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "fd-skel", style: { width: i ? "71%" : "56%", height: 11 } })
9960
+ return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fdc-skel", "aria-hidden": "true", children: [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-skel-turn", children: [
9961
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fd-skel fd-skel-circle", style: { width: 22, height: 22 } }),
9962
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-skel-lines", children: [
9963
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fd-skel", style: { width: i ? "62%" : "44%", height: 11 } }),
9964
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fd-skel", style: { width: i ? "94%" : "78%", height: 11 } }),
9965
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fd-skel", style: { width: i ? "71%" : "56%", height: 11 } })
9606
9966
  ] })
9607
9967
  ] }, i)) });
9608
9968
  }
@@ -9619,10 +9979,10 @@ function ChatTranscript({
9619
9979
  renderEmpty,
9620
9980
  className = ""
9621
9981
  }) {
9622
- const scroller = React43.useRef(null);
9623
- const stick = React43.useRef(true);
9624
- const [pill, setPill] = React43.useState(0);
9625
- const seen = React43.useRef(0);
9982
+ const scroller = React47.useRef(null);
9983
+ const stick = React47.useRef(true);
9984
+ const [pill, setPill] = React47.useState(0);
9985
+ const seen = React47.useRef(0);
9626
9986
  const toBottom = (smooth) => {
9627
9987
  const el = scroller.current;
9628
9988
  if (!el) return;
@@ -9641,7 +10001,7 @@ function ChatTranscript({
9641
10001
  seen.current = messages.length;
9642
10002
  }
9643
10003
  };
9644
- React43.useLayoutEffect(() => {
10004
+ React47.useLayoutEffect(() => {
9645
10005
  const el = scroller.current;
9646
10006
  if (!el) return;
9647
10007
  if (stick.current) {
@@ -9649,7 +10009,7 @@ function ChatTranscript({
9649
10009
  seen.current = messages.length;
9650
10010
  } else setPill(Math.max(0, messages.length - seen.current));
9651
10011
  }, [messages]);
9652
- React43.useEffect(() => {
10012
+ React47.useEffect(() => {
9653
10013
  const el = scroller.current;
9654
10014
  const inner = el && el.firstChild;
9655
10015
  if (!el || !inner || typeof ResizeObserver === "undefined") return;
@@ -9659,38 +10019,38 @@ function ChatTranscript({
9659
10019
  ro.observe(inner);
9660
10020
  return () => ro.disconnect();
9661
10021
  }, []);
9662
- const groups = React43.useMemo(() => groupMessages(messages), [messages]);
10022
+ const groups = React47.useMemo(() => groupMessages(messages), [messages]);
9663
10023
  const empty = !messages.length && status === "ready";
9664
- return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
9665
- /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
9666
- status === "loading" || status === "resolving" ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(LoadingTurns, {}) : null,
9667
- status === "disconnected" ? /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fdc-dead", children: [
9668
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fdc-dead-icon", children: /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("i", { className: "ph ph-plugs", "aria-hidden": "true" }) }),
9669
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("h3", { className: "fdc-dead-title", children: ctx.deadTitle || "The assistant isn\u2019t reachable" }),
9670
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("p", { className: "fdc-dead-body", children: fatal === "chat_unavailable" ? "The service reported chat_unavailable. Nothing you type will be lost \u2014 reopen the panel once it\u2019s back." : "The thread couldn\u2019t be resolved" + (fatal ? " (" + fatal + ")" : "") + ". The composer stays disabled until it resolves." }),
9671
- ctx.onReconnect ? /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("button", { type: "button", className: "fd-btn fd-btn-secondary fd-btn-sm", onClick: ctx.onReconnect, children: [
9672
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }),
10024
+ return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
10025
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
10026
+ status === "loading" || status === "resolving" ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(LoadingTurns, {}) : null,
10027
+ status === "disconnected" ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-dead", children: [
10028
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fdc-dead-icon", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-plugs", "aria-hidden": "true" }) }),
10029
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h3", { className: "fdc-dead-title", children: ctx.deadTitle || "The assistant isn\u2019t reachable" }),
10030
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("p", { className: "fdc-dead-body", children: fatal === "chat_unavailable" ? "The service reported chat_unavailable. Nothing you type will be lost \u2014 reopen the panel once it\u2019s back." : "The thread couldn\u2019t be resolved" + (fatal ? " (" + fatal + ")" : "") + ". The composer stays disabled until it resolves." }),
10031
+ ctx.onReconnect ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "fd-btn fd-btn-secondary fd-btn-sm", onClick: ctx.onReconnect, children: [
10032
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }),
9673
10033
  "Try again"
9674
10034
  ] }) : null
9675
10035
  ] }) : null,
9676
- empty ? renderEmpty ? renderEmpty() : /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fdc-empty", children: [
9677
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fdc-empty-icon", children: /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("i", { className: "ph ph-" + emptyIcon, "aria-hidden": "true" }) }),
9678
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("h3", { className: "fdc-empty-title", children: emptyTitle }),
9679
- emptyDescription ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("p", { className: "fdc-empty-body", children: emptyDescription }) : null,
9680
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(Suggestions, { items: suggestions, onPick })
10036
+ empty ? renderEmpty ? renderEmpty() : /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-empty", children: [
10037
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fdc-empty-icon", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-" + emptyIcon, "aria-hidden": "true" }) }),
10038
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h3", { className: "fdc-empty-title", children: emptyTitle }),
10039
+ emptyDescription ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("p", { className: "fdc-empty-body", children: emptyDescription }) : null,
10040
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Suggestions, { items: suggestions, onPick })
9681
10041
  ] }) : null,
9682
10042
  groups.map((g, i) => {
9683
10043
  const prev = groups[i - 1];
9684
10044
  const k = dayKey(g.messages[0].timestamp);
9685
10045
  const showDay = !!k && (!prev || dayKey(prev.messages[0].timestamp) !== k);
9686
- return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(React43.Fragment, { children: [
9687
- showDay ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "fdc-day", children: /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { children: dayLabel(k) }) }) : null,
9688
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(ChatTurn, { group: g, ctx })
10046
+ return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React47.Fragment, { children: [
10047
+ showDay ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fdc-day", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: dayLabel(k) }) }) : null,
10048
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(ChatTurn, { group: g, ctx })
9689
10049
  ] }, g.key || i);
9690
10050
  })
9691
10051
  ] }),
9692
- pill ? /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("button", { type: "button", className: "fdc-pill", onClick: () => toBottom(true), children: [
9693
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("i", { className: "ph ph-arrow-down", "aria-hidden": "true" }),
10052
+ pill ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "fdc-pill", onClick: () => toBottom(true), children: [
10053
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-arrow-down", "aria-hidden": "true" }),
9694
10054
  pill,
9695
10055
  " new message",
9696
10056
  pill === 1 ? "" : "s"
@@ -9700,8 +10060,8 @@ function ChatTranscript({
9700
10060
  var TranscriptKit = { groupMessages };
9701
10061
 
9702
10062
  // src/components/chat/ChatComposer.tsx
9703
- var React44 = __toESM(require("react"), 1);
9704
- var import_jsx_runtime75 = require("react/jsx-runtime");
10063
+ var React48 = __toESM(require("react"), 1);
10064
+ var import_jsx_runtime77 = require("react/jsx-runtime");
9705
10065
  function ChatComposer({
9706
10066
  onSubmit,
9707
10067
  onStop,
@@ -9728,17 +10088,17 @@ function ChatComposer({
9728
10088
  onReject,
9729
10089
  onOpenAttachment
9730
10090
  }) {
9731
- const [text, setText] = React44.useState(draft || "");
9732
- const [trigger, setTrigger] = React44.useState(null);
9733
- const [mentionItems, setMentionItems] = React44.useState([]);
9734
- const [listening, setListening] = React44.useState(false);
9735
- const [notice, setNotice] = React44.useState(null);
9736
- const editor = React44.useRef(null);
9737
- const wrap = React44.useRef(null);
9738
- const stopVoice = React44.useRef(null);
10091
+ const [text, setText] = React48.useState(draft || "");
10092
+ const [trigger, setTrigger] = React48.useState(null);
10093
+ const [mentionItems, setMentionItems] = React48.useState([]);
10094
+ const [listening, setListening] = React48.useState(false);
10095
+ const [notice, setNotice] = React48.useState(null);
10096
+ const editor = React48.useRef(null);
10097
+ const wrap = React48.useRef(null);
10098
+ const stopVoice = React48.useRef(null);
9739
10099
  const staged = useStagedFiles(fileUploadHandler, { onError: () => {
9740
10100
  } });
9741
- React44.useEffect(() => {
10101
+ React48.useEffect(() => {
9742
10102
  if (draft != null && draft !== text) setText(draft);
9743
10103
  }, [draft]);
9744
10104
  const change = (v) => {
@@ -9774,7 +10134,7 @@ function ChatComposer({
9774
10134
  e.preventDefault();
9775
10135
  staged.add(found.files);
9776
10136
  };
9777
- React44.useEffect(() => {
10137
+ React48.useEffect(() => {
9778
10138
  if (!trigger || trigger.type !== "mention" || !mentionSources) {
9779
10139
  setMentionItems([]);
9780
10140
  return;
@@ -9792,7 +10152,7 @@ function ChatComposer({
9792
10152
  const q = (trigger.query || "").toLowerCase();
9793
10153
  setMentionItems(mentionSources.filter((m) => !q || (m.label + " " + (m.description || "")).toLowerCase().includes(q)));
9794
10154
  }, [trigger, mentionSources]);
9795
- const slashItems = React44.useMemo(() => {
10155
+ const slashItems = React48.useMemo(() => {
9796
10156
  if (!trigger || trigger.type !== "slash" || !slashCommands) return [];
9797
10157
  const q = (trigger.query || "").toLowerCase();
9798
10158
  return slashCommands.filter((c) => !q || (c.id + " " + c.label + " " + (c.description || "")).toLowerCase().includes(q));
@@ -9806,7 +10166,7 @@ function ChatComposer({
9806
10166
  description: it.description,
9807
10167
  icon: it.icon,
9808
10168
  meta: mention.meta,
9809
- shortcut: slash.shortcut ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(KeyHint, { keys: slash.shortcut, size: "sm" }) : void 0,
10169
+ shortcut: slash.shortcut ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(KeyHint, { keys: slash.shortcut, size: "sm" }) : void 0,
9810
10170
  onSelect: () => {
9811
10171
  const insert = trigger.type === "slash" ? slash.immediate ? "/" + slash.id : "/" + slash.id + " " : "@" + (mention.value || mention.label) + " ";
9812
10172
  editor.current && editor.current.replaceRange(trigger.from, trigger.to, insert);
@@ -9869,14 +10229,14 @@ function ChatComposer({
9869
10229
  stopVoice.current = typeof res === "function" ? res : () => {
9870
10230
  };
9871
10231
  };
9872
- return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-composer" + (disabled ? " is-disabled" : "") + (narrow ? " is-narrow" : ""), children: [
9873
- queue.length ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-queue", "aria-label": "Queued messages", children: queue.map((q) => /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-queue-row", children: [
9874
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-clock-countdown", "aria-hidden": "true" }),
9875
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-queue-text", title: truncationTitle(q.text || (q.attachments ? q.attachments.length + " file(s)" : "")), children: q.text || (q.attachments ? q.attachments.length + " file(s)" : "") }),
9876
- onRemoveQueued ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("button", { type: "button", className: "fdc-queue-x", onClick: () => onRemoveQueued(q.id), "aria-label": "Remove queued message", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-x", "aria-hidden": "true" }) }) : null
10232
+ return /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "fdc-composer" + (disabled ? " is-disabled" : "") + (narrow ? " is-narrow" : ""), children: [
10233
+ queue.length ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "fdc-queue", "aria-label": "Queued messages", children: queue.map((q) => /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "fdc-queue-row", children: [
10234
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-clock-countdown", "aria-hidden": "true" }),
10235
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { className: "fdc-queue-text", title: truncationTitle(q.text || (q.attachments ? q.attachments.length + " file(s)" : "")), children: q.text || (q.attachments ? q.attachments.length + " file(s)" : "") }),
10236
+ onRemoveQueued ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("button", { type: "button", className: "fdc-queue-x", onClick: () => onRemoveQueued(q.id), "aria-label": "Remove queued message", children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-x", "aria-hidden": "true" }) }) : null
9877
10237
  ] }, q.id)) }) : null,
9878
10238
  sessionBar,
9879
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
10239
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
9880
10240
  Dropzone,
9881
10241
  {
9882
10242
  className: "fdc-field",
@@ -9890,9 +10250,9 @@ function ChatComposer({
9890
10250
  disabled: !fileUploadHandler || disabled,
9891
10251
  label: "Drop to attach",
9892
10252
  hint: acceptFiles ? acceptFiles.replace(/,/g, " \xB7 ") : void 0,
9893
- children: /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { ref: wrap, className: "fdc-field-inner", children: [
9894
- staged.items.length ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-staged", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(FileStrip, { files: staged.items, size: narrow ? 60 : 68, onRemove: staged.remove, onRetry: staged.retry, onOpen: onOpenAttachment }) }) : null,
9895
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
10253
+ children: /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { ref: wrap, className: "fdc-field-inner", children: [
10254
+ staged.items.length ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "fdc-staged", children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(FileStrip, { files: staged.items, size: narrow ? 60 : 68, onRemove: staged.remove, onRetry: staged.retry, onOpen: onOpenAttachment }) }) : null,
10255
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
9896
10256
  MarkdownEditor,
9897
10257
  {
9898
10258
  ref: editor,
@@ -9911,33 +10271,33 @@ function ChatComposer({
9911
10271
  ariaLabel: "Message"
9912
10272
  }
9913
10273
  ),
9914
- /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-tools", children: [
9915
- fileUploadHandler ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(FilePickButton, { onFiles: staged.add, onReject: (r) => flash(r[0].message), accept: acceptFiles, maxFileSize, label: "Attach files", icon: "plus" }) : null,
9916
- voiceHandler ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("button", { type: "button", className: "fd-attachbtn" + (listening ? " is-live" : ""), onClick: voice, "aria-pressed": listening, "aria-label": listening ? "Stop dictation" : "Dictate", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-" + (listening ? "waveform" : "microphone"), "aria-hidden": "true" }) }) : null,
10274
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "fdc-tools", children: [
10275
+ fileUploadHandler ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(FilePickButton, { onFiles: staged.add, onReject: (r) => flash(r[0].message), accept: acceptFiles, maxFileSize, label: "Attach files", icon: "plus" }) : null,
10276
+ voiceHandler ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("button", { type: "button", className: "fd-attachbtn" + (listening ? " is-live" : ""), onClick: voice, "aria-pressed": listening, "aria-label": listening ? "Stop dictation" : "Dictate", children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-" + (listening ? "waveform" : "microphone"), "aria-hidden": "true" }) }) : null,
9917
10277
  toolbarExtras,
9918
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-tools-gap" }),
9919
- maxLength && text.length > maxLength * 0.6 ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-count fd-tabular" + (text.length > maxLength * 0.95 ? " is-hot" : ""), children: [
10278
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { className: "fdc-tools-gap" }),
10279
+ maxLength && text.length > maxLength * 0.6 ? /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("span", { className: "fdc-count fd-tabular" + (text.length > maxLength * 0.95 ? " is-hot" : ""), children: [
9920
10280
  text.length,
9921
10281
  "/",
9922
10282
  maxLength
9923
10283
  ] }) : null,
9924
- busy ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-stop", onClick: onStop, "aria-label": "Stop generating", children: [
9925
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-stop-circle", "aria-hidden": "true" }),
9926
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { children: "Stop" })
9927
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-send", onClick: submit, disabled: !canSend, "aria-label": "Send message", children: [
9928
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-paper-plane-right", "aria-hidden": "true" }),
9929
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-send-label", children: "Send" })
10284
+ busy ? /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("button", { type: "button", className: "fdc-stop", onClick: onStop, "aria-label": "Stop generating", children: [
10285
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-stop-circle", "aria-hidden": "true" }),
10286
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { children: "Stop" })
10287
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("button", { type: "button", className: "fdc-send", onClick: submit, disabled: !canSend, "aria-label": "Send message", children: [
10288
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-paper-plane-right", "aria-hidden": "true" }),
10289
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { className: "fdc-send-label", children: "Send" })
9930
10290
  ] })
9931
10291
  ] })
9932
10292
  ] })
9933
10293
  }
9934
10294
  ),
9935
- notice ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-notice", role: "status", children: [
9936
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
10295
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "fdc-notice", role: "status", children: [
10296
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
9937
10297
  notice
9938
10298
  ] }) : null,
9939
- hint && !notice ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-hint", children: hint }) : null,
9940
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
10299
+ hint && !notice ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "fdc-hint", children: hint }) : null,
10300
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
9941
10301
  Popover,
9942
10302
  {
9943
10303
  open: menuOpen,
@@ -9951,12 +10311,12 @@ function ChatComposer({
9951
10311
  returnFocus: false,
9952
10312
  closeOnOutside: true,
9953
10313
  label: trigger && trigger.type === "slash" ? "Commands" : "Mentions",
9954
- children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
10314
+ children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
9955
10315
  Menu,
9956
10316
  {
9957
10317
  items: menuItems,
9958
10318
  autoFocus: false,
9959
- header: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fd-pop-group", children: trigger && trigger.type === "slash" ? "Commands" : "Attach context" }),
10319
+ header: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "fd-pop-group", children: trigger && trigger.type === "slash" ? "Commands" : "Attach context" }),
9960
10320
  onClose: () => setTrigger(null)
9961
10321
  }
9962
10322
  )
@@ -9966,12 +10326,12 @@ function ChatComposer({
9966
10326
  }
9967
10327
 
9968
10328
  // src/components/chat/ChatSessionBar.tsx
9969
- var React45 = __toESM(require("react"), 1);
9970
- var import_jsx_runtime76 = require("react/jsx-runtime");
10329
+ var React49 = __toESM(require("react"), 1);
10330
+ var import_jsx_runtime78 = require("react/jsx-runtime");
9971
10331
  var compact2 = meterFormats.compact;
9972
10332
  function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }) {
9973
- const [open, setOpen] = React45.useState(false);
9974
- const anchor = React45.useRef(null);
10333
+ const [open, setOpen] = React49.useState(false);
10334
+ const anchor = React49.useRef(null);
9975
10335
  const stats = sessionStats || null;
9976
10336
  const cu = contextUsage || null;
9977
10337
  const limits = usageLimits || null;
@@ -9986,9 +10346,9 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9986
10346
  if (stats && stats.runningTasks) bits.push(stats.runningTasks + " running task" + (stats.runningTasks === 1 ? "" : "s"));
9987
10347
  if (cu) bits.push(pct + "% context");
9988
10348
  const expandable = !!(cu || limits);
9989
- return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React45.Fragment, { children: [
9990
- /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-bar", children: [
9991
- /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(
10349
+ return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(React49.Fragment, { children: [
10350
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "fdc-bar", children: [
10351
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(
9992
10352
  "button",
9993
10353
  {
9994
10354
  type: "button",
@@ -9999,15 +10359,15 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9999
10359
  "aria-expanded": expandable ? open : void 0,
10000
10360
  "aria-label": expandable ? "Usage details" : void 0,
10001
10361
  children: [
10002
- cu ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fdc-bar-mini", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(SegmentedMeter, { total, segments: cu.segments, height: 4, showTotal: false }) }) : null,
10003
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fdc-bar-text", title: truncationTitle(bits.join(" \xB7 ")), children: bits.join(" \xB7 ") }),
10004
- expandable ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-caret-right fdc-bar-caret", "aria-hidden": "true" }) : null
10362
+ cu ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fdc-bar-mini", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(SegmentedMeter, { total, segments: cu.segments, height: 4, showTotal: false }) }) : null,
10363
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fdc-bar-text", title: truncationTitle(bits.join(" \xB7 ")), children: bits.join(" \xB7 ") }),
10364
+ expandable ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("i", { className: "ph ph-caret-right fdc-bar-caret", "aria-hidden": "true" }) : null
10005
10365
  ]
10006
10366
  }
10007
10367
  ),
10008
10368
  extras
10009
10369
  ] }),
10010
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
10370
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
10011
10371
  Popover,
10012
10372
  {
10013
10373
  open,
@@ -10018,15 +10378,15 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
10018
10378
  maxHeight: 460,
10019
10379
  padded: true,
10020
10380
  label: "Usage",
10021
- footer: onClear ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "fdc-usage-clear", onClick: () => {
10381
+ footer: onClear ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("button", { type: "button", className: "fdc-usage-clear", onClick: () => {
10022
10382
  setOpen(false);
10023
10383
  onClear();
10024
10384
  }, children: [
10025
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-trash", "aria-hidden": "true" }),
10385
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("i", { className: "ph ph-trash", "aria-hidden": "true" }),
10026
10386
  "Clear conversation"
10027
10387
  ] }) : void 0,
10028
- children: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-usage", children: [
10029
- cu ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("section", { className: "fdc-usage-sec", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
10388
+ children: /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "fdc-usage", children: [
10389
+ cu ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("section", { className: "fdc-usage-sec", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
10030
10390
  SegmentedMeter,
10031
10391
  {
10032
10392
  total,
@@ -10038,9 +10398,9 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
10038
10398
  remainderLabel: "Free"
10039
10399
  }
10040
10400
  ) }) : null,
10041
- limits && limits.length ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { className: "fdc-usage-sec", children: [
10042
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h4", { className: "fdc-usage-h", children: "Usage limits" }),
10043
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fdc-usage-rows", children: limits.map((l) => /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
10401
+ limits && limits.length ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("section", { className: "fdc-usage-sec", children: [
10402
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("h4", { className: "fdc-usage-h", children: "Usage limits" }),
10403
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("div", { className: "fdc-usage-rows", children: limits.map((l) => /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
10044
10404
  QuotaRow,
10045
10405
  {
10046
10406
  label: l.label,
@@ -10050,25 +10410,25 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
10050
10410
  l.id
10051
10411
  )) })
10052
10412
  ] }) : null,
10053
- stats ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { className: "fdc-usage-sec fdc-usage-stats", children: [
10054
- stats.elapsedMs ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
10055
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Session" }),
10056
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: formatDuration(stats.elapsedMs) })
10413
+ stats ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("section", { className: "fdc-usage-sec fdc-usage-stats", children: [
10414
+ stats.elapsedMs ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { children: [
10415
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: "Session" }),
10416
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("b", { className: "fd-tabular", children: formatDuration(stats.elapsedMs) })
10057
10417
  ] }) : null,
10058
- stats.tokens ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
10059
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Tokens" }),
10060
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: stats.tokens.toLocaleString() })
10418
+ stats.tokens ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { children: [
10419
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: "Tokens" }),
10420
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("b", { className: "fd-tabular", children: stats.tokens.toLocaleString() })
10061
10421
  ] }) : null,
10062
- stats.costUsd != null ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
10063
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Cost" }),
10064
- /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("b", { className: "fd-tabular", children: [
10422
+ stats.costUsd != null ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { children: [
10423
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: "Cost" }),
10424
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("b", { className: "fd-tabular", children: [
10065
10425
  "$",
10066
10426
  Number(stats.costUsd).toFixed(3)
10067
10427
  ] })
10068
10428
  ] }) : null,
10069
- stats.turns ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
10070
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Turns" }),
10071
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: stats.turns })
10429
+ stats.turns ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { children: [
10430
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: "Turns" }),
10431
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("b", { className: "fd-tabular", children: stats.turns })
10072
10432
  ] }) : null
10073
10433
  ] }) : null
10074
10434
  ] })
@@ -10107,7 +10467,7 @@ function ModelControls({
10107
10467
  disabled: m.disabled,
10108
10468
  checked: m.id === (current2 && current2.id),
10109
10469
  meta: m.meta,
10110
- shortcut: m.shortcut ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(KeyHint, { keys: m.shortcut, size: "sm" }) : void 0,
10470
+ shortcut: m.shortcut ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(KeyHint, { keys: m.shortcut, size: "sm" }) : void 0,
10111
10471
  onSelect: () => onModelChange && onModelChange(m.id)
10112
10472
  });
10113
10473
  const items = [{ kind: "section", label: "Models" }].concat(flat.map(item));
@@ -10120,15 +10480,15 @@ function ModelControls({
10120
10480
  items.push({ kind: "section", label: "Fast mode" });
10121
10481
  items.push({
10122
10482
  kind: "custom",
10123
- render: () => /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("label", { className: "fdc-switchrow", children: [
10124
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: fastModeLabel }),
10125
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("input", { type: "checkbox", className: "fd-sr", checked: !!fastMode, onChange: (e) => onFastModeChange(e.target.checked) }),
10126
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fd-switch-track" + (fastMode ? " is-on" : ""), "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fd-switch-thumb" }) })
10483
+ render: () => /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("label", { className: "fdc-switchrow", children: [
10484
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: fastModeLabel }),
10485
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("input", { type: "checkbox", className: "fd-sr", checked: !!fastMode, onChange: (e) => onFastModeChange(e.target.checked) }),
10486
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fd-switch-track" + (fastMode ? " is-on" : ""), "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fd-switch-thumb" }) })
10127
10487
  ] })
10128
10488
  });
10129
10489
  }
10130
- return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React45.Fragment, { children: [
10131
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
10490
+ return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(React49.Fragment, { children: [
10491
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
10132
10492
  MenuButton,
10133
10493
  {
10134
10494
  items,
@@ -10139,7 +10499,7 @@ function ModelControls({
10139
10499
  title: "Choose a model"
10140
10500
  }
10141
10501
  ),
10142
- effortLevels && effortLevels.length ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
10502
+ effortLevels && effortLevels.length ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
10143
10503
  MenuButton,
10144
10504
  {
10145
10505
  placement: "top-end",
@@ -10160,7 +10520,7 @@ function ModelControls({
10160
10520
  }
10161
10521
 
10162
10522
  // src/components/chat/AgentChatPanel.tsx
10163
- var import_jsx_runtime77 = require("react/jsx-runtime");
10523
+ var import_jsx_runtime79 = require("react/jsx-runtime");
10164
10524
  var SURFACES = { sidebar: "is-sidebar", inline: "is-inline", page: "is-page", modal: "is-modal", sheet: "is-sheet" };
10165
10525
  function AgentChatPanel({
10166
10526
  /* required */
@@ -10244,11 +10604,11 @@ function AgentChatPanel({
10244
10604
  onFeedback,
10245
10605
  onEditMessage
10246
10606
  }) {
10247
- const [panelWidth, setPanelWidth] = React46.useState(width);
10248
- const [applied, setApplied] = React46.useState({});
10249
- const [draft, setDraft] = React46.useState("");
10250
- const dragging = React46.useRef(null);
10251
- React46.useEffect(() => setPanelWidth(width), [width]);
10607
+ const [panelWidth, setPanelWidth] = React50.useState(width);
10608
+ const [applied, setApplied] = React50.useState({});
10609
+ const [draft, setDraft] = React50.useState("");
10610
+ const dragging = React50.useRef(null);
10611
+ React50.useEffect(() => setPanelWidth(width), [width]);
10252
10612
  const engine = useChatEngine({
10253
10613
  contextType,
10254
10614
  contextId,
@@ -10314,7 +10674,7 @@ function AgentChatPanel({
10314
10674
  onReconnect: engine.reload,
10315
10675
  deadTitle: "The assistant isn\u2019t reachable"
10316
10676
  };
10317
- const sessionBar = /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
10677
+ const sessionBar = /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10318
10678
  ChatSessionBar,
10319
10679
  {
10320
10680
  sessionStats,
@@ -10323,7 +10683,7 @@ function AgentChatPanel({
10323
10683
  onClear: engine.visible.length ? engine.clear : void 0
10324
10684
  }
10325
10685
  );
10326
- const modelControls = /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
10686
+ const modelControls = /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10327
10687
  ModelControls,
10328
10688
  {
10329
10689
  models,
@@ -10338,7 +10698,7 @@ function AgentChatPanel({
10338
10698
  narrow
10339
10699
  }
10340
10700
  );
10341
- const threadMenu = threads && threads.length ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
10701
+ const threadMenu = threads && threads.length ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10342
10702
  MenuButton,
10343
10703
  {
10344
10704
  variant: "ghost",
@@ -10365,7 +10725,7 @@ function AgentChatPanel({
10365
10725
  })))
10366
10726
  }
10367
10727
  ) : null;
10368
- return /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)(
10728
+ return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
10369
10729
  "aside",
10370
10730
  {
10371
10731
  className: ["fdc-panel", SURFACES[surface] || SURFACES.sidebar, narrow ? "is-narrow" : "", className].filter(Boolean).join(" "),
@@ -10376,7 +10736,7 @@ function AgentChatPanel({
10376
10736
  },
10377
10737
  "aria-label": title,
10378
10738
  children: [
10379
- surface === "sidebar" && resizable ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
10739
+ surface === "sidebar" && resizable ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10380
10740
  "div",
10381
10741
  {
10382
10742
  className: "fdc-grip",
@@ -10391,20 +10751,20 @@ function AgentChatPanel({
10391
10751
  }
10392
10752
  }
10393
10753
  ) : null,
10394
- showHeader ? /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("header", { className: "fdc-head", children: [
10395
- /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { className: "fdc-head-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-" + assistantIcon }) }),
10396
- /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("span", { className: "fdc-head-titles", children: [
10397
- /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { className: "fdc-head-title", title: truncationTitle(title), children: title }),
10398
- subtitle ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { className: "fdc-head-sub", title: truncationTitle(subtitle), children: subtitle }) : null
10754
+ showHeader ? /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("header", { className: "fdc-head", children: [
10755
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fdc-head-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-" + assistantIcon }) }),
10756
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fdc-head-titles", children: [
10757
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fdc-head-title", title: truncationTitle(title), children: title }),
10758
+ subtitle ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fdc-head-sub", title: truncationTitle(subtitle), children: subtitle }) : null
10399
10759
  ] }),
10400
- /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("span", { className: "fdc-head-acts", children: [
10760
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fdc-head-acts", children: [
10401
10761
  headerActions,
10402
10762
  threadMenu,
10403
- onNewThread ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("button", { type: "button", className: "fdc-iconbtn", onClick: onNewThread, "aria-label": "New conversation", title: "New conversation", children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-plus", "aria-hidden": "true" }) }) : null,
10404
- onClose ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("button", { type: "button", className: "fdc-iconbtn", onClick: onClose, "aria-label": "Close panel", title: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-x", "aria-hidden": "true" }) }) : null
10763
+ onNewThread ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("button", { type: "button", className: "fdc-iconbtn", onClick: onNewThread, "aria-label": "New conversation", title: "New conversation", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-plus", "aria-hidden": "true" }) }) : null,
10764
+ onClose ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("button", { type: "button", className: "fdc-iconbtn", onClick: onClose, "aria-label": "Close panel", title: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-x", "aria-hidden": "true" }) }) : null
10405
10765
  ] })
10406
10766
  ] }) : null,
10407
- /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
10767
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10408
10768
  ChatTranscript,
10409
10769
  {
10410
10770
  messages: engine.visible,
@@ -10419,7 +10779,7 @@ function AgentChatPanel({
10419
10779
  renderEmpty
10420
10780
  }
10421
10781
  ),
10422
- /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
10782
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10423
10783
  ChatComposer,
10424
10784
  {
10425
10785
  onSubmit: (text, atts) => engine.send(text, atts),
@@ -10452,8 +10812,8 @@ function AgentChatPanel({
10452
10812
  }
10453
10813
 
10454
10814
  // src/components/files/FileViewer.tsx
10455
- var React47 = __toESM(require("react"), 1);
10456
- var import_jsx_runtime78 = require("react/jsx-runtime");
10815
+ var React51 = __toESM(require("react"), 1);
10816
+ var import_jsx_runtime80 = require("react/jsx-runtime");
10457
10817
  function previewKindOf(file) {
10458
10818
  const mime = (file.mime || "").toLowerCase();
10459
10819
  const ext = extensionOf(file.name || "");
@@ -10509,13 +10869,13 @@ function parseDelimited(text, delimiter) {
10509
10869
  return rows;
10510
10870
  }
10511
10871
  function usePreview(file, kind, resolveUrl) {
10512
- const [state, setState] = React47.useState({ status: "loading" });
10513
- const [attempt, setAttempt] = React47.useState(0);
10514
- const resolveRef = React47.useRef(resolveUrl);
10515
- React47.useEffect(() => {
10872
+ const [state, setState] = React51.useState({ status: "loading" });
10873
+ const [attempt, setAttempt] = React51.useState(0);
10874
+ const resolveRef = React51.useRef(resolveUrl);
10875
+ React51.useEffect(() => {
10516
10876
  resolveRef.current = resolveUrl;
10517
10877
  }, [resolveUrl]);
10518
- React47.useEffect(() => {
10878
+ React51.useEffect(() => {
10519
10879
  if (kind === "none") {
10520
10880
  setState({ status: "ready" });
10521
10881
  return;
@@ -10545,19 +10905,19 @@ function usePreview(file, kind, resolveUrl) {
10545
10905
  if (ac) ac.abort();
10546
10906
  };
10547
10907
  }, [file.id, kind, attempt]);
10548
- return { ...state, retry: React47.useCallback(() => setAttempt((a) => a + 1), []) };
10908
+ return { ...state, retry: React51.useCallback(() => setAttempt((a) => a + 1), []) };
10549
10909
  }
10550
10910
  function PreviewBody({ file, kind, state }) {
10551
- if (kind === "image") return /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("img", { className: "fd-fv-image", src: state.url, alt: file.name || "Preview" });
10552
- if (kind === "pdf") return /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("iframe", { className: "fd-fv-pdf", src: state.url, title: file.name || "PDF preview" });
10553
- if (kind === "markdown") return /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Markdown, { className: "fd-fv-markdown", source: state.text || "" });
10911
+ if (kind === "image") return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("img", { className: "fd-fv-image", src: state.url, alt: file.name || "Preview" });
10912
+ if (kind === "pdf") return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("iframe", { className: "fd-fv-pdf", src: state.url, title: file.name || "PDF preview" });
10913
+ if (kind === "markdown") return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Markdown, { className: "fd-fv-markdown", source: state.text || "" });
10554
10914
  if (kind === "json") {
10555
10915
  let pretty = state.text || "";
10556
10916
  try {
10557
10917
  pretty = JSON.stringify(JSON.parse(pretty), null, 2);
10558
10918
  } catch {
10559
10919
  }
10560
- return /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(CodeBlock, { code: pretty, language: "json" });
10920
+ return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(CodeBlock, { code: pretty, language: "json" });
10561
10921
  }
10562
10922
  if (kind === "csv") {
10563
10923
  const tsv = extensionOf(file.name || "") === "tsv" || (file.mime || "").toLowerCase().includes("tab-separated");
@@ -10565,12 +10925,12 @@ function PreviewBody({ file, kind, state }) {
10565
10925
  const head = rows[0] || [];
10566
10926
  const dataRows = rows.slice(1);
10567
10927
  const shown = dataRows.slice(0, FILE_VIEWER_CSV_ROW_LIMIT);
10568
- return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "fd-fv-csv", children: [
10569
- /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("table", { className: "fd-fv-csv-table", children: [
10570
- /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("tr", { children: head.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("th", { children: c }, i)) }) }),
10571
- /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("tbody", { children: shown.map((r, ri) => /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("tr", { children: head.map((_, ci) => /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("td", { children: r[ci] ?? "" }, ci)) }, ri)) })
10928
+ return /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "fd-fv-csv", children: [
10929
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("table", { className: "fd-fv-csv-table", children: [
10930
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("tr", { children: head.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("th", { children: c }, i)) }) }),
10931
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("tbody", { children: shown.map((r, ri) => /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("tr", { children: head.map((_, ci) => /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("td", { children: r[ci] ?? "" }, ci)) }, ri)) })
10572
10932
  ] }),
10573
- dataRows.length > FILE_VIEWER_CSV_ROW_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("p", { className: "fd-fv-csv-note fd-body-sm fd-muted", children: [
10933
+ dataRows.length > FILE_VIEWER_CSV_ROW_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("p", { className: "fd-fv-csv-note fd-body-sm fd-muted", children: [
10574
10934
  "Showing the first ",
10575
10935
  FILE_VIEWER_CSV_ROW_LIMIT.toLocaleString(),
10576
10936
  " of ",
@@ -10579,24 +10939,24 @@ function PreviewBody({ file, kind, state }) {
10579
10939
  ] }) : null
10580
10940
  ] });
10581
10941
  }
10582
- if (kind === "text") return /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(CodeBlock, { code: state.text || "", language: extensionOf(file.name || "") || void 0 });
10583
- return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "fd-fv-fallback", children: [
10584
- /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fd-fv-fallback-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("i", { className: "ph ph-" + iconForMime(file.mime, file.name) }) }),
10585
- /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fd-fv-fallback-title", children: "No preview available" }),
10586
- /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("span", { className: "fd-fv-fallback-meta", children: [
10942
+ if (kind === "text") return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(CodeBlock, { code: state.text || "", language: extensionOf(file.name || "") || void 0 });
10943
+ return /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "fd-fv-fallback", children: [
10944
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("span", { className: "fd-fv-fallback-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("i", { className: "ph ph-" + iconForMime(file.mime, file.name) }) }),
10945
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("span", { className: "fd-fv-fallback-title", children: "No preview available" }),
10946
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("span", { className: "fd-fv-fallback-meta", children: [
10587
10947
  file.mime || "Unknown type",
10588
10948
  file.size ? " \xB7 " + formatBytes(file.size) : ""
10589
10949
  ] })
10590
10950
  ] });
10591
10951
  }
10592
10952
  function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", onClose, actions, className = "" }) {
10593
- const kind = React47.useMemo(() => previewKindOf(file), [file]);
10953
+ const kind = React51.useMemo(() => previewKindOf(file), [file]);
10594
10954
  const preview = usePreview(file, kind, resolveUrl);
10595
- const [tab, setTab] = React47.useState("preview");
10596
- const [downloading, setDownloading] = React47.useState(false);
10597
- const [downloadError, setDownloadError] = React47.useState(null);
10955
+ const [tab, setTab] = React51.useState("preview");
10956
+ const [downloading, setDownloading] = React51.useState(false);
10957
+ const [downloadError, setDownloadError] = React51.useState(null);
10598
10958
  const hasSide = sidePanel != null;
10599
- React47.useEffect(() => {
10959
+ React51.useEffect(() => {
10600
10960
  setTab("preview");
10601
10961
  }, [file.id]);
10602
10962
  const download = () => {
@@ -10612,29 +10972,29 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
10612
10972
  a.remove();
10613
10973
  }).catch((err) => setDownloadError(err && err.message || "Download failed.")).finally(() => setDownloading(false));
10614
10974
  };
10615
- return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(
10975
+ return /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(
10616
10976
  "section",
10617
10977
  {
10618
10978
  className: ["fd-fv", hasSide && tab === "side" ? "is-tab-side" : "is-tab-preview", className].filter(Boolean).join(" "),
10619
10979
  "aria-label": file.name || "File",
10620
10980
  children: [
10621
- /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("header", { className: "fd-fv-head", children: [
10622
- /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fd-fv-head-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("i", { className: "ph ph-" + iconForMime(file.mime, file.name) }) }),
10623
- /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("span", { className: "fd-fv-head-titles", children: [
10624
- /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fd-fv-head-title", title: file.name, children: file.name }),
10625
- /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("span", { className: "fd-fv-head-meta", children: [
10981
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("header", { className: "fd-fv-head", children: [
10982
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("span", { className: "fd-fv-head-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("i", { className: "ph ph-" + iconForMime(file.mime, file.name) }) }),
10983
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("span", { className: "fd-fv-head-titles", children: [
10984
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("span", { className: "fd-fv-head-title", title: file.name, children: file.name }),
10985
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("span", { className: "fd-fv-head-meta", children: [
10626
10986
  file.mime || "Unknown type",
10627
10987
  file.size ? " \xB7 " + formatBytes(file.size) : ""
10628
10988
  ] })
10629
10989
  ] }),
10630
- /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("span", { className: "fd-fv-head-actions", children: [
10990
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("span", { className: "fd-fv-head-actions", children: [
10631
10991
  actions,
10632
- /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Button, { variant: "secondary", size: "sm", icon: "download-simple", loading: downloading, onClick: download, children: "Download" }),
10633
- onClose ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(IconButton, { icon: "x", label: "Close", onClick: onClose }) : null
10992
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Button, { variant: "secondary", size: "sm", icon: "download-simple", loading: downloading, onClick: download, children: "Download" }),
10993
+ onClose ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(IconButton, { icon: "x", label: "Close", onClick: onClose }) : null
10634
10994
  ] })
10635
10995
  ] }),
10636
- downloadError ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("div", { className: "fd-fv-download-error", role: "alert", children: downloadError }) : null,
10637
- hasSide ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
10996
+ downloadError ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("div", { className: "fd-fv-download-error", role: "alert", children: downloadError }) : null,
10997
+ hasSide ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
10638
10998
  Tabs,
10639
10999
  {
10640
11000
  className: "fd-fv-tabs",
@@ -10643,19 +11003,19 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
10643
11003
  onChange: (v) => setTab(v)
10644
11004
  }
10645
11005
  ) : null,
10646
- /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "fd-fv-body", children: [
10647
- /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("div", { className: "fd-fv-preview", role: "region", "aria-label": "Preview of " + (file.name || "file"), children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
11006
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "fd-fv-body", children: [
11007
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("div", { className: "fd-fv-preview", role: "region", "aria-label": "Preview of " + (file.name || "file"), children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
10648
11008
  LoadingRegion,
10649
11009
  {
10650
11010
  state: preview.status === "loading" ? "loading" : preview.status === "error" ? "error" : "ready",
10651
11011
  error: preview.error,
10652
11012
  label: "Loading preview of " + (file.name || "file"),
10653
11013
  onRetry: preview.status === "error" ? preview.retry : void 0,
10654
- skeleton: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Skeleton, { shape: "card", h: 220 }),
10655
- children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(PreviewBody, { file, kind, state: preview })
11014
+ skeleton: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Skeleton, { shape: "card", h: 220 }),
11015
+ children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(PreviewBody, { file, kind, state: preview })
10656
11016
  }
10657
11017
  ) }),
10658
- hasSide ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("div", { className: "fd-fv-side", role: "region", "aria-label": sidePanelLabel, children: sidePanel }) : null
11018
+ hasSide ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("div", { className: "fd-fv-side", role: "region", "aria-label": sidePanelLabel, children: sidePanel }) : null
10659
11019
  ] })
10660
11020
  ]
10661
11021
  }
@@ -10663,8 +11023,8 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
10663
11023
  }
10664
11024
 
10665
11025
  // src/components/files/FileBrowser.tsx
10666
- var React48 = __toESM(require("react"), 1);
10667
- var import_jsx_runtime79 = require("react/jsx-runtime");
11026
+ var React52 = __toESM(require("react"), 1);
11027
+ var import_jsx_runtime81 = require("react/jsx-runtime");
10668
11028
  function childrenOf(folders, parentId) {
10669
11029
  return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
10670
11030
  }
@@ -10734,10 +11094,10 @@ function FolderPickerMenu({
10734
11094
  destinations,
10735
11095
  onPick
10736
11096
  }) {
10737
- const [open, setOpen] = React48.useState(false);
11097
+ const [open, setOpen] = React52.useState(false);
10738
11098
  const zIndex = useOverlayLayer(open);
10739
- const ref = React48.useRef(null);
10740
- React48.useEffect(() => {
11099
+ const ref = React52.useRef(null);
11100
+ React52.useEffect(() => {
10741
11101
  if (!open) return;
10742
11102
  const away = (e) => {
10743
11103
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -10752,8 +11112,8 @@ function FolderPickerMenu({
10752
11112
  document.removeEventListener("keydown", esc);
10753
11113
  };
10754
11114
  }, [open]);
10755
- return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fd-fb-menu-anchor", ref, children: [
10756
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11115
+ return /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("span", { className: "fd-fb-menu-anchor", ref, children: [
11116
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
10757
11117
  IconButton,
10758
11118
  {
10759
11119
  icon: "arrows-out-cardinal",
@@ -10765,9 +11125,9 @@ function FolderPickerMenu({
10765
11125
  onClick: () => setOpen((v) => !v)
10766
11126
  }
10767
11127
  ),
10768
- open ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-menu", role: "menu", "aria-label": label, style: { zIndex }, children: [
10769
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-overline fd-muted fd-fb-menu-title", children: "Move to\u2026" }),
10770
- destinations.map((d) => /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
11128
+ open ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-menu", role: "menu", "aria-label": label, style: { zIndex }, children: [
11129
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-overline fd-muted fd-fb-menu-title", children: "Move to\u2026" }),
11130
+ destinations.map((d) => /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(
10771
11131
  "button",
10772
11132
  {
10773
11133
  type: "button",
@@ -10779,7 +11139,7 @@ function FolderPickerMenu({
10779
11139
  setOpen(false);
10780
11140
  },
10781
11141
  children: [
10782
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-" + (d.id == null ? "house-simple" : "folder-simple"), "aria-hidden": "true" }),
11142
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("i", { className: "ph ph-" + (d.id == null ? "house-simple" : "folder-simple"), "aria-hidden": "true" }),
10783
11143
  d.name
10784
11144
  ]
10785
11145
  },
@@ -10789,8 +11149,8 @@ function FolderPickerMenu({
10789
11149
  ] });
10790
11150
  }
10791
11151
  function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
10792
- const [over, setOver] = React48.useState(false);
10793
- const dragCounter = React48.useRef(0);
11152
+ const [over, setOver] = React52.useState(false);
11153
+ const dragCounter = React52.useRef(0);
10794
11154
  const dropHandlers = isDropTarget ? {
10795
11155
  onDragEnter: (e) => {
10796
11156
  if (!hasInternalPayload(e)) return;
@@ -10815,8 +11175,8 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
10815
11175
  if (payload && onDropPayload) onDropPayload(payload);
10816
11176
  }
10817
11177
  } : {};
10818
- return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: ["fd-fb-row", over ? "is-drop-target" : "", className].filter(Boolean).join(" "), ...dropHandlers, children: [
10819
- /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
11178
+ return /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: ["fd-fb-row", over ? "is-drop-target" : "", className].filter(Boolean).join(" "), ...dropHandlers, children: [
11179
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(
10820
11180
  "button",
10821
11181
  {
10822
11182
  type: "button",
@@ -10825,26 +11185,26 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
10825
11185
  draggable: !!draggableId,
10826
11186
  onDragStart: draggableId ? (e) => writeDragPayload(e, draggableId) : void 0,
10827
11187
  children: [
10828
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-" + icon }) }),
10829
- /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fd-fb-row-text", children: [
10830
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-name", title: name, children: name }),
10831
- meta ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-meta", children: meta }) : null
11188
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-fb-row-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("i", { className: "ph ph-" + icon }) }),
11189
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("span", { className: "fd-fb-row-text", children: [
11190
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-fb-row-name", title: name, children: name }),
11191
+ meta ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-fb-row-meta", children: meta }) : null
10832
11192
  ] })
10833
11193
  ]
10834
11194
  }
10835
11195
  ),
10836
- actions ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-actions", children: actions }) : null
11196
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-fb-row-actions", children: actions }) : null
10837
11197
  ] });
10838
11198
  }
10839
11199
  function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
10840
11200
  const kids = childrenOf(folders, folder.id);
10841
- const [expanded, setExpanded] = React48.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
10842
- React48.useEffect(() => {
11201
+ const [expanded, setExpanded] = React52.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
11202
+ React52.useEffect(() => {
10843
11203
  if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
10844
11204
  }, [currentFolderId, folder.id, folders]);
10845
11205
  const isCurrent = folder.id === currentFolderId;
10846
- return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-tree-node", children: [
10847
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11206
+ return /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-tree-node", children: [
11207
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
10848
11208
  RowShell,
10849
11209
  {
10850
11210
  icon: folder.id === currentFolderId ? "folder-open" : "folder-simple",
@@ -10854,7 +11214,7 @@ function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveI
10854
11214
  isDropTarget: canMove,
10855
11215
  onDropPayload: (payload) => onMoveInto(payload, folder.id),
10856
11216
  className: ["fd-fb-tree-row", isCurrent ? "is-current" : ""].filter(Boolean).join(" "),
10857
- actions: kids.length ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11217
+ actions: kids.length ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
10858
11218
  IconButton,
10859
11219
  {
10860
11220
  icon: expanded ? "caret-down" : "caret-right",
@@ -10866,7 +11226,7 @@ function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveI
10866
11226
  ) : void 0
10867
11227
  }
10868
11228
  ),
10869
- expanded && kids.length ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "fd-fb-tree-children", style: { paddingLeft: 14 }, children: kids.map((k) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11229
+ expanded && kids.length ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("div", { className: "fd-fb-tree-children", style: { paddingLeft: 14 }, children: kids.map((k) => /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
10870
11230
  TreeNode,
10871
11231
  {
10872
11232
  folder: k,
@@ -10905,15 +11265,15 @@ function FileBrowser({
10905
11265
  const canUpload = perm.canUpload !== false && !!onUpload;
10906
11266
  const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
10907
11267
  const canDeleteFolder = perm.canDeleteFolder !== false && !!onDeleteFolder;
10908
- const [createOpen, setCreateOpen] = React48.useState(false);
10909
- const [createName, setCreateName] = React48.useState("");
10910
- const [renameTarget, setRenameTarget] = React48.useState(null);
10911
- const [renameValue, setRenameValue] = React48.useState("");
10912
- const [deleteTarget, setDeleteTarget] = React48.useState(null);
10913
- const uploadInputRef = React48.useRef(null);
10914
- const crumbFolders = React48.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
10915
- const subfolders = React48.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
10916
- const currentFiles = React48.useMemo(
11268
+ const [createOpen, setCreateOpen] = React52.useState(false);
11269
+ const [createName, setCreateName] = React52.useState("");
11270
+ const [renameTarget, setRenameTarget] = React52.useState(null);
11271
+ const [renameValue, setRenameValue] = React52.useState("");
11272
+ const [deleteTarget, setDeleteTarget] = React52.useState(null);
11273
+ const uploadInputRef = React52.useRef(null);
11274
+ const crumbFolders = React52.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
11275
+ const subfolders = React52.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
11276
+ const currentFiles = React52.useMemo(
10917
11277
  () => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
10918
11278
  [files, currentFolderId]
10919
11279
  );
@@ -10952,10 +11312,10 @@ function FileBrowser({
10952
11312
  if (deleteTarget && onDeleteFolder) onDeleteFolder(deleteTarget);
10953
11313
  setDeleteTarget(null);
10954
11314
  };
10955
- const content = /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: ["fd-fb", className].filter(Boolean).join(" "), children: [
10956
- /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-head", children: [
10957
- /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("nav", { className: "fd-fb-crumbs", "aria-label": "Folder path", children: [
10958
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11315
+ const content = /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: ["fd-fb", className].filter(Boolean).join(" "), children: [
11316
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-head", children: [
11317
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("nav", { className: "fd-fb-crumbs", "aria-label": "Folder path", children: [
11318
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
10959
11319
  BreadcrumbCrumb,
10960
11320
  {
10961
11321
  label: "Home",
@@ -10965,7 +11325,7 @@ function FileBrowser({
10965
11325
  onDropPayload: (p) => moveIntoFolder(p, null)
10966
11326
  }
10967
11327
  ),
10968
- crumbFolders.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11328
+ crumbFolders.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
10969
11329
  BreadcrumbCrumb,
10970
11330
  {
10971
11331
  label: f.name,
@@ -10977,11 +11337,11 @@ function FileBrowser({
10977
11337
  f.id
10978
11338
  ))
10979
11339
  ] }),
10980
- /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fd-fb-actions", children: [
10981
- canCreateFolder ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", size: "sm", icon: "folder-simple-plus", onClick: openCreate, children: "New folder" }) : null,
10982
- canUpload ? /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
10983
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", size: "sm", icon: "upload-simple", onClick: () => uploadInputRef.current && uploadInputRef.current.click(), children: "Upload" }),
10984
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11340
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("span", { className: "fd-fb-actions", children: [
11341
+ canCreateFolder ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "secondary", size: "sm", icon: "folder-simple-plus", onClick: openCreate, children: "New folder" }) : null,
11342
+ canUpload ? /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
11343
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "secondary", size: "sm", icon: "upload-simple", onClick: () => uploadInputRef.current && uploadInputRef.current.click(), children: "Upload" }),
11344
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
10985
11345
  "input",
10986
11346
  {
10987
11347
  ref: uploadInputRef,
@@ -10999,9 +11359,9 @@ function FileBrowser({
10999
11359
  ] }) : null
11000
11360
  ] })
11001
11361
  ] }),
11002
- /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-body", children: [
11003
- /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-tree", role: "region", "aria-label": "Folder tree", children: [
11004
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11362
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-body", children: [
11363
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-tree", role: "region", "aria-label": "Folder tree", children: [
11364
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
11005
11365
  RowShell,
11006
11366
  {
11007
11367
  icon: "house-simple",
@@ -11012,7 +11372,7 @@ function FileBrowser({
11012
11372
  className: ["fd-fb-tree-row", currentFolderId == null ? "is-current" : ""].filter(Boolean).join(" ")
11013
11373
  }
11014
11374
  ),
11015
- childrenOf(folders, null).map((f) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11375
+ childrenOf(folders, null).map((f) => /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
11016
11376
  TreeNode,
11017
11377
  {
11018
11378
  folder: f,
@@ -11026,18 +11386,18 @@ function FileBrowser({
11026
11386
  f.id
11027
11387
  ))
11028
11388
  ] }),
11029
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "fd-fb-main", role: "region", "aria-label": "Folder contents", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11389
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("div", { className: "fd-fb-main", role: "region", "aria-label": "Folder contents", children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
11030
11390
  LoadingRegion,
11031
11391
  {
11032
11392
  state: loading ? "loading" : "ready",
11033
11393
  label: "Loading folder contents",
11034
- skeleton: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-stack", style: { gap: 8, padding: 12 }, children: [
11035
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Skeleton, { h: 44, radius: "10px" }),
11036
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Skeleton, { h: 44, radius: "10px" }),
11037
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Skeleton, { h: 44, radius: "10px" })
11394
+ skeleton: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-stack", style: { gap: 8, padding: 12 }, children: [
11395
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Skeleton, { h: 44, radius: "10px" }),
11396
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Skeleton, { h: 44, radius: "10px" }),
11397
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Skeleton, { h: 44, radius: "10px" })
11038
11398
  ] }),
11039
- children: isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(EmptyState, { icon: "folder-open", title: "This folder is empty", children: canUpload ? "Drop files here, or use Upload above." : "Nothing has been filed here yet." }) : /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-list", children: [
11040
- subfolders.map((f) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11399
+ children: isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(EmptyState, { icon: "folder-open", title: "This folder is empty", children: canUpload ? "Drop files here, or use Upload above." : "Nothing has been filed here yet." }) : /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-list", children: [
11400
+ subfolders.map((f) => /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
11041
11401
  RowShell,
11042
11402
  {
11043
11403
  icon: "folder-simple",
@@ -11046,9 +11406,9 @@ function FileBrowser({
11046
11406
  draggableId: canMove ? { kind: "folder", id: f.id } : void 0,
11047
11407
  isDropTarget: canMove,
11048
11408
  onDropPayload: (p) => moveIntoFolder(p, f.id),
11049
- actions: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
11050
- canRenameFolder ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(IconButton, { icon: "pencil-simple", label: "Rename " + f.name, size: "sm", variant: "ghost", onClick: () => openRename(f) }) : null,
11051
- canMove ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11409
+ actions: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
11410
+ canRenameFolder ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(IconButton, { icon: "pencil-simple", label: "Rename " + f.name, size: "sm", variant: "ghost", onClick: () => openRename(f) }) : null,
11411
+ canMove ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
11052
11412
  FolderPickerMenu,
11053
11413
  {
11054
11414
  label: "Move " + f.name,
@@ -11056,12 +11416,12 @@ function FileBrowser({
11056
11416
  onPick: (target) => moveIntoFolder({ kind: "folder", id: f.id }, target)
11057
11417
  }
11058
11418
  ) : null,
11059
- canDeleteFolder ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(IconButton, { icon: "trash-simple", label: "Delete " + f.name, size: "sm", variant: "ghost", onClick: () => openDelete(f) }) : null
11419
+ canDeleteFolder ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(IconButton, { icon: "trash-simple", label: "Delete " + f.name, size: "sm", variant: "ghost", onClick: () => openDelete(f) }) : null
11060
11420
  ] })
11061
11421
  },
11062
11422
  f.id
11063
11423
  )),
11064
- currentFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11424
+ currentFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
11065
11425
  RowShell,
11066
11426
  {
11067
11427
  icon: iconForMime(file.mime, file.name),
@@ -11069,8 +11429,8 @@ function FileBrowser({
11069
11429
  meta: file.size ? formatBytes(file.size) : void 0,
11070
11430
  onOpen: onOpenFile ? () => onOpenFile(file) : void 0,
11071
11431
  draggableId: canMove ? { kind: "file", id: file.id } : void 0,
11072
- actions: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
11073
- canMove ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11432
+ actions: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
11433
+ canMove ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
11074
11434
  FolderPickerMenu,
11075
11435
  {
11076
11436
  label: "Move " + file.name,
@@ -11078,7 +11438,7 @@ function FileBrowser({
11078
11438
  onPick: (target) => onMoveFile && onMoveFile(file.id, target)
11079
11439
  }
11080
11440
  ) : null,
11081
- canRemoveFile ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(IconButton, { icon: "trash-simple", label: "Remove " + file.name, size: "sm", variant: "ghost", onClick: () => onRemoveFile && onRemoveFile(file) }) : null
11441
+ canRemoveFile ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(IconButton, { icon: "trash-simple", label: "Remove " + file.name, size: "sm", variant: "ghost", onClick: () => onRemoveFile && onRemoveFile(file) }) : null
11082
11442
  ] })
11083
11443
  },
11084
11444
  file.id
@@ -11087,10 +11447,10 @@ function FileBrowser({
11087
11447
  }
11088
11448
  ) })
11089
11449
  ] }),
11090
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Modal, { open: createOpen, title: "New folder", onClose: () => setCreateOpen(false), footer: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
11091
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", onClick: () => setCreateOpen(false), children: "Cancel" }),
11092
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { onClick: submitCreate, disabled: !createName.trim(), children: "Create" })
11093
- ] }), children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11450
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Modal, { open: createOpen, title: "New folder", onClose: () => setCreateOpen(false), footer: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
11451
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "secondary", onClick: () => setCreateOpen(false), children: "Cancel" }),
11452
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { onClick: submitCreate, disabled: !createName.trim(), children: "Create" })
11453
+ ] }), children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
11094
11454
  "input",
11095
11455
  {
11096
11456
  className: "fd-fb-input",
@@ -11104,10 +11464,10 @@ function FileBrowser({
11104
11464
  autoFocus: true
11105
11465
  }
11106
11466
  ) }),
11107
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Modal, { open: renameTarget != null, title: "Rename folder", onClose: () => setRenameTarget(null), footer: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
11108
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", onClick: () => setRenameTarget(null), children: "Cancel" }),
11109
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { onClick: submitRename, disabled: !renameValue.trim(), children: "Save" })
11110
- ] }), children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11467
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Modal, { open: renameTarget != null, title: "Rename folder", onClose: () => setRenameTarget(null), footer: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
11468
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "secondary", onClick: () => setRenameTarget(null), children: "Cancel" }),
11469
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { onClick: submitRename, disabled: !renameValue.trim(), children: "Save" })
11470
+ ] }), children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
11111
11471
  "input",
11112
11472
  {
11113
11473
  className: "fd-fb-input",
@@ -11121,22 +11481,22 @@ function FileBrowser({
11121
11481
  autoFocus: true
11122
11482
  }
11123
11483
  ) }),
11124
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11484
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
11125
11485
  Modal,
11126
11486
  {
11127
11487
  open: deleteTarget != null,
11128
11488
  title: deleteTarget ? "Delete \u201C" + deleteTarget.name + "\u201D?" : "Delete folder?",
11129
11489
  description: "This can't be undone. The folder must be empty.",
11130
11490
  onClose: () => setDeleteTarget(null),
11131
- footer: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
11132
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", onClick: () => setDeleteTarget(null), children: "Cancel" }),
11133
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "danger", onClick: confirmDelete, children: "Delete" })
11491
+ footer: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
11492
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "secondary", onClick: () => setDeleteTarget(null), children: "Cancel" }),
11493
+ /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "danger", onClick: confirmDelete, children: "Delete" })
11134
11494
  ] })
11135
11495
  }
11136
11496
  )
11137
11497
  ] });
11138
11498
  if (!canUpload) return content;
11139
- return /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Dropzone, { className: "fd-fb-dropzone", onFiles: (picked) => onUpload && onUpload(picked, currentFolderId), label: "Drop files to upload", children: content });
11499
+ return /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Dropzone, { className: "fd-fb-dropzone", onFiles: (picked) => onUpload && onUpload(picked, currentFolderId), label: "Drop files to upload", children: content });
11140
11500
  }
11141
11501
  function BreadcrumbCrumb({
11142
11502
  label,
@@ -11145,8 +11505,8 @@ function BreadcrumbCrumb({
11145
11505
  isDropTarget,
11146
11506
  onDropPayload
11147
11507
  }) {
11148
- const [over, setOver] = React48.useState(false);
11149
- const dragCounter = React48.useRef(0);
11508
+ const [over, setOver] = React52.useState(false);
11509
+ const dragCounter = React52.useRef(0);
11150
11510
  const dropHandlers = isDropTarget ? {
11151
11511
  onDragEnter: (e) => {
11152
11512
  if (!hasInternalPayload(e)) return;
@@ -11171,11 +11531,11 @@ function BreadcrumbCrumb({
11171
11531
  if (payload && onDropPayload) onDropPayload(payload);
11172
11532
  }
11173
11533
  } : {};
11174
- return /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: ["fd-fb-crumb", over ? "is-drop-target" : ""].filter(Boolean).join(" "), ...dropHandlers, children: isCurrent ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-crumb-current", "aria-current": "page", children: label }) : /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("button", { type: "button", className: "fd-fb-crumb-link", onClick, children: label }) });
11534
+ return /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: ["fd-fb-crumb", over ? "is-drop-target" : ""].filter(Boolean).join(" "), ...dropHandlers, children: isCurrent ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-fb-crumb-current", "aria-current": "page", children: label }) : /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("button", { type: "button", className: "fd-fb-crumb-link", onClick, children: label }) });
11175
11535
  }
11176
11536
 
11177
11537
  // src/kits/query.ts
11178
- var React49 = __toESM(require("react"), 1);
11538
+ var React53 = __toESM(require("react"), 1);
11179
11539
  function eqFilter(get2) {
11180
11540
  return (row, value) => Array.isArray(value) ? value.includes(get2(row)) : get2(row) === value;
11181
11541
  }
@@ -11207,22 +11567,22 @@ function compare(a, b, dir) {
11207
11567
  var API = null;
11208
11568
  var PREFS = null;
11209
11569
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
11210
- const [query, setQuery] = React49.useState(() => {
11570
+ const [query, setQuery] = React53.useState(() => {
11211
11571
  const store = PREFS || window.PlannerPrefs;
11212
11572
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
11213
11573
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
11214
11574
  });
11215
- const savePref = React49.useCallback((patch2) => {
11575
+ const savePref = React53.useCallback((patch2) => {
11216
11576
  const store = PREFS || window.PlannerPrefs;
11217
11577
  if (prefsKey && store) store.setTable(prefsKey, patch2);
11218
11578
  }, [prefsKey]);
11219
- const [res, setRes] = React49.useState(null);
11220
- const [loading, setLoading] = React49.useState(true);
11221
- const seq2 = React49.useRef(0);
11579
+ const [res, setRes] = React53.useState(null);
11580
+ const [loading, setLoading] = React53.useState(true);
11581
+ const seq2 = React53.useRef(0);
11222
11582
  const depKey = (deps || []).join("|");
11223
11583
  const paramKey = JSON.stringify(params || {});
11224
11584
  const queryKey = JSON.stringify(query);
11225
- React49.useEffect(() => {
11585
+ React53.useEffect(() => {
11226
11586
  const id = ++seq2.current;
11227
11587
  setLoading(true);
11228
11588
  const t = setTimeout(() => {
@@ -11524,6 +11884,8 @@ function createVersionStore(initialState, options) {
11524
11884
  AccountMenu,
11525
11885
  AgentChatPanel,
11526
11886
  ApiSpecBrowser,
11887
+ AppSwitcher,
11888
+ AppearanceMenu,
11527
11889
  Avatar,
11528
11890
  AvatarStack,
11529
11891
  Badge,
@@ -11554,8 +11916,11 @@ function createVersionStore(initialState, options) {
11554
11916
  CsiBadge,
11555
11917
  CsiHero,
11556
11918
  CsiMeter,
11919
+ DEFAULT_PREFS,
11920
+ DEFAULT_STORAGE_KEY,
11557
11921
  DataTable,
11558
11922
  DatePicker,
11923
+ DemoSessionProvider,
11559
11924
  DiffBlock,
11560
11925
  Drawer,
11561
11926
  Dropzone,
@@ -11625,6 +11990,7 @@ function createVersionStore(initialState, options) {
11625
11990
  SegmentedMeter,
11626
11991
  Select,
11627
11992
  SessionKit,
11993
+ SessionProvider,
11628
11994
  SidebarNav,
11629
11995
  Skeleton,
11630
11996
  SkeletonText,
@@ -11653,9 +12019,11 @@ function createVersionStore(initialState, options) {
11653
12019
  VirtualList,
11654
12020
  acceptMatches,
11655
12021
  anyOfFilter,
12022
+ applyPrefs,
11656
12023
  channelWeightOf,
11657
12024
  computeNeedMore,
11658
12025
  computeVirtualWindow,
12026
+ createLocalStorageAppearanceStore,
11659
12027
  createVersionStore,
11660
12028
  eqFilter,
11661
12029
  extensionOf,
@@ -11676,6 +12044,7 @@ function createVersionStore(initialState, options) {
11676
12044
  isSystemMessage,
11677
12045
  jobBucket,
11678
12046
  languageLabel,
12047
+ loadPrefs,
11679
12048
  markdownToText,
11680
12049
  meterFormats,
11681
12050
  modifierLabel,
@@ -11685,14 +12054,18 @@ function createVersionStore(initialState, options) {
11685
12054
  preferenceScore,
11686
12055
  preferenceSegments,
11687
12056
  rangeFilter,
12057
+ resolveTheme,
11688
12058
  revokeAttachment,
12059
+ savePrefs,
11689
12060
  toAttachment,
11690
12061
  tokenize,
12062
+ useAppearancePrefs,
11691
12063
  useChatEngine,
11692
12064
  useFeatureStatus,
11693
12065
  usePopoverPosition,
11694
12066
  useRuntimeMode,
11695
12067
  useServerTable,
12068
+ useSessionContext,
11696
12069
  useStagedFiles,
11697
12070
  visibleMessages
11698
12071
  });