@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.js CHANGED
@@ -5137,8 +5137,835 @@ var MarkdownEditor = React29.forwardRef(function MarkdownEditor2({
5137
5137
  // src/components/platform/AccountMenu.tsx
5138
5138
  import * as React31 from "react";
5139
5139
 
5140
- // src/kits/session.ts
5140
+ // src/kits/sessionContext.tsx
5141
5141
  import * as React30 from "react";
5142
+ var SessionContext = React30.createContext(null);
5143
+ function SessionProvider({ value, children }) {
5144
+ return React30.createElement(SessionContext.Provider, { value }, children);
5145
+ }
5146
+ function useSessionContext() {
5147
+ const ctx = React30.useContext(SessionContext);
5148
+ if (!ctx) {
5149
+ throw new Error(
5150
+ "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."
5151
+ );
5152
+ }
5153
+ return ctx;
5154
+ }
5155
+
5156
+ // src/components/platform/AccountMenu.tsx
5157
+ import { Fragment as Fragment14, jsx as jsx57, jsxs as jsxs53 } from "react/jsx-runtime";
5158
+ var DEFAULT_LINKS = [
5159
+ { id: "profile", label: "Your profile", icon: "user-circle", href: "../admin/index.html#profile" },
5160
+ { id: "preferences", label: "Preferences", icon: "sliders-horizontal", href: "../admin/index.html#preferences" }
5161
+ ];
5162
+ var DEFAULT_GROUPS = [
5163
+ {
5164
+ label: "Administration",
5165
+ links: [
5166
+ { id: "users", label: "Users", icon: "users-three", href: "../admin/index.html#users", perm: "users.view" },
5167
+ { id: "roles", label: "Roles & permissions", icon: "shield-check", href: "../admin/index.html#roles", perm: "roles.manage" },
5168
+ { id: "flags", label: "Feature flags", icon: "toggle-right", href: "../admin/index.html#flags", perm: "flags.manage" }
5169
+ ]
5170
+ }
5171
+ ];
5172
+ function Item({ item, onPick }) {
5173
+ return /* @__PURE__ */ jsxs53(
5174
+ "button",
5175
+ {
5176
+ type: "button",
5177
+ role: "menuitem",
5178
+ className: "fd-acct-item",
5179
+ onClick: () => onPick(item),
5180
+ children: [
5181
+ item.icon ? /* @__PURE__ */ jsx57("i", { className: "ph ph-" + item.icon, "aria-hidden": "true" }) : null,
5182
+ /* @__PURE__ */ jsx57("span", { style: { flex: 1 }, children: item.label }),
5183
+ item.badge ? /* @__PURE__ */ jsx57(Badge, { tone: "neutral", children: item.badge }) : null
5184
+ ]
5185
+ }
5186
+ );
5187
+ }
5188
+ function AccountMenu({
5189
+ links = DEFAULT_LINKS,
5190
+ groups = DEFAULT_GROUPS,
5191
+ onNavigate,
5192
+ onSignOut,
5193
+ showRoles = true,
5194
+ allowUserSwitch = true,
5195
+ className = "",
5196
+ ...rest
5197
+ }) {
5198
+ const { user, roles, can: can2, signOut: ctxSignOut, demo } = useSessionContext();
5199
+ const [open, setOpen] = React31.useState(false);
5200
+ const [switching, setSwitching] = React31.useState(false);
5201
+ const [signingOut, setSigningOut] = React31.useState(false);
5202
+ const ref = React31.useRef(null);
5203
+ const visibleLinks = links.filter((l) => !l.perm || can2(l.perm));
5204
+ const visibleGroups = groups.map((g) => ({ label: g.label, links: g.links.filter((l) => !l.perm || can2(l.perm)) })).filter((g) => g.links.length);
5205
+ const pick = (item) => {
5206
+ setOpen(false);
5207
+ if (item.onSelect) return item.onSelect();
5208
+ if (onNavigate) return onNavigate(item.href, item);
5209
+ if (item.href) window.location.href = item.href;
5210
+ };
5211
+ const signOut = async () => {
5212
+ const handler = onSignOut ?? ctxSignOut;
5213
+ setSigningOut(true);
5214
+ try {
5215
+ await handler();
5216
+ } catch (err) {
5217
+ console.error("AccountMenu: sign-out handler failed", err);
5218
+ } finally {
5219
+ setSigningOut(false);
5220
+ setOpen(false);
5221
+ }
5222
+ };
5223
+ const showSwitcher = allowUserSwitch && !!demo;
5224
+ return /* @__PURE__ */ jsxs53(Fragment14, { children: [
5225
+ /* @__PURE__ */ jsxs53(
5226
+ "button",
5227
+ {
5228
+ type: "button",
5229
+ ref,
5230
+ className: ["fd-acct-trigger", className].filter(Boolean).join(" "),
5231
+ "aria-haspopup": "menu",
5232
+ "aria-expanded": open,
5233
+ "aria-label": "Account \u2014 " + user.name,
5234
+ onClick: () => setOpen((o) => !o),
5235
+ ...rest,
5236
+ children: [
5237
+ /* @__PURE__ */ jsx57(Avatar, { name: user.name, size: "sm" }),
5238
+ /* @__PURE__ */ jsx57("i", { className: "ph ph-caret-down", "aria-hidden": "true", style: { fontSize: 11, color: "var(--text-muted)" } })
5239
+ ]
5240
+ }
5241
+ ),
5242
+ /* @__PURE__ */ jsx57(Popover, { open, anchorRef: ref, onClose: () => setOpen(false), placement: "bottom-end", width: 272, children: /* @__PURE__ */ jsxs53("div", { role: "menu", className: "fd-stack", style: { gap: 0 }, children: [
5243
+ /* @__PURE__ */ jsxs53("div", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
5244
+ /* @__PURE__ */ jsx57(Avatar, { name: user.name }),
5245
+ /* @__PURE__ */ jsxs53("span", { className: "fd-stack", style: { gap: 1, minWidth: 0, flex: 1 }, children: [
5246
+ /* @__PURE__ */ jsx57("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: user.name }),
5247
+ /* @__PURE__ */ jsx57("span", { className: "fd-body-sm fd-muted fd-table-trunc", title: truncationTitle(user.email), children: user.email })
5248
+ ] })
5249
+ ] }),
5250
+ showRoles && roles.length ? /* @__PURE__ */ jsx57("div", { className: "fd-row", style: { gap: 6, padding: "10px 14px", flexWrap: "wrap", borderBottom: "1px solid var(--border)" }, children: roles.map((r) => /* @__PURE__ */ jsx57(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)) }) : null,
5251
+ visibleLinks.length ? /* @__PURE__ */ jsx57("div", { className: "fd-acct-group", children: visibleLinks.map((l) => /* @__PURE__ */ jsx57(Item, { item: l, onPick: pick }, l.id)) }) : null,
5252
+ visibleGroups.map((g) => /* @__PURE__ */ jsxs53("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
5253
+ /* @__PURE__ */ jsx57("span", { className: "fd-overline fd-muted", style: { padding: "8px 14px 4px", display: "block" }, children: g.label }),
5254
+ g.links.map((l) => /* @__PURE__ */ jsx57(Item, { item: l, onPick: pick }, l.id))
5255
+ ] }, g.label)),
5256
+ showSwitcher && demo ? /* @__PURE__ */ jsxs53("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
5257
+ /* @__PURE__ */ jsxs53("button", { type: "button", role: "menuitem", className: "fd-acct-item", onClick: () => setSwitching((s) => !s), children: [
5258
+ /* @__PURE__ */ jsx57("i", { className: "ph ph-user-switch", "aria-hidden": "true" }),
5259
+ /* @__PURE__ */ jsx57("span", { style: { flex: 1 }, children: "View as another member" }),
5260
+ /* @__PURE__ */ jsx57("i", { className: "ph ph-caret-" + (switching ? "up" : "down"), "aria-hidden": "true", style: { fontSize: 11 } })
5261
+ ] }),
5262
+ switching ? /* @__PURE__ */ jsx57("div", { className: "fd-stack", style: { gap: 0 }, children: demo.allUsers.map((u) => /* @__PURE__ */ jsxs53(
5263
+ "button",
5264
+ {
5265
+ type: "button",
5266
+ className: "fd-acct-item",
5267
+ "data-current": u.id === user.id ? "true" : "false",
5268
+ onClick: () => {
5269
+ demo.setActiveUser(u.id);
5270
+ setOpen(false);
5271
+ setSwitching(false);
5272
+ },
5273
+ children: [
5274
+ /* @__PURE__ */ jsx57(Avatar, { name: u.name, size: "sm" }),
5275
+ /* @__PURE__ */ jsxs53("span", { className: "fd-stack", style: { gap: 0, flex: 1, minWidth: 0, alignItems: "flex-start" }, children: [
5276
+ /* @__PURE__ */ jsx57("span", { style: { fontWeight: u.id === user.id ? 700 : 500 }, children: u.name }),
5277
+ u.roles ? /* @__PURE__ */ jsx57("span", { className: "fd-muted", style: { fontSize: 11.5 }, children: u.roles.join(", ") }) : null
5278
+ ] }),
5279
+ u.id === user.id ? /* @__PURE__ */ jsx57("i", { className: "ph ph-check", "aria-hidden": "true", style: { color: "var(--ok-text)" } }) : null
5280
+ ]
5281
+ },
5282
+ u.id
5283
+ )) }) : null
5284
+ ] }) : null,
5285
+ /* @__PURE__ */ jsx57("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: /* @__PURE__ */ jsxs53(
5286
+ "button",
5287
+ {
5288
+ type: "button",
5289
+ role: "menuitem",
5290
+ className: "fd-acct-item",
5291
+ "data-danger": "true",
5292
+ disabled: signingOut,
5293
+ onClick: signOut,
5294
+ children: [
5295
+ signingOut ? /* @__PURE__ */ jsx57(Spinner, { size: "sm" }) : /* @__PURE__ */ jsx57("i", { className: "ph ph-sign-out", "aria-hidden": "true" }),
5296
+ /* @__PURE__ */ jsx57("span", { style: { flex: 1 }, children: signingOut ? "Signing out\u2026" : "Sign out" })
5297
+ ]
5298
+ }
5299
+ ) })
5300
+ ] }) })
5301
+ ] });
5302
+ }
5303
+
5304
+ // src/components/platform/AppSwitcher.tsx
5305
+ import * as React32 from "react";
5306
+ import { jsx as jsx58, jsxs as jsxs54 } from "react/jsx-runtime";
5307
+ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
5308
+ const { apps: contextApps } = useSessionContext();
5309
+ const apps = appsProp ?? contextApps ?? [];
5310
+ const [open, setOpen] = React32.useState(false);
5311
+ const anchorRef = React32.useRef(null);
5312
+ if (!apps.length) return null;
5313
+ const items = apps.map((app) => ({
5314
+ id: app.key,
5315
+ label: app.name,
5316
+ icon: app.icon,
5317
+ meta: app.current ? "Current" : void 0,
5318
+ disabled: app.current,
5319
+ onSelect: () => {
5320
+ if (app.current) return;
5321
+ if (onNavigate) return onNavigate(app);
5322
+ window.location.href = app.url;
5323
+ }
5324
+ }));
5325
+ return /* @__PURE__ */ jsxs54(React32.Fragment, { children: [
5326
+ /* @__PURE__ */ jsx58("span", { ref: anchorRef, className: ["fd-appswitch-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx58(
5327
+ IconButton,
5328
+ {
5329
+ icon: "squares-four",
5330
+ label: "All flytedesk apps",
5331
+ "aria-haspopup": "menu",
5332
+ "aria-expanded": open,
5333
+ onClick: () => setOpen((v) => !v)
5334
+ }
5335
+ ) }),
5336
+ /* @__PURE__ */ jsx58(
5337
+ Popover,
5338
+ {
5339
+ open,
5340
+ anchorRef,
5341
+ onClose: () => setOpen(false),
5342
+ placement: "bottom-end",
5343
+ role: "menu",
5344
+ label: "All flytedesk apps",
5345
+ width: 240,
5346
+ maxHeight: 360,
5347
+ children: /* @__PURE__ */ jsx58(Menu, { items, onClose: () => setOpen(false) })
5348
+ }
5349
+ )
5350
+ ] });
5351
+ }
5352
+
5353
+ // src/components/platform/AppearanceMenu.tsx
5354
+ import * as React34 from "react";
5355
+
5356
+ // src/kits/appearance.ts
5357
+ import * as React33 from "react";
5358
+ var DEFAULT_STORAGE_KEY = "fd-appearance";
5359
+ var DEFAULT_PREFS = {
5360
+ theme: "system",
5361
+ textScale: "md",
5362
+ highContrast: false,
5363
+ reduceMotion: false
5364
+ };
5365
+ function resolveDefaults(opts) {
5366
+ return { ...DEFAULT_PREFS, ...opts?.defaults };
5367
+ }
5368
+ function resolveStorage(opts) {
5369
+ if (opts?.storage) return opts.storage;
5370
+ return typeof window !== "undefined" ? window.localStorage : void 0;
5371
+ }
5372
+ function resolveTheme(mode2) {
5373
+ if (mode2 !== "system") return mode2;
5374
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
5375
+ }
5376
+ function loadPrefs(opts) {
5377
+ const defaults = resolveDefaults(opts);
5378
+ const storage = resolveStorage(opts);
5379
+ const key = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
5380
+ if (!storage) return defaults;
5381
+ let stored;
5382
+ try {
5383
+ const raw = storage.getItem(key);
5384
+ stored = raw ? JSON.parse(raw) : null;
5385
+ } catch {
5386
+ stored = null;
5387
+ }
5388
+ if (typeof stored !== "object" || stored === null) return defaults;
5389
+ const s = stored;
5390
+ return {
5391
+ theme: s.theme === "light" || s.theme === "dark" || s.theme === "system" ? s.theme : defaults.theme,
5392
+ textScale: s.textScale === "sm" || s.textScale === "md" || s.textScale === "lg" || s.textScale === "xl" ? s.textScale : defaults.textScale,
5393
+ highContrast: typeof s.highContrast === "boolean" ? s.highContrast : defaults.highContrast,
5394
+ reduceMotion: typeof s.reduceMotion === "boolean" ? s.reduceMotion : defaults.reduceMotion
5395
+ };
5396
+ }
5397
+ function savePrefs(prefs, opts) {
5398
+ const storage = resolveStorage(opts);
5399
+ if (!storage) return;
5400
+ const key = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
5401
+ try {
5402
+ storage.setItem(key, JSON.stringify(prefs));
5403
+ } catch {
5404
+ }
5405
+ }
5406
+ function createLocalStorageAppearanceStore(opts) {
5407
+ const key = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
5408
+ const storage = resolveStorage(opts);
5409
+ let snapshot = loadPrefs(opts);
5410
+ const listeners3 = /* @__PURE__ */ new Set();
5411
+ const notify = () => listeners3.forEach((l) => l());
5412
+ const onStorageEvent = (e) => {
5413
+ if (e.key !== null && e.key !== key) return;
5414
+ snapshot = loadPrefs(opts);
5415
+ notify();
5416
+ };
5417
+ return {
5418
+ getSnapshot: () => snapshot,
5419
+ write(prefs) {
5420
+ snapshot = prefs;
5421
+ if (storage) {
5422
+ try {
5423
+ storage.setItem(key, JSON.stringify(prefs));
5424
+ } catch {
5425
+ }
5426
+ }
5427
+ notify();
5428
+ },
5429
+ subscribe(onChange) {
5430
+ listeners3.add(onChange);
5431
+ if (listeners3.size === 1 && typeof window !== "undefined") {
5432
+ window.addEventListener("storage", onStorageEvent);
5433
+ }
5434
+ return () => {
5435
+ listeners3.delete(onChange);
5436
+ if (listeners3.size === 0 && typeof window !== "undefined") {
5437
+ window.removeEventListener("storage", onStorageEvent);
5438
+ }
5439
+ };
5440
+ }
5441
+ };
5442
+ }
5443
+ function applyPrefs(prefs, root = document.documentElement) {
5444
+ root.setAttribute("data-theme", resolveTheme(prefs.theme));
5445
+ root.setAttribute("data-text-scale", prefs.textScale);
5446
+ if (prefs.highContrast) root.setAttribute("data-contrast", "high");
5447
+ else root.removeAttribute("data-contrast");
5448
+ if (prefs.reduceMotion) root.setAttribute("data-motion", "reduce");
5449
+ else root.removeAttribute("data-motion");
5450
+ }
5451
+ function useAppearancePrefs(store) {
5452
+ const prefs = React33.useSyncExternalStore(store.subscribe, store.getSnapshot);
5453
+ React33.useEffect(() => {
5454
+ applyPrefs(prefs);
5455
+ }, [prefs]);
5456
+ React33.useEffect(() => {
5457
+ if (prefs.theme !== "system" || typeof window === "undefined" || typeof window.matchMedia !== "function") {
5458
+ return;
5459
+ }
5460
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
5461
+ const onChange = () => applyPrefs(prefs);
5462
+ mq.addEventListener("change", onChange);
5463
+ return () => mq.removeEventListener("change", onChange);
5464
+ }, [prefs]);
5465
+ const update = React33.useCallback(
5466
+ (patch) => {
5467
+ store.write({ ...store.getSnapshot(), ...patch });
5468
+ },
5469
+ [store]
5470
+ );
5471
+ return [prefs, update];
5472
+ }
5473
+
5474
+ // src/components/platform/AppearanceMenu.tsx
5475
+ import { jsx as jsx59, jsxs as jsxs55 } from "react/jsx-runtime";
5476
+ var THEME_OPTIONS = [
5477
+ { value: "light", label: "Light" },
5478
+ { value: "dark", label: "Dark" },
5479
+ { value: "system", label: "System" }
5480
+ ];
5481
+ var TEXT_SCALE_OPTIONS = [
5482
+ { value: "sm", label: "S" },
5483
+ { value: "md", label: "M" },
5484
+ { value: "lg", label: "L" },
5485
+ { value: "xl", label: "XL" }
5486
+ ];
5487
+ function AppearanceMenu({ className = "", store: storeProp, storageKey, storage, defaults }) {
5488
+ const store = React34.useMemo(
5489
+ () => storeProp ?? createLocalStorageAppearanceStore({ storageKey, storage, defaults }),
5490
+ [storeProp, storageKey, storage, defaults]
5491
+ );
5492
+ const [prefs, update] = useAppearancePrefs(store);
5493
+ const [open, setOpen] = React34.useState(false);
5494
+ const anchorRef = React34.useRef(null);
5495
+ const patch = (p) => update(p);
5496
+ return /* @__PURE__ */ jsxs55(React34.Fragment, { children: [
5497
+ /* @__PURE__ */ jsx59("span", { ref: anchorRef, className: ["fd-appearance-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx59(
5498
+ IconButton,
5499
+ {
5500
+ icon: "circle-half",
5501
+ label: "Appearance settings",
5502
+ onClick: () => setOpen((v) => !v)
5503
+ }
5504
+ ) }),
5505
+ /* @__PURE__ */ jsx59(
5506
+ Popover,
5507
+ {
5508
+ open,
5509
+ anchorRef,
5510
+ onClose: () => setOpen(false),
5511
+ placement: "bottom-end",
5512
+ padded: true,
5513
+ width: 260,
5514
+ label: "Appearance settings",
5515
+ children: /* @__PURE__ */ jsxs55("div", { className: "fd-stack", style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
5516
+ /* @__PURE__ */ jsxs55("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
5517
+ /* @__PURE__ */ jsx59("span", { className: "fd-field-label", children: "Theme" }),
5518
+ /* @__PURE__ */ jsx59(
5519
+ SegmentedControl,
5520
+ {
5521
+ options: THEME_OPTIONS,
5522
+ value: prefs.theme,
5523
+ onChange: (v) => patch({ theme: v }),
5524
+ "aria-label": "Theme"
5525
+ }
5526
+ )
5527
+ ] }),
5528
+ /* @__PURE__ */ jsxs55("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
5529
+ /* @__PURE__ */ jsx59("span", { className: "fd-field-label", children: "Text size" }),
5530
+ /* @__PURE__ */ jsx59(
5531
+ SegmentedControl,
5532
+ {
5533
+ options: TEXT_SCALE_OPTIONS,
5534
+ value: prefs.textScale,
5535
+ onChange: (v) => patch({ textScale: v }),
5536
+ "aria-label": "Text size"
5537
+ }
5538
+ )
5539
+ ] }),
5540
+ /* @__PURE__ */ jsx59(
5541
+ Switch,
5542
+ {
5543
+ label: "High contrast",
5544
+ description: "Stronger borders and text color",
5545
+ checked: prefs.highContrast,
5546
+ onChange: (e) => patch({ highContrast: e.target.checked })
5547
+ }
5548
+ ),
5549
+ /* @__PURE__ */ jsx59(
5550
+ Switch,
5551
+ {
5552
+ label: "Reduce motion",
5553
+ description: "Minimize animations and transitions",
5554
+ checked: prefs.reduceMotion,
5555
+ onChange: (e) => patch({ reduceMotion: e.target.checked })
5556
+ }
5557
+ )
5558
+ ] })
5559
+ }
5560
+ )
5561
+ ] });
5562
+ }
5563
+
5564
+ // src/components/platform/ApiSpecBrowser.tsx
5565
+ import * as React35 from "react";
5566
+ import { jsx as jsx60, jsxs as jsxs56 } from "react/jsx-runtime";
5567
+ var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
5568
+ function Json({ obj }) {
5569
+ return /* @__PURE__ */ jsx60("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
5570
+ }
5571
+ function Endpoint({ s, onRequest }) {
5572
+ const [tried, setTried] = React35.useState(null);
5573
+ const run = async () => {
5574
+ setTried("busy");
5575
+ const t0 = (window.performance || Date).now();
5576
+ try {
5577
+ const data = await onRequest(s.id, s.request || (s.isList ? { page: 1, pageSize: 5 } : {}));
5578
+ setTried({ ms: Math.round((window.performance || Date).now() - t0), data });
5579
+ } catch (e) {
5580
+ setTried({ ms: Math.round((window.performance || Date).now() - t0), error: String(e && e.message || e) });
5581
+ }
5582
+ };
5583
+ return /* @__PURE__ */ jsx60(Card, { elevation: "flat", padded: false, children: /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 0 }, children: [
5584
+ /* @__PURE__ */ jsxs56("div", { className: "fd-row", style: { gap: 10, padding: "14px 18px", flexWrap: "wrap" }, children: [
5585
+ /* @__PURE__ */ jsx60(Badge, { tone: METHOD_TONE[s.method] || "neutral", children: s.method }),
5586
+ /* @__PURE__ */ jsx60("code", { className: "fd-mono", style: { fontSize: 12.5, fontWeight: 600, color: "var(--text)", wordBreak: "break-all" }, children: s.path }),
5587
+ s.isList ? /* @__PURE__ */ jsx60(Badge, { tone: "neutral", icon: "rows", children: "Paged list" }) : null,
5588
+ /* @__PURE__ */ jsx60("span", { style: { flex: 1 } }),
5589
+ /* @__PURE__ */ jsxs56("span", { className: "fd-body-sm fd-muted fd-mono", children: [
5590
+ s.latency[0],
5591
+ "\u2013",
5592
+ s.latency[1],
5593
+ "ms"
5594
+ ] }),
5595
+ /* @__PURE__ */ jsx60(Button, { size: "sm", variant: "secondary", icon: "play", loading: tried === "busy", onClick: run, children: "Try it" })
5596
+ ] }),
5597
+ /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 14, padding: "0 18px 16px" }, children: [
5598
+ /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 6 }, children: [
5599
+ /* @__PURE__ */ jsx60("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: s.title }),
5600
+ /* @__PURE__ */ jsx60("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: s.purpose }),
5601
+ s.usedBy && s.usedBy.length ? /* @__PURE__ */ jsx60("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: s.usedBy.map((u) => /* @__PURE__ */ jsx60(Tag, { children: u }, u)) }) : null
5602
+ ] }),
5603
+ /* @__PURE__ */ jsxs56("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(280px,1fr))", gap: 12, alignItems: "start" }, children: [
5604
+ s.request ? /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 6 }, children: [
5605
+ /* @__PURE__ */ jsx60("span", { className: "fd-overline fd-muted", children: "Request body" }),
5606
+ /* @__PURE__ */ jsx60(Json, { obj: s.request })
5607
+ ] }) : null,
5608
+ s.query ? /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 6 }, children: [
5609
+ /* @__PURE__ */ jsx60("span", { className: "fd-overline fd-muted", children: "Query" }),
5610
+ /* @__PURE__ */ jsx60(Json, { obj: s.query })
5611
+ ] }) : null,
5612
+ /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 6 }, children: [
5613
+ /* @__PURE__ */ jsx60("span", { className: "fd-overline fd-muted", children: "Response 200" }),
5614
+ /* @__PURE__ */ jsx60(Json, { obj: s.response })
5615
+ ] })
5616
+ ] }),
5617
+ s.notes ? /* @__PURE__ */ jsx60(Flag, { tone: "info", statement: "Implementation note", cost: s.notes, actions: null }) : null,
5618
+ tried && tried !== "busy" ? /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 6 }, children: [
5619
+ /* @__PURE__ */ jsxs56("span", { className: "fd-row", style: { gap: 8 }, children: [
5620
+ /* @__PURE__ */ jsx60("span", { className: "fd-overline fd-muted", children: "Simulated response" }),
5621
+ /* @__PURE__ */ jsxs56(Badge, { tone: tried.error ? "danger" : "success", icon: "timer", children: [
5622
+ tried.ms,
5623
+ "ms"
5624
+ ] })
5625
+ ] }),
5626
+ /* @__PURE__ */ jsx60(Json, { obj: tried.error ? { error: tried.error } : tried.data })
5627
+ ] }) : null
5628
+ ] })
5629
+ ] }) });
5630
+ }
5631
+ function ApiSpecBrowser({
5632
+ spec = [],
5633
+ onRequest,
5634
+ modules,
5635
+ groups,
5636
+ title = "API specification",
5637
+ kicker = "Architecture",
5638
+ lede,
5639
+ sourceNote,
5640
+ conventions,
5641
+ openQuestions,
5642
+ className = "",
5643
+ ...rest
5644
+ }) {
5645
+ const [q, setQ] = React35.useState("");
5646
+ const [method, setMethod] = React35.useState(null);
5647
+ const [mod, setMod] = React35.useState(null);
5648
+ const [listOnly, setListOnly] = React35.useState(false);
5649
+ const activeModule = modules && modules.find((m) => m.id === mod);
5650
+ 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())));
5651
+ const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
5652
+ const methods = [...new Set(spec.map((s) => s.method))];
5653
+ const listCount = spec.filter((s) => s.isList).length;
5654
+ return /* @__PURE__ */ jsxs56("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 1100 }, ...rest, children: [
5655
+ /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 10 }, children: [
5656
+ kicker ? /* @__PURE__ */ jsx60("span", { className: "fd-overline fd-muted", children: kicker }) : null,
5657
+ /* @__PURE__ */ jsx60("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
5658
+ lede ? /* @__PURE__ */ jsx60("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: lede }) : null
5659
+ ] }),
5660
+ modules && modules.length ? /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 8 }, children: [
5661
+ /* @__PURE__ */ jsx60("span", { className: "fd-overline fd-muted", children: "Filter by screen \u2014 every endpoint that screen depends on" }),
5662
+ /* @__PURE__ */ jsxs56("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
5663
+ /* @__PURE__ */ jsxs56(Tag, { icon: "stack", selected: !mod, onClick: () => setMod(null), children: [
5664
+ "All screens ",
5665
+ /* @__PURE__ */ jsx60("span", { className: "fd-mono", children: spec.length })
5666
+ ] }),
5667
+ modules.map((m) => /* @__PURE__ */ jsxs56(Tag, { icon: m.icon, selected: mod === m.id, onClick: () => setMod(mod === m.id ? null : m.id), children: [
5668
+ m.label,
5669
+ " ",
5670
+ /* @__PURE__ */ jsx60("span", { className: "fd-mono", children: m.endpoints.length })
5671
+ ] }, m.id))
5672
+ ] }),
5673
+ activeModule ? /* @__PURE__ */ jsx60(
5674
+ Flag,
5675
+ {
5676
+ tone: "info",
5677
+ statement: activeModule.label + " calls " + activeModule.endpoints.length + " endpoints.",
5678
+ cost: "Integration checklist for this screen: " + activeModule.endpoints.join(", ") + ". Wire these and the screen is done.",
5679
+ actions: /* @__PURE__ */ jsx60(Button, { size: "sm", variant: "ghost", icon: "x", onClick: () => setMod(null), children: "Show all screens" })
5680
+ }
5681
+ ) : null
5682
+ ] }) : null,
5683
+ sourceNote ? /* @__PURE__ */ jsx60(Flag, { tone: "info", statement: "Design-first: this page is the spec.", cost: sourceNote, actions: null }) : null,
5684
+ /* @__PURE__ */ jsxs56("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
5685
+ /* @__PURE__ */ jsx60(Input, { icon: "magnifying-glass", placeholder: "Find an endpoint, screen, or behavior", value: q, onChange: (e) => setQ(e.target.value), style: { width: 300 } }),
5686
+ methods.map((m) => /* @__PURE__ */ jsxs56(Tag, { selected: method === m, onClick: () => setMethod(method === m ? null : m), children: [
5687
+ m,
5688
+ " ",
5689
+ /* @__PURE__ */ jsx60("span", { className: "fd-mono", children: spec.filter((s) => s.method === m).length })
5690
+ ] }, m)),
5691
+ listCount ? /* @__PURE__ */ jsxs56(Tag, { icon: "rows", selected: listOnly, onClick: () => setListOnly(!listOnly), children: [
5692
+ "Paged lists ",
5693
+ /* @__PURE__ */ jsx60("span", { className: "fd-mono", children: listCount })
5694
+ ] }) : null,
5695
+ /* @__PURE__ */ jsx60("span", { style: { flex: 1 } }),
5696
+ /* @__PURE__ */ jsxs56("span", { className: "fd-body-sm fd-muted", children: [
5697
+ hits.length,
5698
+ " of ",
5699
+ spec.length,
5700
+ " endpoints",
5701
+ listCount ? " \xB7 " + listCount + " paged" : ""
5702
+ ] })
5703
+ ] }),
5704
+ hits.length === 0 ? /* @__PURE__ */ jsx60(Card, { elevation: "flat", children: /* @__PURE__ */ jsx60("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: "No endpoint matches those filters." }) }) : effGroups.map(([g, ids]) => {
5705
+ const items = hits.filter((s) => ids.indexOf(s.id) >= 0);
5706
+ if (!items.length) return null;
5707
+ return /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 12 }, children: [
5708
+ /* @__PURE__ */ jsx60("span", { className: "fd-label-lg", children: g }),
5709
+ items.map((s) => /* @__PURE__ */ jsx60(Endpoint, { s, onRequest }, s.id))
5710
+ ] }, g);
5711
+ }),
5712
+ conventions && conventions.length ? /* @__PURE__ */ jsx60(Card, { title: "Cross-cutting conventions", elevation: "flat", children: /* @__PURE__ */ jsx60("div", { className: "fd-stack", style: { gap: 10 }, children: conventions.map(([k, v]) => /* @__PURE__ */ jsxs56("div", { className: "fd-row", style: { gap: 12, alignItems: "flex-start" }, children: [
5713
+ /* @__PURE__ */ jsx60("span", { className: "fd-overline fd-muted", style: { width: 110, flex: "none", paddingTop: 2 }, children: k }),
5714
+ /* @__PURE__ */ jsx60("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: v })
5715
+ ] }, k)) }) }) : null,
5716
+ openQuestions && openQuestions.length ? /* @__PURE__ */ jsx60(Collapsible, { icon: "list-checks", title: "Open questions before implementation", subtitle: openQuestions.length + " unresolved", children: /* @__PURE__ */ jsx60("div", { className: "fd-stack", style: { gap: 8 }, children: openQuestions.map(([id, question, why]) => /* @__PURE__ */ jsxs56("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start", padding: "8px 0", borderTop: "1px solid var(--border)" }, children: [
5717
+ /* @__PURE__ */ jsx60(Badge, { tone: "danger", children: id }),
5718
+ /* @__PURE__ */ jsxs56("span", { className: "fd-stack", style: { gap: 2, flex: 1 }, children: [
5719
+ /* @__PURE__ */ jsx60("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: question }),
5720
+ /* @__PURE__ */ jsx60("span", { className: "fd-body-sm fd-muted", children: why })
5721
+ ] })
5722
+ ] }, id)) }) }) : null
5723
+ ] });
5724
+ }
5725
+
5726
+ // src/components/platform/ProfilePage.tsx
5727
+ import * as React36 from "react";
5728
+ import { jsx as jsx61, jsxs as jsxs57 } from "react/jsx-runtime";
5729
+ function fieldInitialValue(field, user) {
5730
+ if (field.value) return field.value(user);
5731
+ const raw = user[field.key];
5732
+ return raw == null ? "" : String(raw);
5733
+ }
5734
+ function groupFields(fields) {
5735
+ const order = [];
5736
+ const byGroup = /* @__PURE__ */ new Map();
5737
+ for (const field of fields) {
5738
+ const group = field.group ?? "Identity";
5739
+ if (!byGroup.has(group)) {
5740
+ byGroup.set(group, { group, description: field.groupDescription, fields: [] });
5741
+ order.push(group);
5742
+ }
5743
+ const entry = byGroup.get(group);
5744
+ if (entry.description === void 0) entry.description = field.groupDescription;
5745
+ entry.fields.push(field);
5746
+ }
5747
+ return order.map((g) => byGroup.get(g));
5748
+ }
5749
+ function ProfilePage({
5750
+ user: userProp,
5751
+ fields = [],
5752
+ notifications = [],
5753
+ extra,
5754
+ onChangePhoto,
5755
+ onSave,
5756
+ onNavigate,
5757
+ sessions,
5758
+ onSignOutEverywhere,
5759
+ className = ""
5760
+ }) {
5761
+ const { user: sessionUser, roles, permissions, saveProfile: saveProfile2 } = useSessionContext();
5762
+ const user = userProp || sessionUser;
5763
+ const initialFieldValues = React36.useMemo(() => {
5764
+ const values2 = {};
5765
+ for (const field of fields) values2[field.key] = fieldInitialValue(field, user);
5766
+ return values2;
5767
+ }, [user.id, fields]);
5768
+ const initialNotify = React36.useMemo(() => {
5769
+ const values2 = {};
5770
+ for (const n of notifications) values2[n.key] = !!user.notify?.[n.key];
5771
+ return values2;
5772
+ }, [user.id, notifications]);
5773
+ const [values, setValues] = React36.useState(initialFieldValues);
5774
+ const [notify, setNotify] = React36.useState(initialNotify);
5775
+ const [saving, setSaving] = React36.useState(false);
5776
+ const [saved, setSaved] = React36.useState(false);
5777
+ const [prevUserId, setPrevUserId] = React36.useState(user.id);
5778
+ if (user.id !== prevUserId) {
5779
+ setPrevUserId(user.id);
5780
+ setValues(initialFieldValues);
5781
+ setNotify(initialNotify);
5782
+ setSaved(false);
5783
+ }
5784
+ const setField = (key, v) => {
5785
+ setValues((d) => ({ ...d, [key]: v }));
5786
+ setSaved(false);
5787
+ };
5788
+ const toggleNotify = (key) => {
5789
+ setNotify((d) => ({ ...d, [key]: !d[key] }));
5790
+ setSaved(false);
5791
+ };
5792
+ const dirty = fields.some((f) => values[f.key] !== initialFieldValues[f.key]) || notifications.some((n) => notify[n.key] !== initialNotify[n.key]);
5793
+ const canSave = !!(onSave || saveProfile2) && (fields.length > 0 || notifications.length > 0);
5794
+ const save = async () => {
5795
+ if (!canSave) return;
5796
+ setSaving(true);
5797
+ const draft = { ...values };
5798
+ if (notifications.length > 0) draft.notify = notify;
5799
+ try {
5800
+ if (onSave) await onSave(draft);
5801
+ else await saveProfile2(draft);
5802
+ setSaved(true);
5803
+ } finally {
5804
+ setSaving(false);
5805
+ }
5806
+ };
5807
+ const discard = () => {
5808
+ setValues(initialFieldValues);
5809
+ setNotify(initialNotify);
5810
+ setSaved(false);
5811
+ };
5812
+ const displayName = fields.some((f) => f.key === "name") ? values.name || user.name : user.name;
5813
+ const fieldGroups = groupFields(fields);
5814
+ return /* @__PURE__ */ jsxs57("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 860 }, children: [
5815
+ /* @__PURE__ */ jsxs57("div", { className: "fd-stack", style: { gap: 10 }, children: [
5816
+ /* @__PURE__ */ jsx61("span", { className: "fd-overline fd-muted", children: "Account" }),
5817
+ /* @__PURE__ */ jsx61("h1", { className: "fd-h1", style: { margin: 0 }, children: "Your profile" }),
5818
+ /* @__PURE__ */ jsx61("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." })
5819
+ ] }),
5820
+ /* @__PURE__ */ jsx61(Card, { elevation: "flat", children: /* @__PURE__ */ jsxs57("div", { className: "fd-row", style: { gap: 16, flexWrap: "wrap", alignItems: "flex-start" }, children: [
5821
+ /* @__PURE__ */ jsx61(Avatar, { name: displayName, size: "lg" }),
5822
+ /* @__PURE__ */ jsxs57("div", { className: "fd-stack", style: { gap: 4, flex: 1, minWidth: 200 }, children: [
5823
+ /* @__PURE__ */ jsx61("span", { className: "fd-h3", style: { margin: 0 }, children: displayName }),
5824
+ /* @__PURE__ */ jsxs57("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 4 }, children: [
5825
+ roles.map((r) => /* @__PURE__ */ jsx61(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)),
5826
+ user.sso ? /* @__PURE__ */ jsx61(Badge, { tone: "info", icon: "shield-check", children: "SSO" }) : null
5827
+ ] })
5828
+ ] }),
5829
+ onChangePhoto ? /* @__PURE__ */ jsx61(Button, { variant: "secondary", size: "sm", icon: "image", onClick: onChangePhoto, children: "Change photo" }) : null
5830
+ ] }) }),
5831
+ fieldGroups.map(({ group, description, fields: groupFieldDefs }) => /* @__PURE__ */ jsx61(
5832
+ Card,
5833
+ {
5834
+ title: description ? /* @__PURE__ */ jsxs57("span", { className: "fd-stack", style: { gap: 2 }, children: [
5835
+ /* @__PURE__ */ jsx61("span", { children: group }),
5836
+ /* @__PURE__ */ jsx61("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: description })
5837
+ ] }) : group,
5838
+ elevation: "flat",
5839
+ children: /* @__PURE__ */ jsxs57("div", { className: "fd-stack", style: { gap: 14 }, children: [
5840
+ /* @__PURE__ */ jsx61("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: groupFieldDefs.filter((f) => f.type !== "textarea").map((field) => {
5841
+ const help = typeof field.help === "function" ? field.help(user) : field.help;
5842
+ const readOnly = field.readOnly || field.type === "readonly";
5843
+ if (field.type === "select") {
5844
+ return /* @__PURE__ */ jsx61(
5845
+ Select,
5846
+ {
5847
+ label: field.label,
5848
+ value: values[field.key],
5849
+ onChange: readOnly ? void 0 : (e) => setField(field.key, e.target.value),
5850
+ options: field.options ?? [],
5851
+ placeholder: field.placeholder,
5852
+ help,
5853
+ disabled: readOnly
5854
+ },
5855
+ field.key
5856
+ );
5857
+ }
5858
+ return /* @__PURE__ */ jsx61(
5859
+ Input,
5860
+ {
5861
+ label: field.label,
5862
+ value: values[field.key],
5863
+ onChange: readOnly ? void 0 : (e) => setField(field.key, e.target.value),
5864
+ readOnly,
5865
+ disabled: readOnly,
5866
+ placeholder: field.placeholder,
5867
+ help
5868
+ },
5869
+ field.key
5870
+ );
5871
+ }) }),
5872
+ groupFieldDefs.filter((f) => f.type === "textarea").map((field) => {
5873
+ const help = typeof field.help === "function" ? field.help(user) : field.help;
5874
+ return /* @__PURE__ */ jsx61(
5875
+ Textarea,
5876
+ {
5877
+ label: field.label,
5878
+ value: values[field.key],
5879
+ onChange: field.readOnly ? void 0 : (e) => setField(field.key, e.target.value),
5880
+ readOnly: field.readOnly,
5881
+ disabled: field.readOnly,
5882
+ rows: field.rows ?? 3,
5883
+ placeholder: field.placeholder,
5884
+ help
5885
+ },
5886
+ field.key
5887
+ );
5888
+ })
5889
+ ] })
5890
+ },
5891
+ group
5892
+ )),
5893
+ extra,
5894
+ notifications.length > 0 ? /* @__PURE__ */ jsx61(Card, { title: "Notifications", elevation: "flat", children: /* @__PURE__ */ jsx61("div", { className: "fd-stack", style: { gap: 14 }, children: notifications.map((n) => /* @__PURE__ */ jsx61(Switch, { checked: !!notify[n.key], onChange: () => toggleNotify(n.key), label: n.label, description: n.description }, n.key)) }) }) : null,
5895
+ /* @__PURE__ */ jsxs57(
5896
+ Card,
5897
+ {
5898
+ title: /* @__PURE__ */ jsxs57("span", { className: "fd-stack", style: { gap: 2 }, children: [
5899
+ /* @__PURE__ */ jsx61("span", { children: "Access" }),
5900
+ /* @__PURE__ */ jsx61("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "What your roles grant you" })
5901
+ ] }),
5902
+ elevation: "flat",
5903
+ action: /* @__PURE__ */ jsx61(
5904
+ Button,
5905
+ {
5906
+ size: "sm",
5907
+ variant: "ghost",
5908
+ icon: "arrow-right",
5909
+ onClick: () => onNavigate && onNavigate("#roles"),
5910
+ children: "See role definitions"
5911
+ }
5912
+ ),
5913
+ children: [
5914
+ /* @__PURE__ */ jsxs57("div", { className: "fd-stack", style: { gap: 0 }, children: [
5915
+ /* @__PURE__ */ jsx61(CardRow, { label: "Roles", children: roles.map((r) => r.name).join(", ") || "None" }),
5916
+ /* @__PURE__ */ jsxs57(CardRow, { label: "Permissions", children: [
5917
+ permissions.length,
5918
+ " granted"
5919
+ ] }),
5920
+ /* @__PURE__ */ jsx61(CardRow, { label: "Member since", children: user.joined || "\u2014" })
5921
+ ] }),
5922
+ /* @__PURE__ */ jsx61("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." })
5923
+ ]
5924
+ }
5925
+ ),
5926
+ sessions ? /* @__PURE__ */ jsx61(
5927
+ Card,
5928
+ {
5929
+ title: "Signed-in devices",
5930
+ elevation: "flat",
5931
+ action: /* @__PURE__ */ jsx61(Button, { size: "sm", variant: "ghost", icon: "sign-out", onClick: onSignOutEverywhere, children: "Sign out everywhere" }),
5932
+ children: /* @__PURE__ */ jsx61("div", { className: "fd-stack", style: { gap: 0 }, children: sessions.length === 0 ? /* @__PURE__ */ jsx61("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: "No other signed-in devices." }) : sessions.map((s) => /* @__PURE__ */ jsxs57("div", { className: "fd-row", style: { gap: 12, padding: "10px 0", borderTop: "1px solid var(--border)", flexWrap: "wrap" }, children: [
5933
+ /* @__PURE__ */ jsx61("i", { className: "ph ph-device-mobile", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
5934
+ /* @__PURE__ */ jsxs57("span", { className: "fd-stack", style: { gap: 1, flex: 1, minWidth: 160 }, children: [
5935
+ /* @__PURE__ */ jsx61("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: s.device }),
5936
+ /* @__PURE__ */ jsxs57("span", { className: "fd-body-sm fd-muted", children: [
5937
+ s.where,
5938
+ " \xB7 ",
5939
+ s.when
5940
+ ] })
5941
+ ] }),
5942
+ s.current ? /* @__PURE__ */ jsx61(Badge, { tone: "success", dot: true, children: "This device" }) : /* @__PURE__ */ jsx61(Button, { size: "sm", variant: "ghost", children: "Revoke" })
5943
+ ] }, s.id)) })
5944
+ }
5945
+ ) : null,
5946
+ /* @__PURE__ */ jsxs57("div", { className: "fd-row", style: {
5947
+ gap: 10,
5948
+ flexWrap: "wrap",
5949
+ position: "sticky",
5950
+ bottom: 0,
5951
+ padding: "12px 14px",
5952
+ borderRadius: 10,
5953
+ background: "var(--surface)",
5954
+ border: "1px solid var(--border)",
5955
+ boxShadow: "0 -4px 16px rgba(11,13,17,.06)"
5956
+ }, children: [
5957
+ /* @__PURE__ */ jsx61("span", { className: "fd-body-sm", style: { fontWeight: 600, flex: 1 }, children: saved ? "Saved." : dirty ? "Unsaved changes" : "No pending changes" }),
5958
+ /* @__PURE__ */ jsx61(Button, { size: "sm", variant: "ghost", disabled: !dirty || saving, onClick: discard, children: "Discard" }),
5959
+ /* @__PURE__ */ jsx61(Button, { size: "sm", icon: "check", disabled: !dirty || !canSave, loading: saving, onClick: save, children: "Save changes" })
5960
+ ] })
5961
+ ] });
5962
+ }
5963
+
5964
+ // src/components/platform/RoadmapTimeline.tsx
5965
+ import * as React39 from "react";
5966
+
5967
+ // src/kits/session.ts
5968
+ import * as React37 from "react";
5142
5969
  var PERMISSION_CATALOG = [
5143
5970
  { group: "Plans", items: [
5144
5971
  { key: "plan.view", label: "View plans", detail: "Read any plan in the workspace." },
@@ -6620,627 +7447,146 @@ function saveProfile(patch) {
6620
7447
  return s.user;
6621
7448
  }
6622
7449
  function refresh() {
6623
- const id = getSession().user.id;
6624
- current = null;
6625
- const s = loadSession(id);
6626
- listeners.forEach((fn) => fn(s));
6627
- return s;
6628
- }
6629
- function subscribe(fn) {
6630
- listeners.add(fn);
6631
- return () => listeners.delete(fn);
6632
- }
6633
- function can(permission) {
6634
- if (!permission) return true;
6635
- const s = getSession();
6636
- const list = Array.isArray(permission) ? permission : [permission];
6637
- return list.every((p) => s.permissions.indexOf(p) >= 0);
6638
- }
6639
- function canAny(permissions) {
6640
- const s = getSession();
6641
- return (permissions || []).some((p) => s.permissions.indexOf(p) >= 0);
6642
- }
6643
- function hasRole(roleId) {
6644
- return getSession().user.roles.indexOf(roleId) >= 0;
6645
- }
6646
- function flagOn(key) {
6647
- return !!getSession().flags[key];
6648
- }
6649
- function findFlag(key) {
6650
- return (getSession().allFlags || []).find((f) => f.key === key) || null;
6651
- }
6652
- function flagState(key) {
6653
- if (!key) return "on";
6654
- const f = findFlag(key);
6655
- if (!f) return "on";
6656
- return flagOn(key) ? "on" : "off";
6657
- }
6658
- function isEnabled(key) {
6659
- return flagState(key) !== "off";
6660
- }
6661
- function flagVisible(key) {
6662
- return isEnabled(key);
6663
- }
6664
- function flagLive(key) {
6665
- return isEnabled(key);
6666
- }
6667
- function roadmap(overrides) {
6668
- const flags = overrides && overrides.flags || (getSession().allFlags || []);
6669
- const isComplete = overrides && overrides.isComplete || (() => false);
6670
- const items = flags.map((f) => {
6671
- const done = !!isComplete(f.key);
6672
- const date = done ? f.shippedAt || f.eta : f.eta || f.shippedAt;
6673
- const phaseInfo = (f.phase != null ? PHASES[f.phase] : void 0) || {};
6674
- return {
6675
- key: f.key,
6676
- label: f.label,
6677
- project: f.project,
6678
- description: f.description,
6679
- backend: f.backend,
6680
- effort: f.effort,
6681
- owner: f.owner,
6682
- dependsOn: f.dependsOn || [],
6683
- screen: f.screen || null,
6684
- enabled: f.enabled,
6685
- /* Which build phase this sits in, and why that phase comes where it does. */
6686
- phase: f.phase,
6687
- phaseName: phaseInfo.name || "Unscheduled",
6688
- phaseWhy: phaseInfo.why || null,
6689
- phaseStart: phaseInfo.start || null,
6690
- /* Derived, not stored. */
6691
- implemented: done,
6692
- date: date || null,
6693
- /* True when the only date we have is when the UI was designed, not when it ships. */
6694
- dateIsDesign: !done && !f.eta && !!f.shippedAt,
6695
- status: done ? "shipped" : f.enabled ? "next" : "planned"
6696
- };
6697
- }).filter((i) => i.date);
6698
- items.sort((a, b) => (a.phase ?? 0) - (b.phase ?? 0) || String(a.date).localeCompare(String(b.date)) || a.label.localeCompare(b.label));
6699
- const nextIndex = items.findIndex((i) => !i.implemented);
6700
- return {
6701
- items,
6702
- nextIndex: nextIndex < 0 ? items.length : nextIndex,
6703
- shipped: items.filter((i) => i.implemented).length,
6704
- remaining: items.filter((i) => !i.implemented).length
6705
- };
6706
- }
6707
- function useSession() {
6708
- const [s, setS] = React30.useState(getSession);
6709
- React30.useEffect(() => subscribe(setS), []);
7450
+ const id = getSession().user.id;
7451
+ current = null;
7452
+ const s = loadSession(id);
7453
+ listeners.forEach((fn) => fn(s));
6710
7454
  return s;
6711
7455
  }
6712
- var SessionKit = {
6713
- PERMISSION_CATALOG,
6714
- ALL_PERMISSIONS,
6715
- ROLES,
6716
- FLAGS,
6717
- USERS,
6718
- loadSession,
6719
- getSession,
6720
- setActiveUser,
6721
- saveProfile,
6722
- refresh,
6723
- subscribe,
6724
- can,
6725
- canAny,
6726
- hasRole,
6727
- flagOn,
6728
- useSession,
6729
- findFlag,
6730
- flagState,
6731
- isEnabled,
6732
- flagVisible,
6733
- flagLive,
6734
- roadmap,
6735
- evaluateFlag,
6736
- explainFlag,
6737
- bucket,
6738
- buildSession
6739
- };
6740
-
6741
- // src/components/platform/AccountMenu.tsx
6742
- import { Fragment as Fragment14, jsx as jsx57, jsxs as jsxs53 } from "react/jsx-runtime";
6743
- var DEFAULT_LINKS = [
6744
- { id: "profile", label: "Your profile", icon: "user-circle", href: "../admin/index.html#profile" },
6745
- { id: "preferences", label: "Preferences", icon: "sliders-horizontal", href: "../admin/index.html#preferences" }
6746
- ];
6747
- var DEFAULT_ADMIN_LINKS = [
6748
- { id: "users", label: "Users", icon: "users-three", href: "../admin/index.html#users", perm: "users.view" },
6749
- { id: "roles", label: "Roles & permissions", icon: "shield-check", href: "../admin/index.html#roles", perm: "roles.manage" },
6750
- { id: "flags", label: "Feature flags", icon: "toggle-right", href: "../admin/index.html#flags", perm: "flags.manage" }
6751
- ];
6752
- function Item({ item, onPick }) {
6753
- return /* @__PURE__ */ jsxs53(
6754
- "button",
6755
- {
6756
- type: "button",
6757
- role: "menuitem",
6758
- className: "fd-acct-item",
6759
- onClick: () => onPick(item),
6760
- children: [
6761
- /* @__PURE__ */ jsx57("i", { className: "ph ph-" + item.icon, "aria-hidden": "true" }),
6762
- /* @__PURE__ */ jsx57("span", { style: { flex: 1 }, children: item.label }),
6763
- item.badge ? /* @__PURE__ */ jsx57(Badge, { tone: "neutral", children: item.badge }) : null
6764
- ]
6765
- }
6766
- );
7456
+ function subscribe(fn) {
7457
+ listeners.add(fn);
7458
+ return () => listeners.delete(fn);
6767
7459
  }
6768
- function AccountMenu({
6769
- links = DEFAULT_LINKS,
6770
- adminLinks = DEFAULT_ADMIN_LINKS,
6771
- onNavigate,
6772
- onSignOut,
6773
- showRoles = true,
6774
- allowUserSwitch = true,
6775
- className = "",
6776
- ...rest
6777
- }) {
6778
- const session = SessionKit.useSession();
6779
- const [open, setOpen] = React31.useState(false);
6780
- const [switching, setSwitching] = React31.useState(false);
6781
- const ref = React31.useRef(null);
6782
- const user = session.user;
6783
- const visibleAdmin = adminLinks.filter((l) => !l.perm || SessionKit.can(l.perm));
6784
- const pick = (item) => {
6785
- setOpen(false);
6786
- if (item.onSelect) return item.onSelect();
6787
- if (onNavigate) return onNavigate(item.href, item);
6788
- if (item.href) window.location.href = item.href;
6789
- };
6790
- const signOut = () => {
6791
- setOpen(false);
6792
- if (onSignOut) return onSignOut();
6793
- window.alert("Signed out. (Simulated \u2014 no auth provider is wired up.)");
6794
- };
6795
- return /* @__PURE__ */ jsxs53(Fragment14, { children: [
6796
- /* @__PURE__ */ jsxs53(
6797
- "button",
6798
- {
6799
- type: "button",
6800
- ref,
6801
- className: ["fd-acct-trigger", className].filter(Boolean).join(" "),
6802
- "aria-haspopup": "menu",
6803
- "aria-expanded": open,
6804
- "aria-label": "Account \u2014 " + user.name,
6805
- onClick: () => setOpen((o) => !o),
6806
- ...rest,
6807
- children: [
6808
- /* @__PURE__ */ jsx57(Avatar, { name: user.name, size: "sm" }),
6809
- /* @__PURE__ */ jsx57("i", { className: "ph ph-caret-down", "aria-hidden": "true", style: { fontSize: 11, color: "var(--text-muted)" } })
6810
- ]
6811
- }
6812
- ),
6813
- /* @__PURE__ */ jsx57(Popover, { open, anchorRef: ref, onClose: () => setOpen(false), placement: "bottom-end", width: 272, children: /* @__PURE__ */ jsxs53("div", { role: "menu", className: "fd-stack", style: { gap: 0 }, children: [
6814
- /* @__PURE__ */ jsxs53("div", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
6815
- /* @__PURE__ */ jsx57(Avatar, { name: user.name }),
6816
- /* @__PURE__ */ jsxs53("span", { className: "fd-stack", style: { gap: 1, minWidth: 0, flex: 1 }, children: [
6817
- /* @__PURE__ */ jsx57("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: user.name }),
6818
- /* @__PURE__ */ jsx57("span", { className: "fd-body-sm fd-muted fd-table-trunc", title: truncationTitle(user.email), children: user.email })
6819
- ] })
6820
- ] }),
6821
- showRoles && session.roles.length ? /* @__PURE__ */ jsx57("div", { className: "fd-row", style: { gap: 6, padding: "10px 14px", flexWrap: "wrap", borderBottom: "1px solid var(--border)" }, children: session.roles.map((r) => /* @__PURE__ */ jsx57(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)) }) : null,
6822
- /* @__PURE__ */ jsx57("div", { className: "fd-acct-group", children: links.map((l) => /* @__PURE__ */ jsx57(Item, { item: l, onPick: pick }, l.id)) }),
6823
- visibleAdmin.length ? /* @__PURE__ */ jsxs53("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
6824
- /* @__PURE__ */ jsx57("span", { className: "fd-overline fd-muted", style: { padding: "8px 14px 4px", display: "block" }, children: "Administration" }),
6825
- visibleAdmin.map((l) => /* @__PURE__ */ jsx57(Item, { item: l, onPick: pick }, l.id))
6826
- ] }) : null,
6827
- allowUserSwitch ? /* @__PURE__ */ jsxs53("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
6828
- /* @__PURE__ */ jsxs53("button", { type: "button", role: "menuitem", className: "fd-acct-item", onClick: () => setSwitching((s) => !s), children: [
6829
- /* @__PURE__ */ jsx57("i", { className: "ph ph-user-switch", "aria-hidden": "true" }),
6830
- /* @__PURE__ */ jsx57("span", { style: { flex: 1 }, children: "View as another member" }),
6831
- /* @__PURE__ */ jsx57("i", { className: "ph ph-caret-" + (switching ? "up" : "down"), "aria-hidden": "true", style: { fontSize: 11 } })
6832
- ] }),
6833
- switching ? /* @__PURE__ */ jsx57("div", { className: "fd-stack", style: { gap: 0 }, children: session.allUsers.filter((u) => u.status === "active").map((u) => /* @__PURE__ */ jsxs53(
6834
- "button",
6835
- {
6836
- type: "button",
6837
- className: "fd-acct-item",
6838
- "data-current": u.id === user.id ? "true" : "false",
6839
- onClick: () => {
6840
- SessionKit.setActiveUser(u.id);
6841
- setOpen(false);
6842
- setSwitching(false);
6843
- },
6844
- children: [
6845
- /* @__PURE__ */ jsx57(Avatar, { name: u.name, size: "sm" }),
6846
- /* @__PURE__ */ jsxs53("span", { className: "fd-stack", style: { gap: 0, flex: 1, minWidth: 0, alignItems: "flex-start" }, children: [
6847
- /* @__PURE__ */ jsx57("span", { style: { fontWeight: u.id === user.id ? 700 : 500 }, children: u.name }),
6848
- /* @__PURE__ */ jsx57("span", { className: "fd-muted", style: { fontSize: 11.5 }, children: u.roles.join(", ") })
6849
- ] }),
6850
- u.id === user.id ? /* @__PURE__ */ jsx57("i", { className: "ph ph-check", "aria-hidden": "true", style: { color: "var(--ok-text)" } }) : null
6851
- ]
6852
- },
6853
- u.id
6854
- )) }) : null
6855
- ] }) : null,
6856
- /* @__PURE__ */ jsx57("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: /* @__PURE__ */ jsxs53("button", { type: "button", role: "menuitem", className: "fd-acct-item", "data-danger": "true", onClick: signOut, children: [
6857
- /* @__PURE__ */ jsx57("i", { className: "ph ph-sign-out", "aria-hidden": "true" }),
6858
- /* @__PURE__ */ jsx57("span", { style: { flex: 1 }, children: "Sign out" })
6859
- ] }) })
6860
- ] }) })
6861
- ] });
7460
+ function can(permission) {
7461
+ if (!permission) return true;
7462
+ const s = getSession();
7463
+ const list = Array.isArray(permission) ? permission : [permission];
7464
+ return list.every((p) => s.permissions.indexOf(p) >= 0);
6862
7465
  }
6863
-
6864
- // src/components/platform/ApiSpecBrowser.tsx
6865
- import * as React32 from "react";
6866
- import { jsx as jsx58, jsxs as jsxs54 } from "react/jsx-runtime";
6867
- var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
6868
- function Json({ obj }) {
6869
- return /* @__PURE__ */ jsx58("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
7466
+ function canAny(permissions) {
7467
+ const s = getSession();
7468
+ return (permissions || []).some((p) => s.permissions.indexOf(p) >= 0);
6870
7469
  }
6871
- function Endpoint({ s, onRequest }) {
6872
- const [tried, setTried] = React32.useState(null);
6873
- const run = async () => {
6874
- setTried("busy");
6875
- const t0 = (window.performance || Date).now();
6876
- try {
6877
- const data = await onRequest(s.id, s.request || (s.isList ? { page: 1, pageSize: 5 } : {}));
6878
- setTried({ ms: Math.round((window.performance || Date).now() - t0), data });
6879
- } catch (e) {
6880
- setTried({ ms: Math.round((window.performance || Date).now() - t0), error: String(e && e.message || e) });
6881
- }
6882
- };
6883
- return /* @__PURE__ */ jsx58(Card, { elevation: "flat", padded: false, children: /* @__PURE__ */ jsxs54("div", { className: "fd-stack", style: { gap: 0 }, children: [
6884
- /* @__PURE__ */ jsxs54("div", { className: "fd-row", style: { gap: 10, padding: "14px 18px", flexWrap: "wrap" }, children: [
6885
- /* @__PURE__ */ jsx58(Badge, { tone: METHOD_TONE[s.method] || "neutral", children: s.method }),
6886
- /* @__PURE__ */ jsx58("code", { className: "fd-mono", style: { fontSize: 12.5, fontWeight: 600, color: "var(--text)", wordBreak: "break-all" }, children: s.path }),
6887
- s.isList ? /* @__PURE__ */ jsx58(Badge, { tone: "neutral", icon: "rows", children: "Paged list" }) : null,
6888
- /* @__PURE__ */ jsx58("span", { style: { flex: 1 } }),
6889
- /* @__PURE__ */ jsxs54("span", { className: "fd-body-sm fd-muted fd-mono", children: [
6890
- s.latency[0],
6891
- "\u2013",
6892
- s.latency[1],
6893
- "ms"
6894
- ] }),
6895
- /* @__PURE__ */ jsx58(Button, { size: "sm", variant: "secondary", icon: "play", loading: tried === "busy", onClick: run, children: "Try it" })
6896
- ] }),
6897
- /* @__PURE__ */ jsxs54("div", { className: "fd-stack", style: { gap: 14, padding: "0 18px 16px" }, children: [
6898
- /* @__PURE__ */ jsxs54("div", { className: "fd-stack", style: { gap: 6 }, children: [
6899
- /* @__PURE__ */ jsx58("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: s.title }),
6900
- /* @__PURE__ */ jsx58("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: s.purpose }),
6901
- s.usedBy && s.usedBy.length ? /* @__PURE__ */ jsx58("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: s.usedBy.map((u) => /* @__PURE__ */ jsx58(Tag, { children: u }, u)) }) : null
6902
- ] }),
6903
- /* @__PURE__ */ jsxs54("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(280px,1fr))", gap: 12, alignItems: "start" }, children: [
6904
- s.request ? /* @__PURE__ */ jsxs54("div", { className: "fd-stack", style: { gap: 6 }, children: [
6905
- /* @__PURE__ */ jsx58("span", { className: "fd-overline fd-muted", children: "Request body" }),
6906
- /* @__PURE__ */ jsx58(Json, { obj: s.request })
6907
- ] }) : null,
6908
- s.query ? /* @__PURE__ */ jsxs54("div", { className: "fd-stack", style: { gap: 6 }, children: [
6909
- /* @__PURE__ */ jsx58("span", { className: "fd-overline fd-muted", children: "Query" }),
6910
- /* @__PURE__ */ jsx58(Json, { obj: s.query })
6911
- ] }) : null,
6912
- /* @__PURE__ */ jsxs54("div", { className: "fd-stack", style: { gap: 6 }, children: [
6913
- /* @__PURE__ */ jsx58("span", { className: "fd-overline fd-muted", children: "Response 200" }),
6914
- /* @__PURE__ */ jsx58(Json, { obj: s.response })
6915
- ] })
6916
- ] }),
6917
- s.notes ? /* @__PURE__ */ jsx58(Flag, { tone: "info", statement: "Implementation note", cost: s.notes, actions: null }) : null,
6918
- tried && tried !== "busy" ? /* @__PURE__ */ jsxs54("div", { className: "fd-stack", style: { gap: 6 }, children: [
6919
- /* @__PURE__ */ jsxs54("span", { className: "fd-row", style: { gap: 8 }, children: [
6920
- /* @__PURE__ */ jsx58("span", { className: "fd-overline fd-muted", children: "Simulated response" }),
6921
- /* @__PURE__ */ jsxs54(Badge, { tone: tried.error ? "danger" : "success", icon: "timer", children: [
6922
- tried.ms,
6923
- "ms"
6924
- ] })
6925
- ] }),
6926
- /* @__PURE__ */ jsx58(Json, { obj: tried.error ? { error: tried.error } : tried.data })
6927
- ] }) : null
6928
- ] })
6929
- ] }) });
7470
+ function hasRole(roleId) {
7471
+ return getSession().user.roles.indexOf(roleId) >= 0;
6930
7472
  }
6931
- function ApiSpecBrowser({
6932
- spec = [],
6933
- onRequest,
6934
- modules,
6935
- groups,
6936
- title = "API specification",
6937
- kicker = "Architecture",
6938
- lede,
6939
- sourceNote,
6940
- conventions,
6941
- openQuestions,
6942
- className = "",
6943
- ...rest
6944
- }) {
6945
- const [q, setQ] = React32.useState("");
6946
- const [method, setMethod] = React32.useState(null);
6947
- const [mod, setMod] = React32.useState(null);
6948
- const [listOnly, setListOnly] = React32.useState(false);
6949
- const activeModule = modules && modules.find((m) => m.id === mod);
6950
- 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())));
6951
- const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
6952
- const methods = [...new Set(spec.map((s) => s.method))];
6953
- const listCount = spec.filter((s) => s.isList).length;
6954
- return /* @__PURE__ */ jsxs54("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 1100 }, ...rest, children: [
6955
- /* @__PURE__ */ jsxs54("div", { className: "fd-stack", style: { gap: 10 }, children: [
6956
- kicker ? /* @__PURE__ */ jsx58("span", { className: "fd-overline fd-muted", children: kicker }) : null,
6957
- /* @__PURE__ */ jsx58("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
6958
- lede ? /* @__PURE__ */ jsx58("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: lede }) : null
6959
- ] }),
6960
- modules && modules.length ? /* @__PURE__ */ jsxs54("div", { className: "fd-stack", style: { gap: 8 }, children: [
6961
- /* @__PURE__ */ jsx58("span", { className: "fd-overline fd-muted", children: "Filter by screen \u2014 every endpoint that screen depends on" }),
6962
- /* @__PURE__ */ jsxs54("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
6963
- /* @__PURE__ */ jsxs54(Tag, { icon: "stack", selected: !mod, onClick: () => setMod(null), children: [
6964
- "All screens ",
6965
- /* @__PURE__ */ jsx58("span", { className: "fd-mono", children: spec.length })
6966
- ] }),
6967
- modules.map((m) => /* @__PURE__ */ jsxs54(Tag, { icon: m.icon, selected: mod === m.id, onClick: () => setMod(mod === m.id ? null : m.id), children: [
6968
- m.label,
6969
- " ",
6970
- /* @__PURE__ */ jsx58("span", { className: "fd-mono", children: m.endpoints.length })
6971
- ] }, m.id))
6972
- ] }),
6973
- activeModule ? /* @__PURE__ */ jsx58(
6974
- Flag,
6975
- {
6976
- tone: "info",
6977
- statement: activeModule.label + " calls " + activeModule.endpoints.length + " endpoints.",
6978
- cost: "Integration checklist for this screen: " + activeModule.endpoints.join(", ") + ". Wire these and the screen is done.",
6979
- actions: /* @__PURE__ */ jsx58(Button, { size: "sm", variant: "ghost", icon: "x", onClick: () => setMod(null), children: "Show all screens" })
6980
- }
6981
- ) : null
6982
- ] }) : null,
6983
- sourceNote ? /* @__PURE__ */ jsx58(Flag, { tone: "info", statement: "Design-first: this page is the spec.", cost: sourceNote, actions: null }) : null,
6984
- /* @__PURE__ */ jsxs54("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
6985
- /* @__PURE__ */ jsx58(Input, { icon: "magnifying-glass", placeholder: "Find an endpoint, screen, or behavior", value: q, onChange: (e) => setQ(e.target.value), style: { width: 300 } }),
6986
- methods.map((m) => /* @__PURE__ */ jsxs54(Tag, { selected: method === m, onClick: () => setMethod(method === m ? null : m), children: [
6987
- m,
6988
- " ",
6989
- /* @__PURE__ */ jsx58("span", { className: "fd-mono", children: spec.filter((s) => s.method === m).length })
6990
- ] }, m)),
6991
- listCount ? /* @__PURE__ */ jsxs54(Tag, { icon: "rows", selected: listOnly, onClick: () => setListOnly(!listOnly), children: [
6992
- "Paged lists ",
6993
- /* @__PURE__ */ jsx58("span", { className: "fd-mono", children: listCount })
6994
- ] }) : null,
6995
- /* @__PURE__ */ jsx58("span", { style: { flex: 1 } }),
6996
- /* @__PURE__ */ jsxs54("span", { className: "fd-body-sm fd-muted", children: [
6997
- hits.length,
6998
- " of ",
6999
- spec.length,
7000
- " endpoints",
7001
- listCount ? " \xB7 " + listCount + " paged" : ""
7002
- ] })
7003
- ] }),
7004
- hits.length === 0 ? /* @__PURE__ */ jsx58(Card, { elevation: "flat", children: /* @__PURE__ */ jsx58("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: "No endpoint matches those filters." }) }) : effGroups.map(([g, ids]) => {
7005
- const items = hits.filter((s) => ids.indexOf(s.id) >= 0);
7006
- if (!items.length) return null;
7007
- return /* @__PURE__ */ jsxs54("div", { className: "fd-stack", style: { gap: 12 }, children: [
7008
- /* @__PURE__ */ jsx58("span", { className: "fd-label-lg", children: g }),
7009
- items.map((s) => /* @__PURE__ */ jsx58(Endpoint, { s, onRequest }, s.id))
7010
- ] }, g);
7011
- }),
7012
- conventions && conventions.length ? /* @__PURE__ */ jsx58(Card, { title: "Cross-cutting conventions", elevation: "flat", children: /* @__PURE__ */ jsx58("div", { className: "fd-stack", style: { gap: 10 }, children: conventions.map(([k, v]) => /* @__PURE__ */ jsxs54("div", { className: "fd-row", style: { gap: 12, alignItems: "flex-start" }, children: [
7013
- /* @__PURE__ */ jsx58("span", { className: "fd-overline fd-muted", style: { width: 110, flex: "none", paddingTop: 2 }, children: k }),
7014
- /* @__PURE__ */ jsx58("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: v })
7015
- ] }, k)) }) }) : null,
7016
- openQuestions && openQuestions.length ? /* @__PURE__ */ jsx58(Collapsible, { icon: "list-checks", title: "Open questions before implementation", subtitle: openQuestions.length + " unresolved", children: /* @__PURE__ */ jsx58("div", { className: "fd-stack", style: { gap: 8 }, children: openQuestions.map(([id, question, why]) => /* @__PURE__ */ jsxs54("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start", padding: "8px 0", borderTop: "1px solid var(--border)" }, children: [
7017
- /* @__PURE__ */ jsx58(Badge, { tone: "danger", children: id }),
7018
- /* @__PURE__ */ jsxs54("span", { className: "fd-stack", style: { gap: 2, flex: 1 }, children: [
7019
- /* @__PURE__ */ jsx58("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: question }),
7020
- /* @__PURE__ */ jsx58("span", { className: "fd-body-sm fd-muted", children: why })
7021
- ] })
7022
- ] }, id)) }) }) : null
7023
- ] });
7473
+ function flagOn(key) {
7474
+ return !!getSession().flags[key];
7024
7475
  }
7025
-
7026
- // src/components/platform/ProfilePage.tsx
7027
- import * as React33 from "react";
7028
- import { jsx as jsx59, jsxs as jsxs55 } from "react/jsx-runtime";
7029
- var TIMEZONES = ["America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles", "America/Anchorage", "Pacific/Honolulu", "Europe/London", "Europe/Berlin"];
7030
- var NOTIFY = [
7031
- { key: "planShared", label: "A plan is shared with me", detail: "Someone sends you a plan or a client link." },
7032
- { key: "planChanged", label: "A plan I own changes", detail: "Budget moves, campuses added or removed." },
7033
- { key: "goalMissed", label: "The reviewer flags a missed criterion", detail: "After every solve, if a criterion is short." },
7034
- { key: "flagChanged", label: "A feature flag affecting me changes", detail: "Someone toggles a feature you can see." },
7035
- { key: "weekly", label: "Weekly digest", detail: "Monday summary of plan activity." }
7036
- ];
7037
- function ProfilePage({ user: userProp, onSave, onNavigate, sessions, showSessions = true, className = "", ...rest }) {
7038
- const session = SessionKit.useSession();
7039
- const user = userProp || session.user;
7040
- const [draft, setDraft] = React33.useState(() => ({
7041
- name: user.name || "",
7042
- title: user.title || "",
7043
- email: user.email || "",
7044
- phone: user.phone || "",
7045
- timezone: user.timezone || TIMEZONES[2],
7046
- bio: user.bio || "",
7047
- notify: user.notify || { planShared: true, planChanged: true, goalMissed: true, flagChanged: false, weekly: true }
7048
- }));
7049
- const [saving, setSaving] = React33.useState(false);
7050
- const [saved, setSaved] = React33.useState(false);
7051
- const set = (k, v) => {
7052
- setDraft((d) => Object.assign({}, d, { [k]: v }));
7053
- setSaved(false);
7054
- };
7055
- const setNotify = (k) => {
7056
- setDraft((d) => Object.assign({}, d, { notify: Object.assign({}, d.notify, { [k]: !d.notify[k] }) }));
7057
- setSaved(false);
7058
- };
7059
- 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 });
7060
- const save = async () => {
7061
- setSaving(true);
7062
- if (onSave) await onSave(draft);
7063
- else SessionKit.saveProfile(draft);
7064
- setSaving(false);
7065
- setSaved(true);
7476
+ function findFlag(key) {
7477
+ return (getSession().allFlags || []).find((f) => f.key === key) || null;
7478
+ }
7479
+ function flagState(key) {
7480
+ if (!key) return "on";
7481
+ const f = findFlag(key);
7482
+ if (!f) return "on";
7483
+ return flagOn(key) ? "on" : "off";
7484
+ }
7485
+ function isEnabled(key) {
7486
+ return flagState(key) !== "off";
7487
+ }
7488
+ function flagVisible(key) {
7489
+ return isEnabled(key);
7490
+ }
7491
+ function flagLive(key) {
7492
+ return isEnabled(key);
7493
+ }
7494
+ function roadmap(overrides) {
7495
+ const flags = overrides && overrides.flags || (getSession().allFlags || []);
7496
+ const isComplete = overrides && overrides.isComplete || (() => false);
7497
+ const items = flags.map((f) => {
7498
+ const done = !!isComplete(f.key);
7499
+ const date = done ? f.shippedAt || f.eta : f.eta || f.shippedAt;
7500
+ const phaseInfo = (f.phase != null ? PHASES[f.phase] : void 0) || {};
7501
+ return {
7502
+ key: f.key,
7503
+ label: f.label,
7504
+ project: f.project,
7505
+ description: f.description,
7506
+ backend: f.backend,
7507
+ effort: f.effort,
7508
+ owner: f.owner,
7509
+ dependsOn: f.dependsOn || [],
7510
+ screen: f.screen || null,
7511
+ enabled: f.enabled,
7512
+ /* Which build phase this sits in, and why that phase comes where it does. */
7513
+ phase: f.phase,
7514
+ phaseName: phaseInfo.name || "Unscheduled",
7515
+ phaseWhy: phaseInfo.why || null,
7516
+ phaseStart: phaseInfo.start || null,
7517
+ /* Derived, not stored. */
7518
+ implemented: done,
7519
+ date: date || null,
7520
+ /* True when the only date we have is when the UI was designed, not when it ships. */
7521
+ dateIsDesign: !done && !f.eta && !!f.shippedAt,
7522
+ status: done ? "shipped" : f.enabled ? "next" : "planned"
7523
+ };
7524
+ }).filter((i) => i.date);
7525
+ items.sort((a, b) => (a.phase ?? 0) - (b.phase ?? 0) || String(a.date).localeCompare(String(b.date)) || a.label.localeCompare(b.label));
7526
+ const nextIndex = items.findIndex((i) => !i.implemented);
7527
+ return {
7528
+ items,
7529
+ nextIndex: nextIndex < 0 ? items.length : nextIndex,
7530
+ shipped: items.filter((i) => i.implemented).length,
7531
+ remaining: items.filter((i) => !i.implemented).length
7066
7532
  };
7067
- const liveSessions = sessions || [
7068
- { id: "s1", device: "MacBook Pro \xB7 Chrome", where: "Denver, CO", when: "Active now", current: true },
7069
- { id: "s2", device: "iPhone 15 \xB7 Safari", where: "Denver, CO", when: "2 hours ago" },
7070
- { id: "s3", device: "Windows \xB7 Edge", where: "Chicago, IL", when: "Aug 12" }
7071
- ];
7072
- return /* @__PURE__ */ jsxs55("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 860 }, ...rest, children: [
7073
- /* @__PURE__ */ jsxs55("div", { className: "fd-stack", style: { gap: 10 }, children: [
7074
- /* @__PURE__ */ jsx59("span", { className: "fd-overline fd-muted", children: "Account" }),
7075
- /* @__PURE__ */ jsx59("h1", { className: "fd-h1", style: { margin: 0 }, children: "Your profile" }),
7076
- /* @__PURE__ */ jsx59("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." })
7077
- ] }),
7078
- /* @__PURE__ */ jsx59(Card, { elevation: "flat", children: /* @__PURE__ */ jsxs55("div", { className: "fd-row", style: { gap: 16, flexWrap: "wrap", alignItems: "flex-start" }, children: [
7079
- /* @__PURE__ */ jsx59(Avatar, { name: draft.name || user.name, size: "lg" }),
7080
- /* @__PURE__ */ jsxs55("div", { className: "fd-stack", style: { gap: 4, flex: 1, minWidth: 200 }, children: [
7081
- /* @__PURE__ */ jsx59("span", { className: "fd-h3", style: { margin: 0 }, children: draft.name || user.name }),
7082
- /* @__PURE__ */ jsxs55("span", { className: "fd-body-sm fd-muted", children: [
7083
- draft.title || "No title set",
7084
- " \xB7 ",
7085
- user.team || "No team"
7086
- ] }),
7087
- /* @__PURE__ */ jsxs55("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 4 }, children: [
7088
- session.roles.map((r) => /* @__PURE__ */ jsx59(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)),
7089
- user.sso ? /* @__PURE__ */ jsx59(Badge, { tone: "info", icon: "shield-check", children: "SSO" }) : null
7090
- ] })
7091
- ] }),
7092
- /* @__PURE__ */ jsx59(Button, { variant: "secondary", size: "sm", icon: "image", children: "Change photo" })
7093
- ] }) }),
7094
- /* @__PURE__ */ jsx59(
7095
- Card,
7096
- {
7097
- title: /* @__PURE__ */ jsxs55("span", { className: "fd-stack", style: { gap: 2 }, children: [
7098
- /* @__PURE__ */ jsx59("span", { children: "Identity" }),
7099
- /* @__PURE__ */ jsx59("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "Name and title appear on plans you share" })
7100
- ] }),
7101
- elevation: "flat",
7102
- children: /* @__PURE__ */ jsxs55("div", { className: "fd-stack", style: { gap: 14 }, children: [
7103
- /* @__PURE__ */ jsxs55("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: [
7104
- /* @__PURE__ */ jsx59(Input, { label: "Full name", value: draft.name, onChange: (e) => set("name", e.target.value) }),
7105
- /* @__PURE__ */ jsx59(Input, { label: "Job title", value: draft.title, onChange: (e) => set("title", e.target.value), placeholder: "e.g. Senior media planner" })
7106
- ] }),
7107
- /* @__PURE__ */ jsxs55("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: [
7108
- /* @__PURE__ */ jsx59(
7109
- Input,
7110
- {
7111
- label: "Work email",
7112
- value: draft.email,
7113
- readOnly: true,
7114
- disabled: true,
7115
- help: user.sso ? "Managed by your identity provider \u2014 change it there." : "Contact an administrator to change this."
7116
- }
7117
- ),
7118
- /* @__PURE__ */ jsx59(Input, { label: "Phone", value: draft.phone, onChange: (e) => set("phone", e.target.value), placeholder: "Optional" })
7119
- ] }),
7120
- /* @__PURE__ */ jsx59(
7121
- Textarea,
7122
- {
7123
- label: "Short bio",
7124
- value: draft.bio,
7125
- onChange: (e) => set("bio", e.target.value),
7126
- rows: 3,
7127
- placeholder: "Optional \u2014 shown to colleagues on shared plans.",
7128
- help: "Never shown to clients."
7129
- }
7130
- )
7131
- ] })
7132
- }
7133
- ),
7134
- /* @__PURE__ */ jsx59(Card, { title: "Working preferences", elevation: "flat", children: /* @__PURE__ */ jsxs55("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: [
7135
- /* @__PURE__ */ jsx59(
7136
- Select,
7137
- {
7138
- label: "Time zone",
7139
- value: draft.timezone,
7140
- onChange: (e) => set("timezone", e.target.value),
7141
- options: TIMEZONES,
7142
- help: "Flight dates and schedules render in this zone."
7143
- }
7144
- ),
7145
- /* @__PURE__ */ jsx59(Select, { label: "Start of week", options: ["Monday", "Sunday"], placeholder: "Monday" })
7146
- ] }) }),
7147
- /* @__PURE__ */ jsx59(
7148
- Card,
7149
- {
7150
- title: /* @__PURE__ */ jsxs55("span", { className: "fd-stack", style: { gap: 2 }, children: [
7151
- /* @__PURE__ */ jsx59("span", { children: "Notifications" }),
7152
- /* @__PURE__ */ jsx59("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "Email only for now \u2014 in-app notifications are on the roadmap" })
7153
- ] }),
7154
- elevation: "flat",
7155
- children: /* @__PURE__ */ jsx59("div", { className: "fd-stack", style: { gap: 14 }, children: NOTIFY.map((n) => /* @__PURE__ */ jsx59(Switch, { checked: !!draft.notify[n.key], onChange: () => setNotify(n.key), label: n.label, description: n.detail }, n.key)) })
7156
- }
7157
- ),
7158
- /* @__PURE__ */ jsxs55(
7159
- Card,
7160
- {
7161
- title: /* @__PURE__ */ jsxs55("span", { className: "fd-stack", style: { gap: 2 }, children: [
7162
- /* @__PURE__ */ jsx59("span", { children: "Access" }),
7163
- /* @__PURE__ */ jsx59("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "What your roles grant you" })
7164
- ] }),
7165
- elevation: "flat",
7166
- action: /* @__PURE__ */ jsx59(
7167
- Button,
7168
- {
7169
- size: "sm",
7170
- variant: "ghost",
7171
- icon: "arrow-right",
7172
- onClick: () => onNavigate && onNavigate("#roles"),
7173
- children: "See role definitions"
7174
- }
7175
- ),
7176
- children: [
7177
- /* @__PURE__ */ jsxs55("div", { className: "fd-stack", style: { gap: 0 }, children: [
7178
- /* @__PURE__ */ jsx59(CardRow, { label: "Roles", children: session.roles.map((r) => r.name).join(", ") || "None" }),
7179
- /* @__PURE__ */ jsxs55(CardRow, { label: "Permissions", children: [
7180
- session.permissions.length,
7181
- " granted"
7182
- ] }),
7183
- /* @__PURE__ */ jsx59(CardRow, { label: "Member since", children: user.joined || "\u2014" })
7184
- ] }),
7185
- /* @__PURE__ */ jsx59("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." })
7186
- ]
7187
- }
7188
- ),
7189
- showSessions ? /* @__PURE__ */ jsx59(
7190
- Card,
7191
- {
7192
- title: "Signed-in devices",
7193
- elevation: "flat",
7194
- action: /* @__PURE__ */ jsx59(Button, { size: "sm", variant: "ghost", icon: "sign-out", children: "Sign out everywhere" }),
7195
- children: /* @__PURE__ */ jsx59("div", { className: "fd-stack", style: { gap: 0 }, children: liveSessions.map((s) => /* @__PURE__ */ jsxs55("div", { className: "fd-row", style: { gap: 12, padding: "10px 0", borderTop: "1px solid var(--border)", flexWrap: "wrap" }, children: [
7196
- /* @__PURE__ */ jsx59("i", { className: "ph ph-device-mobile", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
7197
- /* @__PURE__ */ jsxs55("span", { className: "fd-stack", style: { gap: 1, flex: 1, minWidth: 160 }, children: [
7198
- /* @__PURE__ */ jsx59("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: s.device }),
7199
- /* @__PURE__ */ jsxs55("span", { className: "fd-body-sm fd-muted", children: [
7200
- s.where,
7201
- " \xB7 ",
7202
- s.when
7203
- ] })
7204
- ] }),
7205
- s.current ? /* @__PURE__ */ jsx59(Badge, { tone: "success", dot: true, children: "This device" }) : /* @__PURE__ */ jsx59(Button, { size: "sm", variant: "ghost", children: "Revoke" })
7206
- ] }, s.id)) })
7207
- }
7208
- ) : null,
7209
- /* @__PURE__ */ jsxs55("div", { className: "fd-row", style: {
7210
- gap: 10,
7211
- flexWrap: "wrap",
7212
- position: "sticky",
7213
- bottom: 0,
7214
- padding: "12px 14px",
7215
- borderRadius: 10,
7216
- background: "var(--surface)",
7217
- border: "1px solid var(--border)",
7218
- boxShadow: "0 -4px 16px rgba(11,13,17,.06)"
7219
- }, children: [
7220
- /* @__PURE__ */ jsx59("span", { className: "fd-body-sm", style: { fontWeight: 600, flex: 1 }, children: saved ? "Saved." : dirty ? "Unsaved changes" : "No pending changes" }),
7221
- /* @__PURE__ */ jsx59(
7222
- Button,
7223
- {
7224
- size: "sm",
7225
- variant: "ghost",
7226
- disabled: !dirty || saving,
7227
- onClick: () => {
7228
- 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 } });
7229
- setSaved(false);
7230
- },
7231
- children: "Discard"
7232
- }
7233
- ),
7234
- /* @__PURE__ */ jsx59(Button, { size: "sm", icon: "check", disabled: !dirty, loading: saving, onClick: save, children: "Save changes" })
7235
- ] })
7236
- ] });
7237
7533
  }
7238
-
7239
- // src/components/platform/RoadmapTimeline.tsx
7240
- import * as React35 from "react";
7534
+ function useSession() {
7535
+ const [s, setS] = React37.useState(getSession);
7536
+ React37.useEffect(() => subscribe(setS), []);
7537
+ return s;
7538
+ }
7539
+ var SessionKit = {
7540
+ PERMISSION_CATALOG,
7541
+ ALL_PERMISSIONS,
7542
+ ROLES,
7543
+ FLAGS,
7544
+ USERS,
7545
+ loadSession,
7546
+ getSession,
7547
+ setActiveUser,
7548
+ saveProfile,
7549
+ refresh,
7550
+ subscribe,
7551
+ can,
7552
+ canAny,
7553
+ hasRole,
7554
+ flagOn,
7555
+ useSession,
7556
+ findFlag,
7557
+ flagState,
7558
+ isEnabled,
7559
+ flagVisible,
7560
+ flagLive,
7561
+ roadmap,
7562
+ evaluateFlag,
7563
+ explainFlag,
7564
+ bucket,
7565
+ buildSession
7566
+ };
7567
+ function DemoSessionProvider({ children }) {
7568
+ const session = useSession();
7569
+ const value = React37.useMemo(
7570
+ () => ({
7571
+ user: session.user,
7572
+ roles: session.roles,
7573
+ permissions: session.permissions,
7574
+ can,
7575
+ canAny,
7576
+ hasRole,
7577
+ signOut: () => window.alert("Signed out. (Simulated \u2014 no auth provider is wired up.)"),
7578
+ saveProfile: (patch) => {
7579
+ saveProfile(patch);
7580
+ },
7581
+ demo: { allUsers: session.allUsers.filter((u) => u.status === "active"), setActiveUser }
7582
+ }),
7583
+ [session]
7584
+ );
7585
+ return React37.createElement(SessionProvider, { value }, children);
7586
+ }
7241
7587
 
7242
7588
  // src/kits/runtime.ts
7243
- import * as React34 from "react";
7589
+ import * as React38 from "react";
7244
7590
  var WIRED_ENDPOINTS = [
7245
7591
  // Nothing yet. Every id below would come from a real service:
7246
7592
  // "plan.get", "placements.list", …
@@ -7411,8 +7757,8 @@ var RuntimeKit = {
7411
7757
  };
7412
7758
  RuntimeKit.declare(FEATURE_NEEDS);
7413
7759
  function useRuntimeMode() {
7414
- const [m, setM] = React34.useState(RuntimeKit.getMode());
7415
- React34.useEffect(() => RuntimeKit.subscribe(setM), []);
7760
+ const [m, setM] = React38.useState(RuntimeKit.getMode());
7761
+ React38.useEffect(() => RuntimeKit.subscribe(setM), []);
7416
7762
  return m;
7417
7763
  }
7418
7764
  function useFeatureStatus(key) {
@@ -7431,7 +7777,7 @@ var UseRuntimeMode = useRuntimeMode;
7431
7777
  var UseFeatureStatus = useFeatureStatus;
7432
7778
 
7433
7779
  // src/components/platform/RoadmapTimeline.tsx
7434
- import { jsx as jsx60, jsxs as jsxs56 } from "react/jsx-runtime";
7780
+ import { jsx as jsx62, jsxs as jsxs58 } from "react/jsx-runtime";
7435
7781
  var STATUS = {
7436
7782
  shipped: { tone: "success", icon: "check-circle", label: "Wired" },
7437
7783
  next: { tone: "warning", icon: "traffic-cone", label: "Not wired" },
@@ -7446,45 +7792,45 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
7446
7792
  const s = STATUS[item.status] || STATUS.planned;
7447
7793
  const deps = (item.dependsOn || []).map((k) => byKey[k]).filter(Boolean);
7448
7794
  const blocking = deps.filter((d) => !d.implemented);
7449
- return /* @__PURE__ */ jsx60(Card, { elevation: "flat", children: /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 10 }, children: [
7450
- /* @__PURE__ */ jsxs56("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
7451
- /* @__PURE__ */ jsx60("span", { className: "fd-body", style: { fontWeight: 700, flex: 1, minWidth: 140 }, children: item.label }),
7452
- /* @__PURE__ */ jsx60(Badge, { tone: "neutral", icon: PROJECT_ICON[item.project] || "squares-four", children: item.project }),
7453
- /* @__PURE__ */ jsx60(Badge, { tone: s.tone, icon: s.icon, children: s.label })
7795
+ return /* @__PURE__ */ jsx62(Card, { elevation: "flat", children: /* @__PURE__ */ jsxs58("div", { className: "fd-stack", style: { gap: 10 }, children: [
7796
+ /* @__PURE__ */ jsxs58("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
7797
+ /* @__PURE__ */ jsx62("span", { className: "fd-body", style: { fontWeight: 700, flex: 1, minWidth: 140 }, children: item.label }),
7798
+ /* @__PURE__ */ jsx62(Badge, { tone: "neutral", icon: PROJECT_ICON[item.project] || "squares-four", children: item.project }),
7799
+ /* @__PURE__ */ jsx62(Badge, { tone: s.tone, icon: s.icon, children: s.label })
7454
7800
  ] }),
7455
- /* @__PURE__ */ jsx60("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: item.description }),
7456
- item.backend ? /* @__PURE__ */ jsxs56("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start" }, children: [
7457
- /* @__PURE__ */ jsx60("span", { className: "fd-overline fd-muted", style: { width: 62, flex: "none", paddingTop: 2 }, children: "Backend" }),
7458
- /* @__PURE__ */ jsx60("span", { className: "fd-body-sm", style: { flex: 1, textWrap: "pretty" }, children: item.backend })
7801
+ /* @__PURE__ */ jsx62("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: item.description }),
7802
+ item.backend ? /* @__PURE__ */ jsxs58("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start" }, children: [
7803
+ /* @__PURE__ */ jsx62("span", { className: "fd-overline fd-muted", style: { width: 62, flex: "none", paddingTop: 2 }, children: "Backend" }),
7804
+ /* @__PURE__ */ jsx62("span", { className: "fd-body-sm", style: { flex: 1, textWrap: "pretty" }, children: item.backend })
7459
7805
  ] }) : null,
7460
- /* @__PURE__ */ jsxs56("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
7461
- item.effort ? /* @__PURE__ */ jsxs56("span", { className: "fd-body-sm fd-muted", children: [
7462
- /* @__PURE__ */ jsx60("i", { className: "ph ph-hourglass-medium" }),
7806
+ /* @__PURE__ */ jsxs58("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
7807
+ item.effort ? /* @__PURE__ */ jsxs58("span", { className: "fd-body-sm fd-muted", children: [
7808
+ /* @__PURE__ */ jsx62("i", { className: "ph ph-hourglass-medium" }),
7463
7809
  " ",
7464
7810
  item.effort
7465
7811
  ] }) : null,
7466
- /* @__PURE__ */ jsxs56("span", { className: "fd-body-sm fd-muted", children: [
7467
- /* @__PURE__ */ jsx60("i", { className: "ph ph-user" }),
7812
+ /* @__PURE__ */ jsxs58("span", { className: "fd-body-sm fd-muted", children: [
7813
+ /* @__PURE__ */ jsx62("i", { className: "ph ph-user" }),
7468
7814
  " ",
7469
7815
  item.owner
7470
7816
  ] }),
7471
- item.screen ? /* @__PURE__ */ jsx60(Badge, { tone: "neutral", icon: "browser", children: "screen" }) : null,
7472
- /* @__PURE__ */ jsx60("span", { style: { flex: 1 } }),
7473
- /* @__PURE__ */ jsx60(
7817
+ item.screen ? /* @__PURE__ */ jsx62(Badge, { tone: "neutral", icon: "browser", children: "screen" }) : null,
7818
+ /* @__PURE__ */ jsx62("span", { style: { flex: 1 } }),
7819
+ /* @__PURE__ */ jsx62(
7474
7820
  "button",
7475
7821
  {
7476
7822
  type: "button",
7477
7823
  onClick: () => onOpenFlag && onOpenFlag(item.key),
7478
7824
  style: { all: "unset", cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 5 },
7479
- children: /* @__PURE__ */ jsx60("code", { className: "fd-mono", style: { fontSize: 11, color: "var(--brand)" }, children: item.key })
7825
+ children: /* @__PURE__ */ jsx62("code", { className: "fd-mono", style: { fontSize: 11, color: "var(--brand)" }, children: item.key })
7480
7826
  }
7481
7827
  )
7482
7828
  ] }),
7483
- deps.length ? /* @__PURE__ */ jsxs56("div", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 6, borderTop: "1px solid var(--border)" }, children: [
7484
- /* @__PURE__ */ jsx60("span", { className: "fd-overline fd-muted", style: { paddingTop: 3 }, children: "After" }),
7485
- deps.map((d) => /* @__PURE__ */ jsx60(Tag, { icon: d.implemented ? "check" : "clock", children: d.label }, d.key))
7829
+ deps.length ? /* @__PURE__ */ jsxs58("div", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 6, borderTop: "1px solid var(--border)" }, children: [
7830
+ /* @__PURE__ */ jsx62("span", { className: "fd-overline fd-muted", style: { paddingTop: 3 }, children: "After" }),
7831
+ deps.map((d) => /* @__PURE__ */ jsx62(Tag, { icon: d.implemented ? "check" : "clock", children: d.label }, d.key))
7486
7832
  ] }) : null,
7487
- blocking.length && !item.implemented ? /* @__PURE__ */ jsxs56("span", { className: "fd-body-sm", style: { color: "var(--warn-text)" }, children: [
7833
+ blocking.length && !item.implemented ? /* @__PURE__ */ jsxs58("span", { className: "fd-body-sm", style: { color: "var(--warn-text)" }, children: [
7488
7834
  "Blocked until ",
7489
7835
  blocking.map((d) => d.label).join(" and "),
7490
7836
  " ",
@@ -7495,12 +7841,12 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
7495
7841
  }
7496
7842
  function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7497
7843
  const session = SessionKit.useSession();
7498
- const [project, setProject] = React35.useState("");
7499
- const [q, setQ] = React35.useState("");
7500
- const scrollRef = React35.useRef(null);
7501
- const nowRef = React35.useRef(null);
7502
- const rm = React35.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
7503
- const byKey = React35.useMemo(() => {
7844
+ const [project, setProject] = React39.useState("");
7845
+ const [q, setQ] = React39.useState("");
7846
+ const scrollRef = React39.useRef(null);
7847
+ const nowRef = React39.useRef(null);
7848
+ const rm = React39.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
7849
+ const byKey = React39.useMemo(() => {
7504
7850
  const m = {};
7505
7851
  rm.items.forEach((i) => {
7506
7852
  m[i.key] = i;
@@ -7509,7 +7855,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7509
7855
  }, [rm]);
7510
7856
  const items = rm.items.filter((i) => (!project || i.project === project) && (!q || (i.label + " " + i.description + " " + (i.backend || "") + " " + i.key).toLowerCase().includes(q.toLowerCase())));
7511
7857
  const projects = [...new Set(rm.items.map((i) => i.project))];
7512
- React35.useEffect(() => {
7858
+ React39.useEffect(() => {
7513
7859
  let raf1 = 0, raf2 = 0;
7514
7860
  const place = () => {
7515
7861
  const box = scrollRef.current, mark = nowRef.current;
@@ -7537,23 +7883,23 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7537
7883
  }, 0);
7538
7884
  const nextPhase = items.find((i) => !i.implemented);
7539
7885
  let lastPhase = null;
7540
- return /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 16, maxWidth: 1e3 }, children: [
7541
- /* @__PURE__ */ jsxs56("div", { className: "fd-stack", style: { gap: 10 }, children: [
7542
- /* @__PURE__ */ jsx60("span", { className: "fd-overline fd-muted", children: "Architecture" }),
7543
- /* @__PURE__ */ jsx60("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
7544
- /* @__PURE__ */ jsx60("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." })
7886
+ return /* @__PURE__ */ jsxs58("div", { className: "fd-stack", style: { gap: 16, maxWidth: 1e3 }, children: [
7887
+ /* @__PURE__ */ jsxs58("div", { className: "fd-stack", style: { gap: 10 }, children: [
7888
+ /* @__PURE__ */ jsx62("span", { className: "fd-overline fd-muted", children: "Architecture" }),
7889
+ /* @__PURE__ */ jsx62("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
7890
+ /* @__PURE__ */ jsx62("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." })
7545
7891
  ] }),
7546
- /* @__PURE__ */ jsxs56("div", { className: "fd-grid-stats is-thin", children: [
7547
- /* @__PURE__ */ jsx60(StatTile, { compact: true, label: "Wired", value: String(rm.shipped), sub: "of " + rm.items.length + " features" }),
7548
- /* @__PURE__ */ jsx60(StatTile, { compact: true, label: "Remaining", value: String(rm.remaining), sub: "backend work" }),
7549
- /* @__PURE__ */ jsx60(StatTile, { compact: true, label: "Est. effort", value: days ? days + " days" : "\u2014", sub: "sum of estimates, not calendar" }),
7550
- /* @__PURE__ */ jsx60(StatTile, { compact: true, label: "Up next", value: nextPhase ? "Phase " + nextPhase.phase : "\u2014", sub: nextPhase ? nextPhase.phaseName : "all wired" })
7892
+ /* @__PURE__ */ jsxs58("div", { className: "fd-grid-stats is-thin", children: [
7893
+ /* @__PURE__ */ jsx62(StatTile, { compact: true, label: "Wired", value: String(rm.shipped), sub: "of " + rm.items.length + " features" }),
7894
+ /* @__PURE__ */ jsx62(StatTile, { compact: true, label: "Remaining", value: String(rm.remaining), sub: "backend work" }),
7895
+ /* @__PURE__ */ jsx62(StatTile, { compact: true, label: "Est. effort", value: days ? days + " days" : "\u2014", sub: "sum of estimates, not calendar" }),
7896
+ /* @__PURE__ */ jsx62(StatTile, { compact: true, label: "Up next", value: nextPhase ? "Phase " + nextPhase.phase : "\u2014", sub: nextPhase ? nextPhase.phaseName : "all wired" })
7551
7897
  ] }),
7552
- /* @__PURE__ */ jsxs56("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
7553
- /* @__PURE__ */ jsx60(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 } }),
7554
- /* @__PURE__ */ jsx60(Select, { placeholder: "All projects", value: project, onChange: (e) => setProject(e.target.value), options: projects, style: { width: 190 } }),
7555
- /* @__PURE__ */ jsx60("span", { style: { flex: 1 } }),
7556
- /* @__PURE__ */ jsx60(
7898
+ /* @__PURE__ */ jsxs58("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
7899
+ /* @__PURE__ */ jsx62(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 } }),
7900
+ /* @__PURE__ */ jsx62(Select, { placeholder: "All projects", value: project, onChange: (e) => setProject(e.target.value), options: projects, style: { width: 190 } }),
7901
+ /* @__PURE__ */ jsx62("span", { style: { flex: 1 } }),
7902
+ /* @__PURE__ */ jsx62(
7557
7903
  Button,
7558
7904
  {
7559
7905
  size: "sm",
@@ -7567,7 +7913,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7567
7913
  }
7568
7914
  )
7569
7915
  ] }),
7570
- /* @__PURE__ */ jsx60(
7916
+ /* @__PURE__ */ jsx62(
7571
7917
  Flag,
7572
7918
  {
7573
7919
  tone: "info",
@@ -7576,60 +7922,60 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7576
7922
  actions: null
7577
7923
  }
7578
7924
  ),
7579
- /* @__PURE__ */ jsx60("div", { className: "fd-rm", children: /* @__PURE__ */ jsx60("div", { className: "fd-rm-scroll", ref: scrollRef, children: /* @__PURE__ */ jsxs56("div", { className: "fd-rm-track", children: [
7580
- /* @__PURE__ */ jsx60("span", { className: "fd-rm-spine", children: /* @__PURE__ */ jsx60("span", { className: "fd-rm-spine-done", style: { height: items.length ? 100 * shippedShown / items.length + "%" : "0%" } }) }),
7925
+ /* @__PURE__ */ jsx62("div", { className: "fd-rm", children: /* @__PURE__ */ jsx62("div", { className: "fd-rm-scroll", ref: scrollRef, children: /* @__PURE__ */ jsxs58("div", { className: "fd-rm-track", children: [
7926
+ /* @__PURE__ */ jsx62("span", { className: "fd-rm-spine", children: /* @__PURE__ */ jsx62("span", { className: "fd-rm-spine-done", style: { height: items.length ? 100 * shippedShown / items.length + "%" : "0%" } }) }),
7581
7927
  items.map((item, n) => {
7582
7928
  const showPhase = item.phase !== lastPhase;
7583
7929
  lastPhase = item.phase;
7584
7930
  const inPhase = items.filter((i) => i.phase === item.phase).length;
7585
7931
  const isBoundary = n === firstPending;
7586
- return /* @__PURE__ */ jsxs56(React35.Fragment, { children: [
7587
- showPhase ? /* @__PURE__ */ jsxs56("div", { className: "fd-rm-era", children: [
7588
- /* @__PURE__ */ jsxs56("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
7589
- /* @__PURE__ */ jsx60("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
7590
- item.phase === 0 ? null : /* @__PURE__ */ jsxs56("span", { className: "fd-mono", style: { opacity: 0.7, fontWeight: 400 }, children: [
7932
+ return /* @__PURE__ */ jsxs58(React39.Fragment, { children: [
7933
+ showPhase ? /* @__PURE__ */ jsxs58("div", { className: "fd-rm-era", children: [
7934
+ /* @__PURE__ */ jsxs58("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
7935
+ /* @__PURE__ */ jsx62("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
7936
+ item.phase === 0 ? null : /* @__PURE__ */ jsxs58("span", { className: "fd-mono", style: { opacity: 0.7, fontWeight: 400 }, children: [
7591
7937
  fmtDate(item.phaseStart),
7592
7938
  " \u2013 ",
7593
7939
  fmtDate(item.date)
7594
7940
  ] }),
7595
- /* @__PURE__ */ jsxs56("span", { style: { opacity: 0.7, fontWeight: 400 }, children: [
7941
+ /* @__PURE__ */ jsxs58("span", { style: { opacity: 0.7, fontWeight: 400 }, children: [
7596
7942
  "\xB7 ",
7597
7943
  inPhase,
7598
7944
  " feature",
7599
7945
  inPhase === 1 ? "" : "s"
7600
7946
  ] })
7601
7947
  ] }),
7602
- item.phaseWhy ? /* @__PURE__ */ jsx60("p", { className: "fd-rm-era-why", children: item.phaseWhy }) : null
7948
+ item.phaseWhy ? /* @__PURE__ */ jsx62("p", { className: "fd-rm-era-why", children: item.phaseWhy }) : null
7603
7949
  ] }) : null,
7604
- isBoundary ? /* @__PURE__ */ jsx60("div", { className: "fd-rm-now", ref: nowRef, children: /* @__PURE__ */ jsxs56("span", { className: "fd-rm-now-pill", children: [
7605
- /* @__PURE__ */ jsx60("i", { className: "ph ph-map-pin" }),
7950
+ isBoundary ? /* @__PURE__ */ jsx62("div", { className: "fd-rm-now", ref: nowRef, children: /* @__PURE__ */ jsxs58("span", { className: "fd-rm-now-pill", children: [
7951
+ /* @__PURE__ */ jsx62("i", { className: "ph ph-map-pin" }),
7606
7952
  " You are here \u2014 everything above is wired"
7607
7953
  ] }) }) : null,
7608
- /* @__PURE__ */ jsxs56("div", { className: "fd-rm-row", children: [
7609
- /* @__PURE__ */ jsx60("span", { className: "fd-rm-dot " + (item.implemented ? "is-shipped" : item.status === "next" ? "is-next" : "") }),
7610
- /* @__PURE__ */ jsxs56("div", { className: "fd-rm-date", children: [
7954
+ /* @__PURE__ */ jsxs58("div", { className: "fd-rm-row", children: [
7955
+ /* @__PURE__ */ jsx62("span", { className: "fd-rm-dot " + (item.implemented ? "is-shipped" : item.status === "next" ? "is-next" : "") }),
7956
+ /* @__PURE__ */ jsxs58("div", { className: "fd-rm-date", children: [
7611
7957
  fmtDate(item.date),
7612
- /* @__PURE__ */ jsx60("br", {}),
7613
- /* @__PURE__ */ jsx60("span", { style: { opacity: 0.75 }, children: item.implemented ? "shipped" : "phase " + item.phase })
7958
+ /* @__PURE__ */ jsx62("br", {}),
7959
+ /* @__PURE__ */ jsx62("span", { style: { opacity: 0.75 }, children: item.implemented ? "shipped" : "phase " + item.phase })
7614
7960
  ] }),
7615
- /* @__PURE__ */ jsx60(RoadmapCard, { item, byKey, onOpenFlag })
7961
+ /* @__PURE__ */ jsx62(RoadmapCard, { item, byKey, onOpenFlag })
7616
7962
  ] })
7617
7963
  ] }, item.key);
7618
7964
  }),
7619
- !items.length ? /* @__PURE__ */ jsx60("p", { className: "fd-body-sm fd-muted", children: "Nothing matches that filter." }) : null
7965
+ !items.length ? /* @__PURE__ */ jsx62("p", { className: "fd-body-sm fd-muted", children: "Nothing matches that filter." }) : null
7620
7966
  ] }) }) }),
7621
- /* @__PURE__ */ jsxs56("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: [
7967
+ /* @__PURE__ */ jsxs58("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: [
7622
7968
  "Derived from the feature-flag registry \u2014 each entry's status is its flag's ",
7623
- /* @__PURE__ */ jsx60("code", { className: "fd-mono", children: "implemented" }),
7969
+ /* @__PURE__ */ jsx62("code", { className: "fd-mono", children: "implemented" }),
7624
7970
  " field, so this page cannot drift from what the apps actually do."
7625
7971
  ] })
7626
7972
  ] });
7627
7973
  }
7628
7974
 
7629
7975
  // src/components/platform/ComingSoon.tsx
7630
- import * as React36 from "react";
7976
+ import * as React40 from "react";
7631
7977
  import { createPortal as createPortal5 } from "react-dom";
7632
- import { Fragment as Fragment16, jsx as jsx61, jsxs as jsxs57 } from "react/jsx-runtime";
7978
+ import { Fragment as Fragment18, jsx as jsx63, jsxs as jsxs59 } from "react/jsx-runtime";
7633
7979
  var BYPASS_STORE = "fd.soon.bypass.v1";
7634
7980
  function readBypassed() {
7635
7981
  try {
@@ -7645,8 +7991,8 @@ function writeBypassed(list) {
7645
7991
  }
7646
7992
  }
7647
7993
  function useBypass(key) {
7648
- const [on, setOn] = React36.useState(() => !!key && readBypassed().indexOf(key) >= 0);
7649
- React36.useEffect(() => {
7994
+ const [on, setOn] = React40.useState(() => !!key && readBypassed().indexOf(key) >= 0);
7995
+ React40.useEffect(() => {
7650
7996
  setOn(!!key && readBypassed().indexOf(key) >= 0);
7651
7997
  }, [key]);
7652
7998
  const set = (next) => {
@@ -7659,43 +8005,43 @@ function useBypass(key) {
7659
8005
  return [on, set];
7660
8006
  }
7661
8007
  function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass }) {
7662
- return /* @__PURE__ */ jsxs57(Fragment16, { children: [
7663
- /* @__PURE__ */ jsxs57("span", { className: "fd-soon-badge", children: [
7664
- /* @__PURE__ */ jsx61("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
8008
+ return /* @__PURE__ */ jsxs59(Fragment18, { children: [
8009
+ /* @__PURE__ */ jsxs59("span", { className: "fd-soon-badge", children: [
8010
+ /* @__PURE__ */ jsx63("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
7665
8011
  label
7666
8012
  ] }),
7667
- detail ? /* @__PURE__ */ jsx61("span", { className: "fd-soon-detail", children: detail }) : null,
7668
- backend ? /* @__PURE__ */ jsxs57("span", { className: "fd-soon-backend", children: [
7669
- /* @__PURE__ */ jsx61("span", { className: "fd-overline", children: "Needs" }),
8013
+ detail ? /* @__PURE__ */ jsx63("span", { className: "fd-soon-detail", children: detail }) : null,
8014
+ backend ? /* @__PURE__ */ jsxs59("span", { className: "fd-soon-backend", children: [
8015
+ /* @__PURE__ */ jsx63("span", { className: "fd-overline", children: "Needs" }),
7670
8016
  " ",
7671
8017
  backend
7672
8018
  ] }) : null,
7673
- eta || effort || onRoadmap ? /* @__PURE__ */ jsxs57("span", { className: "fd-soon-meta", children: [
7674
- eta ? /* @__PURE__ */ jsxs57("span", { children: [
7675
- /* @__PURE__ */ jsx61("i", { className: "ph ph-calendar-blank", "aria-hidden": "true" }),
8019
+ eta || effort || onRoadmap ? /* @__PURE__ */ jsxs59("span", { className: "fd-soon-meta", children: [
8020
+ eta ? /* @__PURE__ */ jsxs59("span", { children: [
8021
+ /* @__PURE__ */ jsx63("i", { className: "ph ph-calendar-blank", "aria-hidden": "true" }),
7676
8022
  " ",
7677
8023
  eta
7678
8024
  ] }) : null,
7679
- effort ? /* @__PURE__ */ jsxs57("span", { children: [
7680
- /* @__PURE__ */ jsx61("i", { className: "ph ph-hourglass-medium", "aria-hidden": "true" }),
8025
+ effort ? /* @__PURE__ */ jsxs59("span", { children: [
8026
+ /* @__PURE__ */ jsx63("i", { className: "ph ph-hourglass-medium", "aria-hidden": "true" }),
7681
8027
  " ",
7682
8028
  effort
7683
8029
  ] }) : null,
7684
- onRoadmap ? /* @__PURE__ */ jsxs57("button", { type: "button", className: "fd-soon-link", onClick: onRoadmap, children: [
8030
+ onRoadmap ? /* @__PURE__ */ jsxs59("button", { type: "button", className: "fd-soon-link", onClick: onRoadmap, children: [
7685
8031
  "See the roadmap ",
7686
- /* @__PURE__ */ jsx61("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
8032
+ /* @__PURE__ */ jsx63("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
7687
8033
  ] }) : null
7688
8034
  ] }) : null,
7689
- allowed ? /* @__PURE__ */ jsxs57("button", { type: "button", className: "fd-soon-view", onClick: onBypass, children: [
7690
- /* @__PURE__ */ jsx61("i", { className: "ph ph-eye", "aria-hidden": "true" }),
8035
+ allowed ? /* @__PURE__ */ jsxs59("button", { type: "button", className: "fd-soon-view", onClick: onBypass, children: [
8036
+ /* @__PURE__ */ jsx63("i", { className: "ph ph-eye", "aria-hidden": "true" }),
7691
8037
  " View and use it anyway"
7692
8038
  ] }) : null
7693
8039
  ] });
7694
8040
  }
7695
8041
  function useHoverCard(open) {
7696
- const anchor = React36.useRef(null);
7697
- const [pos, setPos] = React36.useState(null);
7698
- React36.useLayoutEffect(() => {
8042
+ const anchor = React40.useRef(null);
8043
+ const [pos, setPos] = React40.useState(null);
8044
+ React40.useLayoutEffect(() => {
7699
8045
  if (!open || !anchor.current) {
7700
8046
  setPos(null);
7701
8047
  return;
@@ -7747,9 +8093,9 @@ function ComingSoon({
7747
8093
  const tip = [label, detail, backend ? "Needs " + backend : null, eta ? "ETA " + eta : null, effort].filter(Boolean).join(" \xB7 ");
7748
8094
  if (inline) {
7749
8095
  if (allowed && bypassed) {
7750
- return /* @__PURE__ */ jsxs57("span", { className: ["fd-soon-inline-on", className].filter(Boolean).join(" "), ...rest, children: [
8096
+ return /* @__PURE__ */ jsxs59("span", { className: ["fd-soon-inline-on", className].filter(Boolean).join(" "), ...rest, children: [
7751
8097
  children,
7752
- /* @__PURE__ */ jsx61(
8098
+ /* @__PURE__ */ jsx63(
7753
8099
  "button",
7754
8100
  {
7755
8101
  type: "button",
@@ -7757,12 +8103,12 @@ function ComingSoon({
7757
8103
  title: "Unwired \u2014 writes go to the simulated backend. " + tip + " Click to re-blur.",
7758
8104
  onClick: () => setBypassed(false),
7759
8105
  "aria-label": "Re-blur this unwired feature",
7760
- children: /* @__PURE__ */ jsx61("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
8106
+ children: /* @__PURE__ */ jsx63("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
7761
8107
  }
7762
8108
  )
7763
8109
  ] });
7764
8110
  }
7765
- return /* @__PURE__ */ jsx61(
8111
+ return /* @__PURE__ */ jsx63(
7766
8112
  InlineSoon,
7767
8113
  {
7768
8114
  label,
@@ -7782,20 +8128,20 @@ function ComingSoon({
7782
8128
  );
7783
8129
  }
7784
8130
  if (allowed && bypassed) {
7785
- return /* @__PURE__ */ jsxs57("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 14 }, ...rest, children: [
7786
- /* @__PURE__ */ jsxs57("div", { className: "fd-soon-bar", role: "status", children: [
7787
- /* @__PURE__ */ jsxs57("span", { className: "fd-soon-badge", children: [
7788
- /* @__PURE__ */ jsx61("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
8131
+ return /* @__PURE__ */ jsxs59("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 14 }, ...rest, children: [
8132
+ /* @__PURE__ */ jsxs59("div", { className: "fd-soon-bar", role: "status", children: [
8133
+ /* @__PURE__ */ jsxs59("span", { className: "fd-soon-badge", children: [
8134
+ /* @__PURE__ */ jsx63("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
7789
8135
  "Unwired feature \u2014 you are using it anyway"
7790
8136
  ] }),
7791
- /* @__PURE__ */ jsx61("span", { className: "fd-soon-bar-detail", children: "Every action here writes to the simulated backend, so nothing you do persists beyond this session." }),
7792
- /* @__PURE__ */ jsxs57("span", { className: "fd-soon-bar-actions", children: [
7793
- onRoadmap ? /* @__PURE__ */ jsxs57("button", { type: "button", className: "fd-soon-link", onClick: onRoadmap, children: [
8137
+ /* @__PURE__ */ jsx63("span", { className: "fd-soon-bar-detail", children: "Every action here writes to the simulated backend, so nothing you do persists beyond this session." }),
8138
+ /* @__PURE__ */ jsxs59("span", { className: "fd-soon-bar-actions", children: [
8139
+ onRoadmap ? /* @__PURE__ */ jsxs59("button", { type: "button", className: "fd-soon-link", onClick: onRoadmap, children: [
7794
8140
  "Roadmap ",
7795
- /* @__PURE__ */ jsx61("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
8141
+ /* @__PURE__ */ jsx63("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
7796
8142
  ] }) : null,
7797
- /* @__PURE__ */ jsxs57("button", { type: "button", className: "fd-soon-link", onClick: () => setBypassed(false), children: [
7798
- /* @__PURE__ */ jsx61("i", { className: "ph ph-eye-slash", "aria-hidden": "true" }),
8143
+ /* @__PURE__ */ jsxs59("button", { type: "button", className: "fd-soon-link", onClick: () => setBypassed(false), children: [
8144
+ /* @__PURE__ */ jsx63("i", { className: "ph ph-eye-slash", "aria-hidden": "true" }),
7799
8145
  " Re-blur"
7800
8146
  ] })
7801
8147
  ] })
@@ -7803,10 +8149,10 @@ function ComingSoon({
7803
8149
  children
7804
8150
  ] });
7805
8151
  }
7806
- return /* @__PURE__ */ jsxs57("div", { className: ["fd-soon", className].filter(Boolean).join(" "), style: minHeight ? { minHeight } : void 0, ...rest, children: [
7807
- /* @__PURE__ */ jsx61("div", { className: "fd-soon-under", style: { filter: "blur(" + blur + "px) saturate(.62)" }, ...{ inert: "" }, "aria-hidden": "true", children }),
7808
- /* @__PURE__ */ jsx61("div", { className: "fd-soon-veil" }),
7809
- /* @__PURE__ */ jsx61("div", { className: "fd-soon-note", role: "note", children: /* @__PURE__ */ jsx61(
8152
+ return /* @__PURE__ */ jsxs59("div", { className: ["fd-soon", className].filter(Boolean).join(" "), style: minHeight ? { minHeight } : void 0, ...rest, children: [
8153
+ /* @__PURE__ */ jsx63("div", { className: "fd-soon-under", style: { filter: "blur(" + blur + "px) saturate(.62)" }, ...{ inert: "" }, "aria-hidden": "true", children }),
8154
+ /* @__PURE__ */ jsx63("div", { className: "fd-soon-veil" }),
8155
+ /* @__PURE__ */ jsx63("div", { className: "fd-soon-note", role: "note", children: /* @__PURE__ */ jsx63(
7810
8156
  SoonCard,
7811
8157
  {
7812
8158
  label,
@@ -7822,10 +8168,10 @@ function ComingSoon({
7822
8168
  ] });
7823
8169
  }
7824
8170
  function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass, blur, tip, className, rest, children }) {
7825
- const [open, setOpen] = React36.useState(false);
8171
+ const [open, setOpen] = React40.useState(false);
7826
8172
  const [anchor, pos] = useHoverCard(open);
7827
8173
  const zIndex = useOverlayLayer(open);
7828
- const close = React36.useRef(null);
8174
+ const close = React40.useRef(null);
7829
8175
  const show = () => {
7830
8176
  if (close.current) {
7831
8177
  clearTimeout(close.current);
@@ -7841,10 +8187,10 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
7841
8187
  setOpen(false);
7842
8188
  }, 140);
7843
8189
  };
7844
- React36.useEffect(() => () => {
8190
+ React40.useEffect(() => () => {
7845
8191
  if (close.current) clearTimeout(close.current);
7846
8192
  }, []);
7847
- return /* @__PURE__ */ jsxs57(
8193
+ return /* @__PURE__ */ jsxs59(
7848
8194
  "span",
7849
8195
  {
7850
8196
  ref: anchor,
@@ -7855,8 +8201,8 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
7855
8201
  onBlur: hide,
7856
8202
  ...rest,
7857
8203
  children: [
7858
- /* @__PURE__ */ jsx61("span", { className: "fd-soon-under", style: { filter: "blur(" + Math.min(blur, 1.1) + "px) saturate(.66)" }, ...{ inert: "" }, "aria-hidden": "true", children }),
7859
- /* @__PURE__ */ jsx61(
8204
+ /* @__PURE__ */ jsx63("span", { className: "fd-soon-under", style: { filter: "blur(" + Math.min(blur, 1.1) + "px) saturate(.66)" }, ...{ inert: "" }, "aria-hidden": "true", children }),
8205
+ /* @__PURE__ */ jsx63(
7860
8206
  "button",
7861
8207
  {
7862
8208
  type: "button",
@@ -7867,11 +8213,11 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
7867
8213
  onFocus: show,
7868
8214
  onBlur: hide,
7869
8215
  onClick: () => open ? setOpen(false) : show(),
7870
- children: /* @__PURE__ */ jsx61("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
8216
+ children: /* @__PURE__ */ jsx63("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
7871
8217
  }
7872
8218
  ),
7873
8219
  open && pos ? createPortal5(
7874
- /* @__PURE__ */ jsx61(LayerProvider, { zIndex, children: /* @__PURE__ */ jsx61(
8220
+ /* @__PURE__ */ jsx63(LayerProvider, { zIndex, children: /* @__PURE__ */ jsx63(
7875
8221
  "div",
7876
8222
  {
7877
8223
  className: "fd-soon-note fd-soon-hovercard",
@@ -7879,7 +8225,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
7879
8225
  style: { position: "fixed", left: pos.left, top: pos.top, bottom: pos.bottom, width: pos.width, zIndex },
7880
8226
  onMouseEnter: show,
7881
8227
  onMouseLeave: hide,
7882
- children: /* @__PURE__ */ jsx61(
8228
+ children: /* @__PURE__ */ jsx63(
7883
8229
  SoonCard,
7884
8230
  {
7885
8231
  label,
@@ -7909,10 +8255,10 @@ function formatEta(iso2) {
7909
8255
  var FormatEta = formatEta;
7910
8256
 
7911
8257
  // src/components/platform/Gate.tsx
7912
- import { jsx as jsx62, jsxs as jsxs58 } from "react/jsx-runtime";
8258
+ import { jsx as jsx64, jsxs as jsxs60 } from "react/jsx-runtime";
7913
8259
  function PermissionDenied({ permission, title, detail, compact: compact3 = false, className = "", ...rest }) {
7914
8260
  const need = Array.isArray(permission) ? permission : [permission].filter(Boolean);
7915
- return /* @__PURE__ */ jsxs58(
8261
+ return /* @__PURE__ */ jsxs60(
7916
8262
  "div",
7917
8263
  {
7918
8264
  className: ["fd-stack", className].filter(Boolean).join(" "),
@@ -7928,14 +8274,14 @@ function PermissionDenied({ permission, title, detail, compact: compact3 = false
7928
8274
  },
7929
8275
  ...rest,
7930
8276
  children: [
7931
- /* @__PURE__ */ jsxs58("span", { className: "fd-row", style: { gap: 8 }, children: [
7932
- /* @__PURE__ */ jsx62("i", { className: "ph ph-lock-simple", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
7933
- /* @__PURE__ */ jsx62("span", { className: compact3 ? "fd-label-lg" : "fd-h3", children: title || "You do not have access to this" })
8277
+ /* @__PURE__ */ jsxs60("span", { className: "fd-row", style: { gap: 8 }, children: [
8278
+ /* @__PURE__ */ jsx64("i", { className: "ph ph-lock-simple", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
8279
+ /* @__PURE__ */ jsx64("span", { className: compact3 ? "fd-label-lg" : "fd-h3", children: title || "You do not have access to this" })
7934
8280
  ] }),
7935
- /* @__PURE__ */ jsx62("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." }),
7936
- need.length ? /* @__PURE__ */ jsxs58("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: [
7937
- /* @__PURE__ */ jsx62("span", { className: "fd-overline fd-muted", children: "Requires" }),
7938
- need.map((p) => /* @__PURE__ */ jsx62("code", { className: "fd-mono", style: {
8281
+ /* @__PURE__ */ jsx64("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." }),
8282
+ need.length ? /* @__PURE__ */ jsxs60("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: [
8283
+ /* @__PURE__ */ jsx64("span", { className: "fd-overline fd-muted", children: "Requires" }),
8284
+ need.map((p) => /* @__PURE__ */ jsx64("code", { className: "fd-mono", style: {
7939
8285
  fontSize: 11.5,
7940
8286
  padding: "2px 7px",
7941
8287
  borderRadius: 5,
@@ -7948,15 +8294,15 @@ function PermissionDenied({ permission, title, detail, compact: compact3 = false
7948
8294
  );
7949
8295
  }
7950
8296
  function Gate({ perm, anyOf, role, silent = false, fallback, compact: compact3 = false, children }) {
7951
- SessionKit.useSession();
8297
+ const { can: can2, canAny: canAny2, hasRole: hasRole2 } = useSessionContext();
7952
8298
  let ok = true;
7953
- if (perm) ok = ok && SessionKit.can(perm);
7954
- if (anyOf) ok = ok && SessionKit.canAny(anyOf);
7955
- if (role) ok = ok && (Array.isArray(role) ? role.some(SessionKit.hasRole) : SessionKit.hasRole(role));
8299
+ if (perm) ok = ok && can2(perm);
8300
+ if (anyOf) ok = ok && canAny2(anyOf);
8301
+ if (role) ok = ok && (Array.isArray(role) ? role.some(hasRole2) : hasRole2(role));
7956
8302
  if (ok) return children;
7957
8303
  if (fallback !== void 0) return fallback;
7958
8304
  if (silent) return null;
7959
- return /* @__PURE__ */ jsx62(PermissionDenied, { permission: perm || anyOf, compact: compact3 });
8305
+ return /* @__PURE__ */ jsx64(PermissionDenied, { permission: perm || anyOf, compact: compact3 });
7960
8306
  }
7961
8307
  function FeatureGate({
7962
8308
  flag,
@@ -7971,14 +8317,15 @@ function FeatureGate({
7971
8317
  bypassPerm = "flags.manage"
7972
8318
  }) {
7973
8319
  SessionKit.useSession();
8320
+ const { can: can2 } = useSessionContext();
7974
8321
  const rt = useFeatureStatus(flag);
7975
8322
  if (!SessionKit.isEnabled(flag)) return fallback;
7976
8323
  if (rt.complete || rt.mode === "test") return children;
7977
8324
  if (!preview) return fallback;
7978
8325
  const f = SessionKit.findFlag(flag) || {};
7979
8326
  const missing = rt.missing || [];
7980
- const canBypass = bypassPerm === null ? false : !bypassPerm || SessionKit.can(bypassPerm);
7981
- return /* @__PURE__ */ jsx62(
8327
+ const canBypass = bypassPerm === null ? false : !bypassPerm || can2(bypassPerm);
8328
+ return /* @__PURE__ */ jsx64(
7982
8329
  ComingSoon,
7983
8330
  {
7984
8331
  label: label || (variant === "inline" ? f.label || "Not wired yet" : "Designed \u2014 backend not wired yet"),
@@ -7997,28 +8344,28 @@ function FeatureGate({
7997
8344
  );
7998
8345
  }
7999
8346
  function PermissionHint({ perm, children }) {
8000
- SessionKit.useSession();
8001
- if (SessionKit.can(perm)) return children;
8002
- return /* @__PURE__ */ jsx62(
8347
+ const { can: can2 } = useSessionContext();
8348
+ if (can2(perm)) return children;
8349
+ return /* @__PURE__ */ jsx64(
8003
8350
  "span",
8004
8351
  {
8005
8352
  title: "Requires " + (Array.isArray(perm) ? perm.join(", ") : perm),
8006
8353
  style: { display: "inline-flex", opacity: 0.45, cursor: "not-allowed" },
8007
8354
  "aria-disabled": "true",
8008
- children: /* @__PURE__ */ jsx62("span", { style: { pointerEvents: "none" }, children })
8355
+ children: /* @__PURE__ */ jsx64("span", { style: { pointerEvents: "none" }, children })
8009
8356
  }
8010
8357
  );
8011
8358
  }
8012
8359
 
8013
8360
  // src/components/platform/ModeSwitch.tsx
8014
- import * as React37 from "react";
8015
- import { Fragment as Fragment17, jsx as jsx63, jsxs as jsxs59 } from "react/jsx-runtime";
8361
+ import * as React41 from "react";
8362
+ import { Fragment as Fragment19, jsx as jsx65, jsxs as jsxs61 } from "react/jsx-runtime";
8016
8363
  function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
8017
8364
  const mode2 = useRuntimeMode();
8018
- const [open, setOpen] = React37.useState(false);
8365
+ const [open, setOpen] = React41.useState(false);
8019
8366
  const zIndex = useOverlayLayer(open);
8020
- const ref = React37.useRef(null);
8021
- React37.useEffect(() => {
8367
+ const ref = React41.useRef(null);
8368
+ React41.useEffect(() => {
8022
8369
  if (!open) return;
8023
8370
  const away = (e) => {
8024
8371
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -8040,8 +8387,8 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
8040
8387
  RuntimeKit.setMode(next);
8041
8388
  setOpen(false);
8042
8389
  };
8043
- return /* @__PURE__ */ jsxs59("span", { style: { position: "relative", display: "inline-flex" }, ref, children: [
8044
- /* @__PURE__ */ jsxs59(
8390
+ return /* @__PURE__ */ jsxs61("span", { style: { position: "relative", display: "inline-flex" }, ref, children: [
8391
+ /* @__PURE__ */ jsxs61(
8045
8392
  "button",
8046
8393
  {
8047
8394
  type: "button",
@@ -8051,29 +8398,29 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
8051
8398
  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.",
8052
8399
  className: "fd-mode-btn" + (test ? " is-test" : "") + (open ? " is-open" : ""),
8053
8400
  children: [
8054
- /* @__PURE__ */ jsx63("i", { className: "ph " + (test ? "ph-flask" : "ph-lightning"), style: { fontSize: 17 } }),
8055
- test && requestCount ? /* @__PURE__ */ jsx63("span", { className: "fd-mono fd-mode-count", children: requestCount > 99 ? "99+" : requestCount }, requestCount) : null
8401
+ /* @__PURE__ */ jsx65("i", { className: "ph " + (test ? "ph-flask" : "ph-lightning"), style: { fontSize: 17 } }),
8402
+ test && requestCount ? /* @__PURE__ */ jsx65("span", { className: "fd-mono fd-mode-count", children: requestCount > 99 ? "99+" : requestCount }, requestCount) : null
8056
8403
  ]
8057
8404
  }
8058
8405
  ),
8059
- open ? /* @__PURE__ */ jsx63(LayerProvider, { zIndex, children: /* @__PURE__ */ jsxs59("span", { className: "fd-view-enter fd-mode-pop", style: { zIndex }, children: [
8060
- /* @__PURE__ */ jsxs59("span", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
8061
- /* @__PURE__ */ jsxs59("span", { className: "fd-badge " + (test ? "fd-badge-warning" : "fd-badge-neutral"), children: [
8062
- /* @__PURE__ */ jsx63("i", { className: "ph " + (test ? "ph-flask" : "ph-lightning"), "aria-hidden": "true" }),
8406
+ open ? /* @__PURE__ */ jsx65(LayerProvider, { zIndex, children: /* @__PURE__ */ jsxs61("span", { className: "fd-view-enter fd-mode-pop", style: { zIndex }, children: [
8407
+ /* @__PURE__ */ jsxs61("span", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
8408
+ /* @__PURE__ */ jsxs61("span", { className: "fd-badge " + (test ? "fd-badge-warning" : "fd-badge-neutral"), children: [
8409
+ /* @__PURE__ */ jsx65("i", { className: "ph " + (test ? "ph-flask" : "ph-lightning"), "aria-hidden": "true" }),
8063
8410
  test ? "Test mode" : "Live mode"
8064
8411
  ] }),
8065
- test ? /* @__PURE__ */ jsxs59("span", { className: "fd-body-sm fd-muted fd-mono", children: [
8412
+ test ? /* @__PURE__ */ jsxs61("span", { className: "fd-body-sm fd-muted fd-mono", children: [
8066
8413
  requestCount,
8067
8414
  " simulated request",
8068
8415
  requestCount === 1 ? "" : "s"
8069
8416
  ] }) : null,
8070
- /* @__PURE__ */ jsx63("span", { style: { flex: 1 } }),
8071
- test && onClearLog ? /* @__PURE__ */ jsx63("button", { type: "button", className: "fd-soon-link", onClick: onClearLog, children: "Clear" }) : null
8417
+ /* @__PURE__ */ jsx65("span", { style: { flex: 1 } }),
8418
+ test && onClearLog ? /* @__PURE__ */ jsx65("button", { type: "button", className: "fd-soon-link", onClick: onClearLog, children: "Clear" }) : null
8072
8419
  ] }),
8073
- /* @__PURE__ */ jsxs59("span", { style: { display: "block", padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
8074
- /* @__PURE__ */ jsx63("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." }),
8075
- /* @__PURE__ */ jsxs59("span", { className: "fd-row", style: { gap: 8, marginTop: 10, flexWrap: "wrap" }, children: [
8076
- /* @__PURE__ */ jsxs59("span", { className: "fd-body-sm fd-muted fd-mono", children: [
8420
+ /* @__PURE__ */ jsxs61("span", { style: { display: "block", padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
8421
+ /* @__PURE__ */ jsx65("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." }),
8422
+ /* @__PURE__ */ jsxs61("span", { className: "fd-row", style: { gap: 8, marginTop: 10, flexWrap: "wrap" }, children: [
8423
+ /* @__PURE__ */ jsxs61("span", { className: "fd-body-sm fd-muted fd-mono", children: [
8077
8424
  s.endpointsWired,
8078
8425
  " endpoint",
8079
8426
  s.endpointsWired === 1 ? "" : "s",
@@ -8083,58 +8430,58 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
8083
8430
  s.total,
8084
8431
  " features complete"
8085
8432
  ] }),
8086
- onOpenSpec ? /* @__PURE__ */ jsxs59(Fragment17, { children: [
8087
- /* @__PURE__ */ jsx63("span", { style: { flex: 1 } }),
8088
- /* @__PURE__ */ jsxs59("button", { type: "button", className: "fd-soon-link", onClick: () => {
8433
+ onOpenSpec ? /* @__PURE__ */ jsxs61(Fragment19, { children: [
8434
+ /* @__PURE__ */ jsx65("span", { style: { flex: 1 } }),
8435
+ /* @__PURE__ */ jsxs61("button", { type: "button", className: "fd-soon-link", onClick: () => {
8089
8436
  setOpen(false);
8090
8437
  onOpenSpec();
8091
8438
  }, children: [
8092
8439
  "API spec ",
8093
- /* @__PURE__ */ jsx63("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
8440
+ /* @__PURE__ */ jsx65("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
8094
8441
  ] })
8095
8442
  ] }) : null
8096
8443
  ] })
8097
8444
  ] }),
8098
- /* @__PURE__ */ jsxs59("span", { className: "fd-mode-choice", children: [
8099
- /* @__PURE__ */ jsxs59("button", { type: "button", className: "fd-mode-opt" + (!test ? " is-on" : ""), onClick: () => go("live"), children: [
8100
- /* @__PURE__ */ jsx63("i", { className: "ph ph-lightning", "aria-hidden": "true" }),
8101
- /* @__PURE__ */ jsxs59("span", { className: "fd-mode-opt-text", children: [
8102
- /* @__PURE__ */ jsx63("span", { className: "fd-mode-opt-title", children: "Live mode" }),
8103
- /* @__PURE__ */ jsx63("span", { className: "fd-mode-opt-desc", children: "Incomplete features under construction" })
8445
+ /* @__PURE__ */ jsxs61("span", { className: "fd-mode-choice", children: [
8446
+ /* @__PURE__ */ jsxs61("button", { type: "button", className: "fd-mode-opt" + (!test ? " is-on" : ""), onClick: () => go("live"), children: [
8447
+ /* @__PURE__ */ jsx65("i", { className: "ph ph-lightning", "aria-hidden": "true" }),
8448
+ /* @__PURE__ */ jsxs61("span", { className: "fd-mode-opt-text", children: [
8449
+ /* @__PURE__ */ jsx65("span", { className: "fd-mode-opt-title", children: "Live mode" }),
8450
+ /* @__PURE__ */ jsx65("span", { className: "fd-mode-opt-desc", children: "Incomplete features under construction" })
8104
8451
  ] }),
8105
- !test ? /* @__PURE__ */ jsx63("i", { className: "ph ph-check", "aria-hidden": "true" }) : null
8452
+ !test ? /* @__PURE__ */ jsx65("i", { className: "ph ph-check", "aria-hidden": "true" }) : null
8106
8453
  ] }),
8107
- /* @__PURE__ */ jsxs59("button", { type: "button", className: "fd-mode-opt" + (test ? " is-on" : ""), onClick: () => go("test"), children: [
8108
- /* @__PURE__ */ jsx63("i", { className: "ph ph-flask", "aria-hidden": "true" }),
8109
- /* @__PURE__ */ jsxs59("span", { className: "fd-mode-opt-text", children: [
8110
- /* @__PURE__ */ jsx63("span", { className: "fd-mode-opt-title", children: "Test mode" }),
8111
- /* @__PURE__ */ jsx63("span", { className: "fd-mode-opt-desc", children: "Everything usable, all data simulated" })
8454
+ /* @__PURE__ */ jsxs61("button", { type: "button", className: "fd-mode-opt" + (test ? " is-on" : ""), onClick: () => go("test"), children: [
8455
+ /* @__PURE__ */ jsx65("i", { className: "ph ph-flask", "aria-hidden": "true" }),
8456
+ /* @__PURE__ */ jsxs61("span", { className: "fd-mode-opt-text", children: [
8457
+ /* @__PURE__ */ jsx65("span", { className: "fd-mode-opt-title", children: "Test mode" }),
8458
+ /* @__PURE__ */ jsx65("span", { className: "fd-mode-opt-desc", children: "Everything usable, all data simulated" })
8112
8459
  ] }),
8113
- test ? /* @__PURE__ */ jsx63("i", { className: "ph ph-check", "aria-hidden": "true" }) : null
8460
+ test ? /* @__PURE__ */ jsx65("i", { className: "ph ph-check", "aria-hidden": "true" }) : null
8114
8461
  ] })
8115
8462
  ] }),
8116
- test && renderLog ? /* @__PURE__ */ jsx63("span", { style: { display: "block", maxHeight: 340, overflowY: "auto", borderTop: "1px solid var(--border)" }, children: renderLog() }) : null
8463
+ test && renderLog ? /* @__PURE__ */ jsx65("span", { style: { display: "block", maxHeight: 340, overflowY: "auto", borderTop: "1px solid var(--border)" }, children: renderLog() }) : null
8117
8464
  ] }) }) : null
8118
8465
  ] });
8119
8466
  }
8120
8467
  function TestModeBar({ onExit }) {
8121
8468
  const mode2 = useRuntimeMode();
8122
8469
  if (mode2 !== "test") return null;
8123
- return /* @__PURE__ */ jsxs59("div", { className: "fd-testbar", role: "status", children: [
8124
- /* @__PURE__ */ jsxs59("span", { className: "fd-badge fd-badge-warning", children: [
8125
- /* @__PURE__ */ jsx63("i", { className: "ph ph-flask", "aria-hidden": "true" }),
8470
+ return /* @__PURE__ */ jsxs61("div", { className: "fd-testbar", role: "status", children: [
8471
+ /* @__PURE__ */ jsxs61("span", { className: "fd-badge fd-badge-warning", children: [
8472
+ /* @__PURE__ */ jsx65("i", { className: "ph ph-flask", "aria-hidden": "true" }),
8126
8473
  "Test mode"
8127
8474
  ] }),
8128
- /* @__PURE__ */ jsx63("span", { className: "fd-testbar-detail", children: "Every feature is unlocked and every response is simulated \u2014 nothing here persists." }),
8129
- /* @__PURE__ */ jsxs59("button", { type: "button", className: "fd-soon-link", onClick: () => onExit ? onExit() : RuntimeKit.setMode("live"), children: [
8130
- /* @__PURE__ */ jsx63("i", { className: "ph ph-lightning", "aria-hidden": "true" }),
8475
+ /* @__PURE__ */ jsx65("span", { className: "fd-testbar-detail", children: "Every feature is unlocked and every response is simulated \u2014 nothing here persists." }),
8476
+ /* @__PURE__ */ jsxs61("button", { type: "button", className: "fd-soon-link", onClick: () => onExit ? onExit() : RuntimeKit.setMode("live"), children: [
8477
+ /* @__PURE__ */ jsx65("i", { className: "ph ph-lightning", "aria-hidden": "true" }),
8131
8478
  " Back to live mode"
8132
8479
  ] })
8133
8480
  ] });
8134
8481
  }
8135
8482
 
8136
8483
  // src/components/planner/ChannelMeta.tsx
8137
- import { jsx as jsx64, jsxs as jsxs60 } from "react/jsx-runtime";
8484
+ import { jsx as jsx66, jsxs as jsxs62 } from "react/jsx-runtime";
8138
8485
  var CHANNEL_WEIGHTS = {
8139
8486
  "OOH": 50,
8140
8487
  "DOOH": 50,
@@ -8187,9 +8534,9 @@ function ChannelTag({ channel, size = "md", showLabel = true, dot = false, weigh
8187
8534
  const m = ChannelMeta(channel);
8188
8535
  const wt = channelWeightOf(channel || "");
8189
8536
  if (dot) {
8190
- return /* @__PURE__ */ jsx64("span", { className: ["fd-chan-dot", className].filter(Boolean).join(" "), title: m.name, style: { background: m.color }, ...rest });
8537
+ return /* @__PURE__ */ jsx66("span", { className: ["fd-chan-dot", className].filter(Boolean).join(" "), title: m.name, style: { background: m.color }, ...rest });
8191
8538
  }
8192
- return /* @__PURE__ */ jsxs60(
8539
+ return /* @__PURE__ */ jsxs62(
8193
8540
  "span",
8194
8541
  {
8195
8542
  className: ["fd-chan", size === "sm" ? "fd-chan-sm" : "", className].filter(Boolean).join(" "),
@@ -8197,16 +8544,16 @@ function ChannelTag({ channel, size = "md", showLabel = true, dot = false, weigh
8197
8544
  style: { "--chan": m.color },
8198
8545
  ...rest,
8199
8546
  children: [
8200
- /* @__PURE__ */ jsx64("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }),
8201
- showLabel ? /* @__PURE__ */ jsx64("span", { className: "fd-chan-label", children: m.label }) : null,
8202
- weight ? /* @__PURE__ */ jsx64("span", { className: "fd-chan-weight", children: wt || 0 }) : null
8547
+ /* @__PURE__ */ jsx66("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }),
8548
+ showLabel ? /* @__PURE__ */ jsx66("span", { className: "fd-chan-label", children: m.label }) : null,
8549
+ weight ? /* @__PURE__ */ jsx66("span", { className: "fd-chan-weight", children: wt || 0 }) : null
8203
8550
  ]
8204
8551
  }
8205
8552
  );
8206
8553
  }
8207
8554
 
8208
8555
  // src/components/planner/SaturationDistribution.tsx
8209
- import { jsx as jsx65, jsxs as jsxs61 } from "react/jsx-runtime";
8556
+ import { jsx as jsx67, jsxs as jsxs63 } from "react/jsx-runtime";
8210
8557
  var BANDS2 = [
8211
8558
  { key: "weak", label: "Weak", n: 1, range: "< 50" },
8212
8559
  { key: "adequate", label: "Adequate", n: 2, range: "50\u2013100" },
@@ -8217,24 +8564,24 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8217
8564
  const grouped = BANDS2.map((b) => ({ ...b, items: campuses.filter((c) => c.band === b.key) }));
8218
8565
  const tallest = Math.max(1, ...grouped.map((g) => g.items.length));
8219
8566
  const floor = BANDS2.find((b) => b.key === floorBand) || BANDS2[0];
8220
- return /* @__PURE__ */ jsxs61("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 16 }, children: [
8221
- /* @__PURE__ */ jsx65("div", { style: { display: "grid", gridTemplateColumns: "repeat(4,minmax(0,1fr))", gap: 12, alignItems: "end" }, children: grouped.map((g) => {
8567
+ return /* @__PURE__ */ jsxs63("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 16 }, children: [
8568
+ /* @__PURE__ */ jsx67("div", { style: { display: "grid", gridTemplateColumns: "repeat(4,minmax(0,1fr))", gap: 12, alignItems: "end" }, children: grouped.map((g) => {
8222
8569
  const mark = "var(--csi-" + g.n + "-mark)";
8223
8570
  const active = selectedBand === g.key;
8224
- return /* @__PURE__ */ jsxs61(
8571
+ return /* @__PURE__ */ jsxs63(
8225
8572
  "button",
8226
8573
  {
8227
8574
  type: "button",
8228
8575
  onClick: onSelectBand ? () => onSelectBand(active ? null : g.key) : void 0,
8229
8576
  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)" },
8230
8577
  children: [
8231
- /* @__PURE__ */ jsx65("span", { style: { display: "flex", flexDirection: "column-reverse", gap: 4, minHeight: tallest * 24, minWidth: 0 }, children: loading ? Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsx65("span", { className: "fd-skel", style: { height: 16, borderRadius: 4 } }, i)) : g.items.map((c) => (
8578
+ /* @__PURE__ */ jsx67("span", { style: { display: "flex", flexDirection: "column-reverse", gap: 4, minHeight: tallest * 24, minWidth: 0 }, children: loading ? Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsx67("span", { className: "fd-skel", style: { height: 16, borderRadius: 4 } }, i)) : g.items.map((c) => (
8232
8579
  // `overflow: hidden` + `white-space: nowrap` only clips once the span
8233
8580
  // actually HAS a bounded width to clip against (see the grid/flex
8234
8581
  // `minWidth: 0` chain above) — `textOverflow: ellipsis` then makes
8235
8582
  // that clip readable instead of a hard cut mid-letter; the full name
8236
8583
  // is still one hover away via `title`.
8237
- /* @__PURE__ */ jsx65(
8584
+ /* @__PURE__ */ jsx67(
8238
8585
  "span",
8239
8586
  {
8240
8587
  title: c.name + " \xB7 " + c.crp,
@@ -8244,13 +8591,13 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8244
8591
  c.name
8245
8592
  )
8246
8593
  )) }),
8247
- /* @__PURE__ */ jsxs61("span", { className: "fd-row", style: { gap: 8, alignItems: "baseline" }, children: [
8248
- /* @__PURE__ */ jsx65("span", { className: "fd-num-hero", style: { fontSize: 30, color: g.items.length ? "var(--text)" : "var(--text-disabled)" }, children: loading ? "\u2013" : g.items.length }),
8249
- /* @__PURE__ */ jsx65("span", { className: "fd-meter", style: { gap: 2, color: mark }, "aria-hidden": "true", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx65("span", { className: "fd-meter-seg" + (i <= g.n ? " is-on" : ""), style: { width: 4, height: 9 } }, i)) })
8594
+ /* @__PURE__ */ jsxs63("span", { className: "fd-row", style: { gap: 8, alignItems: "baseline" }, children: [
8595
+ /* @__PURE__ */ jsx67("span", { className: "fd-num-hero", style: { fontSize: 30, color: g.items.length ? "var(--text)" : "var(--text-disabled)" }, children: loading ? "\u2013" : g.items.length }),
8596
+ /* @__PURE__ */ jsx67("span", { className: "fd-meter", style: { gap: 2, color: mark }, "aria-hidden": "true", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx67("span", { className: "fd-meter-seg" + (i <= g.n ? " is-on" : ""), style: { width: 4, height: 9 } }, i)) })
8250
8597
  ] }),
8251
- /* @__PURE__ */ jsxs61("span", { className: "fd-stack", style: { gap: 1 }, children: [
8252
- /* @__PURE__ */ jsx65("span", { className: "fd-label-lg", children: g.label }),
8253
- /* @__PURE__ */ jsxs61("span", { className: "fd-body-sm fd-muted", style: { fontSize: 11.5 }, children: [
8598
+ /* @__PURE__ */ jsxs63("span", { className: "fd-stack", style: { gap: 1 }, children: [
8599
+ /* @__PURE__ */ jsx67("span", { className: "fd-label-lg", children: g.label }),
8600
+ /* @__PURE__ */ jsxs63("span", { className: "fd-body-sm fd-muted", style: { fontSize: 11.5 }, children: [
8254
8601
  "CRP ",
8255
8602
  g.range
8256
8603
  ] })
@@ -8260,10 +8607,10 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8260
8607
  g.key
8261
8608
  );
8262
8609
  }) }),
8263
- floorBand ? /* @__PURE__ */ jsxs61("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: [
8264
- /* @__PURE__ */ jsx65("span", { className: "fd-meter", style: { gap: 2, color: "var(--csi-" + floor.n + "-mark)" }, "aria-hidden": "true", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx65("span", { className: "fd-meter-seg" + (i <= floor.n ? " is-on" : ""), style: { width: 5, height: 12 } }, i)) }),
8265
- /* @__PURE__ */ jsxs61("span", { className: "fd-body-sm fd-secondary", style: { flex: 1, minWidth: 240 }, children: [
8266
- /* @__PURE__ */ jsxs61("strong", { style: { color: "var(--text)" }, children: [
8610
+ floorBand ? /* @__PURE__ */ jsxs63("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: [
8611
+ /* @__PURE__ */ jsx67("span", { className: "fd-meter", style: { gap: 2, color: "var(--csi-" + floor.n + "-mark)" }, "aria-hidden": "true", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx67("span", { className: "fd-meter-seg" + (i <= floor.n ? " is-on" : ""), style: { width: 5, height: 12 } }, i)) }),
8612
+ /* @__PURE__ */ jsxs63("span", { className: "fd-body-sm fd-secondary", style: { flex: 1, minWidth: 240 }, children: [
8613
+ /* @__PURE__ */ jsxs63("strong", { style: { color: "var(--text)" }, children: [
8267
8614
  "Plan floor ",
8268
8615
  floorScore
8269
8616
  ] }),
@@ -8274,8 +8621,8 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8274
8621
  }
8275
8622
 
8276
8623
  // src/components/planner/MixGap.tsx
8277
- import * as React38 from "react";
8278
- import { Fragment as Fragment18, jsx as jsx66, jsxs as jsxs62 } from "react/jsx-runtime";
8624
+ import * as React42 from "react";
8625
+ import { Fragment as Fragment20, jsx as jsx68, jsxs as jsxs64 } from "react/jsx-runtime";
8279
8626
  var MIX_GAP_LAYOUT = {
8280
8627
  trackHeight: 30,
8281
8628
  barHeight: 20,
@@ -8289,19 +8636,19 @@ function mixGapOffset(value, max) {
8289
8636
  }
8290
8637
  function MixGap({ rows = [], loading = false, className = "" }) {
8291
8638
  const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
8292
- const [hover, setHover] = React38.useState(null);
8639
+ const [hover, setHover] = React42.useState(null);
8293
8640
  const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
8294
8641
  const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
8295
8642
  const { trackHeight, barHeight, targetWidth, labelGap } = MIX_GAP_LAYOUT;
8296
8643
  const barTop = (trackHeight - barHeight) / 2;
8297
- return /* @__PURE__ */ jsxs62("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 4 }, children: [
8298
- /* @__PURE__ */ jsxs62("div", { className: "fd-row", style: { gap: 16, justifyContent: "flex-end", paddingBottom: 6, flexWrap: "wrap" }, children: [
8299
- [["On target", TONE.ok], ["Close", TONE.warn], ["Short", TONE.danger]].map(([l, c]) => /* @__PURE__ */ jsxs62("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
8300
- /* @__PURE__ */ jsx66("span", { style: { width: 14, height: 9, borderRadius: 2, background: c } }),
8644
+ return /* @__PURE__ */ jsxs64("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 4 }, children: [
8645
+ /* @__PURE__ */ jsxs64("div", { className: "fd-row", style: { gap: 16, justifyContent: "flex-end", paddingBottom: 6, flexWrap: "wrap" }, children: [
8646
+ [["On target", TONE.ok], ["Close", TONE.warn], ["Short", TONE.danger]].map(([l, c]) => /* @__PURE__ */ jsxs64("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
8647
+ /* @__PURE__ */ jsx68("span", { style: { width: 14, height: 9, borderRadius: 2, background: c } }),
8301
8648
  l
8302
8649
  ] }, l)),
8303
- /* @__PURE__ */ jsxs62("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
8304
- /* @__PURE__ */ jsx66("span", { "data-mixgap-legend-target": "", style: { width: targetWidth, height: 16, borderRadius: 1, background: MIX_GAP_TARGET_STRIPE } }),
8650
+ /* @__PURE__ */ jsxs64("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
8651
+ /* @__PURE__ */ jsx68("span", { "data-mixgap-legend-target": "", style: { width: targetWidth, height: 16, borderRadius: 1, background: MIX_GAP_TARGET_STRIPE } }),
8305
8652
  "Target"
8306
8653
  ] })
8307
8654
  ] }),
@@ -8310,7 +8657,7 @@ function MixGap({ rows = [], loading = false, className = "" }) {
8310
8657
  const tone = toneOf(gap);
8311
8658
  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 : "");
8312
8659
  const labelLeft = r.realized > r.target ? `calc(${mixGapOffset(r.realized, max)} + ${labelGap}px)` : `calc(${mixGapOffset(r.target, max)} + ${targetWidth / 2 + labelGap}px)`;
8313
- return /* @__PURE__ */ jsxs62(
8660
+ return /* @__PURE__ */ jsxs64(
8314
8661
  "div",
8315
8662
  {
8316
8663
  className: "fd-row",
@@ -8318,15 +8665,15 @@ function MixGap({ rows = [], loading = false, className = "" }) {
8318
8665
  onMouseEnter: () => setHover(r.channel),
8319
8666
  onMouseLeave: () => setHover(null),
8320
8667
  children: [
8321
- /* @__PURE__ */ jsx66("span", { style: { width: 128, flex: "none" }, children: /* @__PURE__ */ jsx66(ChannelTag, { channel: r.channel, size: "sm" }) }),
8322
- /* @__PURE__ */ jsx66("span", { style: { position: "relative", flex: 1, height: trackHeight, minWidth: 120 }, children: loading ? /* @__PURE__ */ jsx66("span", { className: "fd-skel", style: { position: "absolute", left: 0, right: 0, top: barTop, height: barHeight, borderRadius: 3 } }) : /* @__PURE__ */ jsxs62(Fragment18, { children: [
8323
- /* @__PURE__ */ jsx66("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)" } }),
8324
- /* @__PURE__ */ jsx66("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 } }),
8325
- /* @__PURE__ */ jsxs62("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: [
8668
+ /* @__PURE__ */ jsx68("span", { style: { width: 128, flex: "none" }, children: /* @__PURE__ */ jsx68(ChannelTag, { channel: r.channel, size: "sm" }) }),
8669
+ /* @__PURE__ */ jsx68("span", { style: { position: "relative", flex: 1, height: trackHeight, minWidth: 120 }, children: loading ? /* @__PURE__ */ jsx68("span", { className: "fd-skel", style: { position: "absolute", left: 0, right: 0, top: barTop, height: barHeight, borderRadius: 3 } }) : /* @__PURE__ */ jsxs64(Fragment20, { children: [
8670
+ /* @__PURE__ */ jsx68("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)" } }),
8671
+ /* @__PURE__ */ jsx68("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 } }),
8672
+ /* @__PURE__ */ jsxs64("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: [
8326
8673
  r.realized,
8327
8674
  "%"
8328
8675
  ] }),
8329
- hover === r.channel ? /* @__PURE__ */ jsx66("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
8676
+ hover === r.channel ? /* @__PURE__ */ jsx68("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
8330
8677
  ] }) })
8331
8678
  ]
8332
8679
  },
@@ -8337,7 +8684,7 @@ function MixGap({ rows = [], loading = false, className = "" }) {
8337
8684
  }
8338
8685
 
8339
8686
  // src/components/planner/ChannelContribution.tsx
8340
- import { jsx as jsx67, jsxs as jsxs63 } from "react/jsx-runtime";
8687
+ import { jsx as jsx69, jsxs as jsxs65 } from "react/jsx-runtime";
8341
8688
  var money = (n) => "$" + Math.round(n).toLocaleString();
8342
8689
  function ChannelContribution({
8343
8690
  channels = [],
@@ -8353,9 +8700,9 @@ function ChannelContribution({
8353
8700
  const grand = total !== void 0 ? total : base + bonus;
8354
8701
  const pct = (v) => grand ? v / grand * 100 : 0;
8355
8702
  const spendTotal = channels.reduce((s, c) => s + Number(String(c.spend || 0).replace(/[^0-9.]/g, "")), 0);
8356
- return /* @__PURE__ */ jsxs63("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 16 }, children: [
8357
- loading ? /* @__PURE__ */ jsx67("span", { className: "fd-skel", style: { height: 34, borderRadius: "var(--r-sm)" } }) : /* @__PURE__ */ jsxs63("div", { className: "fd-row", style: { height: 34, borderRadius: "var(--r-sm)", overflow: "hidden", gap: 2, background: "var(--surface-3)" }, children: [
8358
- channels.map((c) => /* @__PURE__ */ jsx67(
8703
+ return /* @__PURE__ */ jsxs65("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 16 }, children: [
8704
+ loading ? /* @__PURE__ */ jsx69("span", { className: "fd-skel", style: { height: 34, borderRadius: "var(--r-sm)" } }) : /* @__PURE__ */ jsxs65("div", { className: "fd-row", style: { height: 34, borderRadius: "var(--r-sm)", overflow: "hidden", gap: 2, background: "var(--surface-3)" }, children: [
8705
+ channels.map((c) => /* @__PURE__ */ jsx69(
8359
8706
  "span",
8360
8707
  {
8361
8708
  title: c.name + " \xB7 " + c.crp.toFixed(1) + " CRP",
@@ -8364,7 +8711,7 @@ function ChannelContribution({
8364
8711
  },
8365
8712
  c.name
8366
8713
  )),
8367
- bonus > 0 ? /* @__PURE__ */ jsx67(
8714
+ bonus > 0 ? /* @__PURE__ */ jsx69(
8368
8715
  "span",
8369
8716
  {
8370
8717
  title: "Surround-sound bonus +" + bonusPct + "%",
@@ -8373,18 +8720,18 @@ function ChannelContribution({
8373
8720
  }
8374
8721
  ) : null
8375
8722
  ] }),
8376
- showTable ? /* @__PURE__ */ jsxs63("table", { className: "fd-table", style: { fontSize: "var(--body-sm-size)" }, children: [
8377
- /* @__PURE__ */ jsx67("thead", { children: /* @__PURE__ */ jsxs63("tr", { children: [
8378
- /* @__PURE__ */ jsx67("th", { style: { width: "34%" }, children: "Channel" }),
8379
- /* @__PURE__ */ jsx67("th", { style: { width: "16%" }, children: "Weight" }),
8380
- /* @__PURE__ */ jsx67("th", { className: "is-num", style: { width: "16%" }, children: "Spend" }),
8381
- /* @__PURE__ */ jsx67("th", { className: "is-num", style: { width: "17%" }, children: "CRP" }),
8382
- /* @__PURE__ */ jsx67("th", { className: "is-num", style: { width: "17%" }, children: "Share" })
8723
+ showTable ? /* @__PURE__ */ jsxs65("table", { className: "fd-table", style: { fontSize: "var(--body-sm-size)" }, children: [
8724
+ /* @__PURE__ */ jsx69("thead", { children: /* @__PURE__ */ jsxs65("tr", { children: [
8725
+ /* @__PURE__ */ jsx69("th", { style: { width: "34%" }, children: "Channel" }),
8726
+ /* @__PURE__ */ jsx69("th", { style: { width: "16%" }, children: "Weight" }),
8727
+ /* @__PURE__ */ jsx69("th", { className: "is-num", style: { width: "16%" }, children: "Spend" }),
8728
+ /* @__PURE__ */ jsx69("th", { className: "is-num", style: { width: "17%" }, children: "CRP" }),
8729
+ /* @__PURE__ */ jsx69("th", { className: "is-num", style: { width: "17%" }, children: "Share" })
8383
8730
  ] }) }),
8384
- /* @__PURE__ */ jsxs63("tbody", { children: [
8385
- channels.map((c) => /* @__PURE__ */ jsxs63("tr", { children: [
8386
- /* @__PURE__ */ jsx67("td", { children: /* @__PURE__ */ jsx67(ChannelTag, { channel: c.name, size: "sm" }) }),
8387
- /* @__PURE__ */ jsx67("td", { children: /* @__PURE__ */ jsx67(
8731
+ /* @__PURE__ */ jsxs65("tbody", { children: [
8732
+ channels.map((c) => /* @__PURE__ */ jsxs65("tr", { children: [
8733
+ /* @__PURE__ */ jsx69("td", { children: /* @__PURE__ */ jsx69(ChannelTag, { channel: c.name, size: "sm" }) }),
8734
+ /* @__PURE__ */ jsx69("td", { children: /* @__PURE__ */ jsx69(
8388
8735
  "span",
8389
8736
  {
8390
8737
  className: "fd-badge fd-badge-neutral",
@@ -8393,38 +8740,38 @@ function ChannelContribution({
8393
8740
  children: "weight " + (channelWeightOf(c.name) || 0)
8394
8741
  }
8395
8742
  ) }),
8396
- /* @__PURE__ */ jsx67("td", { className: "is-num", children: c.spend }),
8397
- /* @__PURE__ */ jsx67("td", { className: "is-num", children: c.crp.toFixed(1) }),
8398
- /* @__PURE__ */ jsxs63("td", { className: "is-num", children: [
8743
+ /* @__PURE__ */ jsx69("td", { className: "is-num", children: c.spend }),
8744
+ /* @__PURE__ */ jsx69("td", { className: "is-num", children: c.crp.toFixed(1) }),
8745
+ /* @__PURE__ */ jsxs65("td", { className: "is-num", children: [
8399
8746
  pct(c.crp).toFixed(0),
8400
8747
  "%"
8401
8748
  ] })
8402
8749
  ] }, c.name)),
8403
- bonus > 0 ? /* @__PURE__ */ jsxs63("tr", { children: [
8404
- /* @__PURE__ */ jsx67("td", { children: /* @__PURE__ */ jsxs63("span", { className: "fd-row", style: { gap: 9, fontWeight: 600 }, children: [
8405
- /* @__PURE__ */ jsx67("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" } }),
8750
+ bonus > 0 ? /* @__PURE__ */ jsxs65("tr", { children: [
8751
+ /* @__PURE__ */ jsx69("td", { children: /* @__PURE__ */ jsxs65("span", { className: "fd-row", style: { gap: 9, fontWeight: 600 }, children: [
8752
+ /* @__PURE__ */ jsx69("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" } }),
8406
8753
  "Surround-sound bonus"
8407
8754
  ] }) }),
8408
- /* @__PURE__ */ jsx67("td", { children: /* @__PURE__ */ jsxs63("span", { className: "fd-badge fd-badge-success", style: { height: 20, fontSize: 11 }, children: [
8755
+ /* @__PURE__ */ jsx69("td", { children: /* @__PURE__ */ jsxs65("span", { className: "fd-badge fd-badge-success", style: { height: 20, fontSize: 11 }, children: [
8409
8756
  "+",
8410
8757
  bonusPct,
8411
8758
  "% of +",
8412
8759
  bonusMax,
8413
8760
  "%"
8414
8761
  ] }) }),
8415
- /* @__PURE__ */ jsx67("td", { className: "is-num fd-muted", children: "\u2014" }),
8416
- /* @__PURE__ */ jsx67("td", { className: "is-num", children: bonus.toFixed(1) }),
8417
- /* @__PURE__ */ jsxs63("td", { className: "is-num", children: [
8762
+ /* @__PURE__ */ jsx69("td", { className: "is-num fd-muted", children: "\u2014" }),
8763
+ /* @__PURE__ */ jsx69("td", { className: "is-num", children: bonus.toFixed(1) }),
8764
+ /* @__PURE__ */ jsxs65("td", { className: "is-num", children: [
8418
8765
  pct(bonus).toFixed(0),
8419
8766
  "%"
8420
8767
  ] })
8421
8768
  ] }) : null,
8422
- /* @__PURE__ */ jsxs63("tr", { style: { background: "var(--surface-2)" }, children: [
8423
- /* @__PURE__ */ jsx67("td", { style: { fontWeight: 700 }, children: "Campus total" }),
8424
- /* @__PURE__ */ jsx67("td", {}),
8425
- /* @__PURE__ */ jsx67("td", { className: "is-num", style: { fontWeight: 700 }, children: money(spendTotal) }),
8426
- /* @__PURE__ */ jsx67("td", { className: "is-num", style: { fontWeight: 700 }, children: grand.toFixed(1) }),
8427
- /* @__PURE__ */ jsx67("td", { className: "is-num", style: { fontWeight: 700 }, children: "100%" })
8769
+ /* @__PURE__ */ jsxs65("tr", { style: { background: "var(--surface-2)" }, children: [
8770
+ /* @__PURE__ */ jsx69("td", { style: { fontWeight: 700 }, children: "Campus total" }),
8771
+ /* @__PURE__ */ jsx69("td", {}),
8772
+ /* @__PURE__ */ jsx69("td", { className: "is-num", style: { fontWeight: 700 }, children: money(spendTotal) }),
8773
+ /* @__PURE__ */ jsx69("td", { className: "is-num", style: { fontWeight: 700 }, children: grand.toFixed(1) }),
8774
+ /* @__PURE__ */ jsx69("td", { className: "is-num", style: { fontWeight: 700 }, children: "100%" })
8428
8775
  ] })
8429
8776
  ] })
8430
8777
  ] }) : null
@@ -8432,8 +8779,8 @@ function ChannelContribution({
8432
8779
  }
8433
8780
 
8434
8781
  // src/components/planner/BudgetReallocator.tsx
8435
- import * as React39 from "react";
8436
- import { Fragment as Fragment19, jsx as jsx68, jsxs as jsxs64 } from "react/jsx-runtime";
8782
+ import * as React43 from "react";
8783
+ import { Fragment as Fragment21, jsx as jsx70, jsxs as jsxs66 } from "react/jsx-runtime";
8437
8784
  var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
8438
8785
  function BudgetReallocator({
8439
8786
  campus,
@@ -8448,8 +8795,8 @@ function BudgetReallocator({
8448
8795
  onCancel,
8449
8796
  className = ""
8450
8797
  }) {
8451
- const [draft, setDraft] = React39.useState(spend);
8452
- React39.useEffect(() => setDraft(spend), [spend]);
8798
+ const [draft, setDraft] = React43.useState(spend);
8799
+ React43.useEffect(() => setDraft(spend), [spend]);
8453
8800
  const dirty = draft !== spend;
8454
8801
  const nextCrp = scoreFor ? scoreFor(draft) : crp;
8455
8802
  const nextBand = bandFor(nextCrp);
@@ -8464,10 +8811,10 @@ function BudgetReallocator({
8464
8811
  setDraft(spend);
8465
8812
  if (onCancel) onCancel();
8466
8813
  };
8467
- const trackRef = React39.useRef(null);
8468
- const chipRef = React39.useRef(null);
8469
- const [chipLeft, setChipLeft] = React39.useState(null);
8470
- React39.useLayoutEffect(() => {
8814
+ const trackRef = React43.useRef(null);
8815
+ const chipRef = React43.useRef(null);
8816
+ const [chipLeft, setChipLeft] = React43.useState(null);
8817
+ React43.useLayoutEffect(() => {
8471
8818
  const track = trackRef.current;
8472
8819
  if (!dirty || !track) {
8473
8820
  setChipLeft(null);
@@ -8488,23 +8835,23 @@ function BudgetReallocator({
8488
8835
  ro.observe(track);
8489
8836
  return () => ro.disconnect();
8490
8837
  }, [dirty, pct]);
8491
- return /* @__PURE__ */ jsxs64(
8838
+ return /* @__PURE__ */ jsxs66(
8492
8839
  "div",
8493
8840
  {
8494
8841
  className: ["fd-stack", className].filter(Boolean).join(" "),
8495
8842
  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)" },
8496
8843
  children: [
8497
- /* @__PURE__ */ jsxs64("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
8498
- /* @__PURE__ */ jsx68("span", { className: "fd-h4", style: { flex: 1, minWidth: 140 }, children: campus }),
8499
- /* @__PURE__ */ jsx68(CsiBadge, { band: nowBand, crp, size: "medium" }),
8500
- dirty ? /* @__PURE__ */ jsxs64(Fragment19, { children: [
8501
- /* @__PURE__ */ jsx68("i", { className: "ph ph-arrow-right fd-muted", "aria-hidden": "true" }),
8502
- /* @__PURE__ */ jsx68(CsiBadge, { band: nextBand, crp: nextCrp, size: "medium", preview: true })
8844
+ /* @__PURE__ */ jsxs66("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
8845
+ /* @__PURE__ */ jsx70("span", { className: "fd-h4", style: { flex: 1, minWidth: 140 }, children: campus }),
8846
+ /* @__PURE__ */ jsx70(CsiBadge, { band: nowBand, crp, size: "medium" }),
8847
+ dirty ? /* @__PURE__ */ jsxs66(Fragment21, { children: [
8848
+ /* @__PURE__ */ jsx70("i", { className: "ph ph-arrow-right fd-muted", "aria-hidden": "true" }),
8849
+ /* @__PURE__ */ jsx70(CsiBadge, { band: nextBand, crp: nextCrp, size: "medium", preview: true })
8503
8850
  ] }) : null
8504
8851
  ] }),
8505
- /* @__PURE__ */ jsxs64("div", { className: "fd-slider", ref: trackRef, children: [
8506
- /* @__PURE__ */ jsx68("span", { className: "fd-slider-fill", style: { width: pct + "%" } }),
8507
- dirty ? /* @__PURE__ */ jsx68(
8852
+ /* @__PURE__ */ jsxs66("div", { className: "fd-slider", ref: trackRef, children: [
8853
+ /* @__PURE__ */ jsx70("span", { className: "fd-slider-fill", style: { width: pct + "%" } }),
8854
+ dirty ? /* @__PURE__ */ jsx70(
8508
8855
  "span",
8509
8856
  {
8510
8857
  ref: chipRef,
@@ -8516,7 +8863,7 @@ function BudgetReallocator({
8516
8863
  children: (delta < 0 ? "\u2212$" : "+$") + Math.abs(delta).toLocaleString()
8517
8864
  }
8518
8865
  ) : null,
8519
- /* @__PURE__ */ jsx68(
8866
+ /* @__PURE__ */ jsx70(
8520
8867
  "input",
8521
8868
  {
8522
8869
  type: "range",
@@ -8532,13 +8879,13 @@ function BudgetReallocator({
8532
8879
  }
8533
8880
  )
8534
8881
  ] }),
8535
- /* @__PURE__ */ jsxs64("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
8536
- /* @__PURE__ */ jsxs64("span", { className: "fd-stack", style: { gap: 2, flex: 1, minWidth: 150 }, children: [
8537
- /* @__PURE__ */ jsx68("span", { className: "fd-num", style: { fontSize: 19, fontWeight: 700 }, children: "$" + draft.toLocaleString() }),
8538
- /* @__PURE__ */ jsx68("span", { className: "fd-body-sm fd-muted", children: "Arrow keys step $100, shift+arrow $1,000. Escape reverts." })
8882
+ /* @__PURE__ */ jsxs66("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
8883
+ /* @__PURE__ */ jsxs66("span", { className: "fd-stack", style: { gap: 2, flex: 1, minWidth: 150 }, children: [
8884
+ /* @__PURE__ */ jsx70("span", { className: "fd-num", style: { fontSize: 19, fontWeight: 700 }, children: "$" + draft.toLocaleString() }),
8885
+ /* @__PURE__ */ jsx70("span", { className: "fd-body-sm fd-muted", children: "Arrow keys step $100, shift+arrow $1,000. Escape reverts." })
8539
8886
  ] }),
8540
- /* @__PURE__ */ jsx68("button", { type: "button", className: "fd-btn fd-btn-ghost", disabled: !dirty, onClick: revert, children: "Cancel" }),
8541
- /* @__PURE__ */ jsx68(
8887
+ /* @__PURE__ */ jsx70("button", { type: "button", className: "fd-btn fd-btn-ghost", disabled: !dirty, onClick: revert, children: "Cancel" }),
8888
+ /* @__PURE__ */ jsx70(
8542
8889
  "button",
8543
8890
  {
8544
8891
  type: "button",
@@ -8557,7 +8904,7 @@ function BudgetReallocator({
8557
8904
  }
8558
8905
 
8559
8906
  // src/components/planner/SurroundSound.tsx
8560
- import { jsx as jsx69, jsxs as jsxs65 } from "react/jsx-runtime";
8907
+ import { jsx as jsx71, jsxs as jsxs67 } from "react/jsx-runtime";
8561
8908
  var CATEGORIES = [
8562
8909
  { key: "ooh", label: "OOH", icon: "flag-banner", color: "var(--ch-ooh)" },
8563
8910
  { key: "transit", label: "Transit", icon: "bus", color: "var(--ch-transit)" },
@@ -8569,26 +8916,26 @@ var CATEGORIES = [
8569
8916
  function SurroundSound({ present = [], bonusPct = 0, bonusMax = 40, missedCost, className = "" }) {
8570
8917
  const earned = Math.max(0, Math.min(1, bonusPct / bonusMax));
8571
8918
  const single = present.length <= 2;
8572
- return /* @__PURE__ */ jsxs65("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 14 }, children: [
8573
- /* @__PURE__ */ jsx69("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: CATEGORIES.map((c) => {
8919
+ return /* @__PURE__ */ jsxs67("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 14 }, children: [
8920
+ /* @__PURE__ */ jsx71("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: CATEGORIES.map((c) => {
8574
8921
  const on = present.includes(c.key);
8575
- return /* @__PURE__ */ jsxs65(
8922
+ return /* @__PURE__ */ jsxs67(
8576
8923
  "span",
8577
8924
  {
8578
8925
  title: c.label + (on ? " \u2014 present" : " \u2014 not bought"),
8579
8926
  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)" },
8580
8927
  children: [
8581
- /* @__PURE__ */ jsx69("i", { className: "ph ph-" + c.icon, style: { fontSize: 19, color: on ? c.color : "var(--text-muted)" }, "aria-hidden": "true" }),
8582
- /* @__PURE__ */ jsx69("span", { style: { fontSize: 10.5, fontWeight: 700 }, children: c.label })
8928
+ /* @__PURE__ */ jsx71("i", { className: "ph ph-" + c.icon, style: { fontSize: 19, color: on ? c.color : "var(--text-muted)" }, "aria-hidden": "true" }),
8929
+ /* @__PURE__ */ jsx71("span", { style: { fontSize: 10.5, fontWeight: 700 }, children: c.label })
8583
8930
  ]
8584
8931
  },
8585
8932
  c.key
8586
8933
  );
8587
8934
  }) }),
8588
- /* @__PURE__ */ jsxs65("div", { className: "fd-stack", style: { gap: 8 }, children: [
8589
- /* @__PURE__ */ jsxs65("div", { className: "fd-row", style: { justifyContent: "space-between", gap: 12 }, children: [
8590
- /* @__PURE__ */ jsx69("span", { className: "fd-label-lg", children: "Surround-sound bonus earned" }),
8591
- /* @__PURE__ */ jsxs65("span", { className: "fd-num", style: { color: single ? "var(--warn-text)" : "var(--csi-3-mark)", fontWeight: 700 }, children: [
8935
+ /* @__PURE__ */ jsxs67("div", { className: "fd-stack", style: { gap: 8 }, children: [
8936
+ /* @__PURE__ */ jsxs67("div", { className: "fd-row", style: { justifyContent: "space-between", gap: 12 }, children: [
8937
+ /* @__PURE__ */ jsx71("span", { className: "fd-label-lg", children: "Surround-sound bonus earned" }),
8938
+ /* @__PURE__ */ jsxs67("span", { className: "fd-num", style: { color: single ? "var(--warn-text)" : "var(--csi-3-mark)", fontWeight: 700 }, children: [
8592
8939
  "+",
8593
8940
  bonusPct,
8594
8941
  "% of +",
@@ -8596,8 +8943,8 @@ function SurroundSound({ present = [], bonusPct = 0, bonusMax = 40, missedCost,
8596
8943
  "%"
8597
8944
  ] })
8598
8945
  ] }),
8599
- /* @__PURE__ */ jsx69("div", { style: { height: 8, borderRadius: "var(--r-xs)", background: "var(--surface-3)", overflow: "hidden" }, children: /* @__PURE__ */ jsx69("div", { style: { height: "100%", width: earned * 100 + "%", background: "var(--csi-3-mark)", borderRadius: "inherit", transition: "width var(--dur-slow) var(--ease)" } }) }),
8600
- /* @__PURE__ */ jsxs65("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: [
8946
+ /* @__PURE__ */ jsx71("div", { style: { height: 8, borderRadius: "var(--r-xs)", background: "var(--surface-3)", overflow: "hidden" }, children: /* @__PURE__ */ jsx71("div", { style: { height: "100%", width: earned * 100 + "%", background: "var(--csi-3-mark)", borderRadius: "inherit", transition: "width var(--dur-slow) var(--ease)" } }) }),
8947
+ /* @__PURE__ */ jsxs67("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: [
8601
8948
  present.length,
8602
8949
  " of 6 channel categories present.",
8603
8950
  " ",
@@ -8609,7 +8956,7 @@ function SurroundSound({ present = [], bonusPct = 0, bonusMax = 40, missedCost,
8609
8956
  }
8610
8957
 
8611
8958
  // src/components/planner/Importance.tsx
8612
- import { jsx as jsx70, jsxs as jsxs66 } from "react/jsx-runtime";
8959
+ import { jsx as jsx72, jsxs as jsxs68 } from "react/jsx-runtime";
8613
8960
  var IMPORTANCE_LEVELS = [
8614
8961
  { id: "nudge", rank: 1, label: "Nudge", icon: "feather", description: "Breaks a tie, nothing more." },
8615
8962
  { id: "lean", rank: 2, label: "Lean", icon: "arrow-bend-up-right", description: "A mild preference between otherwise equal options." },
@@ -8639,23 +8986,23 @@ function importanceTokens(id) {
8639
8986
  function ImportanceTag({ level, showLabel = true, size = "sm", className = "" }) {
8640
8987
  const lvl = importanceLevel(level);
8641
8988
  const t = importanceTokens(level);
8642
- return /* @__PURE__ */ jsxs66(
8989
+ return /* @__PURE__ */ jsxs68(
8643
8990
  "span",
8644
8991
  {
8645
8992
  className: ["fd-imp-tag", size === "md" ? "is-md" : "", className].filter(Boolean).join(" "),
8646
8993
  style: { background: t.fill, borderColor: t.edge, color: t.text },
8647
8994
  title: lvl.label + " \u2014 " + lvl.description,
8648
8995
  children: [
8649
- /* @__PURE__ */ jsx70("i", { className: "ph ph-" + lvl.icon, "aria-hidden": "true" }),
8650
- showLabel ? lvl.label : /* @__PURE__ */ jsx70("span", { className: "fd-sr", children: lvl.label })
8996
+ /* @__PURE__ */ jsx72("i", { className: "ph ph-" + lvl.icon, "aria-hidden": "true" }),
8997
+ showLabel ? lvl.label : /* @__PURE__ */ jsx72("span", { className: "fd-sr", children: lvl.label })
8651
8998
  ]
8652
8999
  }
8653
9000
  );
8654
9001
  }
8655
9002
 
8656
9003
  // src/components/planner/PreferenceMeter.tsx
8657
- import * as React40 from "react";
8658
- import { jsx as jsx71, jsxs as jsxs67 } from "react/jsx-runtime";
9004
+ import * as React44 from "react";
9005
+ import { jsx as jsx73, jsxs as jsxs69 } from "react/jsx-runtime";
8659
9006
  function preferenceScore(criteria) {
8660
9007
  const earned = criteria.reduce((n, c) => n + (Number(c.earned) || 0), 0);
8661
9008
  const weight = criteria.reduce((n, c) => n + (Number(c.weight) || 0), 0);
@@ -8684,8 +9031,8 @@ function PreferenceMeter({
8684
9031
  className = ""
8685
9032
  }) {
8686
9033
  const value = score == null ? preferenceScore(criteria) : score;
8687
- const segments = React40.useMemo(() => preferenceSegments(criteria), [criteria]);
8688
- return /* @__PURE__ */ jsx71(
9034
+ const segments = React44.useMemo(() => preferenceSegments(criteria), [criteria]);
9035
+ return /* @__PURE__ */ jsx73(
8689
9036
  ScoreMeter,
8690
9037
  {
8691
9038
  score: value,
@@ -8697,22 +9044,22 @@ function PreferenceMeter({
8697
9044
  placement,
8698
9045
  className,
8699
9046
  breakdownLabel: breakdownTitle,
8700
- breakdown: /* @__PURE__ */ jsxs67("div", { className: "fd-prefbreak", children: [
8701
- /* @__PURE__ */ jsxs67("div", { className: "fd-prefbreak-head", children: [
8702
- /* @__PURE__ */ jsx71("span", { className: "fd-label-lg", children: breakdownTitle }),
8703
- /* @__PURE__ */ jsx71("span", { className: "fd-prefbreak-score fd-tabular", children: Math.round(value) })
9047
+ breakdown: /* @__PURE__ */ jsxs69("div", { className: "fd-prefbreak", children: [
9048
+ /* @__PURE__ */ jsxs69("div", { className: "fd-prefbreak-head", children: [
9049
+ /* @__PURE__ */ jsx73("span", { className: "fd-label-lg", children: breakdownTitle }),
9050
+ /* @__PURE__ */ jsx73("span", { className: "fd-prefbreak-score fd-tabular", children: Math.round(value) })
8704
9051
  ] }),
8705
- /* @__PURE__ */ jsx71("ul", { className: "fd-prefbreak-list", children: criteria.map((c) => {
9052
+ /* @__PURE__ */ jsx73("ul", { className: "fd-prefbreak-list", children: criteria.map((c) => {
8706
9053
  const pct = c.weight > 0 ? Math.round((Number(c.earned) || 0) / c.weight * 100) : 0;
8707
- return /* @__PURE__ */ jsxs67("li", { className: pct === 0 ? "fd-prefbreak-item is-unmet" : "fd-prefbreak-item", children: [
8708
- /* @__PURE__ */ jsx71(ImportanceTag, { level: c.importance }),
8709
- /* @__PURE__ */ jsxs67("span", { className: "fd-prefbreak-text", children: [
8710
- /* @__PURE__ */ jsx71("span", { className: "fd-prefbreak-label", children: c.label }),
8711
- c.note ? /* @__PURE__ */ jsx71("span", { className: "fd-prefbreak-note", children: c.note }) : null
9054
+ return /* @__PURE__ */ jsxs69("li", { className: pct === 0 ? "fd-prefbreak-item is-unmet" : "fd-prefbreak-item", children: [
9055
+ /* @__PURE__ */ jsx73(ImportanceTag, { level: c.importance }),
9056
+ /* @__PURE__ */ jsxs69("span", { className: "fd-prefbreak-text", children: [
9057
+ /* @__PURE__ */ jsx73("span", { className: "fd-prefbreak-label", children: c.label }),
9058
+ c.note ? /* @__PURE__ */ jsx73("span", { className: "fd-prefbreak-note", children: c.note }) : null
8712
9059
  ] }),
8713
- /* @__PURE__ */ jsxs67("span", { className: "fd-prefbreak-val fd-tabular", children: [
9060
+ /* @__PURE__ */ jsxs69("span", { className: "fd-prefbreak-val fd-tabular", children: [
8714
9061
  Math.round(Number(c.earned) || 0),
8715
- /* @__PURE__ */ jsx71("span", { className: "fd-prefbreak-sep", children: "/" }),
9062
+ /* @__PURE__ */ jsx73("span", { className: "fd-prefbreak-sep", children: "/" }),
8716
9063
  Math.round(c.weight)
8717
9064
  ] })
8718
9065
  ] }, c.id);
@@ -8723,10 +9070,10 @@ function PreferenceMeter({
8723
9070
  }
8724
9071
 
8725
9072
  // src/components/chat/AgentChatPanel.tsx
8726
- import * as React46 from "react";
9073
+ import * as React50 from "react";
8727
9074
 
8728
9075
  // src/components/chat/chatEngine.ts
8729
- import * as React41 from "react";
9076
+ import * as React45 from "react";
8730
9077
  var CHAT_UNAVAILABLE = "chat_unavailable";
8731
9078
  var JOB_PENDING = ["queued", "running"];
8732
9079
  var JOB_SUCCESS = ["completed", "recovered"];
@@ -8780,22 +9127,22 @@ function useChatEngine(opts) {
8780
9127
  onClear,
8781
9128
  onFeedback
8782
9129
  } = opts || {};
8783
- const [status, setStatus] = React41.useState("idle");
8784
- const [threadId, setThreadId] = React41.useState(null);
8785
- const [messages, setMessages] = React41.useState([]);
8786
- const [queue, setQueue] = React41.useState([]);
8787
- const [fatal, setFatal] = React41.useState(null);
8788
- const [busy, setBusy] = React41.useState(false);
8789
- const [turnStartedAt, setTurnStartedAt] = React41.useState(null);
8790
- const listRef = React41.useRef([]);
8791
- const queueRef = React41.useRef([]);
8792
- const busyRef = React41.useRef(false);
8793
- const stoppedRef = React41.useRef(false);
8794
- const abortRef = React41.useRef(null);
8795
- const serverCount = React41.useRef(0);
8796
- const threadRef = React41.useRef(null);
8797
- const mounted = React41.useRef(true);
8798
- React41.useEffect(() => {
9130
+ const [status, setStatus] = React45.useState("idle");
9131
+ const [threadId, setThreadId] = React45.useState(null);
9132
+ const [messages, setMessages] = React45.useState([]);
9133
+ const [queue, setQueue] = React45.useState([]);
9134
+ const [fatal, setFatal] = React45.useState(null);
9135
+ const [busy, setBusy] = React45.useState(false);
9136
+ const [turnStartedAt, setTurnStartedAt] = React45.useState(null);
9137
+ const listRef = React45.useRef([]);
9138
+ const queueRef = React45.useRef([]);
9139
+ const busyRef = React45.useRef(false);
9140
+ const stoppedRef = React45.useRef(false);
9141
+ const abortRef = React45.useRef(null);
9142
+ const serverCount = React45.useRef(0);
9143
+ const threadRef = React45.useRef(null);
9144
+ const mounted = React45.useRef(true);
9145
+ React45.useEffect(() => {
8799
9146
  mounted.current = true;
8800
9147
  return () => {
8801
9148
  mounted.current = false;
@@ -8817,14 +9164,14 @@ function useChatEngine(opts) {
8817
9164
  }
8818
9165
  return false;
8819
9166
  };
8820
- const loadThread = React41.useCallback(async (id) => {
9167
+ const loadThread = React45.useCallback(async (id) => {
8821
9168
  const data = await apiAdapter.getThread(id);
8822
9169
  const list = [...data && data.messages || []];
8823
9170
  serverCount.current = list.length;
8824
9171
  commit(list);
8825
9172
  return list;
8826
9173
  }, [apiAdapter]);
8827
- React41.useEffect(() => {
9174
+ React45.useEffect(() => {
8828
9175
  if (!apiAdapter) {
8829
9176
  setStatus("idle");
8830
9177
  setFatal(null);
@@ -9037,7 +9384,7 @@ function useChatEngine(opts) {
9037
9384
  await dispatchTurn(turn);
9038
9385
  }
9039
9386
  }
9040
- const send = React41.useCallback((text, attachments) => {
9387
+ const send = React45.useCallback((text, attachments) => {
9041
9388
  const body = (text || "").trim();
9042
9389
  if (!body && !(attachments && attachments.length)) return;
9043
9390
  if (status === "disconnected") return;
@@ -9047,7 +9394,7 @@ function useChatEngine(opts) {
9047
9394
  stoppedRef.current = false;
9048
9395
  drain();
9049
9396
  }, [status]);
9050
- const stop = React41.useCallback(() => {
9397
+ const stop = React45.useCallback(() => {
9051
9398
  stoppedRef.current = true;
9052
9399
  const ac = abortRef.current;
9053
9400
  if (ac) {
@@ -9066,11 +9413,11 @@ function useChatEngine(opts) {
9066
9413
  store.del(STORAGE_PREFIX + threadRef.current);
9067
9414
  }
9068
9415
  }, [apiAdapter]);
9069
- const removeQueued = React41.useCallback((id) => {
9416
+ const removeQueued = React45.useCallback((id) => {
9070
9417
  queueRef.current = queueRef.current.filter((t) => t.id !== id);
9071
9418
  setQueue(queueRef.current.slice());
9072
9419
  }, []);
9073
- const retry = React41.useCallback(() => {
9420
+ const retry = React45.useCallback(() => {
9074
9421
  const list = listRef.current;
9075
9422
  let at = -1;
9076
9423
  for (let i = list.length - 1; i >= 0; i--) if (list[i].role === "user") {
@@ -9086,19 +9433,19 @@ function useChatEngine(opts) {
9086
9433
  setQueue(queueRef.current.slice());
9087
9434
  drain();
9088
9435
  }, []);
9089
- const clear = React41.useCallback(() => {
9436
+ const clear = React45.useCallback(() => {
9090
9437
  commit([]);
9091
9438
  serverCount.current = 0;
9092
9439
  queueRef.current = [];
9093
9440
  setQueue([]);
9094
9441
  onClear && onClear();
9095
9442
  }, [onClear]);
9096
- const setFeedback = React41.useCallback((id, value) => {
9443
+ const setFeedback = React45.useCallback((id, value) => {
9097
9444
  patch(id, (m) => ({ feedback: m.feedback === value ? null : value }));
9098
9445
  const msg = listRef.current.find((m) => m.id === id);
9099
9446
  onFeedback && onFeedback({ message: msg, feedback: msg ? msg.feedback : value });
9100
9447
  }, [onFeedback]);
9101
- const reload = React41.useCallback(async () => {
9448
+ const reload = React45.useCallback(async () => {
9102
9449
  if (!threadRef.current) return;
9103
9450
  setStatus("loading");
9104
9451
  try {
@@ -9136,11 +9483,11 @@ var ChatKit = {
9136
9483
  };
9137
9484
 
9138
9485
  // src/components/chat/ChatTranscript.tsx
9139
- import * as React43 from "react";
9486
+ import * as React47 from "react";
9140
9487
 
9141
9488
  // src/components/chat/ChatTurn.tsx
9142
- import * as React42 from "react";
9143
- import { jsx as jsx72, jsxs as jsxs68 } from "react/jsx-runtime";
9489
+ import * as React46 from "react";
9490
+ import { jsx as jsx74, jsxs as jsxs70 } from "react/jsx-runtime";
9144
9491
  function JsonView({ value }) {
9145
9492
  let text;
9146
9493
  try {
@@ -9148,67 +9495,67 @@ function JsonView({ value }) {
9148
9495
  } catch (e) {
9149
9496
  text = String(value);
9150
9497
  }
9151
- return /* @__PURE__ */ jsx72(CodeBlock, { code: text, language: "json", collapseAfter: 18 });
9498
+ return /* @__PURE__ */ jsx74(CodeBlock, { code: text, language: "json", collapseAfter: 18 });
9152
9499
  }
9153
9500
  function PacketCard({ packet, schema, render, onApply, applied }) {
9154
9501
  if (!packet) return null;
9155
9502
  const s = schema || {};
9156
9503
  const invalid = packet.valid === false;
9157
9504
  const title = s.heading || packet.type;
9158
- return /* @__PURE__ */ jsxs68("section", { className: "fdc-packet" + (invalid ? " is-invalid" : ""), "aria-label": title, children: [
9159
- /* @__PURE__ */ jsxs68("header", { className: "fdc-packet-head", children: [
9160
- /* @__PURE__ */ jsx72("i", { className: "ph ph-" + (s.icon || "package"), "aria-hidden": "true" }),
9161
- /* @__PURE__ */ jsx72("span", { className: "fdc-packet-title", title: truncationTitle(title), children: title }),
9162
- packet.repaired ? /* @__PURE__ */ jsx72("span", { className: "fdc-packet-badge", children: "Repaired" }) : null,
9163
- invalid ? /* @__PURE__ */ jsx72("span", { className: "fdc-packet-badge is-danger", children: "Invalid" }) : null
9505
+ return /* @__PURE__ */ jsxs70("section", { className: "fdc-packet" + (invalid ? " is-invalid" : ""), "aria-label": title, children: [
9506
+ /* @__PURE__ */ jsxs70("header", { className: "fdc-packet-head", children: [
9507
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-" + (s.icon || "package"), "aria-hidden": "true" }),
9508
+ /* @__PURE__ */ jsx74("span", { className: "fdc-packet-title", title: truncationTitle(title), children: title }),
9509
+ packet.repaired ? /* @__PURE__ */ jsx74("span", { className: "fdc-packet-badge", children: "Repaired" }) : null,
9510
+ invalid ? /* @__PURE__ */ jsx74("span", { className: "fdc-packet-badge is-danger", children: "Invalid" }) : null
9164
9511
  ] }),
9165
- invalid ? /* @__PURE__ */ jsxs68("div", { className: "fdc-packet-alert", role: "alert", children: [
9166
- /* @__PURE__ */ jsx72("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
9167
- /* @__PURE__ */ jsx72("span", { children: packet.error || "This result failed validation and can\u2019t be applied." })
9168
- ] }) : /* @__PURE__ */ jsx72("div", { className: "fdc-packet-body", children: render ? render(packet) : /* @__PURE__ */ jsx72(JsonView, { value: packet.payload }) }),
9169
- !invalid && onApply ? /* @__PURE__ */ jsxs68("footer", { className: "fdc-packet-foot", children: [
9170
- packet.repaired ? /* @__PURE__ */ jsx72("span", { className: "fdc-packet-note", children: "Corrected by the backend after a first attempt." }) : /* @__PURE__ */ jsx72("span", {}),
9171
- /* @__PURE__ */ jsxs68("button", { type: "button", className: "fd-btn fd-btn-primary fd-btn-sm", disabled: applied, onClick: () => onApply(packet), children: [
9172
- /* @__PURE__ */ jsx72("i", { className: "ph ph-" + (applied ? "check" : "arrow-square-in"), "aria-hidden": "true" }),
9512
+ invalid ? /* @__PURE__ */ jsxs70("div", { className: "fdc-packet-alert", role: "alert", children: [
9513
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
9514
+ /* @__PURE__ */ jsx74("span", { children: packet.error || "This result failed validation and can\u2019t be applied." })
9515
+ ] }) : /* @__PURE__ */ jsx74("div", { className: "fdc-packet-body", children: render ? render(packet) : /* @__PURE__ */ jsx74(JsonView, { value: packet.payload }) }),
9516
+ !invalid && onApply ? /* @__PURE__ */ jsxs70("footer", { className: "fdc-packet-foot", children: [
9517
+ packet.repaired ? /* @__PURE__ */ jsx74("span", { className: "fdc-packet-note", children: "Corrected by the backend after a first attempt." }) : /* @__PURE__ */ jsx74("span", {}),
9518
+ /* @__PURE__ */ jsxs70("button", { type: "button", className: "fd-btn fd-btn-primary fd-btn-sm", disabled: applied, onClick: () => onApply(packet), children: [
9519
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-" + (applied ? "check" : "arrow-square-in"), "aria-hidden": "true" }),
9173
9520
  applied ? "Applied" : s.applyLabel || "Apply"
9174
9521
  ] })
9175
9522
  ] }) : null
9176
9523
  ] });
9177
9524
  }
9178
9525
  function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
9179
- const [open, setOpen] = React42.useState(defaultOpen);
9526
+ const [open, setOpen] = React46.useState(defaultOpen);
9180
9527
  if (!text) return null;
9181
- return /* @__PURE__ */ jsxs68("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
9182
- /* @__PURE__ */ jsxs68("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
9183
- /* @__PURE__ */ jsx72("i", { className: "ph ph-brain", "aria-hidden": "true" }),
9184
- /* @__PURE__ */ jsx72("span", { children: streaming ? "Thinking" : durationMs ? "Thought for " + formatDuration(durationMs) : "Thought process" }),
9185
- streaming ? /* @__PURE__ */ jsxs68("span", { className: "fdc-dots", "aria-hidden": "true", children: [
9186
- /* @__PURE__ */ jsx72("span", {}),
9187
- /* @__PURE__ */ jsx72("span", {}),
9188
- /* @__PURE__ */ jsx72("span", {})
9528
+ return /* @__PURE__ */ jsxs70("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
9529
+ /* @__PURE__ */ jsxs70("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
9530
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-brain", "aria-hidden": "true" }),
9531
+ /* @__PURE__ */ jsx74("span", { children: streaming ? "Thinking" : durationMs ? "Thought for " + formatDuration(durationMs) : "Thought process" }),
9532
+ streaming ? /* @__PURE__ */ jsxs70("span", { className: "fdc-dots", "aria-hidden": "true", children: [
9533
+ /* @__PURE__ */ jsx74("span", {}),
9534
+ /* @__PURE__ */ jsx74("span", {}),
9535
+ /* @__PURE__ */ jsx74("span", {})
9189
9536
  ] }) : null,
9190
- /* @__PURE__ */ jsx72("i", { className: "ph ph-caret-" + (open ? "up" : "down"), "aria-hidden": "true" })
9537
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-caret-" + (open ? "up" : "down"), "aria-hidden": "true" })
9191
9538
  ] }),
9192
- open ? /* @__PURE__ */ jsx72("div", { className: "fdc-think-body", children: text }) : null
9539
+ open ? /* @__PURE__ */ jsx74("div", { className: "fdc-think-body", children: text }) : null
9193
9540
  ] });
9194
9541
  }
9195
9542
  function Citations({ items = [], onOpen }) {
9196
- const [open, setOpen] = React42.useState(false);
9543
+ const [open, setOpen] = React46.useState(false);
9197
9544
  if (!items.length) return null;
9198
- return /* @__PURE__ */ jsxs68("div", { className: "fdc-cites", children: [
9199
- /* @__PURE__ */ jsxs68("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
9200
- /* @__PURE__ */ jsx72("i", { className: "ph ph-quotes", "aria-hidden": "true" }),
9201
- /* @__PURE__ */ jsxs68("span", { children: [
9545
+ return /* @__PURE__ */ jsxs70("div", { className: "fdc-cites", children: [
9546
+ /* @__PURE__ */ jsxs70("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
9547
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-quotes", "aria-hidden": "true" }),
9548
+ /* @__PURE__ */ jsxs70("span", { children: [
9202
9549
  items.length,
9203
9550
  " source",
9204
9551
  items.length === 1 ? "" : "s"
9205
9552
  ] }),
9206
- /* @__PURE__ */ jsx72("i", { className: "ph ph-caret-" + (open ? "up" : "down"), "aria-hidden": "true" })
9553
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-caret-" + (open ? "up" : "down"), "aria-hidden": "true" })
9207
9554
  ] }),
9208
- open ? /* @__PURE__ */ jsx72("ol", { className: "fdc-cites-list", children: items.map((c, i) => /* @__PURE__ */ jsxs68("li", { children: [
9209
- /* @__PURE__ */ jsx72("span", { className: "fdc-cite-n fd-tabular", children: c.marker || i + 1 }),
9210
- c.url ? /* @__PURE__ */ jsx72("a", { href: c.url, target: "_blank", rel: "noopener noreferrer", onClick: onOpen ? (e) => onOpen(c, e) : void 0, children: c.title || c.url }) : /* @__PURE__ */ jsx72("span", { children: c.title || "Source" }),
9211
- c.detail ? /* @__PURE__ */ jsx72("span", { className: "fdc-cite-detail", children: c.detail }) : null
9555
+ open ? /* @__PURE__ */ jsx74("ol", { className: "fdc-cites-list", children: items.map((c, i) => /* @__PURE__ */ jsxs70("li", { children: [
9556
+ /* @__PURE__ */ jsx74("span", { className: "fdc-cite-n fd-tabular", children: c.marker || i + 1 }),
9557
+ c.url ? /* @__PURE__ */ jsx74("a", { href: c.url, target: "_blank", rel: "noopener noreferrer", onClick: onOpen ? (e) => onOpen(c, e) : void 0, children: c.title || c.url }) : /* @__PURE__ */ jsx74("span", { children: c.title || "Source" }),
9558
+ c.detail ? /* @__PURE__ */ jsx74("span", { className: "fdc-cite-detail", children: c.detail }) : null
9212
9559
  ] }, c.id || i)) }) : null
9213
9560
  ] });
9214
9561
  }
@@ -9219,29 +9566,29 @@ function clampText(text, max) {
9219
9566
  return (at > max * 0.6 ? cut.slice(0, at) : cut).trimEnd() + "\u2026";
9220
9567
  }
9221
9568
  function MessageBody({ message: m, ctx }) {
9222
- const [expanded, setExpanded] = React42.useState(false);
9569
+ const [expanded, setExpanded] = React46.useState(false);
9223
9570
  const isUser = m.role === "user";
9224
9571
  const raw = m.text || "";
9225
9572
  const clamped = !expanded && !m.streaming ? clampText(raw, ctx.maxVisibleChars) : null;
9226
9573
  const body = clamped != null ? clamped : raw;
9227
9574
  const showMd = ctx.markdown && !isUser;
9228
- return /* @__PURE__ */ jsxs68("div", { className: "fdc-body", children: [
9229
- m.thinking && ctx.showThinking ? /* @__PURE__ */ jsx72(ThinkingBlock, { text: m.thinking, durationMs: m.thinkingMs, streaming: m.streaming && !m.text }) : null,
9230
- m.steps && m.steps.length && ctx.showSteps ? /* @__PURE__ */ jsx72(StepList, { steps: m.steps, defaultOpen: m.steps.some((s) => s.status === "running"), dense: true }) : null,
9231
- body ? /* @__PURE__ */ jsxs68("div", { className: "fdc-text" + (isUser ? " is-user" : ""), children: [
9232
- showMd ? /* @__PURE__ */ jsx72(Markdown, { source: body, headingOffset: 2, codeProps: { collapseAfter: 22 }, renderCitation: ctx.renderCitation }) : /* @__PURE__ */ jsx72("div", { className: "fdc-plain", children: body }),
9233
- m.streaming ? /* @__PURE__ */ jsx72("span", { className: "fdc-caret", "aria-hidden": "true" }) : null
9575
+ return /* @__PURE__ */ jsxs70("div", { className: "fdc-body", children: [
9576
+ m.thinking && ctx.showThinking ? /* @__PURE__ */ jsx74(ThinkingBlock, { text: m.thinking, durationMs: m.thinkingMs, streaming: m.streaming && !m.text }) : null,
9577
+ m.steps && m.steps.length && ctx.showSteps ? /* @__PURE__ */ jsx74(StepList, { steps: m.steps, defaultOpen: m.steps.some((s) => s.status === "running"), dense: true }) : null,
9578
+ body ? /* @__PURE__ */ jsxs70("div", { className: "fdc-text" + (isUser ? " is-user" : ""), children: [
9579
+ showMd ? /* @__PURE__ */ jsx74(Markdown, { source: body, headingOffset: 2, codeProps: { collapseAfter: 22 }, renderCitation: ctx.renderCitation }) : /* @__PURE__ */ jsx74("div", { className: "fdc-plain", children: body }),
9580
+ m.streaming ? /* @__PURE__ */ jsx74("span", { className: "fdc-caret", "aria-hidden": "true" }) : null
9234
9581
  ] }) : null,
9235
- clamped != null ? /* @__PURE__ */ jsxs68("button", { type: "button", className: "fdc-more", onClick: () => setExpanded(true), children: [
9236
- /* @__PURE__ */ jsx72("i", { className: "ph ph-caret-down", "aria-hidden": "true" }),
9582
+ clamped != null ? /* @__PURE__ */ jsxs70("button", { type: "button", className: "fdc-more", onClick: () => setExpanded(true), children: [
9583
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-caret-down", "aria-hidden": "true" }),
9237
9584
  "Show more",
9238
- /* @__PURE__ */ jsx72("span", { className: "fdc-more-len fd-tabular", children: raw.length < 2e3 ? raw.length.toLocaleString() + " characters" : Math.round(raw.length / 100) / 10 + "k characters" })
9239
- ] }) : expanded && ctx.maxVisibleChars && raw.length > ctx.maxVisibleChars ? /* @__PURE__ */ jsxs68("button", { type: "button", className: "fdc-more", onClick: () => setExpanded(false), children: [
9240
- /* @__PURE__ */ jsx72("i", { className: "ph ph-caret-up", "aria-hidden": "true" }),
9585
+ /* @__PURE__ */ jsx74("span", { className: "fdc-more-len fd-tabular", children: raw.length < 2e3 ? raw.length.toLocaleString() + " characters" : Math.round(raw.length / 100) / 10 + "k characters" })
9586
+ ] }) : expanded && ctx.maxVisibleChars && raw.length > ctx.maxVisibleChars ? /* @__PURE__ */ jsxs70("button", { type: "button", className: "fdc-more", onClick: () => setExpanded(false), children: [
9587
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-caret-up", "aria-hidden": "true" }),
9241
9588
  "Show less"
9242
9589
  ] }) : null,
9243
- m.attachments && m.attachments.length ? /* @__PURE__ */ jsx72(FileGrid, { files: m.attachments, onOpen: ctx.onOpenAttachment, tiles: true, maxHeight: ctx.attachmentHeight || 220, compact: ctx.narrow }) : null,
9244
- m.packet ? /* @__PURE__ */ jsx72(
9590
+ m.attachments && m.attachments.length ? /* @__PURE__ */ jsx74(FileGrid, { files: m.attachments, onOpen: ctx.onOpenAttachment, tiles: true, maxHeight: ctx.attachmentHeight || 220, compact: ctx.narrow }) : null,
9591
+ m.packet ? /* @__PURE__ */ jsx74(
9245
9592
  PacketCard,
9246
9593
  {
9247
9594
  packet: m.packet,
@@ -9251,44 +9598,44 @@ function MessageBody({ message: m, ctx }) {
9251
9598
  applied: ctx.appliedPackets && ctx.appliedPackets[m.id]
9252
9599
  }
9253
9600
  ) : null,
9254
- m.citations && m.citations.length ? /* @__PURE__ */ jsx72(Citations, { items: m.citations, onOpen: ctx.onOpenCitation }) : null,
9255
- m.working ? /* @__PURE__ */ jsxs68("div", { className: "fdc-working", role: "status", children: [
9256
- /* @__PURE__ */ jsxs68("span", { className: "fdc-dots", "aria-hidden": "true", children: [
9257
- /* @__PURE__ */ jsx72("span", {}),
9258
- /* @__PURE__ */ jsx72("span", {}),
9259
- /* @__PURE__ */ jsx72("span", {})
9601
+ m.citations && m.citations.length ? /* @__PURE__ */ jsx74(Citations, { items: m.citations, onOpen: ctx.onOpenCitation }) : null,
9602
+ m.working ? /* @__PURE__ */ jsxs70("div", { className: "fdc-working", role: "status", children: [
9603
+ /* @__PURE__ */ jsxs70("span", { className: "fdc-dots", "aria-hidden": "true", children: [
9604
+ /* @__PURE__ */ jsx74("span", {}),
9605
+ /* @__PURE__ */ jsx74("span", {}),
9606
+ /* @__PURE__ */ jsx74("span", {})
9260
9607
  ] }),
9261
- /* @__PURE__ */ jsxs68("span", { className: "fdc-working-label", children: [
9608
+ /* @__PURE__ */ jsxs70("span", { className: "fdc-working-label", children: [
9262
9609
  m.resumed ? "Resuming" : "Working",
9263
- m.job && m.job.status ? /* @__PURE__ */ jsxs68("span", { className: "fdc-working-job", children: [
9610
+ m.job && m.job.status ? /* @__PURE__ */ jsxs70("span", { className: "fdc-working-job", children: [
9264
9611
  " \xB7 ",
9265
9612
  m.job.status
9266
9613
  ] }) : null,
9267
- m.job && m.job.detail ? /* @__PURE__ */ jsxs68("span", { className: "fdc-working-job", children: [
9614
+ m.job && m.job.detail ? /* @__PURE__ */ jsxs70("span", { className: "fdc-working-job", children: [
9268
9615
  " \xB7 ",
9269
9616
  m.job.detail
9270
9617
  ] }) : null
9271
9618
  ] }),
9272
- m.jobId ? /* @__PURE__ */ jsx72("span", { className: "fdc-working-id fd-mono", children: String(m.jobId).slice(0, 12) }) : null
9619
+ m.jobId ? /* @__PURE__ */ jsx74("span", { className: "fdc-working-id fd-mono", children: String(m.jobId).slice(0, 12) }) : null
9273
9620
  ] }) : null,
9274
- m.stopped ? /* @__PURE__ */ jsxs68("div", { className: "fdc-stopped", children: [
9275
- /* @__PURE__ */ jsx72("i", { className: "ph ph-stop-circle", "aria-hidden": "true" }),
9621
+ m.stopped ? /* @__PURE__ */ jsxs70("div", { className: "fdc-stopped", children: [
9622
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-stop-circle", "aria-hidden": "true" }),
9276
9623
  "Stopped"
9277
9624
  ] }) : null,
9278
- m.error ? /* @__PURE__ */ jsxs68("div", { className: "fdc-error", role: "alert", children: [
9279
- /* @__PURE__ */ jsx72("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
9280
- /* @__PURE__ */ jsx72("span", { className: "fdc-error-text", children: ctx.errorCopy ? ctx.errorCopy(m.error) : m.error }),
9281
- m.retryable && ctx.onRetry ? /* @__PURE__ */ jsxs68("button", { type: "button", className: "fdc-error-retry", onClick: ctx.onRetry, children: [
9282
- /* @__PURE__ */ jsx72("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }),
9625
+ m.error ? /* @__PURE__ */ jsxs70("div", { className: "fdc-error", role: "alert", children: [
9626
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
9627
+ /* @__PURE__ */ jsx74("span", { className: "fdc-error-text", children: ctx.errorCopy ? ctx.errorCopy(m.error) : m.error }),
9628
+ m.retryable && ctx.onRetry ? /* @__PURE__ */ jsxs70("button", { type: "button", className: "fdc-error-retry", onClick: ctx.onRetry, children: [
9629
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }),
9283
9630
  "Retry"
9284
9631
  ] }) : null
9285
9632
  ] }) : null
9286
9633
  ] });
9287
9634
  }
9288
9635
  function useCopyRun() {
9289
- const [done, setDone] = React42.useState(false);
9290
- const t = React42.useRef(null);
9291
- React42.useEffect(() => () => {
9636
+ const [done, setDone] = React46.useState(false);
9637
+ const t = React46.useRef(null);
9638
+ React46.useEffect(() => () => {
9292
9639
  if (t.current) clearTimeout(t.current);
9293
9640
  }, []);
9294
9641
  return [done, (text) => {
@@ -9308,16 +9655,16 @@ function RunActions({ group, ctx }) {
9308
9655
  const isAssistant = group.role === "assistant";
9309
9656
  const fb = last.feedback;
9310
9657
  if (!ctx.messageActions) return null;
9311
- return /* @__PURE__ */ jsxs68("div", { className: "fdc-actions", role: "group", "aria-label": "Message actions", children: [
9312
- /* @__PURE__ */ jsxs68("button", { type: "button", className: "fdc-act" + (copied ? " is-done" : ""), onClick: () => copy(markdownToText(text)), "aria-label": "Copy message", children: [
9313
- /* @__PURE__ */ jsx72("i", { className: "ph ph-" + (copied ? "check" : "copy"), "aria-hidden": "true" }),
9314
- /* @__PURE__ */ jsx72("span", { className: "fdc-act-label", children: copied ? "Copied" : "Copy" })
9658
+ return /* @__PURE__ */ jsxs70("div", { className: "fdc-actions", role: "group", "aria-label": "Message actions", children: [
9659
+ /* @__PURE__ */ jsxs70("button", { type: "button", className: "fdc-act" + (copied ? " is-done" : ""), onClick: () => copy(markdownToText(text)), "aria-label": "Copy message", children: [
9660
+ /* @__PURE__ */ jsx74("i", { className: "ph ph-" + (copied ? "check" : "copy"), "aria-hidden": "true" }),
9661
+ /* @__PURE__ */ jsx74("span", { className: "fdc-act-label", children: copied ? "Copied" : "Copy" })
9315
9662
  ] }),
9316
- isAssistant && ctx.onRetry ? /* @__PURE__ */ jsx72("button", { type: "button", className: "fdc-act", onClick: ctx.onRetry, "aria-label": "Retry this turn", children: /* @__PURE__ */ jsx72("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }) }) : null,
9317
- group.role === "user" && ctx.onEdit ? /* @__PURE__ */ jsx72("button", { type: "button", className: "fdc-act", onClick: () => ctx.onEdit(last), "aria-label": "Edit and resend", children: /* @__PURE__ */ jsx72("i", { className: "ph ph-pencil-simple", "aria-hidden": "true" }) }) : null,
9318
- isAssistant && ctx.onFeedback ? /* @__PURE__ */ jsxs68(React42.Fragment, { children: [
9319
- /* @__PURE__ */ jsx72("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__ */ jsx72("i", { className: "ph ph-thumbs-up", "aria-hidden": "true" }) }),
9320
- /* @__PURE__ */ jsx72("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__ */ jsx72("i", { className: "ph ph-thumbs-down", "aria-hidden": "true" }) })
9663
+ isAssistant && ctx.onRetry ? /* @__PURE__ */ jsx74("button", { type: "button", className: "fdc-act", onClick: ctx.onRetry, "aria-label": "Retry this turn", children: /* @__PURE__ */ jsx74("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }) }) : null,
9664
+ group.role === "user" && ctx.onEdit ? /* @__PURE__ */ jsx74("button", { type: "button", className: "fdc-act", onClick: () => ctx.onEdit(last), "aria-label": "Edit and resend", children: /* @__PURE__ */ jsx74("i", { className: "ph ph-pencil-simple", "aria-hidden": "true" }) }) : null,
9665
+ isAssistant && ctx.onFeedback ? /* @__PURE__ */ jsxs70(React46.Fragment, { children: [
9666
+ /* @__PURE__ */ jsx74("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__ */ jsx74("i", { className: "ph ph-thumbs-up", "aria-hidden": "true" }) }),
9667
+ /* @__PURE__ */ jsx74("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__ */ jsx74("i", { className: "ph ph-thumbs-down", "aria-hidden": "true" }) })
9321
9668
  ] }) : null,
9322
9669
  ctx.extraActions ? ctx.extraActions(group) : null
9323
9670
  ] });
@@ -9327,22 +9674,22 @@ function ChatTurn({ group, ctx }) {
9327
9674
  const name = isUser ? ctx.userName : group.author || ctx.assistantName;
9328
9675
  const avatar = isUser ? ctx.userAvatar : ctx.assistantAvatar;
9329
9676
  const stamp = group.messages[0].timestamp;
9330
- return /* @__PURE__ */ jsxs68("article", { className: "fdc-turn is-" + group.role, "aria-label": String(name) + (stamp ? " at " + formatClock(stamp) : ""), children: [
9331
- /* @__PURE__ */ jsxs68("div", { className: "fdc-turn-head", children: [
9332
- ctx.showAvatars ? /* @__PURE__ */ jsx72("span", { className: "fdc-avatar is-" + group.role, "aria-hidden": "true", children: avatar ? /* @__PURE__ */ jsx72("img", { src: avatar, alt: "" }) : isUser ? /* @__PURE__ */ jsx72("span", { className: "fdc-avatar-txt", children: (name || "You").slice(0, 1).toUpperCase() }) : /* @__PURE__ */ jsx72("i", { className: "ph ph-" + (ctx.assistantIcon || "sparkle") }) }) : null,
9333
- /* @__PURE__ */ jsx72("span", { className: "fdc-who", children: name }),
9334
- stamp ? /* @__PURE__ */ jsx72(RelativeTime, { className: "fdc-when", value: stamp }) : null,
9335
- group.messages.some((m) => m.model) ? /* @__PURE__ */ jsx72("span", { className: "fdc-turn-model", children: group.messages.find((m) => m.model).model }) : null
9677
+ return /* @__PURE__ */ jsxs70("article", { className: "fdc-turn is-" + group.role, "aria-label": String(name) + (stamp ? " at " + formatClock(stamp) : ""), children: [
9678
+ /* @__PURE__ */ jsxs70("div", { className: "fdc-turn-head", children: [
9679
+ ctx.showAvatars ? /* @__PURE__ */ jsx74("span", { className: "fdc-avatar is-" + group.role, "aria-hidden": "true", children: avatar ? /* @__PURE__ */ jsx74("img", { src: avatar, alt: "" }) : isUser ? /* @__PURE__ */ jsx74("span", { className: "fdc-avatar-txt", children: (name || "You").slice(0, 1).toUpperCase() }) : /* @__PURE__ */ jsx74("i", { className: "ph ph-" + (ctx.assistantIcon || "sparkle") }) }) : null,
9680
+ /* @__PURE__ */ jsx74("span", { className: "fdc-who", children: name }),
9681
+ stamp ? /* @__PURE__ */ jsx74(RelativeTime, { className: "fdc-when", value: stamp }) : null,
9682
+ group.messages.some((m) => m.model) ? /* @__PURE__ */ jsx74("span", { className: "fdc-turn-model", children: group.messages.find((m) => m.model).model }) : null
9336
9683
  ] }),
9337
- /* @__PURE__ */ jsxs68("div", { className: "fdc-turn-body", children: [
9338
- group.messages.map((m) => /* @__PURE__ */ jsx72("div", { className: "fdc-msg" + (m.pending ? " is-pending" : ""), children: /* @__PURE__ */ jsx72(MessageBody, { message: m, ctx }) }, m.id)),
9339
- /* @__PURE__ */ jsx72(RunActions, { group, ctx })
9684
+ /* @__PURE__ */ jsxs70("div", { className: "fdc-turn-body", children: [
9685
+ group.messages.map((m) => /* @__PURE__ */ jsx74("div", { className: "fdc-msg" + (m.pending ? " is-pending" : ""), children: /* @__PURE__ */ jsx74(MessageBody, { message: m, ctx }) }, m.id)),
9686
+ /* @__PURE__ */ jsx74(RunActions, { group, ctx })
9340
9687
  ] })
9341
9688
  ] });
9342
9689
  }
9343
9690
 
9344
9691
  // src/components/chat/ChatTranscript.tsx
9345
- import { jsx as jsx73, jsxs as jsxs69 } from "react/jsx-runtime";
9692
+ import { jsx as jsx75, jsxs as jsxs71 } from "react/jsx-runtime";
9346
9693
  var GROUP_WINDOW = 6e4;
9347
9694
  var STICK_PX = 100;
9348
9695
  function groupMessages(list) {
@@ -9375,25 +9722,25 @@ function dayLabel(key) {
9375
9722
  }
9376
9723
  function Suggestions({ items = [], onPick }) {
9377
9724
  if (!items.length) return null;
9378
- return /* @__PURE__ */ jsx73("div", { className: "fdc-suggest", children: items.map((s, i) => {
9725
+ return /* @__PURE__ */ jsx75("div", { className: "fdc-suggest", children: items.map((s, i) => {
9379
9726
  const it = typeof s === "string" ? { label: s, text: s } : s;
9380
- return /* @__PURE__ */ jsxs69("button", { type: "button", className: "fdc-suggest-item", onClick: () => onPick && onPick(it.text || it.label), children: [
9381
- it.icon ? /* @__PURE__ */ jsx73("i", { className: "ph ph-" + it.icon, "aria-hidden": "true" }) : null,
9382
- /* @__PURE__ */ jsxs69("span", { className: "fdc-suggest-text", children: [
9383
- /* @__PURE__ */ jsx73("span", { className: "fdc-suggest-label", children: it.label }),
9384
- it.description ? /* @__PURE__ */ jsx73("span", { className: "fdc-suggest-desc", children: it.description }) : null
9727
+ return /* @__PURE__ */ jsxs71("button", { type: "button", className: "fdc-suggest-item", onClick: () => onPick && onPick(it.text || it.label), children: [
9728
+ it.icon ? /* @__PURE__ */ jsx75("i", { className: "ph ph-" + it.icon, "aria-hidden": "true" }) : null,
9729
+ /* @__PURE__ */ jsxs71("span", { className: "fdc-suggest-text", children: [
9730
+ /* @__PURE__ */ jsx75("span", { className: "fdc-suggest-label", children: it.label }),
9731
+ it.description ? /* @__PURE__ */ jsx75("span", { className: "fdc-suggest-desc", children: it.description }) : null
9385
9732
  ] }),
9386
- /* @__PURE__ */ jsx73("i", { className: "ph ph-arrow-up-right fdc-suggest-go", "aria-hidden": "true" })
9733
+ /* @__PURE__ */ jsx75("i", { className: "ph ph-arrow-up-right fdc-suggest-go", "aria-hidden": "true" })
9387
9734
  ] }, it.id || i);
9388
9735
  }) });
9389
9736
  }
9390
9737
  function LoadingTurns() {
9391
- return /* @__PURE__ */ jsx73("div", { className: "fdc-skel", "aria-hidden": "true", children: [0, 1].map((i) => /* @__PURE__ */ jsxs69("div", { className: "fdc-skel-turn", children: [
9392
- /* @__PURE__ */ jsx73("div", { className: "fd-skel fd-skel-circle", style: { width: 22, height: 22 } }),
9393
- /* @__PURE__ */ jsxs69("div", { className: "fdc-skel-lines", children: [
9394
- /* @__PURE__ */ jsx73("div", { className: "fd-skel", style: { width: i ? "62%" : "44%", height: 11 } }),
9395
- /* @__PURE__ */ jsx73("div", { className: "fd-skel", style: { width: i ? "94%" : "78%", height: 11 } }),
9396
- /* @__PURE__ */ jsx73("div", { className: "fd-skel", style: { width: i ? "71%" : "56%", height: 11 } })
9738
+ return /* @__PURE__ */ jsx75("div", { className: "fdc-skel", "aria-hidden": "true", children: [0, 1].map((i) => /* @__PURE__ */ jsxs71("div", { className: "fdc-skel-turn", children: [
9739
+ /* @__PURE__ */ jsx75("div", { className: "fd-skel fd-skel-circle", style: { width: 22, height: 22 } }),
9740
+ /* @__PURE__ */ jsxs71("div", { className: "fdc-skel-lines", children: [
9741
+ /* @__PURE__ */ jsx75("div", { className: "fd-skel", style: { width: i ? "62%" : "44%", height: 11 } }),
9742
+ /* @__PURE__ */ jsx75("div", { className: "fd-skel", style: { width: i ? "94%" : "78%", height: 11 } }),
9743
+ /* @__PURE__ */ jsx75("div", { className: "fd-skel", style: { width: i ? "71%" : "56%", height: 11 } })
9397
9744
  ] })
9398
9745
  ] }, i)) });
9399
9746
  }
@@ -9410,10 +9757,10 @@ function ChatTranscript({
9410
9757
  renderEmpty,
9411
9758
  className = ""
9412
9759
  }) {
9413
- const scroller = React43.useRef(null);
9414
- const stick = React43.useRef(true);
9415
- const [pill, setPill] = React43.useState(0);
9416
- const seen = React43.useRef(0);
9760
+ const scroller = React47.useRef(null);
9761
+ const stick = React47.useRef(true);
9762
+ const [pill, setPill] = React47.useState(0);
9763
+ const seen = React47.useRef(0);
9417
9764
  const toBottom = (smooth) => {
9418
9765
  const el = scroller.current;
9419
9766
  if (!el) return;
@@ -9432,7 +9779,7 @@ function ChatTranscript({
9432
9779
  seen.current = messages.length;
9433
9780
  }
9434
9781
  };
9435
- React43.useLayoutEffect(() => {
9782
+ React47.useLayoutEffect(() => {
9436
9783
  const el = scroller.current;
9437
9784
  if (!el) return;
9438
9785
  if (stick.current) {
@@ -9440,7 +9787,7 @@ function ChatTranscript({
9440
9787
  seen.current = messages.length;
9441
9788
  } else setPill(Math.max(0, messages.length - seen.current));
9442
9789
  }, [messages]);
9443
- React43.useEffect(() => {
9790
+ React47.useEffect(() => {
9444
9791
  const el = scroller.current;
9445
9792
  const inner = el && el.firstChild;
9446
9793
  if (!el || !inner || typeof ResizeObserver === "undefined") return;
@@ -9450,38 +9797,38 @@ function ChatTranscript({
9450
9797
  ro.observe(inner);
9451
9798
  return () => ro.disconnect();
9452
9799
  }, []);
9453
- const groups = React43.useMemo(() => groupMessages(messages), [messages]);
9800
+ const groups = React47.useMemo(() => groupMessages(messages), [messages]);
9454
9801
  const empty = !messages.length && status === "ready";
9455
- return /* @__PURE__ */ jsxs69("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
9456
- /* @__PURE__ */ jsxs69("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
9457
- status === "loading" || status === "resolving" ? /* @__PURE__ */ jsx73(LoadingTurns, {}) : null,
9458
- status === "disconnected" ? /* @__PURE__ */ jsxs69("div", { className: "fdc-dead", children: [
9459
- /* @__PURE__ */ jsx73("span", { className: "fdc-dead-icon", children: /* @__PURE__ */ jsx73("i", { className: "ph ph-plugs", "aria-hidden": "true" }) }),
9460
- /* @__PURE__ */ jsx73("h3", { className: "fdc-dead-title", children: ctx.deadTitle || "The assistant isn\u2019t reachable" }),
9461
- /* @__PURE__ */ jsx73("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." }),
9462
- ctx.onReconnect ? /* @__PURE__ */ jsxs69("button", { type: "button", className: "fd-btn fd-btn-secondary fd-btn-sm", onClick: ctx.onReconnect, children: [
9463
- /* @__PURE__ */ jsx73("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }),
9802
+ return /* @__PURE__ */ jsxs71("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
9803
+ /* @__PURE__ */ jsxs71("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
9804
+ status === "loading" || status === "resolving" ? /* @__PURE__ */ jsx75(LoadingTurns, {}) : null,
9805
+ status === "disconnected" ? /* @__PURE__ */ jsxs71("div", { className: "fdc-dead", children: [
9806
+ /* @__PURE__ */ jsx75("span", { className: "fdc-dead-icon", children: /* @__PURE__ */ jsx75("i", { className: "ph ph-plugs", "aria-hidden": "true" }) }),
9807
+ /* @__PURE__ */ jsx75("h3", { className: "fdc-dead-title", children: ctx.deadTitle || "The assistant isn\u2019t reachable" }),
9808
+ /* @__PURE__ */ jsx75("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." }),
9809
+ ctx.onReconnect ? /* @__PURE__ */ jsxs71("button", { type: "button", className: "fd-btn fd-btn-secondary fd-btn-sm", onClick: ctx.onReconnect, children: [
9810
+ /* @__PURE__ */ jsx75("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }),
9464
9811
  "Try again"
9465
9812
  ] }) : null
9466
9813
  ] }) : null,
9467
- empty ? renderEmpty ? renderEmpty() : /* @__PURE__ */ jsxs69("div", { className: "fdc-empty", children: [
9468
- /* @__PURE__ */ jsx73("span", { className: "fdc-empty-icon", children: /* @__PURE__ */ jsx73("i", { className: "ph ph-" + emptyIcon, "aria-hidden": "true" }) }),
9469
- /* @__PURE__ */ jsx73("h3", { className: "fdc-empty-title", children: emptyTitle }),
9470
- emptyDescription ? /* @__PURE__ */ jsx73("p", { className: "fdc-empty-body", children: emptyDescription }) : null,
9471
- /* @__PURE__ */ jsx73(Suggestions, { items: suggestions, onPick })
9814
+ empty ? renderEmpty ? renderEmpty() : /* @__PURE__ */ jsxs71("div", { className: "fdc-empty", children: [
9815
+ /* @__PURE__ */ jsx75("span", { className: "fdc-empty-icon", children: /* @__PURE__ */ jsx75("i", { className: "ph ph-" + emptyIcon, "aria-hidden": "true" }) }),
9816
+ /* @__PURE__ */ jsx75("h3", { className: "fdc-empty-title", children: emptyTitle }),
9817
+ emptyDescription ? /* @__PURE__ */ jsx75("p", { className: "fdc-empty-body", children: emptyDescription }) : null,
9818
+ /* @__PURE__ */ jsx75(Suggestions, { items: suggestions, onPick })
9472
9819
  ] }) : null,
9473
9820
  groups.map((g, i) => {
9474
9821
  const prev = groups[i - 1];
9475
9822
  const k = dayKey(g.messages[0].timestamp);
9476
9823
  const showDay = !!k && (!prev || dayKey(prev.messages[0].timestamp) !== k);
9477
- return /* @__PURE__ */ jsxs69(React43.Fragment, { children: [
9478
- showDay ? /* @__PURE__ */ jsx73("div", { className: "fdc-day", children: /* @__PURE__ */ jsx73("span", { children: dayLabel(k) }) }) : null,
9479
- /* @__PURE__ */ jsx73(ChatTurn, { group: g, ctx })
9824
+ return /* @__PURE__ */ jsxs71(React47.Fragment, { children: [
9825
+ showDay ? /* @__PURE__ */ jsx75("div", { className: "fdc-day", children: /* @__PURE__ */ jsx75("span", { children: dayLabel(k) }) }) : null,
9826
+ /* @__PURE__ */ jsx75(ChatTurn, { group: g, ctx })
9480
9827
  ] }, g.key || i);
9481
9828
  })
9482
9829
  ] }),
9483
- pill ? /* @__PURE__ */ jsxs69("button", { type: "button", className: "fdc-pill", onClick: () => toBottom(true), children: [
9484
- /* @__PURE__ */ jsx73("i", { className: "ph ph-arrow-down", "aria-hidden": "true" }),
9830
+ pill ? /* @__PURE__ */ jsxs71("button", { type: "button", className: "fdc-pill", onClick: () => toBottom(true), children: [
9831
+ /* @__PURE__ */ jsx75("i", { className: "ph ph-arrow-down", "aria-hidden": "true" }),
9485
9832
  pill,
9486
9833
  " new message",
9487
9834
  pill === 1 ? "" : "s"
@@ -9491,8 +9838,8 @@ function ChatTranscript({
9491
9838
  var TranscriptKit = { groupMessages };
9492
9839
 
9493
9840
  // src/components/chat/ChatComposer.tsx
9494
- import * as React44 from "react";
9495
- import { jsx as jsx74, jsxs as jsxs70 } from "react/jsx-runtime";
9841
+ import * as React48 from "react";
9842
+ import { jsx as jsx76, jsxs as jsxs72 } from "react/jsx-runtime";
9496
9843
  function ChatComposer({
9497
9844
  onSubmit,
9498
9845
  onStop,
@@ -9519,17 +9866,17 @@ function ChatComposer({
9519
9866
  onReject,
9520
9867
  onOpenAttachment
9521
9868
  }) {
9522
- const [text, setText] = React44.useState(draft || "");
9523
- const [trigger, setTrigger] = React44.useState(null);
9524
- const [mentionItems, setMentionItems] = React44.useState([]);
9525
- const [listening, setListening] = React44.useState(false);
9526
- const [notice, setNotice] = React44.useState(null);
9527
- const editor = React44.useRef(null);
9528
- const wrap = React44.useRef(null);
9529
- const stopVoice = React44.useRef(null);
9869
+ const [text, setText] = React48.useState(draft || "");
9870
+ const [trigger, setTrigger] = React48.useState(null);
9871
+ const [mentionItems, setMentionItems] = React48.useState([]);
9872
+ const [listening, setListening] = React48.useState(false);
9873
+ const [notice, setNotice] = React48.useState(null);
9874
+ const editor = React48.useRef(null);
9875
+ const wrap = React48.useRef(null);
9876
+ const stopVoice = React48.useRef(null);
9530
9877
  const staged = useStagedFiles(fileUploadHandler, { onError: () => {
9531
9878
  } });
9532
- React44.useEffect(() => {
9879
+ React48.useEffect(() => {
9533
9880
  if (draft != null && draft !== text) setText(draft);
9534
9881
  }, [draft]);
9535
9882
  const change = (v) => {
@@ -9565,7 +9912,7 @@ function ChatComposer({
9565
9912
  e.preventDefault();
9566
9913
  staged.add(found.files);
9567
9914
  };
9568
- React44.useEffect(() => {
9915
+ React48.useEffect(() => {
9569
9916
  if (!trigger || trigger.type !== "mention" || !mentionSources) {
9570
9917
  setMentionItems([]);
9571
9918
  return;
@@ -9583,7 +9930,7 @@ function ChatComposer({
9583
9930
  const q = (trigger.query || "").toLowerCase();
9584
9931
  setMentionItems(mentionSources.filter((m) => !q || (m.label + " " + (m.description || "")).toLowerCase().includes(q)));
9585
9932
  }, [trigger, mentionSources]);
9586
- const slashItems = React44.useMemo(() => {
9933
+ const slashItems = React48.useMemo(() => {
9587
9934
  if (!trigger || trigger.type !== "slash" || !slashCommands) return [];
9588
9935
  const q = (trigger.query || "").toLowerCase();
9589
9936
  return slashCommands.filter((c) => !q || (c.id + " " + c.label + " " + (c.description || "")).toLowerCase().includes(q));
@@ -9597,7 +9944,7 @@ function ChatComposer({
9597
9944
  description: it.description,
9598
9945
  icon: it.icon,
9599
9946
  meta: mention.meta,
9600
- shortcut: slash.shortcut ? /* @__PURE__ */ jsx74(KeyHint, { keys: slash.shortcut, size: "sm" }) : void 0,
9947
+ shortcut: slash.shortcut ? /* @__PURE__ */ jsx76(KeyHint, { keys: slash.shortcut, size: "sm" }) : void 0,
9601
9948
  onSelect: () => {
9602
9949
  const insert = trigger.type === "slash" ? slash.immediate ? "/" + slash.id : "/" + slash.id + " " : "@" + (mention.value || mention.label) + " ";
9603
9950
  editor.current && editor.current.replaceRange(trigger.from, trigger.to, insert);
@@ -9660,14 +10007,14 @@ function ChatComposer({
9660
10007
  stopVoice.current = typeof res === "function" ? res : () => {
9661
10008
  };
9662
10009
  };
9663
- return /* @__PURE__ */ jsxs70("div", { className: "fdc-composer" + (disabled ? " is-disabled" : "") + (narrow ? " is-narrow" : ""), children: [
9664
- queue.length ? /* @__PURE__ */ jsx74("div", { className: "fdc-queue", "aria-label": "Queued messages", children: queue.map((q) => /* @__PURE__ */ jsxs70("div", { className: "fdc-queue-row", children: [
9665
- /* @__PURE__ */ jsx74("i", { className: "ph ph-clock-countdown", "aria-hidden": "true" }),
9666
- /* @__PURE__ */ jsx74("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)" : "") }),
9667
- onRemoveQueued ? /* @__PURE__ */ jsx74("button", { type: "button", className: "fdc-queue-x", onClick: () => onRemoveQueued(q.id), "aria-label": "Remove queued message", children: /* @__PURE__ */ jsx74("i", { className: "ph ph-x", "aria-hidden": "true" }) }) : null
10010
+ return /* @__PURE__ */ jsxs72("div", { className: "fdc-composer" + (disabled ? " is-disabled" : "") + (narrow ? " is-narrow" : ""), children: [
10011
+ queue.length ? /* @__PURE__ */ jsx76("div", { className: "fdc-queue", "aria-label": "Queued messages", children: queue.map((q) => /* @__PURE__ */ jsxs72("div", { className: "fdc-queue-row", children: [
10012
+ /* @__PURE__ */ jsx76("i", { className: "ph ph-clock-countdown", "aria-hidden": "true" }),
10013
+ /* @__PURE__ */ jsx76("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)" : "") }),
10014
+ onRemoveQueued ? /* @__PURE__ */ jsx76("button", { type: "button", className: "fdc-queue-x", onClick: () => onRemoveQueued(q.id), "aria-label": "Remove queued message", children: /* @__PURE__ */ jsx76("i", { className: "ph ph-x", "aria-hidden": "true" }) }) : null
9668
10015
  ] }, q.id)) }) : null,
9669
10016
  sessionBar,
9670
- /* @__PURE__ */ jsx74(
10017
+ /* @__PURE__ */ jsx76(
9671
10018
  Dropzone,
9672
10019
  {
9673
10020
  className: "fdc-field",
@@ -9681,9 +10028,9 @@ function ChatComposer({
9681
10028
  disabled: !fileUploadHandler || disabled,
9682
10029
  label: "Drop to attach",
9683
10030
  hint: acceptFiles ? acceptFiles.replace(/,/g, " \xB7 ") : void 0,
9684
- children: /* @__PURE__ */ jsxs70("div", { ref: wrap, className: "fdc-field-inner", children: [
9685
- staged.items.length ? /* @__PURE__ */ jsx74("div", { className: "fdc-staged", children: /* @__PURE__ */ jsx74(FileStrip, { files: staged.items, size: narrow ? 60 : 68, onRemove: staged.remove, onRetry: staged.retry, onOpen: onOpenAttachment }) }) : null,
9686
- /* @__PURE__ */ jsx74(
10031
+ children: /* @__PURE__ */ jsxs72("div", { ref: wrap, className: "fdc-field-inner", children: [
10032
+ staged.items.length ? /* @__PURE__ */ jsx76("div", { className: "fdc-staged", children: /* @__PURE__ */ jsx76(FileStrip, { files: staged.items, size: narrow ? 60 : 68, onRemove: staged.remove, onRetry: staged.retry, onOpen: onOpenAttachment }) }) : null,
10033
+ /* @__PURE__ */ jsx76(
9687
10034
  MarkdownEditor,
9688
10035
  {
9689
10036
  ref: editor,
@@ -9702,33 +10049,33 @@ function ChatComposer({
9702
10049
  ariaLabel: "Message"
9703
10050
  }
9704
10051
  ),
9705
- /* @__PURE__ */ jsxs70("div", { className: "fdc-tools", children: [
9706
- fileUploadHandler ? /* @__PURE__ */ jsx74(FilePickButton, { onFiles: staged.add, onReject: (r) => flash(r[0].message), accept: acceptFiles, maxFileSize, label: "Attach files", icon: "plus" }) : null,
9707
- voiceHandler ? /* @__PURE__ */ jsx74("button", { type: "button", className: "fd-attachbtn" + (listening ? " is-live" : ""), onClick: voice, "aria-pressed": listening, "aria-label": listening ? "Stop dictation" : "Dictate", children: /* @__PURE__ */ jsx74("i", { className: "ph ph-" + (listening ? "waveform" : "microphone"), "aria-hidden": "true" }) }) : null,
10052
+ /* @__PURE__ */ jsxs72("div", { className: "fdc-tools", children: [
10053
+ fileUploadHandler ? /* @__PURE__ */ jsx76(FilePickButton, { onFiles: staged.add, onReject: (r) => flash(r[0].message), accept: acceptFiles, maxFileSize, label: "Attach files", icon: "plus" }) : null,
10054
+ voiceHandler ? /* @__PURE__ */ jsx76("button", { type: "button", className: "fd-attachbtn" + (listening ? " is-live" : ""), onClick: voice, "aria-pressed": listening, "aria-label": listening ? "Stop dictation" : "Dictate", children: /* @__PURE__ */ jsx76("i", { className: "ph ph-" + (listening ? "waveform" : "microphone"), "aria-hidden": "true" }) }) : null,
9708
10055
  toolbarExtras,
9709
- /* @__PURE__ */ jsx74("span", { className: "fdc-tools-gap" }),
9710
- maxLength && text.length > maxLength * 0.6 ? /* @__PURE__ */ jsxs70("span", { className: "fdc-count fd-tabular" + (text.length > maxLength * 0.95 ? " is-hot" : ""), children: [
10056
+ /* @__PURE__ */ jsx76("span", { className: "fdc-tools-gap" }),
10057
+ maxLength && text.length > maxLength * 0.6 ? /* @__PURE__ */ jsxs72("span", { className: "fdc-count fd-tabular" + (text.length > maxLength * 0.95 ? " is-hot" : ""), children: [
9711
10058
  text.length,
9712
10059
  "/",
9713
10060
  maxLength
9714
10061
  ] }) : null,
9715
- busy ? /* @__PURE__ */ jsxs70("button", { type: "button", className: "fdc-stop", onClick: onStop, "aria-label": "Stop generating", children: [
9716
- /* @__PURE__ */ jsx74("i", { className: "ph ph-stop-circle", "aria-hidden": "true" }),
9717
- /* @__PURE__ */ jsx74("span", { children: "Stop" })
9718
- ] }) : /* @__PURE__ */ jsxs70("button", { type: "button", className: "fdc-send", onClick: submit, disabled: !canSend, "aria-label": "Send message", children: [
9719
- /* @__PURE__ */ jsx74("i", { className: "ph ph-paper-plane-right", "aria-hidden": "true" }),
9720
- /* @__PURE__ */ jsx74("span", { className: "fdc-send-label", children: "Send" })
10062
+ busy ? /* @__PURE__ */ jsxs72("button", { type: "button", className: "fdc-stop", onClick: onStop, "aria-label": "Stop generating", children: [
10063
+ /* @__PURE__ */ jsx76("i", { className: "ph ph-stop-circle", "aria-hidden": "true" }),
10064
+ /* @__PURE__ */ jsx76("span", { children: "Stop" })
10065
+ ] }) : /* @__PURE__ */ jsxs72("button", { type: "button", className: "fdc-send", onClick: submit, disabled: !canSend, "aria-label": "Send message", children: [
10066
+ /* @__PURE__ */ jsx76("i", { className: "ph ph-paper-plane-right", "aria-hidden": "true" }),
10067
+ /* @__PURE__ */ jsx76("span", { className: "fdc-send-label", children: "Send" })
9721
10068
  ] })
9722
10069
  ] })
9723
10070
  ] })
9724
10071
  }
9725
10072
  ),
9726
- notice ? /* @__PURE__ */ jsxs70("div", { className: "fdc-notice", role: "status", children: [
9727
- /* @__PURE__ */ jsx74("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
10073
+ notice ? /* @__PURE__ */ jsxs72("div", { className: "fdc-notice", role: "status", children: [
10074
+ /* @__PURE__ */ jsx76("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
9728
10075
  notice
9729
10076
  ] }) : null,
9730
- hint && !notice ? /* @__PURE__ */ jsx74("div", { className: "fdc-hint", children: hint }) : null,
9731
- /* @__PURE__ */ jsx74(
10077
+ hint && !notice ? /* @__PURE__ */ jsx76("div", { className: "fdc-hint", children: hint }) : null,
10078
+ /* @__PURE__ */ jsx76(
9732
10079
  Popover,
9733
10080
  {
9734
10081
  open: menuOpen,
@@ -9742,12 +10089,12 @@ function ChatComposer({
9742
10089
  returnFocus: false,
9743
10090
  closeOnOutside: true,
9744
10091
  label: trigger && trigger.type === "slash" ? "Commands" : "Mentions",
9745
- children: /* @__PURE__ */ jsx74(
10092
+ children: /* @__PURE__ */ jsx76(
9746
10093
  Menu,
9747
10094
  {
9748
10095
  items: menuItems,
9749
10096
  autoFocus: false,
9750
- header: /* @__PURE__ */ jsx74("div", { className: "fd-pop-group", children: trigger && trigger.type === "slash" ? "Commands" : "Attach context" }),
10097
+ header: /* @__PURE__ */ jsx76("div", { className: "fd-pop-group", children: trigger && trigger.type === "slash" ? "Commands" : "Attach context" }),
9751
10098
  onClose: () => setTrigger(null)
9752
10099
  }
9753
10100
  )
@@ -9757,12 +10104,12 @@ function ChatComposer({
9757
10104
  }
9758
10105
 
9759
10106
  // src/components/chat/ChatSessionBar.tsx
9760
- import * as React45 from "react";
9761
- import { jsx as jsx75, jsxs as jsxs71 } from "react/jsx-runtime";
10107
+ import * as React49 from "react";
10108
+ import { jsx as jsx77, jsxs as jsxs73 } from "react/jsx-runtime";
9762
10109
  var compact2 = meterFormats.compact;
9763
10110
  function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }) {
9764
- const [open, setOpen] = React45.useState(false);
9765
- const anchor = React45.useRef(null);
10111
+ const [open, setOpen] = React49.useState(false);
10112
+ const anchor = React49.useRef(null);
9766
10113
  const stats = sessionStats || null;
9767
10114
  const cu = contextUsage || null;
9768
10115
  const limits = usageLimits || null;
@@ -9777,9 +10124,9 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9777
10124
  if (stats && stats.runningTasks) bits.push(stats.runningTasks + " running task" + (stats.runningTasks === 1 ? "" : "s"));
9778
10125
  if (cu) bits.push(pct + "% context");
9779
10126
  const expandable = !!(cu || limits);
9780
- return /* @__PURE__ */ jsxs71(React45.Fragment, { children: [
9781
- /* @__PURE__ */ jsxs71("div", { className: "fdc-bar", children: [
9782
- /* @__PURE__ */ jsxs71(
10127
+ return /* @__PURE__ */ jsxs73(React49.Fragment, { children: [
10128
+ /* @__PURE__ */ jsxs73("div", { className: "fdc-bar", children: [
10129
+ /* @__PURE__ */ jsxs73(
9783
10130
  "button",
9784
10131
  {
9785
10132
  type: "button",
@@ -9790,15 +10137,15 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9790
10137
  "aria-expanded": expandable ? open : void 0,
9791
10138
  "aria-label": expandable ? "Usage details" : void 0,
9792
10139
  children: [
9793
- cu ? /* @__PURE__ */ jsx75("span", { className: "fdc-bar-mini", "aria-hidden": "true", children: /* @__PURE__ */ jsx75(SegmentedMeter, { total, segments: cu.segments, height: 4, showTotal: false }) }) : null,
9794
- /* @__PURE__ */ jsx75("span", { className: "fdc-bar-text", title: truncationTitle(bits.join(" \xB7 ")), children: bits.join(" \xB7 ") }),
9795
- expandable ? /* @__PURE__ */ jsx75("i", { className: "ph ph-caret-right fdc-bar-caret", "aria-hidden": "true" }) : null
10140
+ cu ? /* @__PURE__ */ jsx77("span", { className: "fdc-bar-mini", "aria-hidden": "true", children: /* @__PURE__ */ jsx77(SegmentedMeter, { total, segments: cu.segments, height: 4, showTotal: false }) }) : null,
10141
+ /* @__PURE__ */ jsx77("span", { className: "fdc-bar-text", title: truncationTitle(bits.join(" \xB7 ")), children: bits.join(" \xB7 ") }),
10142
+ expandable ? /* @__PURE__ */ jsx77("i", { className: "ph ph-caret-right fdc-bar-caret", "aria-hidden": "true" }) : null
9796
10143
  ]
9797
10144
  }
9798
10145
  ),
9799
10146
  extras
9800
10147
  ] }),
9801
- /* @__PURE__ */ jsx75(
10148
+ /* @__PURE__ */ jsx77(
9802
10149
  Popover,
9803
10150
  {
9804
10151
  open,
@@ -9809,15 +10156,15 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9809
10156
  maxHeight: 460,
9810
10157
  padded: true,
9811
10158
  label: "Usage",
9812
- footer: onClear ? /* @__PURE__ */ jsxs71("button", { type: "button", className: "fdc-usage-clear", onClick: () => {
10159
+ footer: onClear ? /* @__PURE__ */ jsxs73("button", { type: "button", className: "fdc-usage-clear", onClick: () => {
9813
10160
  setOpen(false);
9814
10161
  onClear();
9815
10162
  }, children: [
9816
- /* @__PURE__ */ jsx75("i", { className: "ph ph-trash", "aria-hidden": "true" }),
10163
+ /* @__PURE__ */ jsx77("i", { className: "ph ph-trash", "aria-hidden": "true" }),
9817
10164
  "Clear conversation"
9818
10165
  ] }) : void 0,
9819
- children: /* @__PURE__ */ jsxs71("div", { className: "fdc-usage", children: [
9820
- cu ? /* @__PURE__ */ jsx75("section", { className: "fdc-usage-sec", children: /* @__PURE__ */ jsx75(
10166
+ children: /* @__PURE__ */ jsxs73("div", { className: "fdc-usage", children: [
10167
+ cu ? /* @__PURE__ */ jsx77("section", { className: "fdc-usage-sec", children: /* @__PURE__ */ jsx77(
9821
10168
  SegmentedMeter,
9822
10169
  {
9823
10170
  total,
@@ -9829,9 +10176,9 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9829
10176
  remainderLabel: "Free"
9830
10177
  }
9831
10178
  ) }) : null,
9832
- limits && limits.length ? /* @__PURE__ */ jsxs71("section", { className: "fdc-usage-sec", children: [
9833
- /* @__PURE__ */ jsx75("h4", { className: "fdc-usage-h", children: "Usage limits" }),
9834
- /* @__PURE__ */ jsx75("div", { className: "fdc-usage-rows", children: limits.map((l) => /* @__PURE__ */ jsx75(
10179
+ limits && limits.length ? /* @__PURE__ */ jsxs73("section", { className: "fdc-usage-sec", children: [
10180
+ /* @__PURE__ */ jsx77("h4", { className: "fdc-usage-h", children: "Usage limits" }),
10181
+ /* @__PURE__ */ jsx77("div", { className: "fdc-usage-rows", children: limits.map((l) => /* @__PURE__ */ jsx77(
9835
10182
  QuotaRow,
9836
10183
  {
9837
10184
  label: l.label,
@@ -9841,25 +10188,25 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9841
10188
  l.id
9842
10189
  )) })
9843
10190
  ] }) : null,
9844
- stats ? /* @__PURE__ */ jsxs71("section", { className: "fdc-usage-sec fdc-usage-stats", children: [
9845
- stats.elapsedMs ? /* @__PURE__ */ jsxs71("div", { children: [
9846
- /* @__PURE__ */ jsx75("span", { children: "Session" }),
9847
- /* @__PURE__ */ jsx75("b", { className: "fd-tabular", children: formatDuration(stats.elapsedMs) })
10191
+ stats ? /* @__PURE__ */ jsxs73("section", { className: "fdc-usage-sec fdc-usage-stats", children: [
10192
+ stats.elapsedMs ? /* @__PURE__ */ jsxs73("div", { children: [
10193
+ /* @__PURE__ */ jsx77("span", { children: "Session" }),
10194
+ /* @__PURE__ */ jsx77("b", { className: "fd-tabular", children: formatDuration(stats.elapsedMs) })
9848
10195
  ] }) : null,
9849
- stats.tokens ? /* @__PURE__ */ jsxs71("div", { children: [
9850
- /* @__PURE__ */ jsx75("span", { children: "Tokens" }),
9851
- /* @__PURE__ */ jsx75("b", { className: "fd-tabular", children: stats.tokens.toLocaleString() })
10196
+ stats.tokens ? /* @__PURE__ */ jsxs73("div", { children: [
10197
+ /* @__PURE__ */ jsx77("span", { children: "Tokens" }),
10198
+ /* @__PURE__ */ jsx77("b", { className: "fd-tabular", children: stats.tokens.toLocaleString() })
9852
10199
  ] }) : null,
9853
- stats.costUsd != null ? /* @__PURE__ */ jsxs71("div", { children: [
9854
- /* @__PURE__ */ jsx75("span", { children: "Cost" }),
9855
- /* @__PURE__ */ jsxs71("b", { className: "fd-tabular", children: [
10200
+ stats.costUsd != null ? /* @__PURE__ */ jsxs73("div", { children: [
10201
+ /* @__PURE__ */ jsx77("span", { children: "Cost" }),
10202
+ /* @__PURE__ */ jsxs73("b", { className: "fd-tabular", children: [
9856
10203
  "$",
9857
10204
  Number(stats.costUsd).toFixed(3)
9858
10205
  ] })
9859
10206
  ] }) : null,
9860
- stats.turns ? /* @__PURE__ */ jsxs71("div", { children: [
9861
- /* @__PURE__ */ jsx75("span", { children: "Turns" }),
9862
- /* @__PURE__ */ jsx75("b", { className: "fd-tabular", children: stats.turns })
10207
+ stats.turns ? /* @__PURE__ */ jsxs73("div", { children: [
10208
+ /* @__PURE__ */ jsx77("span", { children: "Turns" }),
10209
+ /* @__PURE__ */ jsx77("b", { className: "fd-tabular", children: stats.turns })
9863
10210
  ] }) : null
9864
10211
  ] }) : null
9865
10212
  ] })
@@ -9898,7 +10245,7 @@ function ModelControls({
9898
10245
  disabled: m.disabled,
9899
10246
  checked: m.id === (current2 && current2.id),
9900
10247
  meta: m.meta,
9901
- shortcut: m.shortcut ? /* @__PURE__ */ jsx75(KeyHint, { keys: m.shortcut, size: "sm" }) : void 0,
10248
+ shortcut: m.shortcut ? /* @__PURE__ */ jsx77(KeyHint, { keys: m.shortcut, size: "sm" }) : void 0,
9902
10249
  onSelect: () => onModelChange && onModelChange(m.id)
9903
10250
  });
9904
10251
  const items = [{ kind: "section", label: "Models" }].concat(flat.map(item));
@@ -9911,15 +10258,15 @@ function ModelControls({
9911
10258
  items.push({ kind: "section", label: "Fast mode" });
9912
10259
  items.push({
9913
10260
  kind: "custom",
9914
- render: () => /* @__PURE__ */ jsxs71("label", { className: "fdc-switchrow", children: [
9915
- /* @__PURE__ */ jsx75("span", { children: fastModeLabel }),
9916
- /* @__PURE__ */ jsx75("input", { type: "checkbox", className: "fd-sr", checked: !!fastMode, onChange: (e) => onFastModeChange(e.target.checked) }),
9917
- /* @__PURE__ */ jsx75("span", { className: "fd-switch-track" + (fastMode ? " is-on" : ""), "aria-hidden": "true", children: /* @__PURE__ */ jsx75("span", { className: "fd-switch-thumb" }) })
10261
+ render: () => /* @__PURE__ */ jsxs73("label", { className: "fdc-switchrow", children: [
10262
+ /* @__PURE__ */ jsx77("span", { children: fastModeLabel }),
10263
+ /* @__PURE__ */ jsx77("input", { type: "checkbox", className: "fd-sr", checked: !!fastMode, onChange: (e) => onFastModeChange(e.target.checked) }),
10264
+ /* @__PURE__ */ jsx77("span", { className: "fd-switch-track" + (fastMode ? " is-on" : ""), "aria-hidden": "true", children: /* @__PURE__ */ jsx77("span", { className: "fd-switch-thumb" }) })
9918
10265
  ] })
9919
10266
  });
9920
10267
  }
9921
- return /* @__PURE__ */ jsxs71(React45.Fragment, { children: [
9922
- /* @__PURE__ */ jsx75(
10268
+ return /* @__PURE__ */ jsxs73(React49.Fragment, { children: [
10269
+ /* @__PURE__ */ jsx77(
9923
10270
  MenuButton,
9924
10271
  {
9925
10272
  items,
@@ -9930,7 +10277,7 @@ function ModelControls({
9930
10277
  title: "Choose a model"
9931
10278
  }
9932
10279
  ),
9933
- effortLevels && effortLevels.length ? /* @__PURE__ */ jsx75(
10280
+ effortLevels && effortLevels.length ? /* @__PURE__ */ jsx77(
9934
10281
  MenuButton,
9935
10282
  {
9936
10283
  placement: "top-end",
@@ -9951,7 +10298,7 @@ function ModelControls({
9951
10298
  }
9952
10299
 
9953
10300
  // src/components/chat/AgentChatPanel.tsx
9954
- import { jsx as jsx76, jsxs as jsxs72 } from "react/jsx-runtime";
10301
+ import { jsx as jsx78, jsxs as jsxs74 } from "react/jsx-runtime";
9955
10302
  var SURFACES = { sidebar: "is-sidebar", inline: "is-inline", page: "is-page", modal: "is-modal", sheet: "is-sheet" };
9956
10303
  function AgentChatPanel({
9957
10304
  /* required */
@@ -10035,11 +10382,11 @@ function AgentChatPanel({
10035
10382
  onFeedback,
10036
10383
  onEditMessage
10037
10384
  }) {
10038
- const [panelWidth, setPanelWidth] = React46.useState(width);
10039
- const [applied, setApplied] = React46.useState({});
10040
- const [draft, setDraft] = React46.useState("");
10041
- const dragging = React46.useRef(null);
10042
- React46.useEffect(() => setPanelWidth(width), [width]);
10385
+ const [panelWidth, setPanelWidth] = React50.useState(width);
10386
+ const [applied, setApplied] = React50.useState({});
10387
+ const [draft, setDraft] = React50.useState("");
10388
+ const dragging = React50.useRef(null);
10389
+ React50.useEffect(() => setPanelWidth(width), [width]);
10043
10390
  const engine = useChatEngine({
10044
10391
  contextType,
10045
10392
  contextId,
@@ -10105,7 +10452,7 @@ function AgentChatPanel({
10105
10452
  onReconnect: engine.reload,
10106
10453
  deadTitle: "The assistant isn\u2019t reachable"
10107
10454
  };
10108
- const sessionBar = /* @__PURE__ */ jsx76(
10455
+ const sessionBar = /* @__PURE__ */ jsx78(
10109
10456
  ChatSessionBar,
10110
10457
  {
10111
10458
  sessionStats,
@@ -10114,7 +10461,7 @@ function AgentChatPanel({
10114
10461
  onClear: engine.visible.length ? engine.clear : void 0
10115
10462
  }
10116
10463
  );
10117
- const modelControls = /* @__PURE__ */ jsx76(
10464
+ const modelControls = /* @__PURE__ */ jsx78(
10118
10465
  ModelControls,
10119
10466
  {
10120
10467
  models,
@@ -10129,7 +10476,7 @@ function AgentChatPanel({
10129
10476
  narrow
10130
10477
  }
10131
10478
  );
10132
- const threadMenu = threads && threads.length ? /* @__PURE__ */ jsx76(
10479
+ const threadMenu = threads && threads.length ? /* @__PURE__ */ jsx78(
10133
10480
  MenuButton,
10134
10481
  {
10135
10482
  variant: "ghost",
@@ -10156,7 +10503,7 @@ function AgentChatPanel({
10156
10503
  })))
10157
10504
  }
10158
10505
  ) : null;
10159
- return /* @__PURE__ */ jsxs72(
10506
+ return /* @__PURE__ */ jsxs74(
10160
10507
  "aside",
10161
10508
  {
10162
10509
  className: ["fdc-panel", SURFACES[surface] || SURFACES.sidebar, narrow ? "is-narrow" : "", className].filter(Boolean).join(" "),
@@ -10167,7 +10514,7 @@ function AgentChatPanel({
10167
10514
  },
10168
10515
  "aria-label": title,
10169
10516
  children: [
10170
- surface === "sidebar" && resizable ? /* @__PURE__ */ jsx76(
10517
+ surface === "sidebar" && resizable ? /* @__PURE__ */ jsx78(
10171
10518
  "div",
10172
10519
  {
10173
10520
  className: "fdc-grip",
@@ -10182,20 +10529,20 @@ function AgentChatPanel({
10182
10529
  }
10183
10530
  }
10184
10531
  ) : null,
10185
- showHeader ? /* @__PURE__ */ jsxs72("header", { className: "fdc-head", children: [
10186
- /* @__PURE__ */ jsx76("span", { className: "fdc-head-mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx76("i", { className: "ph ph-" + assistantIcon }) }),
10187
- /* @__PURE__ */ jsxs72("span", { className: "fdc-head-titles", children: [
10188
- /* @__PURE__ */ jsx76("span", { className: "fdc-head-title", title: truncationTitle(title), children: title }),
10189
- subtitle ? /* @__PURE__ */ jsx76("span", { className: "fdc-head-sub", title: truncationTitle(subtitle), children: subtitle }) : null
10532
+ showHeader ? /* @__PURE__ */ jsxs74("header", { className: "fdc-head", children: [
10533
+ /* @__PURE__ */ jsx78("span", { className: "fdc-head-mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx78("i", { className: "ph ph-" + assistantIcon }) }),
10534
+ /* @__PURE__ */ jsxs74("span", { className: "fdc-head-titles", children: [
10535
+ /* @__PURE__ */ jsx78("span", { className: "fdc-head-title", title: truncationTitle(title), children: title }),
10536
+ subtitle ? /* @__PURE__ */ jsx78("span", { className: "fdc-head-sub", title: truncationTitle(subtitle), children: subtitle }) : null
10190
10537
  ] }),
10191
- /* @__PURE__ */ jsxs72("span", { className: "fdc-head-acts", children: [
10538
+ /* @__PURE__ */ jsxs74("span", { className: "fdc-head-acts", children: [
10192
10539
  headerActions,
10193
10540
  threadMenu,
10194
- onNewThread ? /* @__PURE__ */ jsx76("button", { type: "button", className: "fdc-iconbtn", onClick: onNewThread, "aria-label": "New conversation", title: "New conversation", children: /* @__PURE__ */ jsx76("i", { className: "ph ph-plus", "aria-hidden": "true" }) }) : null,
10195
- onClose ? /* @__PURE__ */ jsx76("button", { type: "button", className: "fdc-iconbtn", onClick: onClose, "aria-label": "Close panel", title: "Close", children: /* @__PURE__ */ jsx76("i", { className: "ph ph-x", "aria-hidden": "true" }) }) : null
10541
+ onNewThread ? /* @__PURE__ */ jsx78("button", { type: "button", className: "fdc-iconbtn", onClick: onNewThread, "aria-label": "New conversation", title: "New conversation", children: /* @__PURE__ */ jsx78("i", { className: "ph ph-plus", "aria-hidden": "true" }) }) : null,
10542
+ onClose ? /* @__PURE__ */ jsx78("button", { type: "button", className: "fdc-iconbtn", onClick: onClose, "aria-label": "Close panel", title: "Close", children: /* @__PURE__ */ jsx78("i", { className: "ph ph-x", "aria-hidden": "true" }) }) : null
10196
10543
  ] })
10197
10544
  ] }) : null,
10198
- /* @__PURE__ */ jsx76(
10545
+ /* @__PURE__ */ jsx78(
10199
10546
  ChatTranscript,
10200
10547
  {
10201
10548
  messages: engine.visible,
@@ -10210,7 +10557,7 @@ function AgentChatPanel({
10210
10557
  renderEmpty
10211
10558
  }
10212
10559
  ),
10213
- /* @__PURE__ */ jsx76(
10560
+ /* @__PURE__ */ jsx78(
10214
10561
  ChatComposer,
10215
10562
  {
10216
10563
  onSubmit: (text, atts) => engine.send(text, atts),
@@ -10243,8 +10590,8 @@ function AgentChatPanel({
10243
10590
  }
10244
10591
 
10245
10592
  // src/components/files/FileViewer.tsx
10246
- import * as React47 from "react";
10247
- import { jsx as jsx77, jsxs as jsxs73 } from "react/jsx-runtime";
10593
+ import * as React51 from "react";
10594
+ import { jsx as jsx79, jsxs as jsxs75 } from "react/jsx-runtime";
10248
10595
  function previewKindOf(file) {
10249
10596
  const mime = (file.mime || "").toLowerCase();
10250
10597
  const ext = extensionOf(file.name || "");
@@ -10300,13 +10647,13 @@ function parseDelimited(text, delimiter) {
10300
10647
  return rows;
10301
10648
  }
10302
10649
  function usePreview(file, kind, resolveUrl) {
10303
- const [state, setState] = React47.useState({ status: "loading" });
10304
- const [attempt, setAttempt] = React47.useState(0);
10305
- const resolveRef = React47.useRef(resolveUrl);
10306
- React47.useEffect(() => {
10650
+ const [state, setState] = React51.useState({ status: "loading" });
10651
+ const [attempt, setAttempt] = React51.useState(0);
10652
+ const resolveRef = React51.useRef(resolveUrl);
10653
+ React51.useEffect(() => {
10307
10654
  resolveRef.current = resolveUrl;
10308
10655
  }, [resolveUrl]);
10309
- React47.useEffect(() => {
10656
+ React51.useEffect(() => {
10310
10657
  if (kind === "none") {
10311
10658
  setState({ status: "ready" });
10312
10659
  return;
@@ -10336,19 +10683,19 @@ function usePreview(file, kind, resolveUrl) {
10336
10683
  if (ac) ac.abort();
10337
10684
  };
10338
10685
  }, [file.id, kind, attempt]);
10339
- return { ...state, retry: React47.useCallback(() => setAttempt((a) => a + 1), []) };
10686
+ return { ...state, retry: React51.useCallback(() => setAttempt((a) => a + 1), []) };
10340
10687
  }
10341
10688
  function PreviewBody({ file, kind, state }) {
10342
- if (kind === "image") return /* @__PURE__ */ jsx77("img", { className: "fd-fv-image", src: state.url, alt: file.name || "Preview" });
10343
- if (kind === "pdf") return /* @__PURE__ */ jsx77("iframe", { className: "fd-fv-pdf", src: state.url, title: file.name || "PDF preview" });
10344
- if (kind === "markdown") return /* @__PURE__ */ jsx77(Markdown, { className: "fd-fv-markdown", source: state.text || "" });
10689
+ if (kind === "image") return /* @__PURE__ */ jsx79("img", { className: "fd-fv-image", src: state.url, alt: file.name || "Preview" });
10690
+ if (kind === "pdf") return /* @__PURE__ */ jsx79("iframe", { className: "fd-fv-pdf", src: state.url, title: file.name || "PDF preview" });
10691
+ if (kind === "markdown") return /* @__PURE__ */ jsx79(Markdown, { className: "fd-fv-markdown", source: state.text || "" });
10345
10692
  if (kind === "json") {
10346
10693
  let pretty = state.text || "";
10347
10694
  try {
10348
10695
  pretty = JSON.stringify(JSON.parse(pretty), null, 2);
10349
10696
  } catch {
10350
10697
  }
10351
- return /* @__PURE__ */ jsx77(CodeBlock, { code: pretty, language: "json" });
10698
+ return /* @__PURE__ */ jsx79(CodeBlock, { code: pretty, language: "json" });
10352
10699
  }
10353
10700
  if (kind === "csv") {
10354
10701
  const tsv = extensionOf(file.name || "") === "tsv" || (file.mime || "").toLowerCase().includes("tab-separated");
@@ -10356,12 +10703,12 @@ function PreviewBody({ file, kind, state }) {
10356
10703
  const head = rows[0] || [];
10357
10704
  const dataRows = rows.slice(1);
10358
10705
  const shown = dataRows.slice(0, FILE_VIEWER_CSV_ROW_LIMIT);
10359
- return /* @__PURE__ */ jsxs73("div", { className: "fd-fv-csv", children: [
10360
- /* @__PURE__ */ jsxs73("table", { className: "fd-fv-csv-table", children: [
10361
- /* @__PURE__ */ jsx77("thead", { children: /* @__PURE__ */ jsx77("tr", { children: head.map((c, i) => /* @__PURE__ */ jsx77("th", { children: c }, i)) }) }),
10362
- /* @__PURE__ */ jsx77("tbody", { children: shown.map((r, ri) => /* @__PURE__ */ jsx77("tr", { children: head.map((_, ci) => /* @__PURE__ */ jsx77("td", { children: r[ci] ?? "" }, ci)) }, ri)) })
10706
+ return /* @__PURE__ */ jsxs75("div", { className: "fd-fv-csv", children: [
10707
+ /* @__PURE__ */ jsxs75("table", { className: "fd-fv-csv-table", children: [
10708
+ /* @__PURE__ */ jsx79("thead", { children: /* @__PURE__ */ jsx79("tr", { children: head.map((c, i) => /* @__PURE__ */ jsx79("th", { children: c }, i)) }) }),
10709
+ /* @__PURE__ */ jsx79("tbody", { children: shown.map((r, ri) => /* @__PURE__ */ jsx79("tr", { children: head.map((_, ci) => /* @__PURE__ */ jsx79("td", { children: r[ci] ?? "" }, ci)) }, ri)) })
10363
10710
  ] }),
10364
- dataRows.length > FILE_VIEWER_CSV_ROW_LIMIT ? /* @__PURE__ */ jsxs73("p", { className: "fd-fv-csv-note fd-body-sm fd-muted", children: [
10711
+ dataRows.length > FILE_VIEWER_CSV_ROW_LIMIT ? /* @__PURE__ */ jsxs75("p", { className: "fd-fv-csv-note fd-body-sm fd-muted", children: [
10365
10712
  "Showing the first ",
10366
10713
  FILE_VIEWER_CSV_ROW_LIMIT.toLocaleString(),
10367
10714
  " of ",
@@ -10370,24 +10717,24 @@ function PreviewBody({ file, kind, state }) {
10370
10717
  ] }) : null
10371
10718
  ] });
10372
10719
  }
10373
- if (kind === "text") return /* @__PURE__ */ jsx77(CodeBlock, { code: state.text || "", language: extensionOf(file.name || "") || void 0 });
10374
- return /* @__PURE__ */ jsxs73("div", { className: "fd-fv-fallback", children: [
10375
- /* @__PURE__ */ jsx77("span", { className: "fd-fv-fallback-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx77("i", { className: "ph ph-" + iconForMime(file.mime, file.name) }) }),
10376
- /* @__PURE__ */ jsx77("span", { className: "fd-fv-fallback-title", children: "No preview available" }),
10377
- /* @__PURE__ */ jsxs73("span", { className: "fd-fv-fallback-meta", children: [
10720
+ if (kind === "text") return /* @__PURE__ */ jsx79(CodeBlock, { code: state.text || "", language: extensionOf(file.name || "") || void 0 });
10721
+ return /* @__PURE__ */ jsxs75("div", { className: "fd-fv-fallback", children: [
10722
+ /* @__PURE__ */ jsx79("span", { className: "fd-fv-fallback-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx79("i", { className: "ph ph-" + iconForMime(file.mime, file.name) }) }),
10723
+ /* @__PURE__ */ jsx79("span", { className: "fd-fv-fallback-title", children: "No preview available" }),
10724
+ /* @__PURE__ */ jsxs75("span", { className: "fd-fv-fallback-meta", children: [
10378
10725
  file.mime || "Unknown type",
10379
10726
  file.size ? " \xB7 " + formatBytes(file.size) : ""
10380
10727
  ] })
10381
10728
  ] });
10382
10729
  }
10383
10730
  function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", onClose, actions, className = "" }) {
10384
- const kind = React47.useMemo(() => previewKindOf(file), [file]);
10731
+ const kind = React51.useMemo(() => previewKindOf(file), [file]);
10385
10732
  const preview = usePreview(file, kind, resolveUrl);
10386
- const [tab, setTab] = React47.useState("preview");
10387
- const [downloading, setDownloading] = React47.useState(false);
10388
- const [downloadError, setDownloadError] = React47.useState(null);
10733
+ const [tab, setTab] = React51.useState("preview");
10734
+ const [downloading, setDownloading] = React51.useState(false);
10735
+ const [downloadError, setDownloadError] = React51.useState(null);
10389
10736
  const hasSide = sidePanel != null;
10390
- React47.useEffect(() => {
10737
+ React51.useEffect(() => {
10391
10738
  setTab("preview");
10392
10739
  }, [file.id]);
10393
10740
  const download = () => {
@@ -10403,29 +10750,29 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
10403
10750
  a.remove();
10404
10751
  }).catch((err) => setDownloadError(err && err.message || "Download failed.")).finally(() => setDownloading(false));
10405
10752
  };
10406
- return /* @__PURE__ */ jsxs73(
10753
+ return /* @__PURE__ */ jsxs75(
10407
10754
  "section",
10408
10755
  {
10409
10756
  className: ["fd-fv", hasSide && tab === "side" ? "is-tab-side" : "is-tab-preview", className].filter(Boolean).join(" "),
10410
10757
  "aria-label": file.name || "File",
10411
10758
  children: [
10412
- /* @__PURE__ */ jsxs73("header", { className: "fd-fv-head", children: [
10413
- /* @__PURE__ */ jsx77("span", { className: "fd-fv-head-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx77("i", { className: "ph ph-" + iconForMime(file.mime, file.name) }) }),
10414
- /* @__PURE__ */ jsxs73("span", { className: "fd-fv-head-titles", children: [
10415
- /* @__PURE__ */ jsx77("span", { className: "fd-fv-head-title", title: file.name, children: file.name }),
10416
- /* @__PURE__ */ jsxs73("span", { className: "fd-fv-head-meta", children: [
10759
+ /* @__PURE__ */ jsxs75("header", { className: "fd-fv-head", children: [
10760
+ /* @__PURE__ */ jsx79("span", { className: "fd-fv-head-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx79("i", { className: "ph ph-" + iconForMime(file.mime, file.name) }) }),
10761
+ /* @__PURE__ */ jsxs75("span", { className: "fd-fv-head-titles", children: [
10762
+ /* @__PURE__ */ jsx79("span", { className: "fd-fv-head-title", title: file.name, children: file.name }),
10763
+ /* @__PURE__ */ jsxs75("span", { className: "fd-fv-head-meta", children: [
10417
10764
  file.mime || "Unknown type",
10418
10765
  file.size ? " \xB7 " + formatBytes(file.size) : ""
10419
10766
  ] })
10420
10767
  ] }),
10421
- /* @__PURE__ */ jsxs73("span", { className: "fd-fv-head-actions", children: [
10768
+ /* @__PURE__ */ jsxs75("span", { className: "fd-fv-head-actions", children: [
10422
10769
  actions,
10423
- /* @__PURE__ */ jsx77(Button, { variant: "secondary", size: "sm", icon: "download-simple", loading: downloading, onClick: download, children: "Download" }),
10424
- onClose ? /* @__PURE__ */ jsx77(IconButton, { icon: "x", label: "Close", onClick: onClose }) : null
10770
+ /* @__PURE__ */ jsx79(Button, { variant: "secondary", size: "sm", icon: "download-simple", loading: downloading, onClick: download, children: "Download" }),
10771
+ onClose ? /* @__PURE__ */ jsx79(IconButton, { icon: "x", label: "Close", onClick: onClose }) : null
10425
10772
  ] })
10426
10773
  ] }),
10427
- downloadError ? /* @__PURE__ */ jsx77("div", { className: "fd-fv-download-error", role: "alert", children: downloadError }) : null,
10428
- hasSide ? /* @__PURE__ */ jsx77(
10774
+ downloadError ? /* @__PURE__ */ jsx79("div", { className: "fd-fv-download-error", role: "alert", children: downloadError }) : null,
10775
+ hasSide ? /* @__PURE__ */ jsx79(
10429
10776
  Tabs,
10430
10777
  {
10431
10778
  className: "fd-fv-tabs",
@@ -10434,19 +10781,19 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
10434
10781
  onChange: (v) => setTab(v)
10435
10782
  }
10436
10783
  ) : null,
10437
- /* @__PURE__ */ jsxs73("div", { className: "fd-fv-body", children: [
10438
- /* @__PURE__ */ jsx77("div", { className: "fd-fv-preview", role: "region", "aria-label": "Preview of " + (file.name || "file"), children: /* @__PURE__ */ jsx77(
10784
+ /* @__PURE__ */ jsxs75("div", { className: "fd-fv-body", children: [
10785
+ /* @__PURE__ */ jsx79("div", { className: "fd-fv-preview", role: "region", "aria-label": "Preview of " + (file.name || "file"), children: /* @__PURE__ */ jsx79(
10439
10786
  LoadingRegion,
10440
10787
  {
10441
10788
  state: preview.status === "loading" ? "loading" : preview.status === "error" ? "error" : "ready",
10442
10789
  error: preview.error,
10443
10790
  label: "Loading preview of " + (file.name || "file"),
10444
10791
  onRetry: preview.status === "error" ? preview.retry : void 0,
10445
- skeleton: /* @__PURE__ */ jsx77(Skeleton, { shape: "card", h: 220 }),
10446
- children: /* @__PURE__ */ jsx77(PreviewBody, { file, kind, state: preview })
10792
+ skeleton: /* @__PURE__ */ jsx79(Skeleton, { shape: "card", h: 220 }),
10793
+ children: /* @__PURE__ */ jsx79(PreviewBody, { file, kind, state: preview })
10447
10794
  }
10448
10795
  ) }),
10449
- hasSide ? /* @__PURE__ */ jsx77("div", { className: "fd-fv-side", role: "region", "aria-label": sidePanelLabel, children: sidePanel }) : null
10796
+ hasSide ? /* @__PURE__ */ jsx79("div", { className: "fd-fv-side", role: "region", "aria-label": sidePanelLabel, children: sidePanel }) : null
10450
10797
  ] })
10451
10798
  ]
10452
10799
  }
@@ -10454,8 +10801,8 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
10454
10801
  }
10455
10802
 
10456
10803
  // src/components/files/FileBrowser.tsx
10457
- import * as React48 from "react";
10458
- import { Fragment as Fragment23, jsx as jsx78, jsxs as jsxs74 } from "react/jsx-runtime";
10804
+ import * as React52 from "react";
10805
+ import { Fragment as Fragment25, jsx as jsx80, jsxs as jsxs76 } from "react/jsx-runtime";
10459
10806
  function childrenOf(folders, parentId) {
10460
10807
  return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
10461
10808
  }
@@ -10525,10 +10872,10 @@ function FolderPickerMenu({
10525
10872
  destinations,
10526
10873
  onPick
10527
10874
  }) {
10528
- const [open, setOpen] = React48.useState(false);
10875
+ const [open, setOpen] = React52.useState(false);
10529
10876
  const zIndex = useOverlayLayer(open);
10530
- const ref = React48.useRef(null);
10531
- React48.useEffect(() => {
10877
+ const ref = React52.useRef(null);
10878
+ React52.useEffect(() => {
10532
10879
  if (!open) return;
10533
10880
  const away = (e) => {
10534
10881
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -10543,8 +10890,8 @@ function FolderPickerMenu({
10543
10890
  document.removeEventListener("keydown", esc);
10544
10891
  };
10545
10892
  }, [open]);
10546
- return /* @__PURE__ */ jsxs74("span", { className: "fd-fb-menu-anchor", ref, children: [
10547
- /* @__PURE__ */ jsx78(
10893
+ return /* @__PURE__ */ jsxs76("span", { className: "fd-fb-menu-anchor", ref, children: [
10894
+ /* @__PURE__ */ jsx80(
10548
10895
  IconButton,
10549
10896
  {
10550
10897
  icon: "arrows-out-cardinal",
@@ -10556,9 +10903,9 @@ function FolderPickerMenu({
10556
10903
  onClick: () => setOpen((v) => !v)
10557
10904
  }
10558
10905
  ),
10559
- open ? /* @__PURE__ */ jsx78(LayerProvider, { zIndex, children: /* @__PURE__ */ jsxs74("div", { className: "fd-fb-menu", role: "menu", "aria-label": label, style: { zIndex }, children: [
10560
- /* @__PURE__ */ jsx78("span", { className: "fd-overline fd-muted fd-fb-menu-title", children: "Move to\u2026" }),
10561
- destinations.map((d) => /* @__PURE__ */ jsxs74(
10906
+ open ? /* @__PURE__ */ jsx80(LayerProvider, { zIndex, children: /* @__PURE__ */ jsxs76("div", { className: "fd-fb-menu", role: "menu", "aria-label": label, style: { zIndex }, children: [
10907
+ /* @__PURE__ */ jsx80("span", { className: "fd-overline fd-muted fd-fb-menu-title", children: "Move to\u2026" }),
10908
+ destinations.map((d) => /* @__PURE__ */ jsxs76(
10562
10909
  "button",
10563
10910
  {
10564
10911
  type: "button",
@@ -10570,7 +10917,7 @@ function FolderPickerMenu({
10570
10917
  setOpen(false);
10571
10918
  },
10572
10919
  children: [
10573
- /* @__PURE__ */ jsx78("i", { className: "ph ph-" + (d.id == null ? "house-simple" : "folder-simple"), "aria-hidden": "true" }),
10920
+ /* @__PURE__ */ jsx80("i", { className: "ph ph-" + (d.id == null ? "house-simple" : "folder-simple"), "aria-hidden": "true" }),
10574
10921
  d.name
10575
10922
  ]
10576
10923
  },
@@ -10580,8 +10927,8 @@ function FolderPickerMenu({
10580
10927
  ] });
10581
10928
  }
10582
10929
  function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
10583
- const [over, setOver] = React48.useState(false);
10584
- const dragCounter = React48.useRef(0);
10930
+ const [over, setOver] = React52.useState(false);
10931
+ const dragCounter = React52.useRef(0);
10585
10932
  const dropHandlers = isDropTarget ? {
10586
10933
  onDragEnter: (e) => {
10587
10934
  if (!hasInternalPayload(e)) return;
@@ -10606,8 +10953,8 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
10606
10953
  if (payload && onDropPayload) onDropPayload(payload);
10607
10954
  }
10608
10955
  } : {};
10609
- return /* @__PURE__ */ jsxs74("div", { className: ["fd-fb-row", over ? "is-drop-target" : "", className].filter(Boolean).join(" "), ...dropHandlers, children: [
10610
- /* @__PURE__ */ jsxs74(
10956
+ return /* @__PURE__ */ jsxs76("div", { className: ["fd-fb-row", over ? "is-drop-target" : "", className].filter(Boolean).join(" "), ...dropHandlers, children: [
10957
+ /* @__PURE__ */ jsxs76(
10611
10958
  "button",
10612
10959
  {
10613
10960
  type: "button",
@@ -10616,26 +10963,26 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
10616
10963
  draggable: !!draggableId,
10617
10964
  onDragStart: draggableId ? (e) => writeDragPayload(e, draggableId) : void 0,
10618
10965
  children: [
10619
- /* @__PURE__ */ jsx78("span", { className: "fd-fb-row-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx78("i", { className: "ph ph-" + icon }) }),
10620
- /* @__PURE__ */ jsxs74("span", { className: "fd-fb-row-text", children: [
10621
- /* @__PURE__ */ jsx78("span", { className: "fd-fb-row-name", title: name, children: name }),
10622
- meta ? /* @__PURE__ */ jsx78("span", { className: "fd-fb-row-meta", children: meta }) : null
10966
+ /* @__PURE__ */ jsx80("span", { className: "fd-fb-row-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx80("i", { className: "ph ph-" + icon }) }),
10967
+ /* @__PURE__ */ jsxs76("span", { className: "fd-fb-row-text", children: [
10968
+ /* @__PURE__ */ jsx80("span", { className: "fd-fb-row-name", title: name, children: name }),
10969
+ meta ? /* @__PURE__ */ jsx80("span", { className: "fd-fb-row-meta", children: meta }) : null
10623
10970
  ] })
10624
10971
  ]
10625
10972
  }
10626
10973
  ),
10627
- actions ? /* @__PURE__ */ jsx78("span", { className: "fd-fb-row-actions", children: actions }) : null
10974
+ actions ? /* @__PURE__ */ jsx80("span", { className: "fd-fb-row-actions", children: actions }) : null
10628
10975
  ] });
10629
10976
  }
10630
10977
  function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
10631
10978
  const kids = childrenOf(folders, folder.id);
10632
- const [expanded, setExpanded] = React48.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
10633
- React48.useEffect(() => {
10979
+ const [expanded, setExpanded] = React52.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
10980
+ React52.useEffect(() => {
10634
10981
  if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
10635
10982
  }, [currentFolderId, folder.id, folders]);
10636
10983
  const isCurrent = folder.id === currentFolderId;
10637
- return /* @__PURE__ */ jsxs74("div", { className: "fd-fb-tree-node", children: [
10638
- /* @__PURE__ */ jsx78(
10984
+ return /* @__PURE__ */ jsxs76("div", { className: "fd-fb-tree-node", children: [
10985
+ /* @__PURE__ */ jsx80(
10639
10986
  RowShell,
10640
10987
  {
10641
10988
  icon: folder.id === currentFolderId ? "folder-open" : "folder-simple",
@@ -10645,7 +10992,7 @@ function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveI
10645
10992
  isDropTarget: canMove,
10646
10993
  onDropPayload: (payload) => onMoveInto(payload, folder.id),
10647
10994
  className: ["fd-fb-tree-row", isCurrent ? "is-current" : ""].filter(Boolean).join(" "),
10648
- actions: kids.length ? /* @__PURE__ */ jsx78(
10995
+ actions: kids.length ? /* @__PURE__ */ jsx80(
10649
10996
  IconButton,
10650
10997
  {
10651
10998
  icon: expanded ? "caret-down" : "caret-right",
@@ -10657,7 +11004,7 @@ function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveI
10657
11004
  ) : void 0
10658
11005
  }
10659
11006
  ),
10660
- expanded && kids.length ? /* @__PURE__ */ jsx78("div", { className: "fd-fb-tree-children", style: { paddingLeft: 14 }, children: kids.map((k) => /* @__PURE__ */ jsx78(
11007
+ expanded && kids.length ? /* @__PURE__ */ jsx80("div", { className: "fd-fb-tree-children", style: { paddingLeft: 14 }, children: kids.map((k) => /* @__PURE__ */ jsx80(
10661
11008
  TreeNode,
10662
11009
  {
10663
11010
  folder: k,
@@ -10696,15 +11043,15 @@ function FileBrowser({
10696
11043
  const canUpload = perm.canUpload !== false && !!onUpload;
10697
11044
  const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
10698
11045
  const canDeleteFolder = perm.canDeleteFolder !== false && !!onDeleteFolder;
10699
- const [createOpen, setCreateOpen] = React48.useState(false);
10700
- const [createName, setCreateName] = React48.useState("");
10701
- const [renameTarget, setRenameTarget] = React48.useState(null);
10702
- const [renameValue, setRenameValue] = React48.useState("");
10703
- const [deleteTarget, setDeleteTarget] = React48.useState(null);
10704
- const uploadInputRef = React48.useRef(null);
10705
- const crumbFolders = React48.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
10706
- const subfolders = React48.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
10707
- const currentFiles = React48.useMemo(
11046
+ const [createOpen, setCreateOpen] = React52.useState(false);
11047
+ const [createName, setCreateName] = React52.useState("");
11048
+ const [renameTarget, setRenameTarget] = React52.useState(null);
11049
+ const [renameValue, setRenameValue] = React52.useState("");
11050
+ const [deleteTarget, setDeleteTarget] = React52.useState(null);
11051
+ const uploadInputRef = React52.useRef(null);
11052
+ const crumbFolders = React52.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
11053
+ const subfolders = React52.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
11054
+ const currentFiles = React52.useMemo(
10708
11055
  () => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
10709
11056
  [files, currentFolderId]
10710
11057
  );
@@ -10743,10 +11090,10 @@ function FileBrowser({
10743
11090
  if (deleteTarget && onDeleteFolder) onDeleteFolder(deleteTarget);
10744
11091
  setDeleteTarget(null);
10745
11092
  };
10746
- const content = /* @__PURE__ */ jsxs74("div", { className: ["fd-fb", className].filter(Boolean).join(" "), children: [
10747
- /* @__PURE__ */ jsxs74("div", { className: "fd-fb-head", children: [
10748
- /* @__PURE__ */ jsxs74("nav", { className: "fd-fb-crumbs", "aria-label": "Folder path", children: [
10749
- /* @__PURE__ */ jsx78(
11093
+ const content = /* @__PURE__ */ jsxs76("div", { className: ["fd-fb", className].filter(Boolean).join(" "), children: [
11094
+ /* @__PURE__ */ jsxs76("div", { className: "fd-fb-head", children: [
11095
+ /* @__PURE__ */ jsxs76("nav", { className: "fd-fb-crumbs", "aria-label": "Folder path", children: [
11096
+ /* @__PURE__ */ jsx80(
10750
11097
  BreadcrumbCrumb,
10751
11098
  {
10752
11099
  label: "Home",
@@ -10756,7 +11103,7 @@ function FileBrowser({
10756
11103
  onDropPayload: (p) => moveIntoFolder(p, null)
10757
11104
  }
10758
11105
  ),
10759
- crumbFolders.map((f, i) => /* @__PURE__ */ jsx78(
11106
+ crumbFolders.map((f, i) => /* @__PURE__ */ jsx80(
10760
11107
  BreadcrumbCrumb,
10761
11108
  {
10762
11109
  label: f.name,
@@ -10768,11 +11115,11 @@ function FileBrowser({
10768
11115
  f.id
10769
11116
  ))
10770
11117
  ] }),
10771
- /* @__PURE__ */ jsxs74("span", { className: "fd-fb-actions", children: [
10772
- canCreateFolder ? /* @__PURE__ */ jsx78(Button, { variant: "secondary", size: "sm", icon: "folder-simple-plus", onClick: openCreate, children: "New folder" }) : null,
10773
- canUpload ? /* @__PURE__ */ jsxs74(Fragment23, { children: [
10774
- /* @__PURE__ */ jsx78(Button, { variant: "secondary", size: "sm", icon: "upload-simple", onClick: () => uploadInputRef.current && uploadInputRef.current.click(), children: "Upload" }),
10775
- /* @__PURE__ */ jsx78(
11118
+ /* @__PURE__ */ jsxs76("span", { className: "fd-fb-actions", children: [
11119
+ canCreateFolder ? /* @__PURE__ */ jsx80(Button, { variant: "secondary", size: "sm", icon: "folder-simple-plus", onClick: openCreate, children: "New folder" }) : null,
11120
+ canUpload ? /* @__PURE__ */ jsxs76(Fragment25, { children: [
11121
+ /* @__PURE__ */ jsx80(Button, { variant: "secondary", size: "sm", icon: "upload-simple", onClick: () => uploadInputRef.current && uploadInputRef.current.click(), children: "Upload" }),
11122
+ /* @__PURE__ */ jsx80(
10776
11123
  "input",
10777
11124
  {
10778
11125
  ref: uploadInputRef,
@@ -10790,9 +11137,9 @@ function FileBrowser({
10790
11137
  ] }) : null
10791
11138
  ] })
10792
11139
  ] }),
10793
- /* @__PURE__ */ jsxs74("div", { className: "fd-fb-body", children: [
10794
- /* @__PURE__ */ jsxs74("div", { className: "fd-fb-tree", role: "region", "aria-label": "Folder tree", children: [
10795
- /* @__PURE__ */ jsx78(
11140
+ /* @__PURE__ */ jsxs76("div", { className: "fd-fb-body", children: [
11141
+ /* @__PURE__ */ jsxs76("div", { className: "fd-fb-tree", role: "region", "aria-label": "Folder tree", children: [
11142
+ /* @__PURE__ */ jsx80(
10796
11143
  RowShell,
10797
11144
  {
10798
11145
  icon: "house-simple",
@@ -10803,7 +11150,7 @@ function FileBrowser({
10803
11150
  className: ["fd-fb-tree-row", currentFolderId == null ? "is-current" : ""].filter(Boolean).join(" ")
10804
11151
  }
10805
11152
  ),
10806
- childrenOf(folders, null).map((f) => /* @__PURE__ */ jsx78(
11153
+ childrenOf(folders, null).map((f) => /* @__PURE__ */ jsx80(
10807
11154
  TreeNode,
10808
11155
  {
10809
11156
  folder: f,
@@ -10817,18 +11164,18 @@ function FileBrowser({
10817
11164
  f.id
10818
11165
  ))
10819
11166
  ] }),
10820
- /* @__PURE__ */ jsx78("div", { className: "fd-fb-main", role: "region", "aria-label": "Folder contents", children: /* @__PURE__ */ jsx78(
11167
+ /* @__PURE__ */ jsx80("div", { className: "fd-fb-main", role: "region", "aria-label": "Folder contents", children: /* @__PURE__ */ jsx80(
10821
11168
  LoadingRegion,
10822
11169
  {
10823
11170
  state: loading ? "loading" : "ready",
10824
11171
  label: "Loading folder contents",
10825
- skeleton: /* @__PURE__ */ jsxs74("div", { className: "fd-stack", style: { gap: 8, padding: 12 }, children: [
10826
- /* @__PURE__ */ jsx78(Skeleton, { h: 44, radius: "10px" }),
10827
- /* @__PURE__ */ jsx78(Skeleton, { h: 44, radius: "10px" }),
10828
- /* @__PURE__ */ jsx78(Skeleton, { h: 44, radius: "10px" })
11172
+ skeleton: /* @__PURE__ */ jsxs76("div", { className: "fd-stack", style: { gap: 8, padding: 12 }, children: [
11173
+ /* @__PURE__ */ jsx80(Skeleton, { h: 44, radius: "10px" }),
11174
+ /* @__PURE__ */ jsx80(Skeleton, { h: 44, radius: "10px" }),
11175
+ /* @__PURE__ */ jsx80(Skeleton, { h: 44, radius: "10px" })
10829
11176
  ] }),
10830
- children: isEmpty ? /* @__PURE__ */ jsx78(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__ */ jsxs74("div", { className: "fd-fb-list", children: [
10831
- subfolders.map((f) => /* @__PURE__ */ jsx78(
11177
+ children: isEmpty ? /* @__PURE__ */ jsx80(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__ */ jsxs76("div", { className: "fd-fb-list", children: [
11178
+ subfolders.map((f) => /* @__PURE__ */ jsx80(
10832
11179
  RowShell,
10833
11180
  {
10834
11181
  icon: "folder-simple",
@@ -10837,9 +11184,9 @@ function FileBrowser({
10837
11184
  draggableId: canMove ? { kind: "folder", id: f.id } : void 0,
10838
11185
  isDropTarget: canMove,
10839
11186
  onDropPayload: (p) => moveIntoFolder(p, f.id),
10840
- actions: /* @__PURE__ */ jsxs74(Fragment23, { children: [
10841
- canRenameFolder ? /* @__PURE__ */ jsx78(IconButton, { icon: "pencil-simple", label: "Rename " + f.name, size: "sm", variant: "ghost", onClick: () => openRename(f) }) : null,
10842
- canMove ? /* @__PURE__ */ jsx78(
11187
+ actions: /* @__PURE__ */ jsxs76(Fragment25, { children: [
11188
+ canRenameFolder ? /* @__PURE__ */ jsx80(IconButton, { icon: "pencil-simple", label: "Rename " + f.name, size: "sm", variant: "ghost", onClick: () => openRename(f) }) : null,
11189
+ canMove ? /* @__PURE__ */ jsx80(
10843
11190
  FolderPickerMenu,
10844
11191
  {
10845
11192
  label: "Move " + f.name,
@@ -10847,12 +11194,12 @@ function FileBrowser({
10847
11194
  onPick: (target) => moveIntoFolder({ kind: "folder", id: f.id }, target)
10848
11195
  }
10849
11196
  ) : null,
10850
- canDeleteFolder ? /* @__PURE__ */ jsx78(IconButton, { icon: "trash-simple", label: "Delete " + f.name, size: "sm", variant: "ghost", onClick: () => openDelete(f) }) : null
11197
+ canDeleteFolder ? /* @__PURE__ */ jsx80(IconButton, { icon: "trash-simple", label: "Delete " + f.name, size: "sm", variant: "ghost", onClick: () => openDelete(f) }) : null
10851
11198
  ] })
10852
11199
  },
10853
11200
  f.id
10854
11201
  )),
10855
- currentFiles.map((file) => /* @__PURE__ */ jsx78(
11202
+ currentFiles.map((file) => /* @__PURE__ */ jsx80(
10856
11203
  RowShell,
10857
11204
  {
10858
11205
  icon: iconForMime(file.mime, file.name),
@@ -10860,8 +11207,8 @@ function FileBrowser({
10860
11207
  meta: file.size ? formatBytes(file.size) : void 0,
10861
11208
  onOpen: onOpenFile ? () => onOpenFile(file) : void 0,
10862
11209
  draggableId: canMove ? { kind: "file", id: file.id } : void 0,
10863
- actions: /* @__PURE__ */ jsxs74(Fragment23, { children: [
10864
- canMove ? /* @__PURE__ */ jsx78(
11210
+ actions: /* @__PURE__ */ jsxs76(Fragment25, { children: [
11211
+ canMove ? /* @__PURE__ */ jsx80(
10865
11212
  FolderPickerMenu,
10866
11213
  {
10867
11214
  label: "Move " + file.name,
@@ -10869,7 +11216,7 @@ function FileBrowser({
10869
11216
  onPick: (target) => onMoveFile && onMoveFile(file.id, target)
10870
11217
  }
10871
11218
  ) : null,
10872
- canRemoveFile ? /* @__PURE__ */ jsx78(IconButton, { icon: "trash-simple", label: "Remove " + file.name, size: "sm", variant: "ghost", onClick: () => onRemoveFile && onRemoveFile(file) }) : null
11219
+ canRemoveFile ? /* @__PURE__ */ jsx80(IconButton, { icon: "trash-simple", label: "Remove " + file.name, size: "sm", variant: "ghost", onClick: () => onRemoveFile && onRemoveFile(file) }) : null
10873
11220
  ] })
10874
11221
  },
10875
11222
  file.id
@@ -10878,10 +11225,10 @@ function FileBrowser({
10878
11225
  }
10879
11226
  ) })
10880
11227
  ] }),
10881
- /* @__PURE__ */ jsx78(Modal, { open: createOpen, title: "New folder", onClose: () => setCreateOpen(false), footer: /* @__PURE__ */ jsxs74(Fragment23, { children: [
10882
- /* @__PURE__ */ jsx78(Button, { variant: "secondary", onClick: () => setCreateOpen(false), children: "Cancel" }),
10883
- /* @__PURE__ */ jsx78(Button, { onClick: submitCreate, disabled: !createName.trim(), children: "Create" })
10884
- ] }), children: /* @__PURE__ */ jsx78(
11228
+ /* @__PURE__ */ jsx80(Modal, { open: createOpen, title: "New folder", onClose: () => setCreateOpen(false), footer: /* @__PURE__ */ jsxs76(Fragment25, { children: [
11229
+ /* @__PURE__ */ jsx80(Button, { variant: "secondary", onClick: () => setCreateOpen(false), children: "Cancel" }),
11230
+ /* @__PURE__ */ jsx80(Button, { onClick: submitCreate, disabled: !createName.trim(), children: "Create" })
11231
+ ] }), children: /* @__PURE__ */ jsx80(
10885
11232
  "input",
10886
11233
  {
10887
11234
  className: "fd-fb-input",
@@ -10895,10 +11242,10 @@ function FileBrowser({
10895
11242
  autoFocus: true
10896
11243
  }
10897
11244
  ) }),
10898
- /* @__PURE__ */ jsx78(Modal, { open: renameTarget != null, title: "Rename folder", onClose: () => setRenameTarget(null), footer: /* @__PURE__ */ jsxs74(Fragment23, { children: [
10899
- /* @__PURE__ */ jsx78(Button, { variant: "secondary", onClick: () => setRenameTarget(null), children: "Cancel" }),
10900
- /* @__PURE__ */ jsx78(Button, { onClick: submitRename, disabled: !renameValue.trim(), children: "Save" })
10901
- ] }), children: /* @__PURE__ */ jsx78(
11245
+ /* @__PURE__ */ jsx80(Modal, { open: renameTarget != null, title: "Rename folder", onClose: () => setRenameTarget(null), footer: /* @__PURE__ */ jsxs76(Fragment25, { children: [
11246
+ /* @__PURE__ */ jsx80(Button, { variant: "secondary", onClick: () => setRenameTarget(null), children: "Cancel" }),
11247
+ /* @__PURE__ */ jsx80(Button, { onClick: submitRename, disabled: !renameValue.trim(), children: "Save" })
11248
+ ] }), children: /* @__PURE__ */ jsx80(
10902
11249
  "input",
10903
11250
  {
10904
11251
  className: "fd-fb-input",
@@ -10912,22 +11259,22 @@ function FileBrowser({
10912
11259
  autoFocus: true
10913
11260
  }
10914
11261
  ) }),
10915
- /* @__PURE__ */ jsx78(
11262
+ /* @__PURE__ */ jsx80(
10916
11263
  Modal,
10917
11264
  {
10918
11265
  open: deleteTarget != null,
10919
11266
  title: deleteTarget ? "Delete \u201C" + deleteTarget.name + "\u201D?" : "Delete folder?",
10920
11267
  description: "This can't be undone. The folder must be empty.",
10921
11268
  onClose: () => setDeleteTarget(null),
10922
- footer: /* @__PURE__ */ jsxs74(Fragment23, { children: [
10923
- /* @__PURE__ */ jsx78(Button, { variant: "secondary", onClick: () => setDeleteTarget(null), children: "Cancel" }),
10924
- /* @__PURE__ */ jsx78(Button, { variant: "danger", onClick: confirmDelete, children: "Delete" })
11269
+ footer: /* @__PURE__ */ jsxs76(Fragment25, { children: [
11270
+ /* @__PURE__ */ jsx80(Button, { variant: "secondary", onClick: () => setDeleteTarget(null), children: "Cancel" }),
11271
+ /* @__PURE__ */ jsx80(Button, { variant: "danger", onClick: confirmDelete, children: "Delete" })
10925
11272
  ] })
10926
11273
  }
10927
11274
  )
10928
11275
  ] });
10929
11276
  if (!canUpload) return content;
10930
- return /* @__PURE__ */ jsx78(Dropzone, { className: "fd-fb-dropzone", onFiles: (picked) => onUpload && onUpload(picked, currentFolderId), label: "Drop files to upload", children: content });
11277
+ return /* @__PURE__ */ jsx80(Dropzone, { className: "fd-fb-dropzone", onFiles: (picked) => onUpload && onUpload(picked, currentFolderId), label: "Drop files to upload", children: content });
10931
11278
  }
10932
11279
  function BreadcrumbCrumb({
10933
11280
  label,
@@ -10936,8 +11283,8 @@ function BreadcrumbCrumb({
10936
11283
  isDropTarget,
10937
11284
  onDropPayload
10938
11285
  }) {
10939
- const [over, setOver] = React48.useState(false);
10940
- const dragCounter = React48.useRef(0);
11286
+ const [over, setOver] = React52.useState(false);
11287
+ const dragCounter = React52.useRef(0);
10941
11288
  const dropHandlers = isDropTarget ? {
10942
11289
  onDragEnter: (e) => {
10943
11290
  if (!hasInternalPayload(e)) return;
@@ -10962,11 +11309,11 @@ function BreadcrumbCrumb({
10962
11309
  if (payload && onDropPayload) onDropPayload(payload);
10963
11310
  }
10964
11311
  } : {};
10965
- return /* @__PURE__ */ jsx78("span", { className: ["fd-fb-crumb", over ? "is-drop-target" : ""].filter(Boolean).join(" "), ...dropHandlers, children: isCurrent ? /* @__PURE__ */ jsx78("span", { className: "fd-fb-crumb-current", "aria-current": "page", children: label }) : /* @__PURE__ */ jsx78("button", { type: "button", className: "fd-fb-crumb-link", onClick, children: label }) });
11312
+ return /* @__PURE__ */ jsx80("span", { className: ["fd-fb-crumb", over ? "is-drop-target" : ""].filter(Boolean).join(" "), ...dropHandlers, children: isCurrent ? /* @__PURE__ */ jsx80("span", { className: "fd-fb-crumb-current", "aria-current": "page", children: label }) : /* @__PURE__ */ jsx80("button", { type: "button", className: "fd-fb-crumb-link", onClick, children: label }) });
10966
11313
  }
10967
11314
 
10968
11315
  // src/kits/query.ts
10969
- import * as React49 from "react";
11316
+ import * as React53 from "react";
10970
11317
  function eqFilter(get2) {
10971
11318
  return (row, value) => Array.isArray(value) ? value.includes(get2(row)) : get2(row) === value;
10972
11319
  }
@@ -10998,22 +11345,22 @@ function compare(a, b, dir) {
10998
11345
  var API = null;
10999
11346
  var PREFS = null;
11000
11347
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
11001
- const [query, setQuery] = React49.useState(() => {
11348
+ const [query, setQuery] = React53.useState(() => {
11002
11349
  const store = PREFS || window.PlannerPrefs;
11003
11350
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
11004
11351
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
11005
11352
  });
11006
- const savePref = React49.useCallback((patch2) => {
11353
+ const savePref = React53.useCallback((patch2) => {
11007
11354
  const store = PREFS || window.PlannerPrefs;
11008
11355
  if (prefsKey && store) store.setTable(prefsKey, patch2);
11009
11356
  }, [prefsKey]);
11010
- const [res, setRes] = React49.useState(null);
11011
- const [loading, setLoading] = React49.useState(true);
11012
- const seq2 = React49.useRef(0);
11357
+ const [res, setRes] = React53.useState(null);
11358
+ const [loading, setLoading] = React53.useState(true);
11359
+ const seq2 = React53.useRef(0);
11013
11360
  const depKey = (deps || []).join("|");
11014
11361
  const paramKey = JSON.stringify(params || {});
11015
11362
  const queryKey = JSON.stringify(query);
11016
- React49.useEffect(() => {
11363
+ React53.useEffect(() => {
11017
11364
  const id = ++seq2.current;
11018
11365
  setLoading(true);
11019
11366
  const t = setTimeout(() => {
@@ -11314,6 +11661,8 @@ export {
11314
11661
  AccountMenu,
11315
11662
  AgentChatPanel,
11316
11663
  ApiSpecBrowser,
11664
+ AppSwitcher,
11665
+ AppearanceMenu,
11317
11666
  Avatar,
11318
11667
  AvatarStack,
11319
11668
  Badge,
@@ -11344,8 +11693,11 @@ export {
11344
11693
  CsiBadge,
11345
11694
  CsiHero,
11346
11695
  CsiMeter,
11696
+ DEFAULT_PREFS,
11697
+ DEFAULT_STORAGE_KEY,
11347
11698
  DataTable,
11348
11699
  DatePicker,
11700
+ DemoSessionProvider,
11349
11701
  DiffBlock,
11350
11702
  Drawer,
11351
11703
  Dropzone,
@@ -11415,6 +11767,7 @@ export {
11415
11767
  SegmentedMeter,
11416
11768
  Select,
11417
11769
  SessionKit,
11770
+ SessionProvider,
11418
11771
  SidebarNav,
11419
11772
  Skeleton,
11420
11773
  SkeletonText,
@@ -11443,9 +11796,11 @@ export {
11443
11796
  VirtualList,
11444
11797
  acceptMatches,
11445
11798
  anyOfFilter,
11799
+ applyPrefs,
11446
11800
  channelWeightOf,
11447
11801
  computeNeedMore,
11448
11802
  computeVirtualWindow,
11803
+ createLocalStorageAppearanceStore,
11449
11804
  createVersionStore,
11450
11805
  eqFilter,
11451
11806
  extensionOf,
@@ -11466,6 +11821,7 @@ export {
11466
11821
  isSystemMessage,
11467
11822
  jobBucket,
11468
11823
  languageLabel,
11824
+ loadPrefs,
11469
11825
  markdownToText,
11470
11826
  meterFormats,
11471
11827
  modifierLabel,
@@ -11475,14 +11831,18 @@ export {
11475
11831
  preferenceScore,
11476
11832
  preferenceSegments,
11477
11833
  rangeFilter,
11834
+ resolveTheme,
11478
11835
  revokeAttachment,
11836
+ savePrefs,
11479
11837
  toAttachment,
11480
11838
  tokenize,
11839
+ useAppearancePrefs,
11481
11840
  useChatEngine,
11482
11841
  useFeatureStatus,
11483
11842
  usePopoverPosition,
11484
11843
  useRuntimeMode,
11485
11844
  useServerTable,
11845
+ useSessionContext,
11486
11846
  useStagedFiles,
11487
11847
  visibleMessages
11488
11848
  };