@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/README.md +44 -0
- package/dist/index.cjs +1665 -1292
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +481 -207
- package/dist/index.d.ts +481 -207
- package/dist/index.js +1667 -1307
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/styles/tokens.css +44 -0
package/dist/index.cjs
CHANGED
|
@@ -33,6 +33,8 @@ __export(index_exports, {
|
|
|
33
33
|
AccountMenu: () => AccountMenu,
|
|
34
34
|
AgentChatPanel: () => AgentChatPanel,
|
|
35
35
|
ApiSpecBrowser: () => ApiSpecBrowser,
|
|
36
|
+
AppSwitcher: () => AppSwitcher,
|
|
37
|
+
AppearanceMenu: () => AppearanceMenu,
|
|
36
38
|
Avatar: () => Avatar,
|
|
37
39
|
AvatarStack: () => AvatarStack,
|
|
38
40
|
Badge: () => Badge,
|
|
@@ -63,8 +65,11 @@ __export(index_exports, {
|
|
|
63
65
|
CsiBadge: () => CsiBadge,
|
|
64
66
|
CsiHero: () => CsiHero,
|
|
65
67
|
CsiMeter: () => CsiMeter,
|
|
68
|
+
DEFAULT_PREFS: () => DEFAULT_PREFS,
|
|
69
|
+
DEFAULT_STORAGE_KEY: () => DEFAULT_STORAGE_KEY,
|
|
66
70
|
DataTable: () => DataTable,
|
|
67
71
|
DatePicker: () => DatePicker,
|
|
72
|
+
DemoSessionProvider: () => DemoSessionProvider,
|
|
68
73
|
DiffBlock: () => DiffBlock,
|
|
69
74
|
Drawer: () => Drawer,
|
|
70
75
|
Dropzone: () => Dropzone,
|
|
@@ -134,6 +139,7 @@ __export(index_exports, {
|
|
|
134
139
|
SegmentedMeter: () => SegmentedMeter,
|
|
135
140
|
Select: () => Select,
|
|
136
141
|
SessionKit: () => SessionKit,
|
|
142
|
+
SessionProvider: () => SessionProvider,
|
|
137
143
|
SidebarNav: () => SidebarNav,
|
|
138
144
|
Skeleton: () => Skeleton,
|
|
139
145
|
SkeletonText: () => SkeletonText,
|
|
@@ -162,9 +168,11 @@ __export(index_exports, {
|
|
|
162
168
|
VirtualList: () => VirtualList,
|
|
163
169
|
acceptMatches: () => acceptMatches,
|
|
164
170
|
anyOfFilter: () => anyOfFilter,
|
|
171
|
+
applyPrefs: () => applyPrefs,
|
|
165
172
|
channelWeightOf: () => channelWeightOf,
|
|
166
173
|
computeNeedMore: () => computeNeedMore,
|
|
167
174
|
computeVirtualWindow: () => computeVirtualWindow,
|
|
175
|
+
createLocalStorageAppearanceStore: () => createLocalStorageAppearanceStore,
|
|
168
176
|
createVersionStore: () => createVersionStore,
|
|
169
177
|
eqFilter: () => eqFilter,
|
|
170
178
|
extensionOf: () => extensionOf,
|
|
@@ -185,6 +193,7 @@ __export(index_exports, {
|
|
|
185
193
|
isSystemMessage: () => isSystemMessage,
|
|
186
194
|
jobBucket: () => jobBucket,
|
|
187
195
|
languageLabel: () => languageLabel,
|
|
196
|
+
loadPrefs: () => loadPrefs,
|
|
188
197
|
markdownToText: () => markdownToText,
|
|
189
198
|
meterFormats: () => meterFormats,
|
|
190
199
|
modifierLabel: () => modifierLabel,
|
|
@@ -194,14 +203,18 @@ __export(index_exports, {
|
|
|
194
203
|
preferenceScore: () => preferenceScore,
|
|
195
204
|
preferenceSegments: () => preferenceSegments,
|
|
196
205
|
rangeFilter: () => rangeFilter,
|
|
206
|
+
resolveTheme: () => resolveTheme,
|
|
197
207
|
revokeAttachment: () => revokeAttachment,
|
|
208
|
+
savePrefs: () => savePrefs,
|
|
198
209
|
toAttachment: () => toAttachment,
|
|
199
210
|
tokenize: () => tokenize,
|
|
211
|
+
useAppearancePrefs: () => useAppearancePrefs,
|
|
200
212
|
useChatEngine: () => useChatEngine,
|
|
201
213
|
useFeatureStatus: () => useFeatureStatus,
|
|
202
214
|
usePopoverPosition: () => usePopoverPosition,
|
|
203
215
|
useRuntimeMode: () => useRuntimeMode,
|
|
204
216
|
useServerTable: () => useServerTable,
|
|
217
|
+
useSessionContext: () => useSessionContext,
|
|
205
218
|
useStagedFiles: () => useStagedFiles,
|
|
206
219
|
visibleMessages: () => visibleMessages
|
|
207
220
|
});
|
|
@@ -5346,8 +5359,835 @@ var MarkdownEditor = React29.forwardRef(function MarkdownEditor2({
|
|
|
5346
5359
|
// src/components/platform/AccountMenu.tsx
|
|
5347
5360
|
var React31 = __toESM(require("react"), 1);
|
|
5348
5361
|
|
|
5349
|
-
// src/kits/
|
|
5362
|
+
// src/kits/sessionContext.tsx
|
|
5350
5363
|
var React30 = __toESM(require("react"), 1);
|
|
5364
|
+
var SessionContext = React30.createContext(null);
|
|
5365
|
+
function SessionProvider({ value, children }) {
|
|
5366
|
+
return React30.createElement(SessionContext.Provider, { value }, children);
|
|
5367
|
+
}
|
|
5368
|
+
function useSessionContext() {
|
|
5369
|
+
const ctx = React30.useContext(SessionContext);
|
|
5370
|
+
if (!ctx) {
|
|
5371
|
+
throw new Error(
|
|
5372
|
+
"useSessionContext() was called outside a <SessionProvider>. Wrap your app root in <SessionProvider value={...}> with your real session, or \u2014 in the docs site or a demo \u2014 in <DemoSessionProvider>, before rendering AccountMenu, Gate, ProfilePage or AppSwitcher."
|
|
5373
|
+
);
|
|
5374
|
+
}
|
|
5375
|
+
return ctx;
|
|
5376
|
+
}
|
|
5377
|
+
|
|
5378
|
+
// src/components/platform/AccountMenu.tsx
|
|
5379
|
+
var import_jsx_runtime58 = require("react/jsx-runtime");
|
|
5380
|
+
var DEFAULT_LINKS = [
|
|
5381
|
+
{ id: "profile", label: "Your profile", icon: "user-circle", href: "../admin/index.html#profile" },
|
|
5382
|
+
{ id: "preferences", label: "Preferences", icon: "sliders-horizontal", href: "../admin/index.html#preferences" }
|
|
5383
|
+
];
|
|
5384
|
+
var DEFAULT_GROUPS = [
|
|
5385
|
+
{
|
|
5386
|
+
label: "Administration",
|
|
5387
|
+
links: [
|
|
5388
|
+
{ id: "users", label: "Users", icon: "users-three", href: "../admin/index.html#users", perm: "users.view" },
|
|
5389
|
+
{ id: "roles", label: "Roles & permissions", icon: "shield-check", href: "../admin/index.html#roles", perm: "roles.manage" },
|
|
5390
|
+
{ id: "flags", label: "Feature flags", icon: "toggle-right", href: "../admin/index.html#flags", perm: "flags.manage" }
|
|
5391
|
+
]
|
|
5392
|
+
}
|
|
5393
|
+
];
|
|
5394
|
+
function Item({ item, onPick }) {
|
|
5395
|
+
return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
|
|
5396
|
+
"button",
|
|
5397
|
+
{
|
|
5398
|
+
type: "button",
|
|
5399
|
+
role: "menuitem",
|
|
5400
|
+
className: "fd-acct-item",
|
|
5401
|
+
onClick: () => onPick(item),
|
|
5402
|
+
children: [
|
|
5403
|
+
item.icon ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-" + item.icon, "aria-hidden": "true" }) : null,
|
|
5404
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: item.label }),
|
|
5405
|
+
item.badge ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Badge, { tone: "neutral", children: item.badge }) : null
|
|
5406
|
+
]
|
|
5407
|
+
}
|
|
5408
|
+
);
|
|
5409
|
+
}
|
|
5410
|
+
function AccountMenu({
|
|
5411
|
+
links = DEFAULT_LINKS,
|
|
5412
|
+
groups = DEFAULT_GROUPS,
|
|
5413
|
+
onNavigate,
|
|
5414
|
+
onSignOut,
|
|
5415
|
+
showRoles = true,
|
|
5416
|
+
allowUserSwitch = true,
|
|
5417
|
+
className = "",
|
|
5418
|
+
...rest
|
|
5419
|
+
}) {
|
|
5420
|
+
const { user, roles, can: can2, signOut: ctxSignOut, demo } = useSessionContext();
|
|
5421
|
+
const [open, setOpen] = React31.useState(false);
|
|
5422
|
+
const [switching, setSwitching] = React31.useState(false);
|
|
5423
|
+
const [signingOut, setSigningOut] = React31.useState(false);
|
|
5424
|
+
const ref = React31.useRef(null);
|
|
5425
|
+
const visibleLinks = links.filter((l) => !l.perm || can2(l.perm));
|
|
5426
|
+
const visibleGroups = groups.map((g) => ({ label: g.label, links: g.links.filter((l) => !l.perm || can2(l.perm)) })).filter((g) => g.links.length);
|
|
5427
|
+
const pick = (item) => {
|
|
5428
|
+
setOpen(false);
|
|
5429
|
+
if (item.onSelect) return item.onSelect();
|
|
5430
|
+
if (onNavigate) return onNavigate(item.href, item);
|
|
5431
|
+
if (item.href) window.location.href = item.href;
|
|
5432
|
+
};
|
|
5433
|
+
const signOut = async () => {
|
|
5434
|
+
const handler = onSignOut ?? ctxSignOut;
|
|
5435
|
+
setSigningOut(true);
|
|
5436
|
+
try {
|
|
5437
|
+
await handler();
|
|
5438
|
+
} catch (err) {
|
|
5439
|
+
console.error("AccountMenu: sign-out handler failed", err);
|
|
5440
|
+
} finally {
|
|
5441
|
+
setSigningOut(false);
|
|
5442
|
+
setOpen(false);
|
|
5443
|
+
}
|
|
5444
|
+
};
|
|
5445
|
+
const showSwitcher = allowUserSwitch && !!demo;
|
|
5446
|
+
return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_jsx_runtime58.Fragment, { children: [
|
|
5447
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
|
|
5448
|
+
"button",
|
|
5449
|
+
{
|
|
5450
|
+
type: "button",
|
|
5451
|
+
ref,
|
|
5452
|
+
className: ["fd-acct-trigger", className].filter(Boolean).join(" "),
|
|
5453
|
+
"aria-haspopup": "menu",
|
|
5454
|
+
"aria-expanded": open,
|
|
5455
|
+
"aria-label": "Account \u2014 " + user.name,
|
|
5456
|
+
onClick: () => setOpen((o) => !o),
|
|
5457
|
+
...rest,
|
|
5458
|
+
children: [
|
|
5459
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: user.name, size: "sm" }),
|
|
5460
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true", style: { fontSize: 11, color: "var(--text-muted)" } })
|
|
5461
|
+
]
|
|
5462
|
+
}
|
|
5463
|
+
),
|
|
5464
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Popover, { open, anchorRef: ref, onClose: () => setOpen(false), placement: "bottom-end", width: 272, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { role: "menu", className: "fd-stack", style: { gap: 0 }, children: [
|
|
5465
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
|
|
5466
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: user.name }),
|
|
5467
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "fd-stack", style: { gap: 1, minWidth: 0, flex: 1 }, children: [
|
|
5468
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: user.name }),
|
|
5469
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-body-sm fd-muted fd-table-trunc", title: truncationTitle(user.email), children: user.email })
|
|
5470
|
+
] })
|
|
5471
|
+
] }),
|
|
5472
|
+
showRoles && roles.length ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-row", style: { gap: 6, padding: "10px 14px", flexWrap: "wrap", borderBottom: "1px solid var(--border)" }, children: roles.map((r) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)) }) : null,
|
|
5473
|
+
visibleLinks.length ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-acct-group", children: visibleLinks.map((l) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Item, { item: l, onPick: pick }, l.id)) }) : null,
|
|
5474
|
+
visibleGroups.map((g) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
|
|
5475
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-overline fd-muted", style: { padding: "8px 14px 4px", display: "block" }, children: g.label }),
|
|
5476
|
+
g.links.map((l) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Item, { item: l, onPick: pick }, l.id))
|
|
5477
|
+
] }, g.label)),
|
|
5478
|
+
showSwitcher && demo ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
|
|
5479
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("button", { type: "button", role: "menuitem", className: "fd-acct-item", onClick: () => setSwitching((s) => !s), children: [
|
|
5480
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-user-switch", "aria-hidden": "true" }),
|
|
5481
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: "View as another member" }),
|
|
5482
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-caret-" + (switching ? "up" : "down"), "aria-hidden": "true", style: { fontSize: 11 } })
|
|
5483
|
+
] }),
|
|
5484
|
+
switching ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-stack", style: { gap: 0 }, children: demo.allUsers.map((u) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
|
|
5485
|
+
"button",
|
|
5486
|
+
{
|
|
5487
|
+
type: "button",
|
|
5488
|
+
className: "fd-acct-item",
|
|
5489
|
+
"data-current": u.id === user.id ? "true" : "false",
|
|
5490
|
+
onClick: () => {
|
|
5491
|
+
demo.setActiveUser(u.id);
|
|
5492
|
+
setOpen(false);
|
|
5493
|
+
setSwitching(false);
|
|
5494
|
+
},
|
|
5495
|
+
children: [
|
|
5496
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: u.name, size: "sm" }),
|
|
5497
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "fd-stack", style: { gap: 0, flex: 1, minWidth: 0, alignItems: "flex-start" }, children: [
|
|
5498
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontWeight: u.id === user.id ? 700 : 500 }, children: u.name }),
|
|
5499
|
+
u.roles ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-muted", style: { fontSize: 11.5 }, children: u.roles.join(", ") }) : null
|
|
5500
|
+
] }),
|
|
5501
|
+
u.id === user.id ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-check", "aria-hidden": "true", style: { color: "var(--ok-text)" } }) : null
|
|
5502
|
+
]
|
|
5503
|
+
},
|
|
5504
|
+
u.id
|
|
5505
|
+
)) }) : null
|
|
5506
|
+
] }) : null,
|
|
5507
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
|
|
5508
|
+
"button",
|
|
5509
|
+
{
|
|
5510
|
+
type: "button",
|
|
5511
|
+
role: "menuitem",
|
|
5512
|
+
className: "fd-acct-item",
|
|
5513
|
+
"data-danger": "true",
|
|
5514
|
+
disabled: signingOut,
|
|
5515
|
+
onClick: signOut,
|
|
5516
|
+
children: [
|
|
5517
|
+
signingOut ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Spinner, { size: "sm" }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-sign-out", "aria-hidden": "true" }),
|
|
5518
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: signingOut ? "Signing out\u2026" : "Sign out" })
|
|
5519
|
+
]
|
|
5520
|
+
}
|
|
5521
|
+
) })
|
|
5522
|
+
] }) })
|
|
5523
|
+
] });
|
|
5524
|
+
}
|
|
5525
|
+
|
|
5526
|
+
// src/components/platform/AppSwitcher.tsx
|
|
5527
|
+
var React32 = __toESM(require("react"), 1);
|
|
5528
|
+
var import_jsx_runtime59 = require("react/jsx-runtime");
|
|
5529
|
+
function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
|
|
5530
|
+
const { apps: contextApps } = useSessionContext();
|
|
5531
|
+
const apps = appsProp ?? contextApps ?? [];
|
|
5532
|
+
const [open, setOpen] = React32.useState(false);
|
|
5533
|
+
const anchorRef = React32.useRef(null);
|
|
5534
|
+
if (!apps.length) return null;
|
|
5535
|
+
const items = apps.map((app) => ({
|
|
5536
|
+
id: app.key,
|
|
5537
|
+
label: app.name,
|
|
5538
|
+
icon: app.icon,
|
|
5539
|
+
meta: app.current ? "Current" : void 0,
|
|
5540
|
+
disabled: app.current,
|
|
5541
|
+
onSelect: () => {
|
|
5542
|
+
if (app.current) return;
|
|
5543
|
+
if (onNavigate) return onNavigate(app);
|
|
5544
|
+
window.location.href = app.url;
|
|
5545
|
+
}
|
|
5546
|
+
}));
|
|
5547
|
+
return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(React32.Fragment, { children: [
|
|
5548
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { ref: anchorRef, className: ["fd-appswitch-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
5549
|
+
IconButton,
|
|
5550
|
+
{
|
|
5551
|
+
icon: "squares-four",
|
|
5552
|
+
label: "All flytedesk apps",
|
|
5553
|
+
"aria-haspopup": "menu",
|
|
5554
|
+
"aria-expanded": open,
|
|
5555
|
+
onClick: () => setOpen((v) => !v)
|
|
5556
|
+
}
|
|
5557
|
+
) }),
|
|
5558
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
5559
|
+
Popover,
|
|
5560
|
+
{
|
|
5561
|
+
open,
|
|
5562
|
+
anchorRef,
|
|
5563
|
+
onClose: () => setOpen(false),
|
|
5564
|
+
placement: "bottom-end",
|
|
5565
|
+
role: "menu",
|
|
5566
|
+
label: "All flytedesk apps",
|
|
5567
|
+
width: 240,
|
|
5568
|
+
maxHeight: 360,
|
|
5569
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Menu, { items, onClose: () => setOpen(false) })
|
|
5570
|
+
}
|
|
5571
|
+
)
|
|
5572
|
+
] });
|
|
5573
|
+
}
|
|
5574
|
+
|
|
5575
|
+
// src/components/platform/AppearanceMenu.tsx
|
|
5576
|
+
var React34 = __toESM(require("react"), 1);
|
|
5577
|
+
|
|
5578
|
+
// src/kits/appearance.ts
|
|
5579
|
+
var React33 = __toESM(require("react"), 1);
|
|
5580
|
+
var DEFAULT_STORAGE_KEY = "fd-appearance";
|
|
5581
|
+
var DEFAULT_PREFS = {
|
|
5582
|
+
theme: "system",
|
|
5583
|
+
textScale: "md",
|
|
5584
|
+
highContrast: false,
|
|
5585
|
+
reduceMotion: false
|
|
5586
|
+
};
|
|
5587
|
+
function resolveDefaults(opts) {
|
|
5588
|
+
return { ...DEFAULT_PREFS, ...opts?.defaults };
|
|
5589
|
+
}
|
|
5590
|
+
function resolveStorage(opts) {
|
|
5591
|
+
if (opts?.storage) return opts.storage;
|
|
5592
|
+
return typeof window !== "undefined" ? window.localStorage : void 0;
|
|
5593
|
+
}
|
|
5594
|
+
function resolveTheme(mode2) {
|
|
5595
|
+
if (mode2 !== "system") return mode2;
|
|
5596
|
+
return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
5597
|
+
}
|
|
5598
|
+
function loadPrefs(opts) {
|
|
5599
|
+
const defaults = resolveDefaults(opts);
|
|
5600
|
+
const storage = resolveStorage(opts);
|
|
5601
|
+
const key = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
5602
|
+
if (!storage) return defaults;
|
|
5603
|
+
let stored;
|
|
5604
|
+
try {
|
|
5605
|
+
const raw = storage.getItem(key);
|
|
5606
|
+
stored = raw ? JSON.parse(raw) : null;
|
|
5607
|
+
} catch {
|
|
5608
|
+
stored = null;
|
|
5609
|
+
}
|
|
5610
|
+
if (typeof stored !== "object" || stored === null) return defaults;
|
|
5611
|
+
const s = stored;
|
|
5612
|
+
return {
|
|
5613
|
+
theme: s.theme === "light" || s.theme === "dark" || s.theme === "system" ? s.theme : defaults.theme,
|
|
5614
|
+
textScale: s.textScale === "sm" || s.textScale === "md" || s.textScale === "lg" || s.textScale === "xl" ? s.textScale : defaults.textScale,
|
|
5615
|
+
highContrast: typeof s.highContrast === "boolean" ? s.highContrast : defaults.highContrast,
|
|
5616
|
+
reduceMotion: typeof s.reduceMotion === "boolean" ? s.reduceMotion : defaults.reduceMotion
|
|
5617
|
+
};
|
|
5618
|
+
}
|
|
5619
|
+
function savePrefs(prefs, opts) {
|
|
5620
|
+
const storage = resolveStorage(opts);
|
|
5621
|
+
if (!storage) return;
|
|
5622
|
+
const key = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
5623
|
+
try {
|
|
5624
|
+
storage.setItem(key, JSON.stringify(prefs));
|
|
5625
|
+
} catch {
|
|
5626
|
+
}
|
|
5627
|
+
}
|
|
5628
|
+
function createLocalStorageAppearanceStore(opts) {
|
|
5629
|
+
const key = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
5630
|
+
const storage = resolveStorage(opts);
|
|
5631
|
+
let snapshot = loadPrefs(opts);
|
|
5632
|
+
const listeners3 = /* @__PURE__ */ new Set();
|
|
5633
|
+
const notify = () => listeners3.forEach((l) => l());
|
|
5634
|
+
const onStorageEvent = (e) => {
|
|
5635
|
+
if (e.key !== null && e.key !== key) return;
|
|
5636
|
+
snapshot = loadPrefs(opts);
|
|
5637
|
+
notify();
|
|
5638
|
+
};
|
|
5639
|
+
return {
|
|
5640
|
+
getSnapshot: () => snapshot,
|
|
5641
|
+
write(prefs) {
|
|
5642
|
+
snapshot = prefs;
|
|
5643
|
+
if (storage) {
|
|
5644
|
+
try {
|
|
5645
|
+
storage.setItem(key, JSON.stringify(prefs));
|
|
5646
|
+
} catch {
|
|
5647
|
+
}
|
|
5648
|
+
}
|
|
5649
|
+
notify();
|
|
5650
|
+
},
|
|
5651
|
+
subscribe(onChange) {
|
|
5652
|
+
listeners3.add(onChange);
|
|
5653
|
+
if (listeners3.size === 1 && typeof window !== "undefined") {
|
|
5654
|
+
window.addEventListener("storage", onStorageEvent);
|
|
5655
|
+
}
|
|
5656
|
+
return () => {
|
|
5657
|
+
listeners3.delete(onChange);
|
|
5658
|
+
if (listeners3.size === 0 && typeof window !== "undefined") {
|
|
5659
|
+
window.removeEventListener("storage", onStorageEvent);
|
|
5660
|
+
}
|
|
5661
|
+
};
|
|
5662
|
+
}
|
|
5663
|
+
};
|
|
5664
|
+
}
|
|
5665
|
+
function applyPrefs(prefs, root = document.documentElement) {
|
|
5666
|
+
root.setAttribute("data-theme", resolveTheme(prefs.theme));
|
|
5667
|
+
root.setAttribute("data-text-scale", prefs.textScale);
|
|
5668
|
+
if (prefs.highContrast) root.setAttribute("data-contrast", "high");
|
|
5669
|
+
else root.removeAttribute("data-contrast");
|
|
5670
|
+
if (prefs.reduceMotion) root.setAttribute("data-motion", "reduce");
|
|
5671
|
+
else root.removeAttribute("data-motion");
|
|
5672
|
+
}
|
|
5673
|
+
function useAppearancePrefs(store) {
|
|
5674
|
+
const prefs = React33.useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
5675
|
+
React33.useEffect(() => {
|
|
5676
|
+
applyPrefs(prefs);
|
|
5677
|
+
}, [prefs]);
|
|
5678
|
+
React33.useEffect(() => {
|
|
5679
|
+
if (prefs.theme !== "system" || typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
5680
|
+
return;
|
|
5681
|
+
}
|
|
5682
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
5683
|
+
const onChange = () => applyPrefs(prefs);
|
|
5684
|
+
mq.addEventListener("change", onChange);
|
|
5685
|
+
return () => mq.removeEventListener("change", onChange);
|
|
5686
|
+
}, [prefs]);
|
|
5687
|
+
const update = React33.useCallback(
|
|
5688
|
+
(patch) => {
|
|
5689
|
+
store.write({ ...store.getSnapshot(), ...patch });
|
|
5690
|
+
},
|
|
5691
|
+
[store]
|
|
5692
|
+
);
|
|
5693
|
+
return [prefs, update];
|
|
5694
|
+
}
|
|
5695
|
+
|
|
5696
|
+
// src/components/platform/AppearanceMenu.tsx
|
|
5697
|
+
var import_jsx_runtime60 = require("react/jsx-runtime");
|
|
5698
|
+
var THEME_OPTIONS = [
|
|
5699
|
+
{ value: "light", label: "Light" },
|
|
5700
|
+
{ value: "dark", label: "Dark" },
|
|
5701
|
+
{ value: "system", label: "System" }
|
|
5702
|
+
];
|
|
5703
|
+
var TEXT_SCALE_OPTIONS = [
|
|
5704
|
+
{ value: "sm", label: "S" },
|
|
5705
|
+
{ value: "md", label: "M" },
|
|
5706
|
+
{ value: "lg", label: "L" },
|
|
5707
|
+
{ value: "xl", label: "XL" }
|
|
5708
|
+
];
|
|
5709
|
+
function AppearanceMenu({ className = "", store: storeProp, storageKey, storage, defaults }) {
|
|
5710
|
+
const store = React34.useMemo(
|
|
5711
|
+
() => storeProp ?? createLocalStorageAppearanceStore({ storageKey, storage, defaults }),
|
|
5712
|
+
[storeProp, storageKey, storage, defaults]
|
|
5713
|
+
);
|
|
5714
|
+
const [prefs, update] = useAppearancePrefs(store);
|
|
5715
|
+
const [open, setOpen] = React34.useState(false);
|
|
5716
|
+
const anchorRef = React34.useRef(null);
|
|
5717
|
+
const patch = (p) => update(p);
|
|
5718
|
+
return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(React34.Fragment, { children: [
|
|
5719
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { ref: anchorRef, className: ["fd-appearance-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
5720
|
+
IconButton,
|
|
5721
|
+
{
|
|
5722
|
+
icon: "circle-half",
|
|
5723
|
+
label: "Appearance settings",
|
|
5724
|
+
onClick: () => setOpen((v) => !v)
|
|
5725
|
+
}
|
|
5726
|
+
) }),
|
|
5727
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
5728
|
+
Popover,
|
|
5729
|
+
{
|
|
5730
|
+
open,
|
|
5731
|
+
anchorRef,
|
|
5732
|
+
onClose: () => setOpen(false),
|
|
5733
|
+
placement: "bottom-end",
|
|
5734
|
+
padded: true,
|
|
5735
|
+
width: 260,
|
|
5736
|
+
label: "Appearance settings",
|
|
5737
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-stack", style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
5738
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
|
|
5739
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-field-label", children: "Theme" }),
|
|
5740
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
5741
|
+
SegmentedControl,
|
|
5742
|
+
{
|
|
5743
|
+
options: THEME_OPTIONS,
|
|
5744
|
+
value: prefs.theme,
|
|
5745
|
+
onChange: (v) => patch({ theme: v }),
|
|
5746
|
+
"aria-label": "Theme"
|
|
5747
|
+
}
|
|
5748
|
+
)
|
|
5749
|
+
] }),
|
|
5750
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
|
|
5751
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-field-label", children: "Text size" }),
|
|
5752
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
5753
|
+
SegmentedControl,
|
|
5754
|
+
{
|
|
5755
|
+
options: TEXT_SCALE_OPTIONS,
|
|
5756
|
+
value: prefs.textScale,
|
|
5757
|
+
onChange: (v) => patch({ textScale: v }),
|
|
5758
|
+
"aria-label": "Text size"
|
|
5759
|
+
}
|
|
5760
|
+
)
|
|
5761
|
+
] }),
|
|
5762
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
5763
|
+
Switch,
|
|
5764
|
+
{
|
|
5765
|
+
label: "High contrast",
|
|
5766
|
+
description: "Stronger borders and text color",
|
|
5767
|
+
checked: prefs.highContrast,
|
|
5768
|
+
onChange: (e) => patch({ highContrast: e.target.checked })
|
|
5769
|
+
}
|
|
5770
|
+
),
|
|
5771
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
5772
|
+
Switch,
|
|
5773
|
+
{
|
|
5774
|
+
label: "Reduce motion",
|
|
5775
|
+
description: "Minimize animations and transitions",
|
|
5776
|
+
checked: prefs.reduceMotion,
|
|
5777
|
+
onChange: (e) => patch({ reduceMotion: e.target.checked })
|
|
5778
|
+
}
|
|
5779
|
+
)
|
|
5780
|
+
] })
|
|
5781
|
+
}
|
|
5782
|
+
)
|
|
5783
|
+
] });
|
|
5784
|
+
}
|
|
5785
|
+
|
|
5786
|
+
// src/components/platform/ApiSpecBrowser.tsx
|
|
5787
|
+
var React35 = __toESM(require("react"), 1);
|
|
5788
|
+
var import_jsx_runtime61 = require("react/jsx-runtime");
|
|
5789
|
+
var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
|
|
5790
|
+
function Json({ obj }) {
|
|
5791
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
|
|
5792
|
+
}
|
|
5793
|
+
function Endpoint({ s, onRequest }) {
|
|
5794
|
+
const [tried, setTried] = React35.useState(null);
|
|
5795
|
+
const run = async () => {
|
|
5796
|
+
setTried("busy");
|
|
5797
|
+
const t0 = (window.performance || Date).now();
|
|
5798
|
+
try {
|
|
5799
|
+
const data = await onRequest(s.id, s.request || (s.isList ? { page: 1, pageSize: 5 } : {}));
|
|
5800
|
+
setTried({ ms: Math.round((window.performance || Date).now() - t0), data });
|
|
5801
|
+
} catch (e) {
|
|
5802
|
+
setTried({ ms: Math.round((window.performance || Date).now() - t0), error: String(e && e.message || e) });
|
|
5803
|
+
}
|
|
5804
|
+
};
|
|
5805
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Card, { elevation: "flat", padded: false, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 0 }, children: [
|
|
5806
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "14px 18px", flexWrap: "wrap" }, children: [
|
|
5807
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Badge, { tone: METHOD_TONE[s.method] || "neutral", children: s.method }),
|
|
5808
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("code", { className: "fd-mono", style: { fontSize: 12.5, fontWeight: 600, color: "var(--text)", wordBreak: "break-all" }, children: s.path }),
|
|
5809
|
+
s.isList ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Badge, { tone: "neutral", icon: "rows", children: "Paged list" }) : null,
|
|
5810
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { style: { flex: 1 } }),
|
|
5811
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-body-sm fd-muted fd-mono", children: [
|
|
5812
|
+
s.latency[0],
|
|
5813
|
+
"\u2013",
|
|
5814
|
+
s.latency[1],
|
|
5815
|
+
"ms"
|
|
5816
|
+
] }),
|
|
5817
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Button, { size: "sm", variant: "secondary", icon: "play", loading: tried === "busy", onClick: run, children: "Try it" })
|
|
5818
|
+
] }),
|
|
5819
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 14, padding: "0 18px 16px" }, children: [
|
|
5820
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
|
|
5821
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: s.title }),
|
|
5822
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: s.purpose }),
|
|
5823
|
+
s.usedBy && s.usedBy.length ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: s.usedBy.map((u) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Tag, { children: u }, u)) }) : null
|
|
5824
|
+
] }),
|
|
5825
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(280px,1fr))", gap: 12, alignItems: "start" }, children: [
|
|
5826
|
+
s.request ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
|
|
5827
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Request body" }),
|
|
5828
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Json, { obj: s.request })
|
|
5829
|
+
] }) : null,
|
|
5830
|
+
s.query ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
|
|
5831
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Query" }),
|
|
5832
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Json, { obj: s.query })
|
|
5833
|
+
] }) : null,
|
|
5834
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
|
|
5835
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Response 200" }),
|
|
5836
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Json, { obj: s.response })
|
|
5837
|
+
] })
|
|
5838
|
+
] }),
|
|
5839
|
+
s.notes ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Flag, { tone: "info", statement: "Implementation note", cost: s.notes, actions: null }) : null,
|
|
5840
|
+
tried && tried !== "busy" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
|
|
5841
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-row", style: { gap: 8 }, children: [
|
|
5842
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Simulated response" }),
|
|
5843
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Badge, { tone: tried.error ? "danger" : "success", icon: "timer", children: [
|
|
5844
|
+
tried.ms,
|
|
5845
|
+
"ms"
|
|
5846
|
+
] })
|
|
5847
|
+
] }),
|
|
5848
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Json, { obj: tried.error ? { error: tried.error } : tried.data })
|
|
5849
|
+
] }) : null
|
|
5850
|
+
] })
|
|
5851
|
+
] }) });
|
|
5852
|
+
}
|
|
5853
|
+
function ApiSpecBrowser({
|
|
5854
|
+
spec = [],
|
|
5855
|
+
onRequest,
|
|
5856
|
+
modules,
|
|
5857
|
+
groups,
|
|
5858
|
+
title = "API specification",
|
|
5859
|
+
kicker = "Architecture",
|
|
5860
|
+
lede,
|
|
5861
|
+
sourceNote,
|
|
5862
|
+
conventions,
|
|
5863
|
+
openQuestions,
|
|
5864
|
+
className = "",
|
|
5865
|
+
...rest
|
|
5866
|
+
}) {
|
|
5867
|
+
const [q, setQ] = React35.useState("");
|
|
5868
|
+
const [method, setMethod] = React35.useState(null);
|
|
5869
|
+
const [mod, setMod] = React35.useState(null);
|
|
5870
|
+
const [listOnly, setListOnly] = React35.useState(false);
|
|
5871
|
+
const activeModule = modules && modules.find((m) => m.id === mod);
|
|
5872
|
+
const hits = spec.filter((s) => (!method || s.method === method) && (!listOnly || s.isList) && (!activeModule || activeModule.endpoints.indexOf(s.id) >= 0) && (!q || (s.path + " " + s.title + " " + s.purpose + " " + (s.usedBy || []).join(" ")).toLowerCase().includes(q.toLowerCase())));
|
|
5873
|
+
const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
|
|
5874
|
+
const methods = [...new Set(spec.map((s) => s.method))];
|
|
5875
|
+
const listCount = spec.filter((s) => s.isList).length;
|
|
5876
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 1100 }, ...rest, children: [
|
|
5877
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
|
|
5878
|
+
kicker ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: kicker }) : null,
|
|
5879
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
|
|
5880
|
+
lede ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: lede }) : null
|
|
5881
|
+
] }),
|
|
5882
|
+
modules && modules.length ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 8 }, children: [
|
|
5883
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", children: "Filter by screen \u2014 every endpoint that screen depends on" }),
|
|
5884
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
|
|
5885
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Tag, { icon: "stack", selected: !mod, onClick: () => setMod(null), children: [
|
|
5886
|
+
"All screens ",
|
|
5887
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-mono", children: spec.length })
|
|
5888
|
+
] }),
|
|
5889
|
+
modules.map((m) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Tag, { icon: m.icon, selected: mod === m.id, onClick: () => setMod(mod === m.id ? null : m.id), children: [
|
|
5890
|
+
m.label,
|
|
5891
|
+
" ",
|
|
5892
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-mono", children: m.endpoints.length })
|
|
5893
|
+
] }, m.id))
|
|
5894
|
+
] }),
|
|
5895
|
+
activeModule ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
5896
|
+
Flag,
|
|
5897
|
+
{
|
|
5898
|
+
tone: "info",
|
|
5899
|
+
statement: activeModule.label + " calls " + activeModule.endpoints.length + " endpoints.",
|
|
5900
|
+
cost: "Integration checklist for this screen: " + activeModule.endpoints.join(", ") + ". Wire these and the screen is done.",
|
|
5901
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Button, { size: "sm", variant: "ghost", icon: "x", onClick: () => setMod(null), children: "Show all screens" })
|
|
5902
|
+
}
|
|
5903
|
+
) : null
|
|
5904
|
+
] }) : null,
|
|
5905
|
+
sourceNote ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Flag, { tone: "info", statement: "Design-first: this page is the spec.", cost: sourceNote, actions: null }) : null,
|
|
5906
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
|
|
5907
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Input, { icon: "magnifying-glass", placeholder: "Find an endpoint, screen, or behavior", value: q, onChange: (e) => setQ(e.target.value), style: { width: 300 } }),
|
|
5908
|
+
methods.map((m) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Tag, { selected: method === m, onClick: () => setMethod(method === m ? null : m), children: [
|
|
5909
|
+
m,
|
|
5910
|
+
" ",
|
|
5911
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-mono", children: spec.filter((s) => s.method === m).length })
|
|
5912
|
+
] }, m)),
|
|
5913
|
+
listCount ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Tag, { icon: "rows", selected: listOnly, onClick: () => setListOnly(!listOnly), children: [
|
|
5914
|
+
"Paged lists ",
|
|
5915
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-mono", children: listCount })
|
|
5916
|
+
] }) : null,
|
|
5917
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { style: { flex: 1 } }),
|
|
5918
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
|
|
5919
|
+
hits.length,
|
|
5920
|
+
" of ",
|
|
5921
|
+
spec.length,
|
|
5922
|
+
" endpoints",
|
|
5923
|
+
listCount ? " \xB7 " + listCount + " paged" : ""
|
|
5924
|
+
] })
|
|
5925
|
+
] }),
|
|
5926
|
+
hits.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: "No endpoint matches those filters." }) }) : effGroups.map(([g, ids]) => {
|
|
5927
|
+
const items = hits.filter((s) => ids.indexOf(s.id) >= 0);
|
|
5928
|
+
if (!items.length) return null;
|
|
5929
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-stack", style: { gap: 12 }, children: [
|
|
5930
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-label-lg", children: g }),
|
|
5931
|
+
items.map((s) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Endpoint, { s, onRequest }, s.id))
|
|
5932
|
+
] }, g);
|
|
5933
|
+
}),
|
|
5934
|
+
conventions && conventions.length ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Card, { title: "Cross-cutting conventions", elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "fd-stack", style: { gap: 10 }, children: conventions.map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 12, alignItems: "flex-start" }, children: [
|
|
5935
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-overline fd-muted", style: { width: 110, flex: "none", paddingTop: 2 }, children: k }),
|
|
5936
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: v })
|
|
5937
|
+
] }, k)) }) }) : null,
|
|
5938
|
+
openQuestions && openQuestions.length ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Collapsible, { icon: "list-checks", title: "Open questions before implementation", subtitle: openQuestions.length + " unresolved", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "fd-stack", style: { gap: 8 }, children: openQuestions.map(([id, question, why]) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start", padding: "8px 0", borderTop: "1px solid var(--border)" }, children: [
|
|
5939
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Badge, { tone: "danger", children: id }),
|
|
5940
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-stack", style: { gap: 2, flex: 1 }, children: [
|
|
5941
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: question }),
|
|
5942
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "fd-body-sm fd-muted", children: why })
|
|
5943
|
+
] })
|
|
5944
|
+
] }, id)) }) }) : null
|
|
5945
|
+
] });
|
|
5946
|
+
}
|
|
5947
|
+
|
|
5948
|
+
// src/components/platform/ProfilePage.tsx
|
|
5949
|
+
var React36 = __toESM(require("react"), 1);
|
|
5950
|
+
var import_jsx_runtime62 = require("react/jsx-runtime");
|
|
5951
|
+
function fieldInitialValue(field, user) {
|
|
5952
|
+
if (field.value) return field.value(user);
|
|
5953
|
+
const raw = user[field.key];
|
|
5954
|
+
return raw == null ? "" : String(raw);
|
|
5955
|
+
}
|
|
5956
|
+
function groupFields(fields) {
|
|
5957
|
+
const order = [];
|
|
5958
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
5959
|
+
for (const field of fields) {
|
|
5960
|
+
const group = field.group ?? "Identity";
|
|
5961
|
+
if (!byGroup.has(group)) {
|
|
5962
|
+
byGroup.set(group, { group, description: field.groupDescription, fields: [] });
|
|
5963
|
+
order.push(group);
|
|
5964
|
+
}
|
|
5965
|
+
const entry = byGroup.get(group);
|
|
5966
|
+
if (entry.description === void 0) entry.description = field.groupDescription;
|
|
5967
|
+
entry.fields.push(field);
|
|
5968
|
+
}
|
|
5969
|
+
return order.map((g) => byGroup.get(g));
|
|
5970
|
+
}
|
|
5971
|
+
function ProfilePage({
|
|
5972
|
+
user: userProp,
|
|
5973
|
+
fields = [],
|
|
5974
|
+
notifications = [],
|
|
5975
|
+
extra,
|
|
5976
|
+
onChangePhoto,
|
|
5977
|
+
onSave,
|
|
5978
|
+
onNavigate,
|
|
5979
|
+
sessions,
|
|
5980
|
+
onSignOutEverywhere,
|
|
5981
|
+
className = ""
|
|
5982
|
+
}) {
|
|
5983
|
+
const { user: sessionUser, roles, permissions, saveProfile: saveProfile2 } = useSessionContext();
|
|
5984
|
+
const user = userProp || sessionUser;
|
|
5985
|
+
const initialFieldValues = React36.useMemo(() => {
|
|
5986
|
+
const values2 = {};
|
|
5987
|
+
for (const field of fields) values2[field.key] = fieldInitialValue(field, user);
|
|
5988
|
+
return values2;
|
|
5989
|
+
}, [user.id, fields]);
|
|
5990
|
+
const initialNotify = React36.useMemo(() => {
|
|
5991
|
+
const values2 = {};
|
|
5992
|
+
for (const n of notifications) values2[n.key] = !!user.notify?.[n.key];
|
|
5993
|
+
return values2;
|
|
5994
|
+
}, [user.id, notifications]);
|
|
5995
|
+
const [values, setValues] = React36.useState(initialFieldValues);
|
|
5996
|
+
const [notify, setNotify] = React36.useState(initialNotify);
|
|
5997
|
+
const [saving, setSaving] = React36.useState(false);
|
|
5998
|
+
const [saved, setSaved] = React36.useState(false);
|
|
5999
|
+
const [prevUserId, setPrevUserId] = React36.useState(user.id);
|
|
6000
|
+
if (user.id !== prevUserId) {
|
|
6001
|
+
setPrevUserId(user.id);
|
|
6002
|
+
setValues(initialFieldValues);
|
|
6003
|
+
setNotify(initialNotify);
|
|
6004
|
+
setSaved(false);
|
|
6005
|
+
}
|
|
6006
|
+
const setField = (key, v) => {
|
|
6007
|
+
setValues((d) => ({ ...d, [key]: v }));
|
|
6008
|
+
setSaved(false);
|
|
6009
|
+
};
|
|
6010
|
+
const toggleNotify = (key) => {
|
|
6011
|
+
setNotify((d) => ({ ...d, [key]: !d[key] }));
|
|
6012
|
+
setSaved(false);
|
|
6013
|
+
};
|
|
6014
|
+
const dirty = fields.some((f) => values[f.key] !== initialFieldValues[f.key]) || notifications.some((n) => notify[n.key] !== initialNotify[n.key]);
|
|
6015
|
+
const canSave = !!(onSave || saveProfile2) && (fields.length > 0 || notifications.length > 0);
|
|
6016
|
+
const save = async () => {
|
|
6017
|
+
if (!canSave) return;
|
|
6018
|
+
setSaving(true);
|
|
6019
|
+
const draft = { ...values };
|
|
6020
|
+
if (notifications.length > 0) draft.notify = notify;
|
|
6021
|
+
try {
|
|
6022
|
+
if (onSave) await onSave(draft);
|
|
6023
|
+
else await saveProfile2(draft);
|
|
6024
|
+
setSaved(true);
|
|
6025
|
+
} finally {
|
|
6026
|
+
setSaving(false);
|
|
6027
|
+
}
|
|
6028
|
+
};
|
|
6029
|
+
const discard = () => {
|
|
6030
|
+
setValues(initialFieldValues);
|
|
6031
|
+
setNotify(initialNotify);
|
|
6032
|
+
setSaved(false);
|
|
6033
|
+
};
|
|
6034
|
+
const displayName = fields.some((f) => f.key === "name") ? values.name || user.name : user.name;
|
|
6035
|
+
const fieldGroups = groupFields(fields);
|
|
6036
|
+
return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 860 }, children: [
|
|
6037
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
|
|
6038
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-overline fd-muted", children: "Account" }),
|
|
6039
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: "Your profile" }),
|
|
6040
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: "How you appear to colleagues and clients across every flytedesk app, and what we send you." })
|
|
6041
|
+
] }),
|
|
6042
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-row", style: { gap: 16, flexWrap: "wrap", alignItems: "flex-start" }, children: [
|
|
6043
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Avatar, { name: displayName, size: "lg" }),
|
|
6044
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-stack", style: { gap: 4, flex: 1, minWidth: 200 }, children: [
|
|
6045
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-h3", style: { margin: 0 }, children: displayName }),
|
|
6046
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 4 }, children: [
|
|
6047
|
+
roles.map((r) => /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)),
|
|
6048
|
+
user.sso ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Badge, { tone: "info", icon: "shield-check", children: "SSO" }) : null
|
|
6049
|
+
] })
|
|
6050
|
+
] }),
|
|
6051
|
+
onChangePhoto ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { variant: "secondary", size: "sm", icon: "image", onClick: onChangePhoto, children: "Change photo" }) : null
|
|
6052
|
+
] }) }),
|
|
6053
|
+
fieldGroups.map(({ group, description, fields: groupFieldDefs }) => /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
6054
|
+
Card,
|
|
6055
|
+
{
|
|
6056
|
+
title: description ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-stack", style: { gap: 2 }, children: [
|
|
6057
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: group }),
|
|
6058
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: description })
|
|
6059
|
+
] }) : group,
|
|
6060
|
+
elevation: "flat",
|
|
6061
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-stack", style: { gap: 14 }, children: [
|
|
6062
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: groupFieldDefs.filter((f) => f.type !== "textarea").map((field) => {
|
|
6063
|
+
const help = typeof field.help === "function" ? field.help(user) : field.help;
|
|
6064
|
+
const readOnly = field.readOnly || field.type === "readonly";
|
|
6065
|
+
if (field.type === "select") {
|
|
6066
|
+
return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
6067
|
+
Select,
|
|
6068
|
+
{
|
|
6069
|
+
label: field.label,
|
|
6070
|
+
value: values[field.key],
|
|
6071
|
+
onChange: readOnly ? void 0 : (e) => setField(field.key, e.target.value),
|
|
6072
|
+
options: field.options ?? [],
|
|
6073
|
+
placeholder: field.placeholder,
|
|
6074
|
+
help,
|
|
6075
|
+
disabled: readOnly
|
|
6076
|
+
},
|
|
6077
|
+
field.key
|
|
6078
|
+
);
|
|
6079
|
+
}
|
|
6080
|
+
return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
6081
|
+
Input,
|
|
6082
|
+
{
|
|
6083
|
+
label: field.label,
|
|
6084
|
+
value: values[field.key],
|
|
6085
|
+
onChange: readOnly ? void 0 : (e) => setField(field.key, e.target.value),
|
|
6086
|
+
readOnly,
|
|
6087
|
+
disabled: readOnly,
|
|
6088
|
+
placeholder: field.placeholder,
|
|
6089
|
+
help
|
|
6090
|
+
},
|
|
6091
|
+
field.key
|
|
6092
|
+
);
|
|
6093
|
+
}) }),
|
|
6094
|
+
groupFieldDefs.filter((f) => f.type === "textarea").map((field) => {
|
|
6095
|
+
const help = typeof field.help === "function" ? field.help(user) : field.help;
|
|
6096
|
+
return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
6097
|
+
Textarea,
|
|
6098
|
+
{
|
|
6099
|
+
label: field.label,
|
|
6100
|
+
value: values[field.key],
|
|
6101
|
+
onChange: field.readOnly ? void 0 : (e) => setField(field.key, e.target.value),
|
|
6102
|
+
readOnly: field.readOnly,
|
|
6103
|
+
disabled: field.readOnly,
|
|
6104
|
+
rows: field.rows ?? 3,
|
|
6105
|
+
placeholder: field.placeholder,
|
|
6106
|
+
help
|
|
6107
|
+
},
|
|
6108
|
+
field.key
|
|
6109
|
+
);
|
|
6110
|
+
})
|
|
6111
|
+
] })
|
|
6112
|
+
},
|
|
6113
|
+
group
|
|
6114
|
+
)),
|
|
6115
|
+
extra,
|
|
6116
|
+
notifications.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Card, { title: "Notifications", elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "fd-stack", style: { gap: 14 }, children: notifications.map((n) => /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Switch, { checked: !!notify[n.key], onChange: () => toggleNotify(n.key), label: n.label, description: n.description }, n.key)) }) }) : null,
|
|
6117
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
|
|
6118
|
+
Card,
|
|
6119
|
+
{
|
|
6120
|
+
title: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-stack", style: { gap: 2 }, children: [
|
|
6121
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: "Access" }),
|
|
6122
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "What your roles grant you" })
|
|
6123
|
+
] }),
|
|
6124
|
+
elevation: "flat",
|
|
6125
|
+
action: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
6126
|
+
Button,
|
|
6127
|
+
{
|
|
6128
|
+
size: "sm",
|
|
6129
|
+
variant: "ghost",
|
|
6130
|
+
icon: "arrow-right",
|
|
6131
|
+
onClick: () => onNavigate && onNavigate("#roles"),
|
|
6132
|
+
children: "See role definitions"
|
|
6133
|
+
}
|
|
6134
|
+
),
|
|
6135
|
+
children: [
|
|
6136
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-stack", style: { gap: 0 }, children: [
|
|
6137
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(CardRow, { label: "Roles", children: roles.map((r) => r.name).join(", ") || "None" }),
|
|
6138
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(CardRow, { label: "Permissions", children: [
|
|
6139
|
+
permissions.length,
|
|
6140
|
+
" granted"
|
|
6141
|
+
] }),
|
|
6142
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(CardRow, { label: "Member since", children: user.joined || "\u2014" })
|
|
6143
|
+
] }),
|
|
6144
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "fd-body-sm fd-muted", style: { margin: "12px 0 0" }, children: "You cannot change your own roles \u2014 that is the point of them. An administrator manages roles from Admin \u2192 Users." })
|
|
6145
|
+
]
|
|
6146
|
+
}
|
|
6147
|
+
),
|
|
6148
|
+
sessions ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
6149
|
+
Card,
|
|
6150
|
+
{
|
|
6151
|
+
title: "Signed-in devices",
|
|
6152
|
+
elevation: "flat",
|
|
6153
|
+
action: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { size: "sm", variant: "ghost", icon: "sign-out", onClick: onSignOutEverywhere, children: "Sign out everywhere" }),
|
|
6154
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "fd-stack", style: { gap: 0 }, children: sessions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: "No other signed-in devices." }) : sessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-row", style: { gap: 12, padding: "10px 0", borderTop: "1px solid var(--border)", flexWrap: "wrap" }, children: [
|
|
6155
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-device-mobile", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
|
|
6156
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-stack", style: { gap: 1, flex: 1, minWidth: 160 }, children: [
|
|
6157
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: s.device }),
|
|
6158
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
|
|
6159
|
+
s.where,
|
|
6160
|
+
" \xB7 ",
|
|
6161
|
+
s.when
|
|
6162
|
+
] })
|
|
6163
|
+
] }),
|
|
6164
|
+
s.current ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Badge, { tone: "success", dot: true, children: "This device" }) : /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { size: "sm", variant: "ghost", children: "Revoke" })
|
|
6165
|
+
] }, s.id)) })
|
|
6166
|
+
}
|
|
6167
|
+
) : null,
|
|
6168
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "fd-row", style: {
|
|
6169
|
+
gap: 10,
|
|
6170
|
+
flexWrap: "wrap",
|
|
6171
|
+
position: "sticky",
|
|
6172
|
+
bottom: 0,
|
|
6173
|
+
padding: "12px 14px",
|
|
6174
|
+
borderRadius: 10,
|
|
6175
|
+
background: "var(--surface)",
|
|
6176
|
+
border: "1px solid var(--border)",
|
|
6177
|
+
boxShadow: "0 -4px 16px rgba(11,13,17,.06)"
|
|
6178
|
+
}, children: [
|
|
6179
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600, flex: 1 }, children: saved ? "Saved." : dirty ? "Unsaved changes" : "No pending changes" }),
|
|
6180
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { size: "sm", variant: "ghost", disabled: !dirty || saving, onClick: discard, children: "Discard" }),
|
|
6181
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { size: "sm", icon: "check", disabled: !dirty || !canSave, loading: saving, onClick: save, children: "Save changes" })
|
|
6182
|
+
] })
|
|
6183
|
+
] });
|
|
6184
|
+
}
|
|
6185
|
+
|
|
6186
|
+
// src/components/platform/RoadmapTimeline.tsx
|
|
6187
|
+
var React39 = __toESM(require("react"), 1);
|
|
6188
|
+
|
|
6189
|
+
// src/kits/session.ts
|
|
6190
|
+
var React37 = __toESM(require("react"), 1);
|
|
5351
6191
|
var PERMISSION_CATALOG = [
|
|
5352
6192
|
{ group: "Plans", items: [
|
|
5353
6193
|
{ key: "plan.view", label: "View plans", detail: "Read any plan in the workspace." },
|
|
@@ -6845,611 +7685,130 @@ function can(permission) {
|
|
|
6845
7685
|
const list = Array.isArray(permission) ? permission : [permission];
|
|
6846
7686
|
return list.every((p) => s.permissions.indexOf(p) >= 0);
|
|
6847
7687
|
}
|
|
6848
|
-
function canAny(permissions) {
|
|
6849
|
-
const s = getSession();
|
|
6850
|
-
return (permissions || []).some((p) => s.permissions.indexOf(p) >= 0);
|
|
6851
|
-
}
|
|
6852
|
-
function hasRole(roleId) {
|
|
6853
|
-
return getSession().user.roles.indexOf(roleId) >= 0;
|
|
6854
|
-
}
|
|
6855
|
-
function flagOn(key) {
|
|
6856
|
-
return !!getSession().flags[key];
|
|
6857
|
-
}
|
|
6858
|
-
function findFlag(key) {
|
|
6859
|
-
return (getSession().allFlags || []).find((f) => f.key === key) || null;
|
|
6860
|
-
}
|
|
6861
|
-
function flagState(key) {
|
|
6862
|
-
if (!key) return "on";
|
|
6863
|
-
const f = findFlag(key);
|
|
6864
|
-
if (!f) return "on";
|
|
6865
|
-
return flagOn(key) ? "on" : "off";
|
|
6866
|
-
}
|
|
6867
|
-
function isEnabled(key) {
|
|
6868
|
-
return flagState(key) !== "off";
|
|
6869
|
-
}
|
|
6870
|
-
function flagVisible(key) {
|
|
6871
|
-
return isEnabled(key);
|
|
6872
|
-
}
|
|
6873
|
-
function flagLive(key) {
|
|
6874
|
-
return isEnabled(key);
|
|
6875
|
-
}
|
|
6876
|
-
function roadmap(overrides) {
|
|
6877
|
-
const flags = overrides && overrides.flags || (getSession().allFlags || []);
|
|
6878
|
-
const isComplete = overrides && overrides.isComplete || (() => false);
|
|
6879
|
-
const items = flags.map((f) => {
|
|
6880
|
-
const done = !!isComplete(f.key);
|
|
6881
|
-
const date = done ? f.shippedAt || f.eta : f.eta || f.shippedAt;
|
|
6882
|
-
const phaseInfo = (f.phase != null ? PHASES[f.phase] : void 0) || {};
|
|
6883
|
-
return {
|
|
6884
|
-
key: f.key,
|
|
6885
|
-
label: f.label,
|
|
6886
|
-
project: f.project,
|
|
6887
|
-
description: f.description,
|
|
6888
|
-
backend: f.backend,
|
|
6889
|
-
effort: f.effort,
|
|
6890
|
-
owner: f.owner,
|
|
6891
|
-
dependsOn: f.dependsOn || [],
|
|
6892
|
-
screen: f.screen || null,
|
|
6893
|
-
enabled: f.enabled,
|
|
6894
|
-
/* Which build phase this sits in, and why that phase comes where it does. */
|
|
6895
|
-
phase: f.phase,
|
|
6896
|
-
phaseName: phaseInfo.name || "Unscheduled",
|
|
6897
|
-
phaseWhy: phaseInfo.why || null,
|
|
6898
|
-
phaseStart: phaseInfo.start || null,
|
|
6899
|
-
/* Derived, not stored. */
|
|
6900
|
-
implemented: done,
|
|
6901
|
-
date: date || null,
|
|
6902
|
-
/* True when the only date we have is when the UI was designed, not when it ships. */
|
|
6903
|
-
dateIsDesign: !done && !f.eta && !!f.shippedAt,
|
|
6904
|
-
status: done ? "shipped" : f.enabled ? "next" : "planned"
|
|
6905
|
-
};
|
|
6906
|
-
}).filter((i) => i.date);
|
|
6907
|
-
items.sort((a, b) => (a.phase ?? 0) - (b.phase ?? 0) || String(a.date).localeCompare(String(b.date)) || a.label.localeCompare(b.label));
|
|
6908
|
-
const nextIndex = items.findIndex((i) => !i.implemented);
|
|
6909
|
-
return {
|
|
6910
|
-
items,
|
|
6911
|
-
nextIndex: nextIndex < 0 ? items.length : nextIndex,
|
|
6912
|
-
shipped: items.filter((i) => i.implemented).length,
|
|
6913
|
-
remaining: items.filter((i) => !i.implemented).length
|
|
6914
|
-
};
|
|
6915
|
-
}
|
|
6916
|
-
function useSession() {
|
|
6917
|
-
const [s, setS] = React30.useState(getSession);
|
|
6918
|
-
React30.useEffect(() => subscribe(setS), []);
|
|
6919
|
-
return s;
|
|
7688
|
+
function canAny(permissions) {
|
|
7689
|
+
const s = getSession();
|
|
7690
|
+
return (permissions || []).some((p) => s.permissions.indexOf(p) >= 0);
|
|
6920
7691
|
}
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
ALL_PERMISSIONS,
|
|
6924
|
-
ROLES,
|
|
6925
|
-
FLAGS,
|
|
6926
|
-
USERS,
|
|
6927
|
-
loadSession,
|
|
6928
|
-
getSession,
|
|
6929
|
-
setActiveUser,
|
|
6930
|
-
saveProfile,
|
|
6931
|
-
refresh,
|
|
6932
|
-
subscribe,
|
|
6933
|
-
can,
|
|
6934
|
-
canAny,
|
|
6935
|
-
hasRole,
|
|
6936
|
-
flagOn,
|
|
6937
|
-
useSession,
|
|
6938
|
-
findFlag,
|
|
6939
|
-
flagState,
|
|
6940
|
-
isEnabled,
|
|
6941
|
-
flagVisible,
|
|
6942
|
-
flagLive,
|
|
6943
|
-
roadmap,
|
|
6944
|
-
evaluateFlag,
|
|
6945
|
-
explainFlag,
|
|
6946
|
-
bucket,
|
|
6947
|
-
buildSession
|
|
6948
|
-
};
|
|
6949
|
-
|
|
6950
|
-
// src/components/platform/AccountMenu.tsx
|
|
6951
|
-
var import_jsx_runtime58 = require("react/jsx-runtime");
|
|
6952
|
-
var DEFAULT_LINKS = [
|
|
6953
|
-
{ id: "profile", label: "Your profile", icon: "user-circle", href: "../admin/index.html#profile" },
|
|
6954
|
-
{ id: "preferences", label: "Preferences", icon: "sliders-horizontal", href: "../admin/index.html#preferences" }
|
|
6955
|
-
];
|
|
6956
|
-
var DEFAULT_ADMIN_LINKS = [
|
|
6957
|
-
{ id: "users", label: "Users", icon: "users-three", href: "../admin/index.html#users", perm: "users.view" },
|
|
6958
|
-
{ id: "roles", label: "Roles & permissions", icon: "shield-check", href: "../admin/index.html#roles", perm: "roles.manage" },
|
|
6959
|
-
{ id: "flags", label: "Feature flags", icon: "toggle-right", href: "../admin/index.html#flags", perm: "flags.manage" }
|
|
6960
|
-
];
|
|
6961
|
-
function Item({ item, onPick }) {
|
|
6962
|
-
return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
|
|
6963
|
-
"button",
|
|
6964
|
-
{
|
|
6965
|
-
type: "button",
|
|
6966
|
-
role: "menuitem",
|
|
6967
|
-
className: "fd-acct-item",
|
|
6968
|
-
onClick: () => onPick(item),
|
|
6969
|
-
children: [
|
|
6970
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-" + item.icon, "aria-hidden": "true" }),
|
|
6971
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: item.label }),
|
|
6972
|
-
item.badge ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Badge, { tone: "neutral", children: item.badge }) : null
|
|
6973
|
-
]
|
|
6974
|
-
}
|
|
6975
|
-
);
|
|
7692
|
+
function hasRole(roleId) {
|
|
7693
|
+
return getSession().user.roles.indexOf(roleId) >= 0;
|
|
6976
7694
|
}
|
|
6977
|
-
function
|
|
6978
|
-
|
|
6979
|
-
adminLinks = DEFAULT_ADMIN_LINKS,
|
|
6980
|
-
onNavigate,
|
|
6981
|
-
onSignOut,
|
|
6982
|
-
showRoles = true,
|
|
6983
|
-
allowUserSwitch = true,
|
|
6984
|
-
className = "",
|
|
6985
|
-
...rest
|
|
6986
|
-
}) {
|
|
6987
|
-
const session = SessionKit.useSession();
|
|
6988
|
-
const [open, setOpen] = React31.useState(false);
|
|
6989
|
-
const [switching, setSwitching] = React31.useState(false);
|
|
6990
|
-
const ref = React31.useRef(null);
|
|
6991
|
-
const user = session.user;
|
|
6992
|
-
const visibleAdmin = adminLinks.filter((l) => !l.perm || SessionKit.can(l.perm));
|
|
6993
|
-
const pick = (item) => {
|
|
6994
|
-
setOpen(false);
|
|
6995
|
-
if (item.onSelect) return item.onSelect();
|
|
6996
|
-
if (onNavigate) return onNavigate(item.href, item);
|
|
6997
|
-
if (item.href) window.location.href = item.href;
|
|
6998
|
-
};
|
|
6999
|
-
const signOut = () => {
|
|
7000
|
-
setOpen(false);
|
|
7001
|
-
if (onSignOut) return onSignOut();
|
|
7002
|
-
window.alert("Signed out. (Simulated \u2014 no auth provider is wired up.)");
|
|
7003
|
-
};
|
|
7004
|
-
return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_jsx_runtime58.Fragment, { children: [
|
|
7005
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
|
|
7006
|
-
"button",
|
|
7007
|
-
{
|
|
7008
|
-
type: "button",
|
|
7009
|
-
ref,
|
|
7010
|
-
className: ["fd-acct-trigger", className].filter(Boolean).join(" "),
|
|
7011
|
-
"aria-haspopup": "menu",
|
|
7012
|
-
"aria-expanded": open,
|
|
7013
|
-
"aria-label": "Account \u2014 " + user.name,
|
|
7014
|
-
onClick: () => setOpen((o) => !o),
|
|
7015
|
-
...rest,
|
|
7016
|
-
children: [
|
|
7017
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: user.name, size: "sm" }),
|
|
7018
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true", style: { fontSize: 11, color: "var(--text-muted)" } })
|
|
7019
|
-
]
|
|
7020
|
-
}
|
|
7021
|
-
),
|
|
7022
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Popover, { open, anchorRef: ref, onClose: () => setOpen(false), placement: "bottom-end", width: 272, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { role: "menu", className: "fd-stack", style: { gap: 0 }, children: [
|
|
7023
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
|
|
7024
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: user.name }),
|
|
7025
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "fd-stack", style: { gap: 1, minWidth: 0, flex: 1 }, children: [
|
|
7026
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: user.name }),
|
|
7027
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-body-sm fd-muted fd-table-trunc", title: truncationTitle(user.email), children: user.email })
|
|
7028
|
-
] })
|
|
7029
|
-
] }),
|
|
7030
|
-
showRoles && session.roles.length ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-row", style: { gap: 6, padding: "10px 14px", flexWrap: "wrap", borderBottom: "1px solid var(--border)" }, children: session.roles.map((r) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)) }) : null,
|
|
7031
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-acct-group", children: links.map((l) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Item, { item: l, onPick: pick }, l.id)) }),
|
|
7032
|
-
visibleAdmin.length ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
|
|
7033
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-overline fd-muted", style: { padding: "8px 14px 4px", display: "block" }, children: "Administration" }),
|
|
7034
|
-
visibleAdmin.map((l) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Item, { item: l, onPick: pick }, l.id))
|
|
7035
|
-
] }) : null,
|
|
7036
|
-
allowUserSwitch ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: [
|
|
7037
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("button", { type: "button", role: "menuitem", className: "fd-acct-item", onClick: () => setSwitching((s) => !s), children: [
|
|
7038
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-user-switch", "aria-hidden": "true" }),
|
|
7039
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: "View as another member" }),
|
|
7040
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-caret-" + (switching ? "up" : "down"), "aria-hidden": "true", style: { fontSize: 11 } })
|
|
7041
|
-
] }),
|
|
7042
|
-
switching ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-stack", style: { gap: 0 }, children: session.allUsers.filter((u) => u.status === "active").map((u) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
|
|
7043
|
-
"button",
|
|
7044
|
-
{
|
|
7045
|
-
type: "button",
|
|
7046
|
-
className: "fd-acct-item",
|
|
7047
|
-
"data-current": u.id === user.id ? "true" : "false",
|
|
7048
|
-
onClick: () => {
|
|
7049
|
-
SessionKit.setActiveUser(u.id);
|
|
7050
|
-
setOpen(false);
|
|
7051
|
-
setSwitching(false);
|
|
7052
|
-
},
|
|
7053
|
-
children: [
|
|
7054
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Avatar, { name: u.name, size: "sm" }),
|
|
7055
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { className: "fd-stack", style: { gap: 0, flex: 1, minWidth: 0, alignItems: "flex-start" }, children: [
|
|
7056
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontWeight: u.id === user.id ? 700 : 500 }, children: u.name }),
|
|
7057
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "fd-muted", style: { fontSize: 11.5 }, children: u.roles.join(", ") })
|
|
7058
|
-
] }),
|
|
7059
|
-
u.id === user.id ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-check", "aria-hidden": "true", style: { color: "var(--ok-text)" } }) : null
|
|
7060
|
-
]
|
|
7061
|
-
},
|
|
7062
|
-
u.id
|
|
7063
|
-
)) }) : null
|
|
7064
|
-
] }) : null,
|
|
7065
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-acct-group", style: { borderTop: "1px solid var(--border)" }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("button", { type: "button", role: "menuitem", className: "fd-acct-item", "data-danger": "true", onClick: signOut, children: [
|
|
7066
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-sign-out", "aria-hidden": "true" }),
|
|
7067
|
-
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: "Sign out" })
|
|
7068
|
-
] }) })
|
|
7069
|
-
] }) })
|
|
7070
|
-
] });
|
|
7695
|
+
function flagOn(key) {
|
|
7696
|
+
return !!getSession().flags[key];
|
|
7071
7697
|
}
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
var React32 = __toESM(require("react"), 1);
|
|
7075
|
-
var import_jsx_runtime59 = require("react/jsx-runtime");
|
|
7076
|
-
var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
|
|
7077
|
-
function Json({ obj }) {
|
|
7078
|
-
return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
|
|
7698
|
+
function findFlag(key) {
|
|
7699
|
+
return (getSession().allFlags || []).find((f) => f.key === key) || null;
|
|
7079
7700
|
}
|
|
7080
|
-
function
|
|
7081
|
-
|
|
7082
|
-
const
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
try {
|
|
7086
|
-
const data = await onRequest(s.id, s.request || (s.isList ? { page: 1, pageSize: 5 } : {}));
|
|
7087
|
-
setTried({ ms: Math.round((window.performance || Date).now() - t0), data });
|
|
7088
|
-
} catch (e) {
|
|
7089
|
-
setTried({ ms: Math.round((window.performance || Date).now() - t0), error: String(e && e.message || e) });
|
|
7090
|
-
}
|
|
7091
|
-
};
|
|
7092
|
-
return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Card, { elevation: "flat", padded: false, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 0 }, children: [
|
|
7093
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "14px 18px", flexWrap: "wrap" }, children: [
|
|
7094
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Badge, { tone: METHOD_TONE[s.method] || "neutral", children: s.method }),
|
|
7095
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("code", { className: "fd-mono", style: { fontSize: 12.5, fontWeight: 600, color: "var(--text)", wordBreak: "break-all" }, children: s.path }),
|
|
7096
|
-
s.isList ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Badge, { tone: "neutral", icon: "rows", children: "Paged list" }) : null,
|
|
7097
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { style: { flex: 1 } }),
|
|
7098
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("span", { className: "fd-body-sm fd-muted fd-mono", children: [
|
|
7099
|
-
s.latency[0],
|
|
7100
|
-
"\u2013",
|
|
7101
|
-
s.latency[1],
|
|
7102
|
-
"ms"
|
|
7103
|
-
] }),
|
|
7104
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Button, { size: "sm", variant: "secondary", icon: "play", loading: tried === "busy", onClick: run, children: "Try it" })
|
|
7105
|
-
] }),
|
|
7106
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 14, padding: "0 18px 16px" }, children: [
|
|
7107
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
|
|
7108
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 700 }, children: s.title }),
|
|
7109
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: s.purpose }),
|
|
7110
|
-
s.usedBy && s.usedBy.length ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: s.usedBy.map((u) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Tag, { children: u }, u)) }) : null
|
|
7111
|
-
] }),
|
|
7112
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(280px,1fr))", gap: 12, alignItems: "start" }, children: [
|
|
7113
|
-
s.request ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
|
|
7114
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: "Request body" }),
|
|
7115
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Json, { obj: s.request })
|
|
7116
|
-
] }) : null,
|
|
7117
|
-
s.query ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
|
|
7118
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: "Query" }),
|
|
7119
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Json, { obj: s.query })
|
|
7120
|
-
] }) : null,
|
|
7121
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
|
|
7122
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: "Response 200" }),
|
|
7123
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Json, { obj: s.response })
|
|
7124
|
-
] })
|
|
7125
|
-
] }),
|
|
7126
|
-
s.notes ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Flag, { tone: "info", statement: "Implementation note", cost: s.notes, actions: null }) : null,
|
|
7127
|
-
tried && tried !== "busy" ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 6 }, children: [
|
|
7128
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("span", { className: "fd-row", style: { gap: 8 }, children: [
|
|
7129
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: "Simulated response" }),
|
|
7130
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Badge, { tone: tried.error ? "danger" : "success", icon: "timer", children: [
|
|
7131
|
-
tried.ms,
|
|
7132
|
-
"ms"
|
|
7133
|
-
] })
|
|
7134
|
-
] }),
|
|
7135
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Json, { obj: tried.error ? { error: tried.error } : tried.data })
|
|
7136
|
-
] }) : null
|
|
7137
|
-
] })
|
|
7138
|
-
] }) });
|
|
7701
|
+
function flagState(key) {
|
|
7702
|
+
if (!key) return "on";
|
|
7703
|
+
const f = findFlag(key);
|
|
7704
|
+
if (!f) return "on";
|
|
7705
|
+
return flagOn(key) ? "on" : "off";
|
|
7139
7706
|
}
|
|
7140
|
-
function
|
|
7141
|
-
|
|
7142
|
-
onRequest,
|
|
7143
|
-
modules,
|
|
7144
|
-
groups,
|
|
7145
|
-
title = "API specification",
|
|
7146
|
-
kicker = "Architecture",
|
|
7147
|
-
lede,
|
|
7148
|
-
sourceNote,
|
|
7149
|
-
conventions,
|
|
7150
|
-
openQuestions,
|
|
7151
|
-
className = "",
|
|
7152
|
-
...rest
|
|
7153
|
-
}) {
|
|
7154
|
-
const [q, setQ] = React32.useState("");
|
|
7155
|
-
const [method, setMethod] = React32.useState(null);
|
|
7156
|
-
const [mod, setMod] = React32.useState(null);
|
|
7157
|
-
const [listOnly, setListOnly] = React32.useState(false);
|
|
7158
|
-
const activeModule = modules && modules.find((m) => m.id === mod);
|
|
7159
|
-
const hits = spec.filter((s) => (!method || s.method === method) && (!listOnly || s.isList) && (!activeModule || activeModule.endpoints.indexOf(s.id) >= 0) && (!q || (s.path + " " + s.title + " " + s.purpose + " " + (s.usedBy || []).join(" ")).toLowerCase().includes(q.toLowerCase())));
|
|
7160
|
-
const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
|
|
7161
|
-
const methods = [...new Set(spec.map((s) => s.method))];
|
|
7162
|
-
const listCount = spec.filter((s) => s.isList).length;
|
|
7163
|
-
return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 1100 }, ...rest, children: [
|
|
7164
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
|
|
7165
|
-
kicker ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: kicker }) : null,
|
|
7166
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
|
|
7167
|
-
lede ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: lede }) : null
|
|
7168
|
-
] }),
|
|
7169
|
-
modules && modules.length ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 8 }, children: [
|
|
7170
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", children: "Filter by screen \u2014 every endpoint that screen depends on" }),
|
|
7171
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
|
|
7172
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Tag, { icon: "stack", selected: !mod, onClick: () => setMod(null), children: [
|
|
7173
|
-
"All screens ",
|
|
7174
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-mono", children: spec.length })
|
|
7175
|
-
] }),
|
|
7176
|
-
modules.map((m) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Tag, { icon: m.icon, selected: mod === m.id, onClick: () => setMod(mod === m.id ? null : m.id), children: [
|
|
7177
|
-
m.label,
|
|
7178
|
-
" ",
|
|
7179
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-mono", children: m.endpoints.length })
|
|
7180
|
-
] }, m.id))
|
|
7181
|
-
] }),
|
|
7182
|
-
activeModule ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
7183
|
-
Flag,
|
|
7184
|
-
{
|
|
7185
|
-
tone: "info",
|
|
7186
|
-
statement: activeModule.label + " calls " + activeModule.endpoints.length + " endpoints.",
|
|
7187
|
-
cost: "Integration checklist for this screen: " + activeModule.endpoints.join(", ") + ". Wire these and the screen is done.",
|
|
7188
|
-
actions: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Button, { size: "sm", variant: "ghost", icon: "x", onClick: () => setMod(null), children: "Show all screens" })
|
|
7189
|
-
}
|
|
7190
|
-
) : null
|
|
7191
|
-
] }) : null,
|
|
7192
|
-
sourceNote ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Flag, { tone: "info", statement: "Design-first: this page is the spec.", cost: sourceNote, actions: null }) : null,
|
|
7193
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
|
|
7194
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Input, { icon: "magnifying-glass", placeholder: "Find an endpoint, screen, or behavior", value: q, onChange: (e) => setQ(e.target.value), style: { width: 300 } }),
|
|
7195
|
-
methods.map((m) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Tag, { selected: method === m, onClick: () => setMethod(method === m ? null : m), children: [
|
|
7196
|
-
m,
|
|
7197
|
-
" ",
|
|
7198
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-mono", children: spec.filter((s) => s.method === m).length })
|
|
7199
|
-
] }, m)),
|
|
7200
|
-
listCount ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Tag, { icon: "rows", selected: listOnly, onClick: () => setListOnly(!listOnly), children: [
|
|
7201
|
-
"Paged lists ",
|
|
7202
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-mono", children: listCount })
|
|
7203
|
-
] }) : null,
|
|
7204
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { style: { flex: 1 } }),
|
|
7205
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
|
|
7206
|
-
hits.length,
|
|
7207
|
-
" of ",
|
|
7208
|
-
spec.length,
|
|
7209
|
-
" endpoints",
|
|
7210
|
-
listCount ? " \xB7 " + listCount + " paged" : ""
|
|
7211
|
-
] })
|
|
7212
|
-
] }),
|
|
7213
|
-
hits.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: "No endpoint matches those filters." }) }) : effGroups.map(([g, ids]) => {
|
|
7214
|
-
const items = hits.filter((s) => ids.indexOf(s.id) >= 0);
|
|
7215
|
-
if (!items.length) return null;
|
|
7216
|
-
return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-stack", style: { gap: 12 }, children: [
|
|
7217
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-label-lg", children: g }),
|
|
7218
|
-
items.map((s) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Endpoint, { s, onRequest }, s.id))
|
|
7219
|
-
] }, g);
|
|
7220
|
-
}),
|
|
7221
|
-
conventions && conventions.length ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Card, { title: "Cross-cutting conventions", elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("div", { className: "fd-stack", style: { gap: 10 }, children: conventions.map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-row", style: { gap: 12, alignItems: "flex-start" }, children: [
|
|
7222
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-overline fd-muted", style: { width: 110, flex: "none", paddingTop: 2 }, children: k }),
|
|
7223
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: v })
|
|
7224
|
-
] }, k)) }) }) : null,
|
|
7225
|
-
openQuestions && openQuestions.length ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Collapsible, { icon: "list-checks", title: "Open questions before implementation", subtitle: openQuestions.length + " unresolved", children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("div", { className: "fd-stack", style: { gap: 8 }, children: openQuestions.map(([id, question, why]) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start", padding: "8px 0", borderTop: "1px solid var(--border)" }, children: [
|
|
7226
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Badge, { tone: "danger", children: id }),
|
|
7227
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("span", { className: "fd-stack", style: { gap: 2, flex: 1 }, children: [
|
|
7228
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: question }),
|
|
7229
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "fd-body-sm fd-muted", children: why })
|
|
7230
|
-
] })
|
|
7231
|
-
] }, id)) }) }) : null
|
|
7232
|
-
] });
|
|
7707
|
+
function isEnabled(key) {
|
|
7708
|
+
return flagState(key) !== "off";
|
|
7233
7709
|
}
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7710
|
+
function flagVisible(key) {
|
|
7711
|
+
return isEnabled(key);
|
|
7712
|
+
}
|
|
7713
|
+
function flagLive(key) {
|
|
7714
|
+
return isEnabled(key);
|
|
7715
|
+
}
|
|
7716
|
+
function roadmap(overrides) {
|
|
7717
|
+
const flags = overrides && overrides.flags || (getSession().allFlags || []);
|
|
7718
|
+
const isComplete = overrides && overrides.isComplete || (() => false);
|
|
7719
|
+
const items = flags.map((f) => {
|
|
7720
|
+
const done = !!isComplete(f.key);
|
|
7721
|
+
const date = done ? f.shippedAt || f.eta : f.eta || f.shippedAt;
|
|
7722
|
+
const phaseInfo = (f.phase != null ? PHASES[f.phase] : void 0) || {};
|
|
7723
|
+
return {
|
|
7724
|
+
key: f.key,
|
|
7725
|
+
label: f.label,
|
|
7726
|
+
project: f.project,
|
|
7727
|
+
description: f.description,
|
|
7728
|
+
backend: f.backend,
|
|
7729
|
+
effort: f.effort,
|
|
7730
|
+
owner: f.owner,
|
|
7731
|
+
dependsOn: f.dependsOn || [],
|
|
7732
|
+
screen: f.screen || null,
|
|
7733
|
+
enabled: f.enabled,
|
|
7734
|
+
/* Which build phase this sits in, and why that phase comes where it does. */
|
|
7735
|
+
phase: f.phase,
|
|
7736
|
+
phaseName: phaseInfo.name || "Unscheduled",
|
|
7737
|
+
phaseWhy: phaseInfo.why || null,
|
|
7738
|
+
phaseStart: phaseInfo.start || null,
|
|
7739
|
+
/* Derived, not stored. */
|
|
7740
|
+
implemented: done,
|
|
7741
|
+
date: date || null,
|
|
7742
|
+
/* True when the only date we have is when the UI was designed, not when it ships. */
|
|
7743
|
+
dateIsDesign: !done && !f.eta && !!f.shippedAt,
|
|
7744
|
+
status: done ? "shipped" : f.enabled ? "next" : "planned"
|
|
7745
|
+
};
|
|
7746
|
+
}).filter((i) => i.date);
|
|
7747
|
+
items.sort((a, b) => (a.phase ?? 0) - (b.phase ?? 0) || String(a.date).localeCompare(String(b.date)) || a.label.localeCompare(b.label));
|
|
7748
|
+
const nextIndex = items.findIndex((i) => !i.implemented);
|
|
7749
|
+
return {
|
|
7750
|
+
items,
|
|
7751
|
+
nextIndex: nextIndex < 0 ? items.length : nextIndex,
|
|
7752
|
+
shipped: items.filter((i) => i.implemented).length,
|
|
7753
|
+
remaining: items.filter((i) => !i.implemented).length
|
|
7275
7754
|
};
|
|
7276
|
-
const liveSessions = sessions || [
|
|
7277
|
-
{ id: "s1", device: "MacBook Pro \xB7 Chrome", where: "Denver, CO", when: "Active now", current: true },
|
|
7278
|
-
{ id: "s2", device: "iPhone 15 \xB7 Safari", where: "Denver, CO", when: "2 hours ago" },
|
|
7279
|
-
{ id: "s3", device: "Windows \xB7 Edge", where: "Chicago, IL", when: "Aug 12" }
|
|
7280
|
-
];
|
|
7281
|
-
return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 20, maxWidth: 860 }, ...rest, children: [
|
|
7282
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
|
|
7283
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-overline fd-muted", children: "Account" }),
|
|
7284
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: "Your profile" }),
|
|
7285
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: "How you appear to colleagues and clients across every flytedesk app, and what we send you." })
|
|
7286
|
-
] }),
|
|
7287
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-row", style: { gap: 16, flexWrap: "wrap", alignItems: "flex-start" }, children: [
|
|
7288
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Avatar, { name: draft.name || user.name, size: "lg" }),
|
|
7289
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-stack", style: { gap: 4, flex: 1, minWidth: 200 }, children: [
|
|
7290
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-h3", style: { margin: 0 }, children: draft.name || user.name }),
|
|
7291
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
|
|
7292
|
-
draft.title || "No title set",
|
|
7293
|
-
" \xB7 ",
|
|
7294
|
-
user.team || "No team"
|
|
7295
|
-
] }),
|
|
7296
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 4 }, children: [
|
|
7297
|
-
session.roles.map((r) => /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Badge, { tone: r.id === "owner" ? "success" : "neutral", children: r.name }, r.id)),
|
|
7298
|
-
user.sso ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Badge, { tone: "info", icon: "shield-check", children: "SSO" }) : null
|
|
7299
|
-
] })
|
|
7300
|
-
] }),
|
|
7301
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Button, { variant: "secondary", size: "sm", icon: "image", children: "Change photo" })
|
|
7302
|
-
] }) }),
|
|
7303
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
7304
|
-
Card,
|
|
7305
|
-
{
|
|
7306
|
-
title: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-stack", style: { gap: 2 }, children: [
|
|
7307
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: "Identity" }),
|
|
7308
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "Name and title appear on plans you share" })
|
|
7309
|
-
] }),
|
|
7310
|
-
elevation: "flat",
|
|
7311
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-stack", style: { gap: 14 }, children: [
|
|
7312
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: [
|
|
7313
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Input, { label: "Full name", value: draft.name, onChange: (e) => set("name", e.target.value) }),
|
|
7314
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Input, { label: "Job title", value: draft.title, onChange: (e) => set("title", e.target.value), placeholder: "e.g. Senior media planner" })
|
|
7315
|
-
] }),
|
|
7316
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: [
|
|
7317
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
7318
|
-
Input,
|
|
7319
|
-
{
|
|
7320
|
-
label: "Work email",
|
|
7321
|
-
value: draft.email,
|
|
7322
|
-
readOnly: true,
|
|
7323
|
-
disabled: true,
|
|
7324
|
-
help: user.sso ? "Managed by your identity provider \u2014 change it there." : "Contact an administrator to change this."
|
|
7325
|
-
}
|
|
7326
|
-
),
|
|
7327
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Input, { label: "Phone", value: draft.phone, onChange: (e) => set("phone", e.target.value), placeholder: "Optional" })
|
|
7328
|
-
] }),
|
|
7329
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
7330
|
-
Textarea,
|
|
7331
|
-
{
|
|
7332
|
-
label: "Short bio",
|
|
7333
|
-
value: draft.bio,
|
|
7334
|
-
onChange: (e) => set("bio", e.target.value),
|
|
7335
|
-
rows: 3,
|
|
7336
|
-
placeholder: "Optional \u2014 shown to colleagues on shared plans.",
|
|
7337
|
-
help: "Never shown to clients."
|
|
7338
|
-
}
|
|
7339
|
-
)
|
|
7340
|
-
] })
|
|
7341
|
-
}
|
|
7342
|
-
),
|
|
7343
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Card, { title: "Working preferences", elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(220px,1fr))", gap: 14 }, children: [
|
|
7344
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
7345
|
-
Select,
|
|
7346
|
-
{
|
|
7347
|
-
label: "Time zone",
|
|
7348
|
-
value: draft.timezone,
|
|
7349
|
-
onChange: (e) => set("timezone", e.target.value),
|
|
7350
|
-
options: TIMEZONES,
|
|
7351
|
-
help: "Flight dates and schedules render in this zone."
|
|
7352
|
-
}
|
|
7353
|
-
),
|
|
7354
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Select, { label: "Start of week", options: ["Monday", "Sunday"], placeholder: "Monday" })
|
|
7355
|
-
] }) }),
|
|
7356
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
7357
|
-
Card,
|
|
7358
|
-
{
|
|
7359
|
-
title: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-stack", style: { gap: 2 }, children: [
|
|
7360
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: "Notifications" }),
|
|
7361
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "Email only for now \u2014 in-app notifications are on the roadmap" })
|
|
7362
|
-
] }),
|
|
7363
|
-
elevation: "flat",
|
|
7364
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "fd-stack", style: { gap: 14 }, children: NOTIFY.map((n) => /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Switch, { checked: !!draft.notify[n.key], onChange: () => setNotify(n.key), label: n.label, description: n.detail }, n.key)) })
|
|
7365
|
-
}
|
|
7366
|
-
),
|
|
7367
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
|
|
7368
|
-
Card,
|
|
7369
|
-
{
|
|
7370
|
-
title: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-stack", style: { gap: 2 }, children: [
|
|
7371
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { children: "Access" }),
|
|
7372
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-body-sm fd-muted", style: { fontWeight: 400 }, children: "What your roles grant you" })
|
|
7373
|
-
] }),
|
|
7374
|
-
elevation: "flat",
|
|
7375
|
-
action: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
7376
|
-
Button,
|
|
7377
|
-
{
|
|
7378
|
-
size: "sm",
|
|
7379
|
-
variant: "ghost",
|
|
7380
|
-
icon: "arrow-right",
|
|
7381
|
-
onClick: () => onNavigate && onNavigate("#roles"),
|
|
7382
|
-
children: "See role definitions"
|
|
7383
|
-
}
|
|
7384
|
-
),
|
|
7385
|
-
children: [
|
|
7386
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-stack", style: { gap: 0 }, children: [
|
|
7387
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(CardRow, { label: "Roles", children: session.roles.map((r) => r.name).join(", ") || "None" }),
|
|
7388
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(CardRow, { label: "Permissions", children: [
|
|
7389
|
-
session.permissions.length,
|
|
7390
|
-
" granted"
|
|
7391
|
-
] }),
|
|
7392
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(CardRow, { label: "Member since", children: user.joined || "\u2014" })
|
|
7393
|
-
] }),
|
|
7394
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("p", { className: "fd-body-sm fd-muted", style: { margin: "12px 0 0" }, children: "You cannot change your own roles \u2014 that is the point of them. An administrator manages roles from Admin \u2192 Users." })
|
|
7395
|
-
]
|
|
7396
|
-
}
|
|
7397
|
-
),
|
|
7398
|
-
showSessions ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
7399
|
-
Card,
|
|
7400
|
-
{
|
|
7401
|
-
title: "Signed-in devices",
|
|
7402
|
-
elevation: "flat",
|
|
7403
|
-
action: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Button, { size: "sm", variant: "ghost", icon: "sign-out", children: "Sign out everywhere" }),
|
|
7404
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "fd-stack", style: { gap: 0 }, children: liveSessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-row", style: { gap: 12, padding: "10px 0", borderTop: "1px solid var(--border)", flexWrap: "wrap" }, children: [
|
|
7405
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("i", { className: "ph ph-device-mobile", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
|
|
7406
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-stack", style: { gap: 1, flex: 1, minWidth: 160 }, children: [
|
|
7407
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: s.device }),
|
|
7408
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
|
|
7409
|
-
s.where,
|
|
7410
|
-
" \xB7 ",
|
|
7411
|
-
s.when
|
|
7412
|
-
] })
|
|
7413
|
-
] }),
|
|
7414
|
-
s.current ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Badge, { tone: "success", dot: true, children: "This device" }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Button, { size: "sm", variant: "ghost", children: "Revoke" })
|
|
7415
|
-
] }, s.id)) })
|
|
7416
|
-
}
|
|
7417
|
-
) : null,
|
|
7418
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "fd-row", style: {
|
|
7419
|
-
gap: 10,
|
|
7420
|
-
flexWrap: "wrap",
|
|
7421
|
-
position: "sticky",
|
|
7422
|
-
bottom: 0,
|
|
7423
|
-
padding: "12px 14px",
|
|
7424
|
-
borderRadius: 10,
|
|
7425
|
-
background: "var(--surface)",
|
|
7426
|
-
border: "1px solid var(--border)",
|
|
7427
|
-
boxShadow: "0 -4px 16px rgba(11,13,17,.06)"
|
|
7428
|
-
}, children: [
|
|
7429
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600, flex: 1 }, children: saved ? "Saved." : dirty ? "Unsaved changes" : "No pending changes" }),
|
|
7430
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
7431
|
-
Button,
|
|
7432
|
-
{
|
|
7433
|
-
size: "sm",
|
|
7434
|
-
variant: "ghost",
|
|
7435
|
-
disabled: !dirty || saving,
|
|
7436
|
-
onClick: () => {
|
|
7437
|
-
setDraft({ name: user.name || "", title: user.title || "", email: user.email || "", phone: user.phone || "", timezone: user.timezone || TIMEZONES[2], bio: user.bio || "", notify: user.notify || { planShared: true, planChanged: true, goalMissed: true, flagChanged: false, weekly: true } });
|
|
7438
|
-
setSaved(false);
|
|
7439
|
-
},
|
|
7440
|
-
children: "Discard"
|
|
7441
|
-
}
|
|
7442
|
-
),
|
|
7443
|
-
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Button, { size: "sm", icon: "check", disabled: !dirty, loading: saving, onClick: save, children: "Save changes" })
|
|
7444
|
-
] })
|
|
7445
|
-
] });
|
|
7446
7755
|
}
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7756
|
+
function useSession() {
|
|
7757
|
+
const [s, setS] = React37.useState(getSession);
|
|
7758
|
+
React37.useEffect(() => subscribe(setS), []);
|
|
7759
|
+
return s;
|
|
7760
|
+
}
|
|
7761
|
+
var SessionKit = {
|
|
7762
|
+
PERMISSION_CATALOG,
|
|
7763
|
+
ALL_PERMISSIONS,
|
|
7764
|
+
ROLES,
|
|
7765
|
+
FLAGS,
|
|
7766
|
+
USERS,
|
|
7767
|
+
loadSession,
|
|
7768
|
+
getSession,
|
|
7769
|
+
setActiveUser,
|
|
7770
|
+
saveProfile,
|
|
7771
|
+
refresh,
|
|
7772
|
+
subscribe,
|
|
7773
|
+
can,
|
|
7774
|
+
canAny,
|
|
7775
|
+
hasRole,
|
|
7776
|
+
flagOn,
|
|
7777
|
+
useSession,
|
|
7778
|
+
findFlag,
|
|
7779
|
+
flagState,
|
|
7780
|
+
isEnabled,
|
|
7781
|
+
flagVisible,
|
|
7782
|
+
flagLive,
|
|
7783
|
+
roadmap,
|
|
7784
|
+
evaluateFlag,
|
|
7785
|
+
explainFlag,
|
|
7786
|
+
bucket,
|
|
7787
|
+
buildSession
|
|
7788
|
+
};
|
|
7789
|
+
function DemoSessionProvider({ children }) {
|
|
7790
|
+
const session = useSession();
|
|
7791
|
+
const value = React37.useMemo(
|
|
7792
|
+
() => ({
|
|
7793
|
+
user: session.user,
|
|
7794
|
+
roles: session.roles,
|
|
7795
|
+
permissions: session.permissions,
|
|
7796
|
+
can,
|
|
7797
|
+
canAny,
|
|
7798
|
+
hasRole,
|
|
7799
|
+
signOut: () => window.alert("Signed out. (Simulated \u2014 no auth provider is wired up.)"),
|
|
7800
|
+
saveProfile: (patch) => {
|
|
7801
|
+
saveProfile(patch);
|
|
7802
|
+
},
|
|
7803
|
+
demo: { allUsers: session.allUsers.filter((u) => u.status === "active"), setActiveUser }
|
|
7804
|
+
}),
|
|
7805
|
+
[session]
|
|
7806
|
+
);
|
|
7807
|
+
return React37.createElement(SessionProvider, { value }, children);
|
|
7808
|
+
}
|
|
7450
7809
|
|
|
7451
7810
|
// src/kits/runtime.ts
|
|
7452
|
-
var
|
|
7811
|
+
var React38 = __toESM(require("react"), 1);
|
|
7453
7812
|
var WIRED_ENDPOINTS = [
|
|
7454
7813
|
// Nothing yet. Every id below would come from a real service:
|
|
7455
7814
|
// "plan.get", "placements.list", …
|
|
@@ -7620,8 +7979,8 @@ var RuntimeKit = {
|
|
|
7620
7979
|
};
|
|
7621
7980
|
RuntimeKit.declare(FEATURE_NEEDS);
|
|
7622
7981
|
function useRuntimeMode() {
|
|
7623
|
-
const [m, setM] =
|
|
7624
|
-
|
|
7982
|
+
const [m, setM] = React38.useState(RuntimeKit.getMode());
|
|
7983
|
+
React38.useEffect(() => RuntimeKit.subscribe(setM), []);
|
|
7625
7984
|
return m;
|
|
7626
7985
|
}
|
|
7627
7986
|
function useFeatureStatus(key) {
|
|
@@ -7640,7 +7999,7 @@ var UseRuntimeMode = useRuntimeMode;
|
|
|
7640
7999
|
var UseFeatureStatus = useFeatureStatus;
|
|
7641
8000
|
|
|
7642
8001
|
// src/components/platform/RoadmapTimeline.tsx
|
|
7643
|
-
var
|
|
8002
|
+
var import_jsx_runtime63 = require("react/jsx-runtime");
|
|
7644
8003
|
var STATUS = {
|
|
7645
8004
|
shipped: { tone: "success", icon: "check-circle", label: "Wired" },
|
|
7646
8005
|
next: { tone: "warning", icon: "traffic-cone", label: "Not wired" },
|
|
@@ -7655,45 +8014,45 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
|
|
|
7655
8014
|
const s = STATUS[item.status] || STATUS.planned;
|
|
7656
8015
|
const deps = (item.dependsOn || []).map((k) => byKey[k]).filter(Boolean);
|
|
7657
8016
|
const blocking = deps.filter((d) => !d.implemented);
|
|
7658
|
-
return /* @__PURE__ */ (0,
|
|
7659
|
-
/* @__PURE__ */ (0,
|
|
7660
|
-
/* @__PURE__ */ (0,
|
|
7661
|
-
/* @__PURE__ */ (0,
|
|
7662
|
-
/* @__PURE__ */ (0,
|
|
8017
|
+
return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Card, { elevation: "flat", children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
|
|
8018
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: [
|
|
8019
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-body", style: { fontWeight: 700, flex: 1, minWidth: 140 }, children: item.label }),
|
|
8020
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Badge, { tone: "neutral", icon: PROJECT_ICON[item.project] || "squares-four", children: item.project }),
|
|
8021
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Badge, { tone: s.tone, icon: s.icon, children: s.label })
|
|
7663
8022
|
] }),
|
|
7664
|
-
/* @__PURE__ */ (0,
|
|
7665
|
-
item.backend ? /* @__PURE__ */ (0,
|
|
7666
|
-
/* @__PURE__ */ (0,
|
|
7667
|
-
/* @__PURE__ */ (0,
|
|
8023
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: item.description }),
|
|
8024
|
+
item.backend ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-row", style: { gap: 10, alignItems: "flex-start" }, children: [
|
|
8025
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-overline fd-muted", style: { width: 62, flex: "none", paddingTop: 2 }, children: "Backend" }),
|
|
8026
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-body-sm", style: { flex: 1, textWrap: "pretty" }, children: item.backend })
|
|
7668
8027
|
] }) : null,
|
|
7669
|
-
/* @__PURE__ */ (0,
|
|
7670
|
-
item.effort ? /* @__PURE__ */ (0,
|
|
7671
|
-
/* @__PURE__ */ (0,
|
|
8028
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
|
|
8029
|
+
item.effort ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
|
|
8030
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("i", { className: "ph ph-hourglass-medium" }),
|
|
7672
8031
|
" ",
|
|
7673
8032
|
item.effort
|
|
7674
8033
|
] }) : null,
|
|
7675
|
-
/* @__PURE__ */ (0,
|
|
7676
|
-
/* @__PURE__ */ (0,
|
|
8034
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
|
|
8035
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("i", { className: "ph ph-user" }),
|
|
7677
8036
|
" ",
|
|
7678
8037
|
item.owner
|
|
7679
8038
|
] }),
|
|
7680
|
-
item.screen ? /* @__PURE__ */ (0,
|
|
7681
|
-
/* @__PURE__ */ (0,
|
|
7682
|
-
/* @__PURE__ */ (0,
|
|
8039
|
+
item.screen ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Badge, { tone: "neutral", icon: "browser", children: "screen" }) : null,
|
|
8040
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { style: { flex: 1 } }),
|
|
8041
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
7683
8042
|
"button",
|
|
7684
8043
|
{
|
|
7685
8044
|
type: "button",
|
|
7686
8045
|
onClick: () => onOpenFlag && onOpenFlag(item.key),
|
|
7687
8046
|
style: { all: "unset", cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 5 },
|
|
7688
|
-
children: /* @__PURE__ */ (0,
|
|
8047
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("code", { className: "fd-mono", style: { fontSize: 11, color: "var(--brand)" }, children: item.key })
|
|
7689
8048
|
}
|
|
7690
8049
|
)
|
|
7691
8050
|
] }),
|
|
7692
|
-
deps.length ? /* @__PURE__ */ (0,
|
|
7693
|
-
/* @__PURE__ */ (0,
|
|
7694
|
-
deps.map((d) => /* @__PURE__ */ (0,
|
|
8051
|
+
deps.length ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-row", style: { gap: 6, flexWrap: "wrap", paddingTop: 6, borderTop: "1px solid var(--border)" }, children: [
|
|
8052
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-overline fd-muted", style: { paddingTop: 3 }, children: "After" }),
|
|
8053
|
+
deps.map((d) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Tag, { icon: d.implemented ? "check" : "clock", children: d.label }, d.key))
|
|
7695
8054
|
] }) : null,
|
|
7696
|
-
blocking.length && !item.implemented ? /* @__PURE__ */ (0,
|
|
8055
|
+
blocking.length && !item.implemented ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-body-sm", style: { color: "var(--warn-text)" }, children: [
|
|
7697
8056
|
"Blocked until ",
|
|
7698
8057
|
blocking.map((d) => d.label).join(" and "),
|
|
7699
8058
|
" ",
|
|
@@ -7704,12 +8063,12 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
|
|
|
7704
8063
|
}
|
|
7705
8064
|
function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
|
|
7706
8065
|
const session = SessionKit.useSession();
|
|
7707
|
-
const [project, setProject] =
|
|
7708
|
-
const [q, setQ] =
|
|
7709
|
-
const scrollRef =
|
|
7710
|
-
const nowRef =
|
|
7711
|
-
const rm =
|
|
7712
|
-
const byKey =
|
|
8066
|
+
const [project, setProject] = React39.useState("");
|
|
8067
|
+
const [q, setQ] = React39.useState("");
|
|
8068
|
+
const scrollRef = React39.useRef(null);
|
|
8069
|
+
const nowRef = React39.useRef(null);
|
|
8070
|
+
const rm = React39.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
|
|
8071
|
+
const byKey = React39.useMemo(() => {
|
|
7713
8072
|
const m = {};
|
|
7714
8073
|
rm.items.forEach((i) => {
|
|
7715
8074
|
m[i.key] = i;
|
|
@@ -7718,7 +8077,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
|
|
|
7718
8077
|
}, [rm]);
|
|
7719
8078
|
const items = rm.items.filter((i) => (!project || i.project === project) && (!q || (i.label + " " + i.description + " " + (i.backend || "") + " " + i.key).toLowerCase().includes(q.toLowerCase())));
|
|
7720
8079
|
const projects = [...new Set(rm.items.map((i) => i.project))];
|
|
7721
|
-
|
|
8080
|
+
React39.useEffect(() => {
|
|
7722
8081
|
let raf1 = 0, raf2 = 0;
|
|
7723
8082
|
const place = () => {
|
|
7724
8083
|
const box = scrollRef.current, mark = nowRef.current;
|
|
@@ -7746,23 +8105,23 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
|
|
|
7746
8105
|
}, 0);
|
|
7747
8106
|
const nextPhase = items.find((i) => !i.implemented);
|
|
7748
8107
|
let lastPhase = null;
|
|
7749
|
-
return /* @__PURE__ */ (0,
|
|
7750
|
-
/* @__PURE__ */ (0,
|
|
7751
|
-
/* @__PURE__ */ (0,
|
|
7752
|
-
/* @__PURE__ */ (0,
|
|
7753
|
-
/* @__PURE__ */ (0,
|
|
8108
|
+
return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-stack", style: { gap: 16, maxWidth: 1e3 }, children: [
|
|
8109
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-stack", style: { gap: 10 }, children: [
|
|
8110
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-overline fd-muted", children: "Architecture" }),
|
|
8111
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("h1", { className: "fd-h1", style: { margin: 0 }, children: title }),
|
|
8112
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { className: "fd-body fd-secondary fd-prose", style: { margin: 0 }, children: lede || "Every screen and feature in these tools is already designed and working against a simulated backend. This is the plan for connecting them to the real one \u2014 so the only work described here is server work." })
|
|
7754
8113
|
] }),
|
|
7755
|
-
/* @__PURE__ */ (0,
|
|
7756
|
-
/* @__PURE__ */ (0,
|
|
7757
|
-
/* @__PURE__ */ (0,
|
|
7758
|
-
/* @__PURE__ */ (0,
|
|
7759
|
-
/* @__PURE__ */ (0,
|
|
8114
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-grid-stats is-thin", children: [
|
|
8115
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(StatTile, { compact: true, label: "Wired", value: String(rm.shipped), sub: "of " + rm.items.length + " features" }),
|
|
8116
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(StatTile, { compact: true, label: "Remaining", value: String(rm.remaining), sub: "backend work" }),
|
|
8117
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(StatTile, { compact: true, label: "Est. effort", value: days ? days + " days" : "\u2014", sub: "sum of estimates, not calendar" }),
|
|
8118
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(StatTile, { compact: true, label: "Up next", value: nextPhase ? "Phase " + nextPhase.phase : "\u2014", sub: nextPhase ? nextPhase.phaseName : "all wired" })
|
|
7760
8119
|
] }),
|
|
7761
|
-
/* @__PURE__ */ (0,
|
|
7762
|
-
/* @__PURE__ */ (0,
|
|
7763
|
-
/* @__PURE__ */ (0,
|
|
7764
|
-
/* @__PURE__ */ (0,
|
|
7765
|
-
/* @__PURE__ */ (0,
|
|
8120
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-row", style: { gap: 10, flexWrap: "wrap" }, children: [
|
|
8121
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Input, { icon: "magnifying-glass", placeholder: "Find a feature or a piece of backend work", value: q, onChange: (e) => setQ(e.target.value), style: { width: 300 } }),
|
|
8122
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Select, { placeholder: "All projects", value: project, onChange: (e) => setProject(e.target.value), options: projects, style: { width: 190 } }),
|
|
8123
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { style: { flex: 1 } }),
|
|
8124
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
7766
8125
|
Button,
|
|
7767
8126
|
{
|
|
7768
8127
|
size: "sm",
|
|
@@ -7776,7 +8135,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
|
|
|
7776
8135
|
}
|
|
7777
8136
|
)
|
|
7778
8137
|
] }),
|
|
7779
|
-
/* @__PURE__ */ (0,
|
|
8138
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
7780
8139
|
Flag,
|
|
7781
8140
|
{
|
|
7782
8141
|
tone: "info",
|
|
@@ -7785,60 +8144,60 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
|
|
|
7785
8144
|
actions: null
|
|
7786
8145
|
}
|
|
7787
8146
|
),
|
|
7788
|
-
/* @__PURE__ */ (0,
|
|
7789
|
-
/* @__PURE__ */ (0,
|
|
8147
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "fd-rm", children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "fd-rm-scroll", ref: scrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-rm-track", children: [
|
|
8148
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-rm-spine", children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-rm-spine-done", style: { height: items.length ? 100 * shippedShown / items.length + "%" : "0%" } }) }),
|
|
7790
8149
|
items.map((item, n) => {
|
|
7791
8150
|
const showPhase = item.phase !== lastPhase;
|
|
7792
8151
|
lastPhase = item.phase;
|
|
7793
8152
|
const inPhase = items.filter((i) => i.phase === item.phase).length;
|
|
7794
8153
|
const isBoundary = n === firstPending;
|
|
7795
|
-
return /* @__PURE__ */ (0,
|
|
7796
|
-
showPhase ? /* @__PURE__ */ (0,
|
|
7797
|
-
/* @__PURE__ */ (0,
|
|
7798
|
-
/* @__PURE__ */ (0,
|
|
7799
|
-
item.phase === 0 ? null : /* @__PURE__ */ (0,
|
|
8154
|
+
return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(React39.Fragment, { children: [
|
|
8155
|
+
showPhase ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-rm-era", children: [
|
|
8156
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
|
|
8157
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
|
|
8158
|
+
item.phase === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-mono", style: { opacity: 0.7, fontWeight: 400 }, children: [
|
|
7800
8159
|
fmtDate(item.phaseStart),
|
|
7801
8160
|
" \u2013 ",
|
|
7802
8161
|
fmtDate(item.date)
|
|
7803
8162
|
] }),
|
|
7804
|
-
/* @__PURE__ */ (0,
|
|
8163
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { style: { opacity: 0.7, fontWeight: 400 }, children: [
|
|
7805
8164
|
"\xB7 ",
|
|
7806
8165
|
inPhase,
|
|
7807
8166
|
" feature",
|
|
7808
8167
|
inPhase === 1 ? "" : "s"
|
|
7809
8168
|
] })
|
|
7810
8169
|
] }),
|
|
7811
|
-
item.phaseWhy ? /* @__PURE__ */ (0,
|
|
8170
|
+
item.phaseWhy ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { className: "fd-rm-era-why", children: item.phaseWhy }) : null
|
|
7812
8171
|
] }) : null,
|
|
7813
|
-
isBoundary ? /* @__PURE__ */ (0,
|
|
7814
|
-
/* @__PURE__ */ (0,
|
|
8172
|
+
isBoundary ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "fd-rm-now", ref: nowRef, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "fd-rm-now-pill", children: [
|
|
8173
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("i", { className: "ph ph-map-pin" }),
|
|
7815
8174
|
" You are here \u2014 everything above is wired"
|
|
7816
8175
|
] }) }) : null,
|
|
7817
|
-
/* @__PURE__ */ (0,
|
|
7818
|
-
/* @__PURE__ */ (0,
|
|
7819
|
-
/* @__PURE__ */ (0,
|
|
8176
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-rm-row", children: [
|
|
8177
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-rm-dot " + (item.implemented ? "is-shipped" : item.status === "next" ? "is-next" : "") }),
|
|
8178
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "fd-rm-date", children: [
|
|
7820
8179
|
fmtDate(item.date),
|
|
7821
|
-
/* @__PURE__ */ (0,
|
|
7822
|
-
/* @__PURE__ */ (0,
|
|
8180
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("br", {}),
|
|
8181
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { style: { opacity: 0.75 }, children: item.implemented ? "shipped" : "phase " + item.phase })
|
|
7823
8182
|
] }),
|
|
7824
|
-
/* @__PURE__ */ (0,
|
|
8183
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(RoadmapCard, { item, byKey, onOpenFlag })
|
|
7825
8184
|
] })
|
|
7826
8185
|
] }, item.key);
|
|
7827
8186
|
}),
|
|
7828
|
-
!items.length ? /* @__PURE__ */ (0,
|
|
8187
|
+
!items.length ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("p", { className: "fd-body-sm fd-muted", children: "Nothing matches that filter." }) : null
|
|
7829
8188
|
] }) }) }),
|
|
7830
|
-
/* @__PURE__ */ (0,
|
|
8189
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("p", { className: "fd-body-sm fd-muted", style: { margin: 0 }, children: [
|
|
7831
8190
|
"Derived from the feature-flag registry \u2014 each entry's status is its flag's ",
|
|
7832
|
-
/* @__PURE__ */ (0,
|
|
8191
|
+
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)("code", { className: "fd-mono", children: "implemented" }),
|
|
7833
8192
|
" field, so this page cannot drift from what the apps actually do."
|
|
7834
8193
|
] })
|
|
7835
8194
|
] });
|
|
7836
8195
|
}
|
|
7837
8196
|
|
|
7838
8197
|
// src/components/platform/ComingSoon.tsx
|
|
7839
|
-
var
|
|
8198
|
+
var React40 = __toESM(require("react"), 1);
|
|
7840
8199
|
var import_react_dom5 = require("react-dom");
|
|
7841
|
-
var
|
|
8200
|
+
var import_jsx_runtime64 = require("react/jsx-runtime");
|
|
7842
8201
|
var BYPASS_STORE = "fd.soon.bypass.v1";
|
|
7843
8202
|
function readBypassed() {
|
|
7844
8203
|
try {
|
|
@@ -7854,8 +8213,8 @@ function writeBypassed(list) {
|
|
|
7854
8213
|
}
|
|
7855
8214
|
}
|
|
7856
8215
|
function useBypass(key) {
|
|
7857
|
-
const [on, setOn] =
|
|
7858
|
-
|
|
8216
|
+
const [on, setOn] = React40.useState(() => !!key && readBypassed().indexOf(key) >= 0);
|
|
8217
|
+
React40.useEffect(() => {
|
|
7859
8218
|
setOn(!!key && readBypassed().indexOf(key) >= 0);
|
|
7860
8219
|
}, [key]);
|
|
7861
8220
|
const set = (next) => {
|
|
@@ -7868,43 +8227,43 @@ function useBypass(key) {
|
|
|
7868
8227
|
return [on, set];
|
|
7869
8228
|
}
|
|
7870
8229
|
function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass }) {
|
|
7871
|
-
return /* @__PURE__ */ (0,
|
|
7872
|
-
/* @__PURE__ */ (0,
|
|
7873
|
-
/* @__PURE__ */ (0,
|
|
8230
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(import_jsx_runtime64.Fragment, { children: [
|
|
8231
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-soon-badge", children: [
|
|
8232
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
|
|
7874
8233
|
label
|
|
7875
8234
|
] }),
|
|
7876
|
-
detail ? /* @__PURE__ */ (0,
|
|
7877
|
-
backend ? /* @__PURE__ */ (0,
|
|
7878
|
-
/* @__PURE__ */ (0,
|
|
8235
|
+
detail ? /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-soon-detail", children: detail }) : null,
|
|
8236
|
+
backend ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-soon-backend", children: [
|
|
8237
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-overline", children: "Needs" }),
|
|
7879
8238
|
" ",
|
|
7880
8239
|
backend
|
|
7881
8240
|
] }) : null,
|
|
7882
|
-
eta || effort || onRoadmap ? /* @__PURE__ */ (0,
|
|
7883
|
-
eta ? /* @__PURE__ */ (0,
|
|
7884
|
-
/* @__PURE__ */ (0,
|
|
8241
|
+
eta || effort || onRoadmap ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-soon-meta", children: [
|
|
8242
|
+
eta ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { children: [
|
|
8243
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-calendar-blank", "aria-hidden": "true" }),
|
|
7885
8244
|
" ",
|
|
7886
8245
|
eta
|
|
7887
8246
|
] }) : null,
|
|
7888
|
-
effort ? /* @__PURE__ */ (0,
|
|
7889
|
-
/* @__PURE__ */ (0,
|
|
8247
|
+
effort ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { children: [
|
|
8248
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-hourglass-medium", "aria-hidden": "true" }),
|
|
7890
8249
|
" ",
|
|
7891
8250
|
effort
|
|
7892
8251
|
] }) : null,
|
|
7893
|
-
onRoadmap ? /* @__PURE__ */ (0,
|
|
8252
|
+
onRoadmap ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: onRoadmap, children: [
|
|
7894
8253
|
"See the roadmap ",
|
|
7895
|
-
/* @__PURE__ */ (0,
|
|
8254
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
|
|
7896
8255
|
] }) : null
|
|
7897
8256
|
] }) : null,
|
|
7898
|
-
allowed ? /* @__PURE__ */ (0,
|
|
7899
|
-
/* @__PURE__ */ (0,
|
|
8257
|
+
allowed ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-soon-view", onClick: onBypass, children: [
|
|
8258
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-eye", "aria-hidden": "true" }),
|
|
7900
8259
|
" View and use it anyway"
|
|
7901
8260
|
] }) : null
|
|
7902
8261
|
] });
|
|
7903
8262
|
}
|
|
7904
8263
|
function useHoverCard(open) {
|
|
7905
|
-
const anchor =
|
|
7906
|
-
const [pos, setPos] =
|
|
7907
|
-
|
|
8264
|
+
const anchor = React40.useRef(null);
|
|
8265
|
+
const [pos, setPos] = React40.useState(null);
|
|
8266
|
+
React40.useLayoutEffect(() => {
|
|
7908
8267
|
if (!open || !anchor.current) {
|
|
7909
8268
|
setPos(null);
|
|
7910
8269
|
return;
|
|
@@ -7956,9 +8315,9 @@ function ComingSoon({
|
|
|
7956
8315
|
const tip = [label, detail, backend ? "Needs " + backend : null, eta ? "ETA " + eta : null, effort].filter(Boolean).join(" \xB7 ");
|
|
7957
8316
|
if (inline) {
|
|
7958
8317
|
if (allowed && bypassed) {
|
|
7959
|
-
return /* @__PURE__ */ (0,
|
|
8318
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: ["fd-soon-inline-on", className].filter(Boolean).join(" "), ...rest, children: [
|
|
7960
8319
|
children,
|
|
7961
|
-
/* @__PURE__ */ (0,
|
|
8320
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
7962
8321
|
"button",
|
|
7963
8322
|
{
|
|
7964
8323
|
type: "button",
|
|
@@ -7966,12 +8325,12 @@ function ComingSoon({
|
|
|
7966
8325
|
title: "Unwired \u2014 writes go to the simulated backend. " + tip + " Click to re-blur.",
|
|
7967
8326
|
onClick: () => setBypassed(false),
|
|
7968
8327
|
"aria-label": "Re-blur this unwired feature",
|
|
7969
|
-
children: /* @__PURE__ */ (0,
|
|
8328
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
|
|
7970
8329
|
}
|
|
7971
8330
|
)
|
|
7972
8331
|
] });
|
|
7973
8332
|
}
|
|
7974
|
-
return /* @__PURE__ */ (0,
|
|
8333
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
7975
8334
|
InlineSoon,
|
|
7976
8335
|
{
|
|
7977
8336
|
label,
|
|
@@ -7991,20 +8350,20 @@ function ComingSoon({
|
|
|
7991
8350
|
);
|
|
7992
8351
|
}
|
|
7993
8352
|
if (allowed && bypassed) {
|
|
7994
|
-
return /* @__PURE__ */ (0,
|
|
7995
|
-
/* @__PURE__ */ (0,
|
|
7996
|
-
/* @__PURE__ */ (0,
|
|
7997
|
-
/* @__PURE__ */ (0,
|
|
8353
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 14 }, ...rest, children: [
|
|
8354
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: "fd-soon-bar", role: "status", children: [
|
|
8355
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-soon-badge", children: [
|
|
8356
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
|
|
7998
8357
|
"Unwired feature \u2014 you are using it anyway"
|
|
7999
8358
|
] }),
|
|
8000
|
-
/* @__PURE__ */ (0,
|
|
8001
|
-
/* @__PURE__ */ (0,
|
|
8002
|
-
onRoadmap ? /* @__PURE__ */ (0,
|
|
8359
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-soon-bar-detail", children: "Every action here writes to the simulated backend, so nothing you do persists beyond this session." }),
|
|
8360
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("span", { className: "fd-soon-bar-actions", children: [
|
|
8361
|
+
onRoadmap ? /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: onRoadmap, children: [
|
|
8003
8362
|
"Roadmap ",
|
|
8004
|
-
/* @__PURE__ */ (0,
|
|
8363
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
|
|
8005
8364
|
] }) : null,
|
|
8006
|
-
/* @__PURE__ */ (0,
|
|
8007
|
-
/* @__PURE__ */ (0,
|
|
8365
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: () => setBypassed(false), children: [
|
|
8366
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-eye-slash", "aria-hidden": "true" }),
|
|
8008
8367
|
" Re-blur"
|
|
8009
8368
|
] })
|
|
8010
8369
|
] })
|
|
@@ -8012,10 +8371,10 @@ function ComingSoon({
|
|
|
8012
8371
|
children
|
|
8013
8372
|
] });
|
|
8014
8373
|
}
|
|
8015
|
-
return /* @__PURE__ */ (0,
|
|
8016
|
-
/* @__PURE__ */ (0,
|
|
8017
|
-
/* @__PURE__ */ (0,
|
|
8018
|
-
/* @__PURE__ */ (0,
|
|
8374
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)("div", { className: ["fd-soon", className].filter(Boolean).join(" "), style: minHeight ? { minHeight } : void 0, ...rest, children: [
|
|
8375
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("div", { className: "fd-soon-under", style: { filter: "blur(" + blur + "px) saturate(.62)" }, ...{ inert: "" }, "aria-hidden": "true", children }),
|
|
8376
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("div", { className: "fd-soon-veil" }),
|
|
8377
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("div", { className: "fd-soon-note", role: "note", children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
8019
8378
|
SoonCard,
|
|
8020
8379
|
{
|
|
8021
8380
|
label,
|
|
@@ -8031,10 +8390,10 @@ function ComingSoon({
|
|
|
8031
8390
|
] });
|
|
8032
8391
|
}
|
|
8033
8392
|
function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass, blur, tip, className, rest, children }) {
|
|
8034
|
-
const [open, setOpen] =
|
|
8393
|
+
const [open, setOpen] = React40.useState(false);
|
|
8035
8394
|
const [anchor, pos] = useHoverCard(open);
|
|
8036
8395
|
const zIndex = useOverlayLayer(open);
|
|
8037
|
-
const close =
|
|
8396
|
+
const close = React40.useRef(null);
|
|
8038
8397
|
const show = () => {
|
|
8039
8398
|
if (close.current) {
|
|
8040
8399
|
clearTimeout(close.current);
|
|
@@ -8050,10 +8409,10 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
|
|
|
8050
8409
|
setOpen(false);
|
|
8051
8410
|
}, 140);
|
|
8052
8411
|
};
|
|
8053
|
-
|
|
8412
|
+
React40.useEffect(() => () => {
|
|
8054
8413
|
if (close.current) clearTimeout(close.current);
|
|
8055
8414
|
}, []);
|
|
8056
|
-
return /* @__PURE__ */ (0,
|
|
8415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(
|
|
8057
8416
|
"span",
|
|
8058
8417
|
{
|
|
8059
8418
|
ref: anchor,
|
|
@@ -8064,8 +8423,8 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
|
|
|
8064
8423
|
onBlur: hide,
|
|
8065
8424
|
...rest,
|
|
8066
8425
|
children: [
|
|
8067
|
-
/* @__PURE__ */ (0,
|
|
8068
|
-
/* @__PURE__ */ (0,
|
|
8426
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)("span", { className: "fd-soon-under", style: { filter: "blur(" + Math.min(blur, 1.1) + "px) saturate(.66)" }, ...{ inert: "" }, "aria-hidden": "true", children }),
|
|
8427
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
8069
8428
|
"button",
|
|
8070
8429
|
{
|
|
8071
8430
|
type: "button",
|
|
@@ -8076,11 +8435,11 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
|
|
|
8076
8435
|
onFocus: show,
|
|
8077
8436
|
onBlur: hide,
|
|
8078
8437
|
onClick: () => open ? setOpen(false) : show(),
|
|
8079
|
-
children: /* @__PURE__ */ (0,
|
|
8438
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
|
|
8080
8439
|
}
|
|
8081
8440
|
),
|
|
8082
8441
|
open && pos ? (0, import_react_dom5.createPortal)(
|
|
8083
|
-
/* @__PURE__ */ (0,
|
|
8442
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
8084
8443
|
"div",
|
|
8085
8444
|
{
|
|
8086
8445
|
className: "fd-soon-note fd-soon-hovercard",
|
|
@@ -8088,7 +8447,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
|
|
|
8088
8447
|
style: { position: "fixed", left: pos.left, top: pos.top, bottom: pos.bottom, width: pos.width, zIndex },
|
|
8089
8448
|
onMouseEnter: show,
|
|
8090
8449
|
onMouseLeave: hide,
|
|
8091
|
-
children: /* @__PURE__ */ (0,
|
|
8450
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
8092
8451
|
SoonCard,
|
|
8093
8452
|
{
|
|
8094
8453
|
label,
|
|
@@ -8118,10 +8477,10 @@ function formatEta(iso2) {
|
|
|
8118
8477
|
var FormatEta = formatEta;
|
|
8119
8478
|
|
|
8120
8479
|
// src/components/platform/Gate.tsx
|
|
8121
|
-
var
|
|
8480
|
+
var import_jsx_runtime65 = require("react/jsx-runtime");
|
|
8122
8481
|
function PermissionDenied({ permission, title, detail, compact: compact3 = false, className = "", ...rest }) {
|
|
8123
8482
|
const need = Array.isArray(permission) ? permission : [permission].filter(Boolean);
|
|
8124
|
-
return /* @__PURE__ */ (0,
|
|
8483
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(
|
|
8125
8484
|
"div",
|
|
8126
8485
|
{
|
|
8127
8486
|
className: ["fd-stack", className].filter(Boolean).join(" "),
|
|
@@ -8137,14 +8496,14 @@ function PermissionDenied({ permission, title, detail, compact: compact3 = false
|
|
|
8137
8496
|
},
|
|
8138
8497
|
...rest,
|
|
8139
8498
|
children: [
|
|
8140
|
-
/* @__PURE__ */ (0,
|
|
8141
|
-
/* @__PURE__ */ (0,
|
|
8142
|
-
/* @__PURE__ */ (0,
|
|
8499
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("span", { className: "fd-row", style: { gap: 8 }, children: [
|
|
8500
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("i", { className: "ph ph-lock-simple", style: { fontSize: 16, color: "var(--text-muted)" }, "aria-hidden": "true" }),
|
|
8501
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: compact3 ? "fd-label-lg" : "fd-h3", children: title || "You do not have access to this" })
|
|
8143
8502
|
] }),
|
|
8144
|
-
/* @__PURE__ */ (0,
|
|
8145
|
-
need.length ? /* @__PURE__ */ (0,
|
|
8146
|
-
/* @__PURE__ */ (0,
|
|
8147
|
-
need.map((p) => /* @__PURE__ */ (0,
|
|
8503
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("p", { className: "fd-body-sm fd-secondary", style: { margin: 0, textWrap: "pretty" }, children: detail || "Your roles do not grant this. An administrator can change that from Admin \u2192 Users." }),
|
|
8504
|
+
need.length ? /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("span", { className: "fd-row", style: { gap: 6, flexWrap: "wrap" }, children: [
|
|
8505
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: "fd-overline fd-muted", children: "Requires" }),
|
|
8506
|
+
need.map((p) => /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("code", { className: "fd-mono", style: {
|
|
8148
8507
|
fontSize: 11.5,
|
|
8149
8508
|
padding: "2px 7px",
|
|
8150
8509
|
borderRadius: 5,
|
|
@@ -8157,15 +8516,15 @@ function PermissionDenied({ permission, title, detail, compact: compact3 = false
|
|
|
8157
8516
|
);
|
|
8158
8517
|
}
|
|
8159
8518
|
function Gate({ perm, anyOf, role, silent = false, fallback, compact: compact3 = false, children }) {
|
|
8160
|
-
|
|
8519
|
+
const { can: can2, canAny: canAny2, hasRole: hasRole2 } = useSessionContext();
|
|
8161
8520
|
let ok = true;
|
|
8162
|
-
if (perm) ok = ok &&
|
|
8163
|
-
if (anyOf) ok = ok &&
|
|
8164
|
-
if (role) ok = ok && (Array.isArray(role) ? role.some(
|
|
8521
|
+
if (perm) ok = ok && can2(perm);
|
|
8522
|
+
if (anyOf) ok = ok && canAny2(anyOf);
|
|
8523
|
+
if (role) ok = ok && (Array.isArray(role) ? role.some(hasRole2) : hasRole2(role));
|
|
8165
8524
|
if (ok) return children;
|
|
8166
8525
|
if (fallback !== void 0) return fallback;
|
|
8167
8526
|
if (silent) return null;
|
|
8168
|
-
return /* @__PURE__ */ (0,
|
|
8527
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(PermissionDenied, { permission: perm || anyOf, compact: compact3 });
|
|
8169
8528
|
}
|
|
8170
8529
|
function FeatureGate({
|
|
8171
8530
|
flag,
|
|
@@ -8180,14 +8539,15 @@ function FeatureGate({
|
|
|
8180
8539
|
bypassPerm = "flags.manage"
|
|
8181
8540
|
}) {
|
|
8182
8541
|
SessionKit.useSession();
|
|
8542
|
+
const { can: can2 } = useSessionContext();
|
|
8183
8543
|
const rt = useFeatureStatus(flag);
|
|
8184
8544
|
if (!SessionKit.isEnabled(flag)) return fallback;
|
|
8185
8545
|
if (rt.complete || rt.mode === "test") return children;
|
|
8186
8546
|
if (!preview) return fallback;
|
|
8187
8547
|
const f = SessionKit.findFlag(flag) || {};
|
|
8188
8548
|
const missing = rt.missing || [];
|
|
8189
|
-
const canBypass = bypassPerm === null ? false : !bypassPerm ||
|
|
8190
|
-
return /* @__PURE__ */ (0,
|
|
8549
|
+
const canBypass = bypassPerm === null ? false : !bypassPerm || can2(bypassPerm);
|
|
8550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
8191
8551
|
ComingSoon,
|
|
8192
8552
|
{
|
|
8193
8553
|
label: label || (variant === "inline" ? f.label || "Not wired yet" : "Designed \u2014 backend not wired yet"),
|
|
@@ -8206,28 +8566,28 @@ function FeatureGate({
|
|
|
8206
8566
|
);
|
|
8207
8567
|
}
|
|
8208
8568
|
function PermissionHint({ perm, children }) {
|
|
8209
|
-
|
|
8210
|
-
if (
|
|
8211
|
-
return /* @__PURE__ */ (0,
|
|
8569
|
+
const { can: can2 } = useSessionContext();
|
|
8570
|
+
if (can2(perm)) return children;
|
|
8571
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
8212
8572
|
"span",
|
|
8213
8573
|
{
|
|
8214
8574
|
title: "Requires " + (Array.isArray(perm) ? perm.join(", ") : perm),
|
|
8215
8575
|
style: { display: "inline-flex", opacity: 0.45, cursor: "not-allowed" },
|
|
8216
8576
|
"aria-disabled": "true",
|
|
8217
|
-
children: /* @__PURE__ */ (0,
|
|
8577
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { style: { pointerEvents: "none" }, children })
|
|
8218
8578
|
}
|
|
8219
8579
|
);
|
|
8220
8580
|
}
|
|
8221
8581
|
|
|
8222
8582
|
// src/components/platform/ModeSwitch.tsx
|
|
8223
|
-
var
|
|
8224
|
-
var
|
|
8583
|
+
var React41 = __toESM(require("react"), 1);
|
|
8584
|
+
var import_jsx_runtime66 = require("react/jsx-runtime");
|
|
8225
8585
|
function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
|
|
8226
8586
|
const mode2 = useRuntimeMode();
|
|
8227
|
-
const [open, setOpen] =
|
|
8587
|
+
const [open, setOpen] = React41.useState(false);
|
|
8228
8588
|
const zIndex = useOverlayLayer(open);
|
|
8229
|
-
const ref =
|
|
8230
|
-
|
|
8589
|
+
const ref = React41.useRef(null);
|
|
8590
|
+
React41.useEffect(() => {
|
|
8231
8591
|
if (!open) return;
|
|
8232
8592
|
const away = (e) => {
|
|
8233
8593
|
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
@@ -8249,8 +8609,8 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
|
|
|
8249
8609
|
RuntimeKit.setMode(next);
|
|
8250
8610
|
setOpen(false);
|
|
8251
8611
|
};
|
|
8252
|
-
return /* @__PURE__ */ (0,
|
|
8253
|
-
/* @__PURE__ */ (0,
|
|
8612
|
+
return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { style: { position: "relative", display: "inline-flex" }, ref, children: [
|
|
8613
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(
|
|
8254
8614
|
"button",
|
|
8255
8615
|
{
|
|
8256
8616
|
type: "button",
|
|
@@ -8260,29 +8620,29 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
|
|
|
8260
8620
|
title: test ? "Test mode \u2014 every response is simulated. Click to inspect requests or return to live mode." : "Live mode \u2014 incomplete features are shown under construction. Click for test mode.",
|
|
8261
8621
|
className: "fd-mode-btn" + (test ? " is-test" : "") + (open ? " is-open" : ""),
|
|
8262
8622
|
children: [
|
|
8263
|
-
/* @__PURE__ */ (0,
|
|
8264
|
-
test && requestCount ? /* @__PURE__ */ (0,
|
|
8623
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph " + (test ? "ph-flask" : "ph-lightning"), style: { fontSize: 17 } }),
|
|
8624
|
+
test && requestCount ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-mono fd-mode-count", children: requestCount > 99 ? "99+" : requestCount }, requestCount) : null
|
|
8265
8625
|
]
|
|
8266
8626
|
}
|
|
8267
8627
|
),
|
|
8268
|
-
open ? /* @__PURE__ */ (0,
|
|
8269
|
-
/* @__PURE__ */ (0,
|
|
8270
|
-
/* @__PURE__ */ (0,
|
|
8271
|
-
/* @__PURE__ */ (0,
|
|
8628
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-view-enter fd-mode-pop", style: { zIndex }, children: [
|
|
8629
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
|
|
8630
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-badge " + (test ? "fd-badge-warning" : "fd-badge-neutral"), children: [
|
|
8631
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph " + (test ? "ph-flask" : "ph-lightning"), "aria-hidden": "true" }),
|
|
8272
8632
|
test ? "Test mode" : "Live mode"
|
|
8273
8633
|
] }),
|
|
8274
|
-
test ? /* @__PURE__ */ (0,
|
|
8634
|
+
test ? /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-body-sm fd-muted fd-mono", children: [
|
|
8275
8635
|
requestCount,
|
|
8276
8636
|
" simulated request",
|
|
8277
8637
|
requestCount === 1 ? "" : "s"
|
|
8278
8638
|
] }) : null,
|
|
8279
|
-
/* @__PURE__ */ (0,
|
|
8280
|
-
test && onClearLog ? /* @__PURE__ */ (0,
|
|
8639
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { style: { flex: 1 } }),
|
|
8640
|
+
test && onClearLog ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("button", { type: "button", className: "fd-soon-link", onClick: onClearLog, children: "Clear" }) : null
|
|
8281
8641
|
] }),
|
|
8282
|
-
/* @__PURE__ */ (0,
|
|
8283
|
-
/* @__PURE__ */ (0,
|
|
8284
|
-
/* @__PURE__ */ (0,
|
|
8285
|
-
/* @__PURE__ */ (0,
|
|
8642
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { style: { display: "block", padding: "12px 14px", borderBottom: "1px solid var(--border)" }, children: [
|
|
8643
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-body-sm fd-secondary", style: { display: "block", textWrap: "pretty" }, children: test ? "Every feature is usable and nothing is obstructed, but no response comes from a real service \u2014 nothing you do here persists. Use this to review and demo the design." : "This is what a user would see today. " + s.incomplete + " of " + s.total + " features depend on backend work that is not wired yet, so they render under construction." }),
|
|
8644
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-row", style: { gap: 8, marginTop: 10, flexWrap: "wrap" }, children: [
|
|
8645
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-body-sm fd-muted fd-mono", children: [
|
|
8286
8646
|
s.endpointsWired,
|
|
8287
8647
|
" endpoint",
|
|
8288
8648
|
s.endpointsWired === 1 ? "" : "s",
|
|
@@ -8292,58 +8652,58 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
|
|
|
8292
8652
|
s.total,
|
|
8293
8653
|
" features complete"
|
|
8294
8654
|
] }),
|
|
8295
|
-
onOpenSpec ? /* @__PURE__ */ (0,
|
|
8296
|
-
/* @__PURE__ */ (0,
|
|
8297
|
-
/* @__PURE__ */ (0,
|
|
8655
|
+
onOpenSpec ? /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(import_jsx_runtime66.Fragment, { children: [
|
|
8656
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { style: { flex: 1 } }),
|
|
8657
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: () => {
|
|
8298
8658
|
setOpen(false);
|
|
8299
8659
|
onOpenSpec();
|
|
8300
8660
|
}, children: [
|
|
8301
8661
|
"API spec ",
|
|
8302
|
-
/* @__PURE__ */ (0,
|
|
8662
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-arrow-right", "aria-hidden": "true" })
|
|
8303
8663
|
] })
|
|
8304
8664
|
] }) : null
|
|
8305
8665
|
] })
|
|
8306
8666
|
] }),
|
|
8307
|
-
/* @__PURE__ */ (0,
|
|
8308
|
-
/* @__PURE__ */ (0,
|
|
8309
|
-
/* @__PURE__ */ (0,
|
|
8310
|
-
/* @__PURE__ */ (0,
|
|
8311
|
-
/* @__PURE__ */ (0,
|
|
8312
|
-
/* @__PURE__ */ (0,
|
|
8667
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-mode-choice", children: [
|
|
8668
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("button", { type: "button", className: "fd-mode-opt" + (!test ? " is-on" : ""), onClick: () => go("live"), children: [
|
|
8669
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-lightning", "aria-hidden": "true" }),
|
|
8670
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-mode-opt-text", children: [
|
|
8671
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-mode-opt-title", children: "Live mode" }),
|
|
8672
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-mode-opt-desc", children: "Incomplete features under construction" })
|
|
8313
8673
|
] }),
|
|
8314
|
-
!test ? /* @__PURE__ */ (0,
|
|
8674
|
+
!test ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-check", "aria-hidden": "true" }) : null
|
|
8315
8675
|
] }),
|
|
8316
|
-
/* @__PURE__ */ (0,
|
|
8317
|
-
/* @__PURE__ */ (0,
|
|
8318
|
-
/* @__PURE__ */ (0,
|
|
8319
|
-
/* @__PURE__ */ (0,
|
|
8320
|
-
/* @__PURE__ */ (0,
|
|
8676
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("button", { type: "button", className: "fd-mode-opt" + (test ? " is-on" : ""), onClick: () => go("test"), children: [
|
|
8677
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-flask", "aria-hidden": "true" }),
|
|
8678
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-mode-opt-text", children: [
|
|
8679
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-mode-opt-title", children: "Test mode" }),
|
|
8680
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-mode-opt-desc", children: "Everything usable, all data simulated" })
|
|
8321
8681
|
] }),
|
|
8322
|
-
test ? /* @__PURE__ */ (0,
|
|
8682
|
+
test ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-check", "aria-hidden": "true" }) : null
|
|
8323
8683
|
] })
|
|
8324
8684
|
] }),
|
|
8325
|
-
test && renderLog ? /* @__PURE__ */ (0,
|
|
8685
|
+
test && renderLog ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { style: { display: "block", maxHeight: 340, overflowY: "auto", borderTop: "1px solid var(--border)" }, children: renderLog() }) : null
|
|
8326
8686
|
] }) }) : null
|
|
8327
8687
|
] });
|
|
8328
8688
|
}
|
|
8329
8689
|
function TestModeBar({ onExit }) {
|
|
8330
8690
|
const mode2 = useRuntimeMode();
|
|
8331
8691
|
if (mode2 !== "test") return null;
|
|
8332
|
-
return /* @__PURE__ */ (0,
|
|
8333
|
-
/* @__PURE__ */ (0,
|
|
8334
|
-
/* @__PURE__ */ (0,
|
|
8692
|
+
return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("div", { className: "fd-testbar", role: "status", children: [
|
|
8693
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("span", { className: "fd-badge fd-badge-warning", children: [
|
|
8694
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-flask", "aria-hidden": "true" }),
|
|
8335
8695
|
"Test mode"
|
|
8336
8696
|
] }),
|
|
8337
|
-
/* @__PURE__ */ (0,
|
|
8338
|
-
/* @__PURE__ */ (0,
|
|
8339
|
-
/* @__PURE__ */ (0,
|
|
8697
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-testbar-detail", children: "Every feature is unlocked and every response is simulated \u2014 nothing here persists." }),
|
|
8698
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("button", { type: "button", className: "fd-soon-link", onClick: () => onExit ? onExit() : RuntimeKit.setMode("live"), children: [
|
|
8699
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)("i", { className: "ph ph-lightning", "aria-hidden": "true" }),
|
|
8340
8700
|
" Back to live mode"
|
|
8341
8701
|
] })
|
|
8342
8702
|
] });
|
|
8343
8703
|
}
|
|
8344
8704
|
|
|
8345
8705
|
// src/components/planner/ChannelMeta.tsx
|
|
8346
|
-
var
|
|
8706
|
+
var import_jsx_runtime67 = require("react/jsx-runtime");
|
|
8347
8707
|
var CHANNEL_WEIGHTS = {
|
|
8348
8708
|
"OOH": 50,
|
|
8349
8709
|
"DOOH": 50,
|
|
@@ -8396,9 +8756,9 @@ function ChannelTag({ channel, size = "md", showLabel = true, dot = false, weigh
|
|
|
8396
8756
|
const m = ChannelMeta(channel);
|
|
8397
8757
|
const wt = channelWeightOf(channel || "");
|
|
8398
8758
|
if (dot) {
|
|
8399
|
-
return /* @__PURE__ */ (0,
|
|
8759
|
+
return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: ["fd-chan-dot", className].filter(Boolean).join(" "), title: m.name, style: { background: m.color }, ...rest });
|
|
8400
8760
|
}
|
|
8401
|
-
return /* @__PURE__ */ (0,
|
|
8761
|
+
return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
|
|
8402
8762
|
"span",
|
|
8403
8763
|
{
|
|
8404
8764
|
className: ["fd-chan", size === "sm" ? "fd-chan-sm" : "", className].filter(Boolean).join(" "),
|
|
@@ -8406,16 +8766,16 @@ function ChannelTag({ channel, size = "md", showLabel = true, dot = false, weigh
|
|
|
8406
8766
|
style: { "--chan": m.color },
|
|
8407
8767
|
...rest,
|
|
8408
8768
|
children: [
|
|
8409
|
-
/* @__PURE__ */ (0,
|
|
8410
|
-
showLabel ? /* @__PURE__ */ (0,
|
|
8411
|
-
weight ? /* @__PURE__ */ (0,
|
|
8769
|
+
/* @__PURE__ */ (0, import_jsx_runtime67.jsx)("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }),
|
|
8770
|
+
showLabel ? /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "fd-chan-label", children: m.label }) : null,
|
|
8771
|
+
weight ? /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "fd-chan-weight", children: wt || 0 }) : null
|
|
8412
8772
|
]
|
|
8413
8773
|
}
|
|
8414
8774
|
);
|
|
8415
8775
|
}
|
|
8416
8776
|
|
|
8417
8777
|
// src/components/planner/SaturationDistribution.tsx
|
|
8418
|
-
var
|
|
8778
|
+
var import_jsx_runtime68 = require("react/jsx-runtime");
|
|
8419
8779
|
var BANDS2 = [
|
|
8420
8780
|
{ key: "weak", label: "Weak", n: 1, range: "< 50" },
|
|
8421
8781
|
{ key: "adequate", label: "Adequate", n: 2, range: "50\u2013100" },
|
|
@@ -8426,24 +8786,24 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
|
|
|
8426
8786
|
const grouped = BANDS2.map((b) => ({ ...b, items: campuses.filter((c) => c.band === b.key) }));
|
|
8427
8787
|
const tallest = Math.max(1, ...grouped.map((g) => g.items.length));
|
|
8428
8788
|
const floor = BANDS2.find((b) => b.key === floorBand) || BANDS2[0];
|
|
8429
|
-
return /* @__PURE__ */ (0,
|
|
8430
|
-
/* @__PURE__ */ (0,
|
|
8789
|
+
return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 16 }, children: [
|
|
8790
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { style: { display: "grid", gridTemplateColumns: "repeat(4,minmax(0,1fr))", gap: 12, alignItems: "end" }, children: grouped.map((g) => {
|
|
8431
8791
|
const mark = "var(--csi-" + g.n + "-mark)";
|
|
8432
8792
|
const active = selectedBand === g.key;
|
|
8433
|
-
return /* @__PURE__ */ (0,
|
|
8793
|
+
return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)(
|
|
8434
8794
|
"button",
|
|
8435
8795
|
{
|
|
8436
8796
|
type: "button",
|
|
8437
8797
|
onClick: onSelectBand ? () => onSelectBand(active ? null : g.key) : void 0,
|
|
8438
8798
|
style: { display: "flex", flexDirection: "column", justifyContent: "flex-end", gap: 10, padding: 12, border: "1px solid " + (active ? "var(--border-strong)" : "var(--border)"), borderRadius: "var(--r-lg)", background: active ? "var(--surface-2)" : "var(--surface)", cursor: onSelectBand ? "pointer" : "default", textAlign: "left", minHeight: 190, minWidth: 0, transition: "background var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)" },
|
|
8439
8799
|
children: [
|
|
8440
|
-
/* @__PURE__ */ (0,
|
|
8800
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { style: { display: "flex", flexDirection: "column-reverse", gap: 4, minHeight: tallest * 24, minWidth: 0 }, children: loading ? Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-skel", style: { height: 16, borderRadius: 4 } }, i)) : g.items.map((c) => (
|
|
8441
8801
|
// `overflow: hidden` + `white-space: nowrap` only clips once the span
|
|
8442
8802
|
// actually HAS a bounded width to clip against (see the grid/flex
|
|
8443
8803
|
// `minWidth: 0` chain above) — `textOverflow: ellipsis` then makes
|
|
8444
8804
|
// that clip readable instead of a hard cut mid-letter; the full name
|
|
8445
8805
|
// is still one hover away via `title`.
|
|
8446
|
-
/* @__PURE__ */ (0,
|
|
8806
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
|
|
8447
8807
|
"span",
|
|
8448
8808
|
{
|
|
8449
8809
|
title: c.name + " \xB7 " + c.crp,
|
|
@@ -8453,13 +8813,13 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
|
|
|
8453
8813
|
c.name
|
|
8454
8814
|
)
|
|
8455
8815
|
)) }),
|
|
8456
|
-
/* @__PURE__ */ (0,
|
|
8457
|
-
/* @__PURE__ */ (0,
|
|
8458
|
-
/* @__PURE__ */ (0,
|
|
8816
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "fd-row", style: { gap: 8, alignItems: "baseline" }, children: [
|
|
8817
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-num-hero", style: { fontSize: 30, color: g.items.length ? "var(--text)" : "var(--text-disabled)" }, children: loading ? "\u2013" : g.items.length }),
|
|
8818
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-meter", style: { gap: 2, color: mark }, "aria-hidden": "true", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-meter-seg" + (i <= g.n ? " is-on" : ""), style: { width: 4, height: 9 } }, i)) })
|
|
8459
8819
|
] }),
|
|
8460
|
-
/* @__PURE__ */ (0,
|
|
8461
|
-
/* @__PURE__ */ (0,
|
|
8462
|
-
/* @__PURE__ */ (0,
|
|
8820
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "fd-stack", style: { gap: 1 }, children: [
|
|
8821
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-label-lg", children: g.label }),
|
|
8822
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "fd-body-sm fd-muted", style: { fontSize: 11.5 }, children: [
|
|
8463
8823
|
"CRP ",
|
|
8464
8824
|
g.range
|
|
8465
8825
|
] })
|
|
@@ -8469,10 +8829,10 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
|
|
|
8469
8829
|
g.key
|
|
8470
8830
|
);
|
|
8471
8831
|
}) }),
|
|
8472
|
-
floorBand ? /* @__PURE__ */ (0,
|
|
8473
|
-
/* @__PURE__ */ (0,
|
|
8474
|
-
/* @__PURE__ */ (0,
|
|
8475
|
-
/* @__PURE__ */ (0,
|
|
8832
|
+
floorBand ? /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "12px 14px", borderRadius: "var(--r-md)", background: "var(--surface-2)", border: "1px solid var(--border)", flexWrap: "wrap" }, children: [
|
|
8833
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-meter", style: { gap: 2, color: "var(--csi-" + floor.n + "-mark)" }, "aria-hidden": "true", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("span", { className: "fd-meter-seg" + (i <= floor.n ? " is-on" : ""), style: { width: 5, height: 12 } }, i)) }),
|
|
8834
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("span", { className: "fd-body-sm fd-secondary", style: { flex: 1, minWidth: 240 }, children: [
|
|
8835
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("strong", { style: { color: "var(--text)" }, children: [
|
|
8476
8836
|
"Plan floor ",
|
|
8477
8837
|
floorScore
|
|
8478
8838
|
] }),
|
|
@@ -8483,8 +8843,8 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
|
|
|
8483
8843
|
}
|
|
8484
8844
|
|
|
8485
8845
|
// src/components/planner/MixGap.tsx
|
|
8486
|
-
var
|
|
8487
|
-
var
|
|
8846
|
+
var React42 = __toESM(require("react"), 1);
|
|
8847
|
+
var import_jsx_runtime69 = require("react/jsx-runtime");
|
|
8488
8848
|
var MIX_GAP_LAYOUT = {
|
|
8489
8849
|
trackHeight: 30,
|
|
8490
8850
|
barHeight: 20,
|
|
@@ -8498,19 +8858,19 @@ function mixGapOffset(value, max) {
|
|
|
8498
8858
|
}
|
|
8499
8859
|
function MixGap({ rows = [], loading = false, className = "" }) {
|
|
8500
8860
|
const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
|
|
8501
|
-
const [hover, setHover] =
|
|
8861
|
+
const [hover, setHover] = React42.useState(null);
|
|
8502
8862
|
const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
|
|
8503
8863
|
const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
|
|
8504
8864
|
const { trackHeight, barHeight, targetWidth, labelGap } = MIX_GAP_LAYOUT;
|
|
8505
8865
|
const barTop = (trackHeight - barHeight) / 2;
|
|
8506
|
-
return /* @__PURE__ */ (0,
|
|
8507
|
-
/* @__PURE__ */ (0,
|
|
8508
|
-
[["On target", TONE.ok], ["Close", TONE.warn], ["Short", TONE.danger]].map(([l, c]) => /* @__PURE__ */ (0,
|
|
8509
|
-
/* @__PURE__ */ (0,
|
|
8866
|
+
return /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 4 }, children: [
|
|
8867
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "fd-row", style: { gap: 16, justifyContent: "flex-end", paddingBottom: 6, flexWrap: "wrap" }, children: [
|
|
8868
|
+
[["On target", TONE.ok], ["Close", TONE.warn], ["Short", TONE.danger]].map(([l, c]) => /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
|
|
8869
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { style: { width: 14, height: 9, borderRadius: 2, background: c } }),
|
|
8510
8870
|
l
|
|
8511
8871
|
] }, l)),
|
|
8512
|
-
/* @__PURE__ */ (0,
|
|
8513
|
-
/* @__PURE__ */ (0,
|
|
8872
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
|
|
8873
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { "data-mixgap-legend-target": "", style: { width: targetWidth, height: 16, borderRadius: 1, background: MIX_GAP_TARGET_STRIPE } }),
|
|
8514
8874
|
"Target"
|
|
8515
8875
|
] })
|
|
8516
8876
|
] }),
|
|
@@ -8519,7 +8879,7 @@ function MixGap({ rows = [], loading = false, className = "" }) {
|
|
|
8519
8879
|
const tone = toneOf(gap);
|
|
8520
8880
|
const tip = "Realized " + r.realized + "% vs " + r.target + "% target" + (gap < -1 ? " \xB7 " + Math.abs(gap).toFixed(0) + " pts short" : gap > 1 ? " \xB7 " + gap.toFixed(0) + " pts over" : " \xB7 on target") + (r.note ? " \u2014 " + r.note : "");
|
|
8521
8881
|
const labelLeft = r.realized > r.target ? `calc(${mixGapOffset(r.realized, max)} + ${labelGap}px)` : `calc(${mixGapOffset(r.target, max)} + ${targetWidth / 2 + labelGap}px)`;
|
|
8522
|
-
return /* @__PURE__ */ (0,
|
|
8882
|
+
return /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)(
|
|
8523
8883
|
"div",
|
|
8524
8884
|
{
|
|
8525
8885
|
className: "fd-row",
|
|
@@ -8527,15 +8887,15 @@ function MixGap({ rows = [], loading = false, className = "" }) {
|
|
|
8527
8887
|
onMouseEnter: () => setHover(r.channel),
|
|
8528
8888
|
onMouseLeave: () => setHover(null),
|
|
8529
8889
|
children: [
|
|
8530
|
-
/* @__PURE__ */ (0,
|
|
8531
|
-
/* @__PURE__ */ (0,
|
|
8532
|
-
/* @__PURE__ */ (0,
|
|
8533
|
-
/* @__PURE__ */ (0,
|
|
8534
|
-
/* @__PURE__ */ (0,
|
|
8890
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { style: { width: 128, flex: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(ChannelTag, { channel: r.channel, size: "sm" }) }),
|
|
8891
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { style: { position: "relative", flex: 1, height: trackHeight, minWidth: 120 }, children: loading ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "fd-skel", style: { position: "absolute", left: 0, right: 0, top: barTop, height: barHeight, borderRadius: 3 } }) : /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)(import_jsx_runtime69.Fragment, { children: [
|
|
8892
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { "data-mixgap-bar": "", style: { position: "absolute", left: 0, top: barTop, height: barHeight, width: mixGapOffset(r.realized, max), background: TONE[tone], borderRadius: "2px 3px 3px 2px", transition: "width var(--dur-slow) var(--ease), background var(--dur-base) var(--ease)" } }),
|
|
8893
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { "data-mixgap-target": "", style: { position: "absolute", left: `calc(${mixGapOffset(r.target, max)} - ${targetWidth / 2}px)`, top: 0, width: targetWidth, height: trackHeight, background: MIX_GAP_TARGET_STRIPE, borderRadius: 1 } }),
|
|
8894
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("span", { "data-mixgap-label": "", className: "fd-num", style: { position: "absolute", left: labelLeft, top: "50%", transform: "translateY(-50%)", fontSize: 12, color: "var(--text-muted)", whiteSpace: "nowrap" }, children: [
|
|
8535
8895
|
r.realized,
|
|
8536
8896
|
"%"
|
|
8537
8897
|
] }),
|
|
8538
|
-
hover === r.channel ? /* @__PURE__ */ (0,
|
|
8898
|
+
hover === r.channel ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "fd-tooltip", role: "tooltip", style: { position: "absolute", left: 0, right: "auto", bottom: trackHeight + 4, maxWidth: "min(340px, 100%)", whiteSpace: "normal", textAlign: "left", width: "max-content" }, children: tip }) : null
|
|
8539
8899
|
] }) })
|
|
8540
8900
|
]
|
|
8541
8901
|
},
|
|
@@ -8546,7 +8906,7 @@ function MixGap({ rows = [], loading = false, className = "" }) {
|
|
|
8546
8906
|
}
|
|
8547
8907
|
|
|
8548
8908
|
// src/components/planner/ChannelContribution.tsx
|
|
8549
|
-
var
|
|
8909
|
+
var import_jsx_runtime70 = require("react/jsx-runtime");
|
|
8550
8910
|
var money = (n) => "$" + Math.round(n).toLocaleString();
|
|
8551
8911
|
function ChannelContribution({
|
|
8552
8912
|
channels = [],
|
|
@@ -8562,9 +8922,9 @@ function ChannelContribution({
|
|
|
8562
8922
|
const grand = total !== void 0 ? total : base + bonus;
|
|
8563
8923
|
const pct = (v) => grand ? v / grand * 100 : 0;
|
|
8564
8924
|
const spendTotal = channels.reduce((s, c) => s + Number(String(c.spend || 0).replace(/[^0-9.]/g, "")), 0);
|
|
8565
|
-
return /* @__PURE__ */ (0,
|
|
8566
|
-
loading ? /* @__PURE__ */ (0,
|
|
8567
|
-
channels.map((c) => /* @__PURE__ */ (0,
|
|
8925
|
+
return /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 16 }, children: [
|
|
8926
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime70.jsx)("span", { className: "fd-skel", style: { height: 34, borderRadius: "var(--r-sm)" } }) : /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("div", { className: "fd-row", style: { height: 34, borderRadius: "var(--r-sm)", overflow: "hidden", gap: 2, background: "var(--surface-3)" }, children: [
|
|
8927
|
+
channels.map((c) => /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(
|
|
8568
8928
|
"span",
|
|
8569
8929
|
{
|
|
8570
8930
|
title: c.name + " \xB7 " + c.crp.toFixed(1) + " CRP",
|
|
@@ -8573,7 +8933,7 @@ function ChannelContribution({
|
|
|
8573
8933
|
},
|
|
8574
8934
|
c.name
|
|
8575
8935
|
)),
|
|
8576
|
-
bonus > 0 ? /* @__PURE__ */ (0,
|
|
8936
|
+
bonus > 0 ? /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(
|
|
8577
8937
|
"span",
|
|
8578
8938
|
{
|
|
8579
8939
|
title: "Surround-sound bonus +" + bonusPct + "%",
|
|
@@ -8582,18 +8942,18 @@ function ChannelContribution({
|
|
|
8582
8942
|
}
|
|
8583
8943
|
) : null
|
|
8584
8944
|
] }),
|
|
8585
|
-
showTable ? /* @__PURE__ */ (0,
|
|
8586
|
-
/* @__PURE__ */ (0,
|
|
8587
|
-
/* @__PURE__ */ (0,
|
|
8588
|
-
/* @__PURE__ */ (0,
|
|
8589
|
-
/* @__PURE__ */ (0,
|
|
8590
|
-
/* @__PURE__ */ (0,
|
|
8591
|
-
/* @__PURE__ */ (0,
|
|
8945
|
+
showTable ? /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("table", { className: "fd-table", style: { fontSize: "var(--body-sm-size)" }, children: [
|
|
8946
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("tr", { children: [
|
|
8947
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("th", { style: { width: "34%" }, children: "Channel" }),
|
|
8948
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("th", { style: { width: "16%" }, children: "Weight" }),
|
|
8949
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("th", { className: "is-num", style: { width: "16%" }, children: "Spend" }),
|
|
8950
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("th", { className: "is-num", style: { width: "17%" }, children: "CRP" }),
|
|
8951
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("th", { className: "is-num", style: { width: "17%" }, children: "Share" })
|
|
8592
8952
|
] }) }),
|
|
8593
|
-
/* @__PURE__ */ (0,
|
|
8594
|
-
channels.map((c) => /* @__PURE__ */ (0,
|
|
8595
|
-
/* @__PURE__ */ (0,
|
|
8596
|
-
/* @__PURE__ */ (0,
|
|
8953
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("tbody", { children: [
|
|
8954
|
+
channels.map((c) => /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("tr", { children: [
|
|
8955
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(ChannelTag, { channel: c.name, size: "sm" }) }),
|
|
8956
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(
|
|
8597
8957
|
"span",
|
|
8598
8958
|
{
|
|
8599
8959
|
className: "fd-badge fd-badge-neutral",
|
|
@@ -8602,38 +8962,38 @@ function ChannelContribution({
|
|
|
8602
8962
|
children: "weight " + (channelWeightOf(c.name) || 0)
|
|
8603
8963
|
}
|
|
8604
8964
|
) }),
|
|
8605
|
-
/* @__PURE__ */ (0,
|
|
8606
|
-
/* @__PURE__ */ (0,
|
|
8607
|
-
/* @__PURE__ */ (0,
|
|
8965
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", children: c.spend }),
|
|
8966
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", children: c.crp.toFixed(1) }),
|
|
8967
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("td", { className: "is-num", children: [
|
|
8608
8968
|
pct(c.crp).toFixed(0),
|
|
8609
8969
|
"%"
|
|
8610
8970
|
] })
|
|
8611
8971
|
] }, c.name)),
|
|
8612
|
-
bonus > 0 ? /* @__PURE__ */ (0,
|
|
8613
|
-
/* @__PURE__ */ (0,
|
|
8614
|
-
/* @__PURE__ */ (0,
|
|
8972
|
+
bonus > 0 ? /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("tr", { children: [
|
|
8973
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { className: "fd-row", style: { gap: 9, fontWeight: 600 }, children: [
|
|
8974
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("span", { style: { width: 9, height: 9, borderRadius: 2, background: "repeating-linear-gradient(135deg,var(--csi-3-mark) 0 3px,var(--csi-2-mark) 3px 6px)", flex: "none" } }),
|
|
8615
8975
|
"Surround-sound bonus"
|
|
8616
8976
|
] }) }),
|
|
8617
|
-
/* @__PURE__ */ (0,
|
|
8977
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("span", { className: "fd-badge fd-badge-success", style: { height: 20, fontSize: 11 }, children: [
|
|
8618
8978
|
"+",
|
|
8619
8979
|
bonusPct,
|
|
8620
8980
|
"% of +",
|
|
8621
8981
|
bonusMax,
|
|
8622
8982
|
"%"
|
|
8623
8983
|
] }) }),
|
|
8624
|
-
/* @__PURE__ */ (0,
|
|
8625
|
-
/* @__PURE__ */ (0,
|
|
8626
|
-
/* @__PURE__ */ (0,
|
|
8984
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num fd-muted", children: "\u2014" }),
|
|
8985
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", children: bonus.toFixed(1) }),
|
|
8986
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("td", { className: "is-num", children: [
|
|
8627
8987
|
pct(bonus).toFixed(0),
|
|
8628
8988
|
"%"
|
|
8629
8989
|
] })
|
|
8630
8990
|
] }) : null,
|
|
8631
|
-
/* @__PURE__ */ (0,
|
|
8632
|
-
/* @__PURE__ */ (0,
|
|
8633
|
-
/* @__PURE__ */ (0,
|
|
8634
|
-
/* @__PURE__ */ (0,
|
|
8635
|
-
/* @__PURE__ */ (0,
|
|
8636
|
-
/* @__PURE__ */ (0,
|
|
8991
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)("tr", { style: { background: "var(--surface-2)" }, children: [
|
|
8992
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { style: { fontWeight: 700 }, children: "Campus total" }),
|
|
8993
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", {}),
|
|
8994
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", style: { fontWeight: 700 }, children: money(spendTotal) }),
|
|
8995
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", style: { fontWeight: 700 }, children: grand.toFixed(1) }),
|
|
8996
|
+
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)("td", { className: "is-num", style: { fontWeight: 700 }, children: "100%" })
|
|
8637
8997
|
] })
|
|
8638
8998
|
] })
|
|
8639
8999
|
] }) : null
|
|
@@ -8641,8 +9001,8 @@ function ChannelContribution({
|
|
|
8641
9001
|
}
|
|
8642
9002
|
|
|
8643
9003
|
// src/components/planner/BudgetReallocator.tsx
|
|
8644
|
-
var
|
|
8645
|
-
var
|
|
9004
|
+
var React43 = __toESM(require("react"), 1);
|
|
9005
|
+
var import_jsx_runtime71 = require("react/jsx-runtime");
|
|
8646
9006
|
var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
|
|
8647
9007
|
function BudgetReallocator({
|
|
8648
9008
|
campus,
|
|
@@ -8657,8 +9017,8 @@ function BudgetReallocator({
|
|
|
8657
9017
|
onCancel,
|
|
8658
9018
|
className = ""
|
|
8659
9019
|
}) {
|
|
8660
|
-
const [draft, setDraft] =
|
|
8661
|
-
|
|
9020
|
+
const [draft, setDraft] = React43.useState(spend);
|
|
9021
|
+
React43.useEffect(() => setDraft(spend), [spend]);
|
|
8662
9022
|
const dirty = draft !== spend;
|
|
8663
9023
|
const nextCrp = scoreFor ? scoreFor(draft) : crp;
|
|
8664
9024
|
const nextBand = bandFor(nextCrp);
|
|
@@ -8673,10 +9033,10 @@ function BudgetReallocator({
|
|
|
8673
9033
|
setDraft(spend);
|
|
8674
9034
|
if (onCancel) onCancel();
|
|
8675
9035
|
};
|
|
8676
|
-
const trackRef =
|
|
8677
|
-
const chipRef =
|
|
8678
|
-
const [chipLeft, setChipLeft] =
|
|
8679
|
-
|
|
9036
|
+
const trackRef = React43.useRef(null);
|
|
9037
|
+
const chipRef = React43.useRef(null);
|
|
9038
|
+
const [chipLeft, setChipLeft] = React43.useState(null);
|
|
9039
|
+
React43.useLayoutEffect(() => {
|
|
8680
9040
|
const track = trackRef.current;
|
|
8681
9041
|
if (!dirty || !track) {
|
|
8682
9042
|
setChipLeft(null);
|
|
@@ -8697,23 +9057,23 @@ function BudgetReallocator({
|
|
|
8697
9057
|
ro.observe(track);
|
|
8698
9058
|
return () => ro.disconnect();
|
|
8699
9059
|
}, [dirty, pct]);
|
|
8700
|
-
return /* @__PURE__ */ (0,
|
|
9060
|
+
return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(
|
|
8701
9061
|
"div",
|
|
8702
9062
|
{
|
|
8703
9063
|
className: ["fd-stack", className].filter(Boolean).join(" "),
|
|
8704
9064
|
style: { gap: 14, padding: 18, border: "1px solid " + (dirty ? "var(--border-brand)" : "var(--border)"), borderRadius: "var(--r-lg)", background: dirty ? "var(--surface-brand)" : "var(--surface)", color: "var(--text)", transition: "background var(--dur-base) var(--ease),border-color var(--dur-base) var(--ease)" },
|
|
8705
9065
|
children: [
|
|
8706
|
-
/* @__PURE__ */ (0,
|
|
8707
|
-
/* @__PURE__ */ (0,
|
|
8708
|
-
/* @__PURE__ */ (0,
|
|
8709
|
-
dirty ? /* @__PURE__ */ (0,
|
|
8710
|
-
/* @__PURE__ */ (0,
|
|
8711
|
-
/* @__PURE__ */ (0,
|
|
9066
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
|
|
9067
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "fd-h4", style: { flex: 1, minWidth: 140 }, children: campus }),
|
|
9068
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(CsiBadge, { band: nowBand, crp, size: "medium" }),
|
|
9069
|
+
dirty ? /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(import_jsx_runtime71.Fragment, { children: [
|
|
9070
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)("i", { className: "ph ph-arrow-right fd-muted", "aria-hidden": "true" }),
|
|
9071
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(CsiBadge, { band: nextBand, crp: nextCrp, size: "medium", preview: true })
|
|
8712
9072
|
] }) : null
|
|
8713
9073
|
] }),
|
|
8714
|
-
/* @__PURE__ */ (0,
|
|
8715
|
-
/* @__PURE__ */ (0,
|
|
8716
|
-
dirty ? /* @__PURE__ */ (0,
|
|
9074
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "fd-slider", ref: trackRef, children: [
|
|
9075
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "fd-slider-fill", style: { width: pct + "%" } }),
|
|
9076
|
+
dirty ? /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
|
|
8717
9077
|
"span",
|
|
8718
9078
|
{
|
|
8719
9079
|
ref: chipRef,
|
|
@@ -8725,7 +9085,7 @@ function BudgetReallocator({
|
|
|
8725
9085
|
children: (delta < 0 ? "\u2212$" : "+$") + Math.abs(delta).toLocaleString()
|
|
8726
9086
|
}
|
|
8727
9087
|
) : null,
|
|
8728
|
-
/* @__PURE__ */ (0,
|
|
9088
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
|
|
8729
9089
|
"input",
|
|
8730
9090
|
{
|
|
8731
9091
|
type: "range",
|
|
@@ -8741,13 +9101,13 @@ function BudgetReallocator({
|
|
|
8741
9101
|
}
|
|
8742
9102
|
)
|
|
8743
9103
|
] }),
|
|
8744
|
-
/* @__PURE__ */ (0,
|
|
8745
|
-
/* @__PURE__ */ (0,
|
|
8746
|
-
/* @__PURE__ */ (0,
|
|
8747
|
-
/* @__PURE__ */ (0,
|
|
9104
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
|
|
9105
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)("span", { className: "fd-stack", style: { gap: 2, flex: 1, minWidth: 150 }, children: [
|
|
9106
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "fd-num", style: { fontSize: 19, fontWeight: 700 }, children: "$" + draft.toLocaleString() }),
|
|
9107
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { className: "fd-body-sm fd-muted", children: "Arrow keys step $100, shift+arrow $1,000. Escape reverts." })
|
|
8748
9108
|
] }),
|
|
8749
|
-
/* @__PURE__ */ (0,
|
|
8750
|
-
/* @__PURE__ */ (0,
|
|
9109
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)("button", { type: "button", className: "fd-btn fd-btn-ghost", disabled: !dirty, onClick: revert, children: "Cancel" }),
|
|
9110
|
+
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
|
|
8751
9111
|
"button",
|
|
8752
9112
|
{
|
|
8753
9113
|
type: "button",
|
|
@@ -8766,7 +9126,7 @@ function BudgetReallocator({
|
|
|
8766
9126
|
}
|
|
8767
9127
|
|
|
8768
9128
|
// src/components/planner/SurroundSound.tsx
|
|
8769
|
-
var
|
|
9129
|
+
var import_jsx_runtime72 = require("react/jsx-runtime");
|
|
8770
9130
|
var CATEGORIES = [
|
|
8771
9131
|
{ key: "ooh", label: "OOH", icon: "flag-banner", color: "var(--ch-ooh)" },
|
|
8772
9132
|
{ key: "transit", label: "Transit", icon: "bus", color: "var(--ch-transit)" },
|
|
@@ -8778,26 +9138,26 @@ var CATEGORIES = [
|
|
|
8778
9138
|
function SurroundSound({ present = [], bonusPct = 0, bonusMax = 40, missedCost, className = "" }) {
|
|
8779
9139
|
const earned = Math.max(0, Math.min(1, bonusPct / bonusMax));
|
|
8780
9140
|
const single = present.length <= 2;
|
|
8781
|
-
return /* @__PURE__ */ (0,
|
|
8782
|
-
/* @__PURE__ */ (0,
|
|
9141
|
+
return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 14 }, children: [
|
|
9142
|
+
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)("div", { className: "fd-row", style: { gap: 8, flexWrap: "wrap" }, children: CATEGORIES.map((c) => {
|
|
8783
9143
|
const on = present.includes(c.key);
|
|
8784
|
-
return /* @__PURE__ */ (0,
|
|
9144
|
+
return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(
|
|
8785
9145
|
"span",
|
|
8786
9146
|
{
|
|
8787
9147
|
title: c.label + (on ? " \u2014 present" : " \u2014 not bought"),
|
|
8788
9148
|
style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 5, width: 62, padding: "10px 0", borderRadius: "var(--r-md)", border: "1px solid " + (on ? "var(--csi-2-edge)" : "var(--border)"), background: on ? "var(--csi-2-fill)" : "var(--surface-2)", color: on ? "var(--csi-2-text)" : "var(--text-disabled)" },
|
|
8789
9149
|
children: [
|
|
8790
|
-
/* @__PURE__ */ (0,
|
|
8791
|
-
/* @__PURE__ */ (0,
|
|
9150
|
+
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)("i", { className: "ph ph-" + c.icon, style: { fontSize: 19, color: on ? c.color : "var(--text-muted)" }, "aria-hidden": "true" }),
|
|
9151
|
+
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { style: { fontSize: 10.5, fontWeight: 700 }, children: c.label })
|
|
8792
9152
|
]
|
|
8793
9153
|
},
|
|
8794
9154
|
c.key
|
|
8795
9155
|
);
|
|
8796
9156
|
}) }),
|
|
8797
|
-
/* @__PURE__ */ (0,
|
|
8798
|
-
/* @__PURE__ */ (0,
|
|
8799
|
-
/* @__PURE__ */ (0,
|
|
8800
|
-
/* @__PURE__ */ (0,
|
|
9157
|
+
/* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: "fd-stack", style: { gap: 8 }, children: [
|
|
9158
|
+
/* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("div", { className: "fd-row", style: { justifyContent: "space-between", gap: 12 }, children: [
|
|
9159
|
+
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { className: "fd-label-lg", children: "Surround-sound bonus earned" }),
|
|
9160
|
+
/* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("span", { className: "fd-num", style: { color: single ? "var(--warn-text)" : "var(--csi-3-mark)", fontWeight: 700 }, children: [
|
|
8801
9161
|
"+",
|
|
8802
9162
|
bonusPct,
|
|
8803
9163
|
"% of +",
|
|
@@ -8805,8 +9165,8 @@ function SurroundSound({ present = [], bonusPct = 0, bonusMax = 40, missedCost,
|
|
|
8805
9165
|
"%"
|
|
8806
9166
|
] })
|
|
8807
9167
|
] }),
|
|
8808
|
-
/* @__PURE__ */ (0,
|
|
8809
|
-
/* @__PURE__ */ (0,
|
|
9168
|
+
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)("div", { style: { height: 8, borderRadius: "var(--r-xs)", background: "var(--surface-3)", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("div", { style: { height: "100%", width: earned * 100 + "%", background: "var(--csi-3-mark)", borderRadius: "inherit", transition: "width var(--dur-slow) var(--ease)" } }) }),
|
|
9169
|
+
/* @__PURE__ */ (0, import_jsx_runtime72.jsxs)("span", { className: "fd-body-sm fd-secondary", style: { textWrap: "pretty" }, children: [
|
|
8810
9170
|
present.length,
|
|
8811
9171
|
" of 6 channel categories present.",
|
|
8812
9172
|
" ",
|
|
@@ -8818,7 +9178,7 @@ function SurroundSound({ present = [], bonusPct = 0, bonusMax = 40, missedCost,
|
|
|
8818
9178
|
}
|
|
8819
9179
|
|
|
8820
9180
|
// src/components/planner/Importance.tsx
|
|
8821
|
-
var
|
|
9181
|
+
var import_jsx_runtime73 = require("react/jsx-runtime");
|
|
8822
9182
|
var IMPORTANCE_LEVELS = [
|
|
8823
9183
|
{ id: "nudge", rank: 1, label: "Nudge", icon: "feather", description: "Breaks a tie, nothing more." },
|
|
8824
9184
|
{ id: "lean", rank: 2, label: "Lean", icon: "arrow-bend-up-right", description: "A mild preference between otherwise equal options." },
|
|
@@ -8848,23 +9208,23 @@ function importanceTokens(id) {
|
|
|
8848
9208
|
function ImportanceTag({ level, showLabel = true, size = "sm", className = "" }) {
|
|
8849
9209
|
const lvl = importanceLevel(level);
|
|
8850
9210
|
const t = importanceTokens(level);
|
|
8851
|
-
return /* @__PURE__ */ (0,
|
|
9211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(
|
|
8852
9212
|
"span",
|
|
8853
9213
|
{
|
|
8854
9214
|
className: ["fd-imp-tag", size === "md" ? "is-md" : "", className].filter(Boolean).join(" "),
|
|
8855
9215
|
style: { background: t.fill, borderColor: t.edge, color: t.text },
|
|
8856
9216
|
title: lvl.label + " \u2014 " + lvl.description,
|
|
8857
9217
|
children: [
|
|
8858
|
-
/* @__PURE__ */ (0,
|
|
8859
|
-
showLabel ? lvl.label : /* @__PURE__ */ (0,
|
|
9218
|
+
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-" + lvl.icon, "aria-hidden": "true" }),
|
|
9219
|
+
showLabel ? lvl.label : /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("span", { className: "fd-sr", children: lvl.label })
|
|
8860
9220
|
]
|
|
8861
9221
|
}
|
|
8862
9222
|
);
|
|
8863
9223
|
}
|
|
8864
9224
|
|
|
8865
9225
|
// src/components/planner/PreferenceMeter.tsx
|
|
8866
|
-
var
|
|
8867
|
-
var
|
|
9226
|
+
var React44 = __toESM(require("react"), 1);
|
|
9227
|
+
var import_jsx_runtime74 = require("react/jsx-runtime");
|
|
8868
9228
|
function preferenceScore(criteria) {
|
|
8869
9229
|
const earned = criteria.reduce((n, c) => n + (Number(c.earned) || 0), 0);
|
|
8870
9230
|
const weight = criteria.reduce((n, c) => n + (Number(c.weight) || 0), 0);
|
|
@@ -8893,8 +9253,8 @@ function PreferenceMeter({
|
|
|
8893
9253
|
className = ""
|
|
8894
9254
|
}) {
|
|
8895
9255
|
const value = score == null ? preferenceScore(criteria) : score;
|
|
8896
|
-
const segments =
|
|
8897
|
-
return /* @__PURE__ */ (0,
|
|
9256
|
+
const segments = React44.useMemo(() => preferenceSegments(criteria), [criteria]);
|
|
9257
|
+
return /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
|
|
8898
9258
|
ScoreMeter,
|
|
8899
9259
|
{
|
|
8900
9260
|
score: value,
|
|
@@ -8906,22 +9266,22 @@ function PreferenceMeter({
|
|
|
8906
9266
|
placement,
|
|
8907
9267
|
className,
|
|
8908
9268
|
breakdownLabel: breakdownTitle,
|
|
8909
|
-
breakdown: /* @__PURE__ */ (0,
|
|
8910
|
-
/* @__PURE__ */ (0,
|
|
8911
|
-
/* @__PURE__ */ (0,
|
|
8912
|
-
/* @__PURE__ */ (0,
|
|
9269
|
+
breakdown: /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fd-prefbreak", children: [
|
|
9270
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fd-prefbreak-head", children: [
|
|
9271
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fd-label-lg", children: breakdownTitle }),
|
|
9272
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fd-prefbreak-score fd-tabular", children: Math.round(value) })
|
|
8913
9273
|
] }),
|
|
8914
|
-
/* @__PURE__ */ (0,
|
|
9274
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)("ul", { className: "fd-prefbreak-list", children: criteria.map((c) => {
|
|
8915
9275
|
const pct = c.weight > 0 ? Math.round((Number(c.earned) || 0) / c.weight * 100) : 0;
|
|
8916
|
-
return /* @__PURE__ */ (0,
|
|
8917
|
-
/* @__PURE__ */ (0,
|
|
8918
|
-
/* @__PURE__ */ (0,
|
|
8919
|
-
/* @__PURE__ */ (0,
|
|
8920
|
-
c.note ? /* @__PURE__ */ (0,
|
|
9276
|
+
return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("li", { className: pct === 0 ? "fd-prefbreak-item is-unmet" : "fd-prefbreak-item", children: [
|
|
9277
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)(ImportanceTag, { level: c.importance }),
|
|
9278
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("span", { className: "fd-prefbreak-text", children: [
|
|
9279
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fd-prefbreak-label", children: c.label }),
|
|
9280
|
+
c.note ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fd-prefbreak-note", children: c.note }) : null
|
|
8921
9281
|
] }),
|
|
8922
|
-
/* @__PURE__ */ (0,
|
|
9282
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("span", { className: "fd-prefbreak-val fd-tabular", children: [
|
|
8923
9283
|
Math.round(Number(c.earned) || 0),
|
|
8924
|
-
/* @__PURE__ */ (0,
|
|
9284
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "fd-prefbreak-sep", children: "/" }),
|
|
8925
9285
|
Math.round(c.weight)
|
|
8926
9286
|
] })
|
|
8927
9287
|
] }, c.id);
|
|
@@ -8932,10 +9292,10 @@ function PreferenceMeter({
|
|
|
8932
9292
|
}
|
|
8933
9293
|
|
|
8934
9294
|
// src/components/chat/AgentChatPanel.tsx
|
|
8935
|
-
var
|
|
9295
|
+
var React50 = __toESM(require("react"), 1);
|
|
8936
9296
|
|
|
8937
9297
|
// src/components/chat/chatEngine.ts
|
|
8938
|
-
var
|
|
9298
|
+
var React45 = __toESM(require("react"), 1);
|
|
8939
9299
|
var CHAT_UNAVAILABLE = "chat_unavailable";
|
|
8940
9300
|
var JOB_PENDING = ["queued", "running"];
|
|
8941
9301
|
var JOB_SUCCESS = ["completed", "recovered"];
|
|
@@ -8989,22 +9349,22 @@ function useChatEngine(opts) {
|
|
|
8989
9349
|
onClear,
|
|
8990
9350
|
onFeedback
|
|
8991
9351
|
} = opts || {};
|
|
8992
|
-
const [status, setStatus] =
|
|
8993
|
-
const [threadId, setThreadId] =
|
|
8994
|
-
const [messages, setMessages] =
|
|
8995
|
-
const [queue, setQueue] =
|
|
8996
|
-
const [fatal, setFatal] =
|
|
8997
|
-
const [busy, setBusy] =
|
|
8998
|
-
const [turnStartedAt, setTurnStartedAt] =
|
|
8999
|
-
const listRef =
|
|
9000
|
-
const queueRef =
|
|
9001
|
-
const busyRef =
|
|
9002
|
-
const stoppedRef =
|
|
9003
|
-
const abortRef =
|
|
9004
|
-
const serverCount =
|
|
9005
|
-
const threadRef =
|
|
9006
|
-
const mounted =
|
|
9007
|
-
|
|
9352
|
+
const [status, setStatus] = React45.useState("idle");
|
|
9353
|
+
const [threadId, setThreadId] = React45.useState(null);
|
|
9354
|
+
const [messages, setMessages] = React45.useState([]);
|
|
9355
|
+
const [queue, setQueue] = React45.useState([]);
|
|
9356
|
+
const [fatal, setFatal] = React45.useState(null);
|
|
9357
|
+
const [busy, setBusy] = React45.useState(false);
|
|
9358
|
+
const [turnStartedAt, setTurnStartedAt] = React45.useState(null);
|
|
9359
|
+
const listRef = React45.useRef([]);
|
|
9360
|
+
const queueRef = React45.useRef([]);
|
|
9361
|
+
const busyRef = React45.useRef(false);
|
|
9362
|
+
const stoppedRef = React45.useRef(false);
|
|
9363
|
+
const abortRef = React45.useRef(null);
|
|
9364
|
+
const serverCount = React45.useRef(0);
|
|
9365
|
+
const threadRef = React45.useRef(null);
|
|
9366
|
+
const mounted = React45.useRef(true);
|
|
9367
|
+
React45.useEffect(() => {
|
|
9008
9368
|
mounted.current = true;
|
|
9009
9369
|
return () => {
|
|
9010
9370
|
mounted.current = false;
|
|
@@ -9026,14 +9386,14 @@ function useChatEngine(opts) {
|
|
|
9026
9386
|
}
|
|
9027
9387
|
return false;
|
|
9028
9388
|
};
|
|
9029
|
-
const loadThread =
|
|
9389
|
+
const loadThread = React45.useCallback(async (id) => {
|
|
9030
9390
|
const data = await apiAdapter.getThread(id);
|
|
9031
9391
|
const list = [...data && data.messages || []];
|
|
9032
9392
|
serverCount.current = list.length;
|
|
9033
9393
|
commit(list);
|
|
9034
9394
|
return list;
|
|
9035
9395
|
}, [apiAdapter]);
|
|
9036
|
-
|
|
9396
|
+
React45.useEffect(() => {
|
|
9037
9397
|
if (!apiAdapter) {
|
|
9038
9398
|
setStatus("idle");
|
|
9039
9399
|
setFatal(null);
|
|
@@ -9246,7 +9606,7 @@ function useChatEngine(opts) {
|
|
|
9246
9606
|
await dispatchTurn(turn);
|
|
9247
9607
|
}
|
|
9248
9608
|
}
|
|
9249
|
-
const send =
|
|
9609
|
+
const send = React45.useCallback((text, attachments) => {
|
|
9250
9610
|
const body = (text || "").trim();
|
|
9251
9611
|
if (!body && !(attachments && attachments.length)) return;
|
|
9252
9612
|
if (status === "disconnected") return;
|
|
@@ -9256,7 +9616,7 @@ function useChatEngine(opts) {
|
|
|
9256
9616
|
stoppedRef.current = false;
|
|
9257
9617
|
drain();
|
|
9258
9618
|
}, [status]);
|
|
9259
|
-
const stop =
|
|
9619
|
+
const stop = React45.useCallback(() => {
|
|
9260
9620
|
stoppedRef.current = true;
|
|
9261
9621
|
const ac = abortRef.current;
|
|
9262
9622
|
if (ac) {
|
|
@@ -9275,11 +9635,11 @@ function useChatEngine(opts) {
|
|
|
9275
9635
|
store.del(STORAGE_PREFIX + threadRef.current);
|
|
9276
9636
|
}
|
|
9277
9637
|
}, [apiAdapter]);
|
|
9278
|
-
const removeQueued =
|
|
9638
|
+
const removeQueued = React45.useCallback((id) => {
|
|
9279
9639
|
queueRef.current = queueRef.current.filter((t) => t.id !== id);
|
|
9280
9640
|
setQueue(queueRef.current.slice());
|
|
9281
9641
|
}, []);
|
|
9282
|
-
const retry =
|
|
9642
|
+
const retry = React45.useCallback(() => {
|
|
9283
9643
|
const list = listRef.current;
|
|
9284
9644
|
let at = -1;
|
|
9285
9645
|
for (let i = list.length - 1; i >= 0; i--) if (list[i].role === "user") {
|
|
@@ -9295,19 +9655,19 @@ function useChatEngine(opts) {
|
|
|
9295
9655
|
setQueue(queueRef.current.slice());
|
|
9296
9656
|
drain();
|
|
9297
9657
|
}, []);
|
|
9298
|
-
const clear =
|
|
9658
|
+
const clear = React45.useCallback(() => {
|
|
9299
9659
|
commit([]);
|
|
9300
9660
|
serverCount.current = 0;
|
|
9301
9661
|
queueRef.current = [];
|
|
9302
9662
|
setQueue([]);
|
|
9303
9663
|
onClear && onClear();
|
|
9304
9664
|
}, [onClear]);
|
|
9305
|
-
const setFeedback =
|
|
9665
|
+
const setFeedback = React45.useCallback((id, value) => {
|
|
9306
9666
|
patch(id, (m) => ({ feedback: m.feedback === value ? null : value }));
|
|
9307
9667
|
const msg = listRef.current.find((m) => m.id === id);
|
|
9308
9668
|
onFeedback && onFeedback({ message: msg, feedback: msg ? msg.feedback : value });
|
|
9309
9669
|
}, [onFeedback]);
|
|
9310
|
-
const reload =
|
|
9670
|
+
const reload = React45.useCallback(async () => {
|
|
9311
9671
|
if (!threadRef.current) return;
|
|
9312
9672
|
setStatus("loading");
|
|
9313
9673
|
try {
|
|
@@ -9345,11 +9705,11 @@ var ChatKit = {
|
|
|
9345
9705
|
};
|
|
9346
9706
|
|
|
9347
9707
|
// src/components/chat/ChatTranscript.tsx
|
|
9348
|
-
var
|
|
9708
|
+
var React47 = __toESM(require("react"), 1);
|
|
9349
9709
|
|
|
9350
9710
|
// src/components/chat/ChatTurn.tsx
|
|
9351
|
-
var
|
|
9352
|
-
var
|
|
9711
|
+
var React46 = __toESM(require("react"), 1);
|
|
9712
|
+
var import_jsx_runtime75 = require("react/jsx-runtime");
|
|
9353
9713
|
function JsonView({ value }) {
|
|
9354
9714
|
let text;
|
|
9355
9715
|
try {
|
|
@@ -9357,67 +9717,67 @@ function JsonView({ value }) {
|
|
|
9357
9717
|
} catch (e) {
|
|
9358
9718
|
text = String(value);
|
|
9359
9719
|
}
|
|
9360
|
-
return /* @__PURE__ */ (0,
|
|
9720
|
+
return /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(CodeBlock, { code: text, language: "json", collapseAfter: 18 });
|
|
9361
9721
|
}
|
|
9362
9722
|
function PacketCard({ packet, schema, render, onApply, applied }) {
|
|
9363
9723
|
if (!packet) return null;
|
|
9364
9724
|
const s = schema || {};
|
|
9365
9725
|
const invalid = packet.valid === false;
|
|
9366
9726
|
const title = s.heading || packet.type;
|
|
9367
|
-
return /* @__PURE__ */ (0,
|
|
9368
|
-
/* @__PURE__ */ (0,
|
|
9369
|
-
/* @__PURE__ */ (0,
|
|
9370
|
-
/* @__PURE__ */ (0,
|
|
9371
|
-
packet.repaired ? /* @__PURE__ */ (0,
|
|
9372
|
-
invalid ? /* @__PURE__ */ (0,
|
|
9727
|
+
return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("section", { className: "fdc-packet" + (invalid ? " is-invalid" : ""), "aria-label": title, children: [
|
|
9728
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("header", { className: "fdc-packet-head", children: [
|
|
9729
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-" + (s.icon || "package"), "aria-hidden": "true" }),
|
|
9730
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-packet-title", title: truncationTitle(title), children: title }),
|
|
9731
|
+
packet.repaired ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-packet-badge", children: "Repaired" }) : null,
|
|
9732
|
+
invalid ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-packet-badge is-danger", children: "Invalid" }) : null
|
|
9373
9733
|
] }),
|
|
9374
|
-
invalid ? /* @__PURE__ */ (0,
|
|
9375
|
-
/* @__PURE__ */ (0,
|
|
9376
|
-
/* @__PURE__ */ (0,
|
|
9377
|
-
] }) : /* @__PURE__ */ (0,
|
|
9378
|
-
!invalid && onApply ? /* @__PURE__ */ (0,
|
|
9379
|
-
packet.repaired ? /* @__PURE__ */ (0,
|
|
9380
|
-
/* @__PURE__ */ (0,
|
|
9381
|
-
/* @__PURE__ */ (0,
|
|
9734
|
+
invalid ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-packet-alert", role: "alert", children: [
|
|
9735
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
|
|
9736
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { children: packet.error || "This result failed validation and can\u2019t be applied." })
|
|
9737
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-packet-body", children: render ? render(packet) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(JsonView, { value: packet.payload }) }),
|
|
9738
|
+
!invalid && onApply ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("footer", { className: "fdc-packet-foot", children: [
|
|
9739
|
+
packet.repaired ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-packet-note", children: "Corrected by the backend after a first attempt." }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {}),
|
|
9740
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fd-btn fd-btn-primary fd-btn-sm", disabled: applied, onClick: () => onApply(packet), children: [
|
|
9741
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-" + (applied ? "check" : "arrow-square-in"), "aria-hidden": "true" }),
|
|
9382
9742
|
applied ? "Applied" : s.applyLabel || "Apply"
|
|
9383
9743
|
] })
|
|
9384
9744
|
] }) : null
|
|
9385
9745
|
] });
|
|
9386
9746
|
}
|
|
9387
9747
|
function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
|
|
9388
|
-
const [open, setOpen] =
|
|
9748
|
+
const [open, setOpen] = React46.useState(defaultOpen);
|
|
9389
9749
|
if (!text) return null;
|
|
9390
|
-
return /* @__PURE__ */ (0,
|
|
9391
|
-
/* @__PURE__ */ (0,
|
|
9392
|
-
/* @__PURE__ */ (0,
|
|
9393
|
-
/* @__PURE__ */ (0,
|
|
9394
|
-
streaming ? /* @__PURE__ */ (0,
|
|
9395
|
-
/* @__PURE__ */ (0,
|
|
9396
|
-
/* @__PURE__ */ (0,
|
|
9397
|
-
/* @__PURE__ */ (0,
|
|
9750
|
+
return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
|
|
9751
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
|
|
9752
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-brain", "aria-hidden": "true" }),
|
|
9753
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { children: streaming ? "Thinking" : durationMs ? "Thought for " + formatDuration(durationMs) : "Thought process" }),
|
|
9754
|
+
streaming ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-dots", "aria-hidden": "true", children: [
|
|
9755
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {}),
|
|
9756
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {}),
|
|
9757
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {})
|
|
9398
9758
|
] }) : null,
|
|
9399
|
-
/* @__PURE__ */ (0,
|
|
9759
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-caret-" + (open ? "up" : "down"), "aria-hidden": "true" })
|
|
9400
9760
|
] }),
|
|
9401
|
-
open ? /* @__PURE__ */ (0,
|
|
9761
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-think-body", children: text }) : null
|
|
9402
9762
|
] });
|
|
9403
9763
|
}
|
|
9404
9764
|
function Citations({ items = [], onOpen }) {
|
|
9405
|
-
const [open, setOpen] =
|
|
9765
|
+
const [open, setOpen] = React46.useState(false);
|
|
9406
9766
|
if (!items.length) return null;
|
|
9407
|
-
return /* @__PURE__ */ (0,
|
|
9408
|
-
/* @__PURE__ */ (0,
|
|
9409
|
-
/* @__PURE__ */ (0,
|
|
9410
|
-
/* @__PURE__ */ (0,
|
|
9767
|
+
return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-cites", children: [
|
|
9768
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
|
|
9769
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-quotes", "aria-hidden": "true" }),
|
|
9770
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { children: [
|
|
9411
9771
|
items.length,
|
|
9412
9772
|
" source",
|
|
9413
9773
|
items.length === 1 ? "" : "s"
|
|
9414
9774
|
] }),
|
|
9415
|
-
/* @__PURE__ */ (0,
|
|
9775
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-caret-" + (open ? "up" : "down"), "aria-hidden": "true" })
|
|
9416
9776
|
] }),
|
|
9417
|
-
open ? /* @__PURE__ */ (0,
|
|
9418
|
-
/* @__PURE__ */ (0,
|
|
9419
|
-
c.url ? /* @__PURE__ */ (0,
|
|
9420
|
-
c.detail ? /* @__PURE__ */ (0,
|
|
9777
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("ol", { className: "fdc-cites-list", children: items.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("li", { children: [
|
|
9778
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-cite-n fd-tabular", children: c.marker || i + 1 }),
|
|
9779
|
+
c.url ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("a", { href: c.url, target: "_blank", rel: "noopener noreferrer", onClick: onOpen ? (e) => onOpen(c, e) : void 0, children: c.title || c.url }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { children: c.title || "Source" }),
|
|
9780
|
+
c.detail ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-cite-detail", children: c.detail }) : null
|
|
9421
9781
|
] }, c.id || i)) }) : null
|
|
9422
9782
|
] });
|
|
9423
9783
|
}
|
|
@@ -9428,29 +9788,29 @@ function clampText(text, max) {
|
|
|
9428
9788
|
return (at > max * 0.6 ? cut.slice(0, at) : cut).trimEnd() + "\u2026";
|
|
9429
9789
|
}
|
|
9430
9790
|
function MessageBody({ message: m, ctx }) {
|
|
9431
|
-
const [expanded, setExpanded] =
|
|
9791
|
+
const [expanded, setExpanded] = React46.useState(false);
|
|
9432
9792
|
const isUser = m.role === "user";
|
|
9433
9793
|
const raw = m.text || "";
|
|
9434
9794
|
const clamped = !expanded && !m.streaming ? clampText(raw, ctx.maxVisibleChars) : null;
|
|
9435
9795
|
const body = clamped != null ? clamped : raw;
|
|
9436
9796
|
const showMd = ctx.markdown && !isUser;
|
|
9437
|
-
return /* @__PURE__ */ (0,
|
|
9438
|
-
m.thinking && ctx.showThinking ? /* @__PURE__ */ (0,
|
|
9439
|
-
m.steps && m.steps.length && ctx.showSteps ? /* @__PURE__ */ (0,
|
|
9440
|
-
body ? /* @__PURE__ */ (0,
|
|
9441
|
-
showMd ? /* @__PURE__ */ (0,
|
|
9442
|
-
m.streaming ? /* @__PURE__ */ (0,
|
|
9797
|
+
return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-body", children: [
|
|
9798
|
+
m.thinking && ctx.showThinking ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(ThinkingBlock, { text: m.thinking, durationMs: m.thinkingMs, streaming: m.streaming && !m.text }) : null,
|
|
9799
|
+
m.steps && m.steps.length && ctx.showSteps ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(StepList, { steps: m.steps, defaultOpen: m.steps.some((s) => s.status === "running"), dense: true }) : null,
|
|
9800
|
+
body ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-text" + (isUser ? " is-user" : ""), children: [
|
|
9801
|
+
showMd ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(Markdown, { source: body, headingOffset: 2, codeProps: { collapseAfter: 22 }, renderCitation: ctx.renderCitation }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-plain", children: body }),
|
|
9802
|
+
m.streaming ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-caret", "aria-hidden": "true" }) : null
|
|
9443
9803
|
] }) : null,
|
|
9444
|
-
clamped != null ? /* @__PURE__ */ (0,
|
|
9445
|
-
/* @__PURE__ */ (0,
|
|
9804
|
+
clamped != null ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-more", onClick: () => setExpanded(true), children: [
|
|
9805
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true" }),
|
|
9446
9806
|
"Show more",
|
|
9447
|
-
/* @__PURE__ */ (0,
|
|
9448
|
-
] }) : expanded && ctx.maxVisibleChars && raw.length > ctx.maxVisibleChars ? /* @__PURE__ */ (0,
|
|
9449
|
-
/* @__PURE__ */ (0,
|
|
9807
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-more-len fd-tabular", children: raw.length < 2e3 ? raw.length.toLocaleString() + " characters" : Math.round(raw.length / 100) / 10 + "k characters" })
|
|
9808
|
+
] }) : expanded && ctx.maxVisibleChars && raw.length > ctx.maxVisibleChars ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-more", onClick: () => setExpanded(false), children: [
|
|
9809
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-caret-up", "aria-hidden": "true" }),
|
|
9450
9810
|
"Show less"
|
|
9451
9811
|
] }) : null,
|
|
9452
|
-
m.attachments && m.attachments.length ? /* @__PURE__ */ (0,
|
|
9453
|
-
m.packet ? /* @__PURE__ */ (0,
|
|
9812
|
+
m.attachments && m.attachments.length ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(FileGrid, { files: m.attachments, onOpen: ctx.onOpenAttachment, tiles: true, maxHeight: ctx.attachmentHeight || 220, compact: ctx.narrow }) : null,
|
|
9813
|
+
m.packet ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
|
|
9454
9814
|
PacketCard,
|
|
9455
9815
|
{
|
|
9456
9816
|
packet: m.packet,
|
|
@@ -9460,44 +9820,44 @@ function MessageBody({ message: m, ctx }) {
|
|
|
9460
9820
|
applied: ctx.appliedPackets && ctx.appliedPackets[m.id]
|
|
9461
9821
|
}
|
|
9462
9822
|
) : null,
|
|
9463
|
-
m.citations && m.citations.length ? /* @__PURE__ */ (0,
|
|
9464
|
-
m.working ? /* @__PURE__ */ (0,
|
|
9465
|
-
/* @__PURE__ */ (0,
|
|
9466
|
-
/* @__PURE__ */ (0,
|
|
9467
|
-
/* @__PURE__ */ (0,
|
|
9468
|
-
/* @__PURE__ */ (0,
|
|
9823
|
+
m.citations && m.citations.length ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(Citations, { items: m.citations, onOpen: ctx.onOpenCitation }) : null,
|
|
9824
|
+
m.working ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-working", role: "status", children: [
|
|
9825
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-dots", "aria-hidden": "true", children: [
|
|
9826
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {}),
|
|
9827
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {}),
|
|
9828
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", {})
|
|
9469
9829
|
] }),
|
|
9470
|
-
/* @__PURE__ */ (0,
|
|
9830
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-working-label", children: [
|
|
9471
9831
|
m.resumed ? "Resuming" : "Working",
|
|
9472
|
-
m.job && m.job.status ? /* @__PURE__ */ (0,
|
|
9832
|
+
m.job && m.job.status ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-working-job", children: [
|
|
9473
9833
|
" \xB7 ",
|
|
9474
9834
|
m.job.status
|
|
9475
9835
|
] }) : null,
|
|
9476
|
-
m.job && m.job.detail ? /* @__PURE__ */ (0,
|
|
9836
|
+
m.job && m.job.detail ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "fdc-working-job", children: [
|
|
9477
9837
|
" \xB7 ",
|
|
9478
9838
|
m.job.detail
|
|
9479
9839
|
] }) : null
|
|
9480
9840
|
] }),
|
|
9481
|
-
m.jobId ? /* @__PURE__ */ (0,
|
|
9841
|
+
m.jobId ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-working-id fd-mono", children: String(m.jobId).slice(0, 12) }) : null
|
|
9482
9842
|
] }) : null,
|
|
9483
|
-
m.stopped ? /* @__PURE__ */ (0,
|
|
9484
|
-
/* @__PURE__ */ (0,
|
|
9843
|
+
m.stopped ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-stopped", children: [
|
|
9844
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-stop-circle", "aria-hidden": "true" }),
|
|
9485
9845
|
"Stopped"
|
|
9486
9846
|
] }) : null,
|
|
9487
|
-
m.error ? /* @__PURE__ */ (0,
|
|
9488
|
-
/* @__PURE__ */ (0,
|
|
9489
|
-
/* @__PURE__ */ (0,
|
|
9490
|
-
m.retryable && ctx.onRetry ? /* @__PURE__ */ (0,
|
|
9491
|
-
/* @__PURE__ */ (0,
|
|
9847
|
+
m.error ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-error", role: "alert", children: [
|
|
9848
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
|
|
9849
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-error-text", children: ctx.errorCopy ? ctx.errorCopy(m.error) : m.error }),
|
|
9850
|
+
m.retryable && ctx.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-error-retry", onClick: ctx.onRetry, children: [
|
|
9851
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }),
|
|
9492
9852
|
"Retry"
|
|
9493
9853
|
] }) : null
|
|
9494
9854
|
] }) : null
|
|
9495
9855
|
] });
|
|
9496
9856
|
}
|
|
9497
9857
|
function useCopyRun() {
|
|
9498
|
-
const [done, setDone] =
|
|
9499
|
-
const t =
|
|
9500
|
-
|
|
9858
|
+
const [done, setDone] = React46.useState(false);
|
|
9859
|
+
const t = React46.useRef(null);
|
|
9860
|
+
React46.useEffect(() => () => {
|
|
9501
9861
|
if (t.current) clearTimeout(t.current);
|
|
9502
9862
|
}, []);
|
|
9503
9863
|
return [done, (text) => {
|
|
@@ -9517,16 +9877,16 @@ function RunActions({ group, ctx }) {
|
|
|
9517
9877
|
const isAssistant = group.role === "assistant";
|
|
9518
9878
|
const fb = last.feedback;
|
|
9519
9879
|
if (!ctx.messageActions) return null;
|
|
9520
|
-
return /* @__PURE__ */ (0,
|
|
9521
|
-
/* @__PURE__ */ (0,
|
|
9522
|
-
/* @__PURE__ */ (0,
|
|
9523
|
-
/* @__PURE__ */ (0,
|
|
9880
|
+
return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-actions", role: "group", "aria-label": "Message actions", children: [
|
|
9881
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("button", { type: "button", className: "fdc-act" + (copied ? " is-done" : ""), onClick: () => copy(markdownToText(text)), "aria-label": "Copy message", children: [
|
|
9882
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-" + (copied ? "check" : "copy"), "aria-hidden": "true" }),
|
|
9883
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-act-label", children: copied ? "Copied" : "Copy" })
|
|
9524
9884
|
] }),
|
|
9525
|
-
isAssistant && ctx.onRetry ? /* @__PURE__ */ (0,
|
|
9526
|
-
group.role === "user" && ctx.onEdit ? /* @__PURE__ */ (0,
|
|
9527
|
-
isAssistant && ctx.onFeedback ? /* @__PURE__ */ (0,
|
|
9528
|
-
/* @__PURE__ */ (0,
|
|
9529
|
-
/* @__PURE__ */ (0,
|
|
9885
|
+
isAssistant && ctx.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("button", { type: "button", className: "fdc-act", onClick: ctx.onRetry, "aria-label": "Retry this turn", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }) }) : null,
|
|
9886
|
+
group.role === "user" && ctx.onEdit ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("button", { type: "button", className: "fdc-act", onClick: () => ctx.onEdit(last), "aria-label": "Edit and resend", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-pencil-simple", "aria-hidden": "true" }) }) : null,
|
|
9887
|
+
isAssistant && ctx.onFeedback ? /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)(React46.Fragment, { children: [
|
|
9888
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("button", { type: "button", className: "fdc-act" + (fb === "up" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "up"), "aria-pressed": fb === "up", "aria-label": "Good response", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-thumbs-up", "aria-hidden": "true" }) }),
|
|
9889
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("button", { type: "button", className: "fdc-act" + (fb === "down" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "down"), "aria-pressed": fb === "down", "aria-label": "Bad response", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-thumbs-down", "aria-hidden": "true" }) })
|
|
9530
9890
|
] }) : null,
|
|
9531
9891
|
ctx.extraActions ? ctx.extraActions(group) : null
|
|
9532
9892
|
] });
|
|
@@ -9536,22 +9896,22 @@ function ChatTurn({ group, ctx }) {
|
|
|
9536
9896
|
const name = isUser ? ctx.userName : group.author || ctx.assistantName;
|
|
9537
9897
|
const avatar = isUser ? ctx.userAvatar : ctx.assistantAvatar;
|
|
9538
9898
|
const stamp = group.messages[0].timestamp;
|
|
9539
|
-
return /* @__PURE__ */ (0,
|
|
9540
|
-
/* @__PURE__ */ (0,
|
|
9541
|
-
ctx.showAvatars ? /* @__PURE__ */ (0,
|
|
9542
|
-
/* @__PURE__ */ (0,
|
|
9543
|
-
stamp ? /* @__PURE__ */ (0,
|
|
9544
|
-
group.messages.some((m) => m.model) ? /* @__PURE__ */ (0,
|
|
9899
|
+
return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("article", { className: "fdc-turn is-" + group.role, "aria-label": String(name) + (stamp ? " at " + formatClock(stamp) : ""), children: [
|
|
9900
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-turn-head", children: [
|
|
9901
|
+
ctx.showAvatars ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-avatar is-" + group.role, "aria-hidden": "true", children: avatar ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("img", { src: avatar, alt: "" }) : isUser ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-avatar-txt", children: (name || "You").slice(0, 1).toUpperCase() }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("i", { className: "ph ph-" + (ctx.assistantIcon || "sparkle") }) }) : null,
|
|
9902
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-who", children: name }),
|
|
9903
|
+
stamp ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(RelativeTime, { className: "fdc-when", value: stamp }) : null,
|
|
9904
|
+
group.messages.some((m) => m.model) ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "fdc-turn-model", children: group.messages.find((m) => m.model).model }) : null
|
|
9545
9905
|
] }),
|
|
9546
|
-
/* @__PURE__ */ (0,
|
|
9547
|
-
group.messages.map((m) => /* @__PURE__ */ (0,
|
|
9548
|
-
/* @__PURE__ */ (0,
|
|
9906
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "fdc-turn-body", children: [
|
|
9907
|
+
group.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: "fdc-msg" + (m.pending ? " is-pending" : ""), children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(MessageBody, { message: m, ctx }) }, m.id)),
|
|
9908
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)(RunActions, { group, ctx })
|
|
9549
9909
|
] })
|
|
9550
9910
|
] });
|
|
9551
9911
|
}
|
|
9552
9912
|
|
|
9553
9913
|
// src/components/chat/ChatTranscript.tsx
|
|
9554
|
-
var
|
|
9914
|
+
var import_jsx_runtime76 = require("react/jsx-runtime");
|
|
9555
9915
|
var GROUP_WINDOW = 6e4;
|
|
9556
9916
|
var STICK_PX = 100;
|
|
9557
9917
|
function groupMessages(list) {
|
|
@@ -9584,25 +9944,25 @@ function dayLabel(key) {
|
|
|
9584
9944
|
}
|
|
9585
9945
|
function Suggestions({ items = [], onPick }) {
|
|
9586
9946
|
if (!items.length) return null;
|
|
9587
|
-
return /* @__PURE__ */ (0,
|
|
9947
|
+
return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fdc-suggest", children: items.map((s, i) => {
|
|
9588
9948
|
const it = typeof s === "string" ? { label: s, text: s } : s;
|
|
9589
|
-
return /* @__PURE__ */ (0,
|
|
9590
|
-
it.icon ? /* @__PURE__ */ (0,
|
|
9591
|
-
/* @__PURE__ */ (0,
|
|
9592
|
-
/* @__PURE__ */ (0,
|
|
9593
|
-
it.description ? /* @__PURE__ */ (0,
|
|
9949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "fdc-suggest-item", onClick: () => onPick && onPick(it.text || it.label), children: [
|
|
9950
|
+
it.icon ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-" + it.icon, "aria-hidden": "true" }) : null,
|
|
9951
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("span", { className: "fdc-suggest-text", children: [
|
|
9952
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fdc-suggest-label", children: it.label }),
|
|
9953
|
+
it.description ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fdc-suggest-desc", children: it.description }) : null
|
|
9594
9954
|
] }),
|
|
9595
|
-
/* @__PURE__ */ (0,
|
|
9955
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-arrow-up-right fdc-suggest-go", "aria-hidden": "true" })
|
|
9596
9956
|
] }, it.id || i);
|
|
9597
9957
|
}) });
|
|
9598
9958
|
}
|
|
9599
9959
|
function LoadingTurns() {
|
|
9600
|
-
return /* @__PURE__ */ (0,
|
|
9601
|
-
/* @__PURE__ */ (0,
|
|
9602
|
-
/* @__PURE__ */ (0,
|
|
9603
|
-
/* @__PURE__ */ (0,
|
|
9604
|
-
/* @__PURE__ */ (0,
|
|
9605
|
-
/* @__PURE__ */ (0,
|
|
9960
|
+
return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fdc-skel", "aria-hidden": "true", children: [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-skel-turn", children: [
|
|
9961
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fd-skel fd-skel-circle", style: { width: 22, height: 22 } }),
|
|
9962
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-skel-lines", children: [
|
|
9963
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fd-skel", style: { width: i ? "62%" : "44%", height: 11 } }),
|
|
9964
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fd-skel", style: { width: i ? "94%" : "78%", height: 11 } }),
|
|
9965
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fd-skel", style: { width: i ? "71%" : "56%", height: 11 } })
|
|
9606
9966
|
] })
|
|
9607
9967
|
] }, i)) });
|
|
9608
9968
|
}
|
|
@@ -9619,10 +9979,10 @@ function ChatTranscript({
|
|
|
9619
9979
|
renderEmpty,
|
|
9620
9980
|
className = ""
|
|
9621
9981
|
}) {
|
|
9622
|
-
const scroller =
|
|
9623
|
-
const stick =
|
|
9624
|
-
const [pill, setPill] =
|
|
9625
|
-
const seen =
|
|
9982
|
+
const scroller = React47.useRef(null);
|
|
9983
|
+
const stick = React47.useRef(true);
|
|
9984
|
+
const [pill, setPill] = React47.useState(0);
|
|
9985
|
+
const seen = React47.useRef(0);
|
|
9626
9986
|
const toBottom = (smooth) => {
|
|
9627
9987
|
const el = scroller.current;
|
|
9628
9988
|
if (!el) return;
|
|
@@ -9641,7 +10001,7 @@ function ChatTranscript({
|
|
|
9641
10001
|
seen.current = messages.length;
|
|
9642
10002
|
}
|
|
9643
10003
|
};
|
|
9644
|
-
|
|
10004
|
+
React47.useLayoutEffect(() => {
|
|
9645
10005
|
const el = scroller.current;
|
|
9646
10006
|
if (!el) return;
|
|
9647
10007
|
if (stick.current) {
|
|
@@ -9649,7 +10009,7 @@ function ChatTranscript({
|
|
|
9649
10009
|
seen.current = messages.length;
|
|
9650
10010
|
} else setPill(Math.max(0, messages.length - seen.current));
|
|
9651
10011
|
}, [messages]);
|
|
9652
|
-
|
|
10012
|
+
React47.useEffect(() => {
|
|
9653
10013
|
const el = scroller.current;
|
|
9654
10014
|
const inner = el && el.firstChild;
|
|
9655
10015
|
if (!el || !inner || typeof ResizeObserver === "undefined") return;
|
|
@@ -9659,38 +10019,38 @@ function ChatTranscript({
|
|
|
9659
10019
|
ro.observe(inner);
|
|
9660
10020
|
return () => ro.disconnect();
|
|
9661
10021
|
}, []);
|
|
9662
|
-
const groups =
|
|
10022
|
+
const groups = React47.useMemo(() => groupMessages(messages), [messages]);
|
|
9663
10023
|
const empty = !messages.length && status === "ready";
|
|
9664
|
-
return /* @__PURE__ */ (0,
|
|
9665
|
-
/* @__PURE__ */ (0,
|
|
9666
|
-
status === "loading" || status === "resolving" ? /* @__PURE__ */ (0,
|
|
9667
|
-
status === "disconnected" ? /* @__PURE__ */ (0,
|
|
9668
|
-
/* @__PURE__ */ (0,
|
|
9669
|
-
/* @__PURE__ */ (0,
|
|
9670
|
-
/* @__PURE__ */ (0,
|
|
9671
|
-
ctx.onReconnect ? /* @__PURE__ */ (0,
|
|
9672
|
-
/* @__PURE__ */ (0,
|
|
10024
|
+
return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
|
|
10025
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
|
|
10026
|
+
status === "loading" || status === "resolving" ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(LoadingTurns, {}) : null,
|
|
10027
|
+
status === "disconnected" ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-dead", children: [
|
|
10028
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fdc-dead-icon", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-plugs", "aria-hidden": "true" }) }),
|
|
10029
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h3", { className: "fdc-dead-title", children: ctx.deadTitle || "The assistant isn\u2019t reachable" }),
|
|
10030
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("p", { className: "fdc-dead-body", children: fatal === "chat_unavailable" ? "The service reported chat_unavailable. Nothing you type will be lost \u2014 reopen the panel once it\u2019s back." : "The thread couldn\u2019t be resolved" + (fatal ? " (" + fatal + ")" : "") + ". The composer stays disabled until it resolves." }),
|
|
10031
|
+
ctx.onReconnect ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "fd-btn fd-btn-secondary fd-btn-sm", onClick: ctx.onReconnect, children: [
|
|
10032
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }),
|
|
9673
10033
|
"Try again"
|
|
9674
10034
|
] }) : null
|
|
9675
10035
|
] }) : null,
|
|
9676
|
-
empty ? renderEmpty ? renderEmpty() : /* @__PURE__ */ (0,
|
|
9677
|
-
/* @__PURE__ */ (0,
|
|
9678
|
-
/* @__PURE__ */ (0,
|
|
9679
|
-
emptyDescription ? /* @__PURE__ */ (0,
|
|
9680
|
-
/* @__PURE__ */ (0,
|
|
10036
|
+
empty ? renderEmpty ? renderEmpty() : /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-empty", children: [
|
|
10037
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "fdc-empty-icon", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-" + emptyIcon, "aria-hidden": "true" }) }),
|
|
10038
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h3", { className: "fdc-empty-title", children: emptyTitle }),
|
|
10039
|
+
emptyDescription ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("p", { className: "fdc-empty-body", children: emptyDescription }) : null,
|
|
10040
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Suggestions, { items: suggestions, onPick })
|
|
9681
10041
|
] }) : null,
|
|
9682
10042
|
groups.map((g, i) => {
|
|
9683
10043
|
const prev = groups[i - 1];
|
|
9684
10044
|
const k = dayKey(g.messages[0].timestamp);
|
|
9685
10045
|
const showDay = !!k && (!prev || dayKey(prev.messages[0].timestamp) !== k);
|
|
9686
|
-
return /* @__PURE__ */ (0,
|
|
9687
|
-
showDay ? /* @__PURE__ */ (0,
|
|
9688
|
-
/* @__PURE__ */ (0,
|
|
10046
|
+
return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React47.Fragment, { children: [
|
|
10047
|
+
showDay ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fdc-day", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: dayLabel(k) }) }) : null,
|
|
10048
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(ChatTurn, { group: g, ctx })
|
|
9689
10049
|
] }, g.key || i);
|
|
9690
10050
|
})
|
|
9691
10051
|
] }),
|
|
9692
|
-
pill ? /* @__PURE__ */ (0,
|
|
9693
|
-
/* @__PURE__ */ (0,
|
|
10052
|
+
pill ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "fdc-pill", onClick: () => toBottom(true), children: [
|
|
10053
|
+
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-arrow-down", "aria-hidden": "true" }),
|
|
9694
10054
|
pill,
|
|
9695
10055
|
" new message",
|
|
9696
10056
|
pill === 1 ? "" : "s"
|
|
@@ -9700,8 +10060,8 @@ function ChatTranscript({
|
|
|
9700
10060
|
var TranscriptKit = { groupMessages };
|
|
9701
10061
|
|
|
9702
10062
|
// src/components/chat/ChatComposer.tsx
|
|
9703
|
-
var
|
|
9704
|
-
var
|
|
10063
|
+
var React48 = __toESM(require("react"), 1);
|
|
10064
|
+
var import_jsx_runtime77 = require("react/jsx-runtime");
|
|
9705
10065
|
function ChatComposer({
|
|
9706
10066
|
onSubmit,
|
|
9707
10067
|
onStop,
|
|
@@ -9728,17 +10088,17 @@ function ChatComposer({
|
|
|
9728
10088
|
onReject,
|
|
9729
10089
|
onOpenAttachment
|
|
9730
10090
|
}) {
|
|
9731
|
-
const [text, setText] =
|
|
9732
|
-
const [trigger, setTrigger] =
|
|
9733
|
-
const [mentionItems, setMentionItems] =
|
|
9734
|
-
const [listening, setListening] =
|
|
9735
|
-
const [notice, setNotice] =
|
|
9736
|
-
const editor =
|
|
9737
|
-
const wrap =
|
|
9738
|
-
const stopVoice =
|
|
10091
|
+
const [text, setText] = React48.useState(draft || "");
|
|
10092
|
+
const [trigger, setTrigger] = React48.useState(null);
|
|
10093
|
+
const [mentionItems, setMentionItems] = React48.useState([]);
|
|
10094
|
+
const [listening, setListening] = React48.useState(false);
|
|
10095
|
+
const [notice, setNotice] = React48.useState(null);
|
|
10096
|
+
const editor = React48.useRef(null);
|
|
10097
|
+
const wrap = React48.useRef(null);
|
|
10098
|
+
const stopVoice = React48.useRef(null);
|
|
9739
10099
|
const staged = useStagedFiles(fileUploadHandler, { onError: () => {
|
|
9740
10100
|
} });
|
|
9741
|
-
|
|
10101
|
+
React48.useEffect(() => {
|
|
9742
10102
|
if (draft != null && draft !== text) setText(draft);
|
|
9743
10103
|
}, [draft]);
|
|
9744
10104
|
const change = (v) => {
|
|
@@ -9774,7 +10134,7 @@ function ChatComposer({
|
|
|
9774
10134
|
e.preventDefault();
|
|
9775
10135
|
staged.add(found.files);
|
|
9776
10136
|
};
|
|
9777
|
-
|
|
10137
|
+
React48.useEffect(() => {
|
|
9778
10138
|
if (!trigger || trigger.type !== "mention" || !mentionSources) {
|
|
9779
10139
|
setMentionItems([]);
|
|
9780
10140
|
return;
|
|
@@ -9792,7 +10152,7 @@ function ChatComposer({
|
|
|
9792
10152
|
const q = (trigger.query || "").toLowerCase();
|
|
9793
10153
|
setMentionItems(mentionSources.filter((m) => !q || (m.label + " " + (m.description || "")).toLowerCase().includes(q)));
|
|
9794
10154
|
}, [trigger, mentionSources]);
|
|
9795
|
-
const slashItems =
|
|
10155
|
+
const slashItems = React48.useMemo(() => {
|
|
9796
10156
|
if (!trigger || trigger.type !== "slash" || !slashCommands) return [];
|
|
9797
10157
|
const q = (trigger.query || "").toLowerCase();
|
|
9798
10158
|
return slashCommands.filter((c) => !q || (c.id + " " + c.label + " " + (c.description || "")).toLowerCase().includes(q));
|
|
@@ -9806,7 +10166,7 @@ function ChatComposer({
|
|
|
9806
10166
|
description: it.description,
|
|
9807
10167
|
icon: it.icon,
|
|
9808
10168
|
meta: mention.meta,
|
|
9809
|
-
shortcut: slash.shortcut ? /* @__PURE__ */ (0,
|
|
10169
|
+
shortcut: slash.shortcut ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(KeyHint, { keys: slash.shortcut, size: "sm" }) : void 0,
|
|
9810
10170
|
onSelect: () => {
|
|
9811
10171
|
const insert = trigger.type === "slash" ? slash.immediate ? "/" + slash.id : "/" + slash.id + " " : "@" + (mention.value || mention.label) + " ";
|
|
9812
10172
|
editor.current && editor.current.replaceRange(trigger.from, trigger.to, insert);
|
|
@@ -9869,14 +10229,14 @@ function ChatComposer({
|
|
|
9869
10229
|
stopVoice.current = typeof res === "function" ? res : () => {
|
|
9870
10230
|
};
|
|
9871
10231
|
};
|
|
9872
|
-
return /* @__PURE__ */ (0,
|
|
9873
|
-
queue.length ? /* @__PURE__ */ (0,
|
|
9874
|
-
/* @__PURE__ */ (0,
|
|
9875
|
-
/* @__PURE__ */ (0,
|
|
9876
|
-
onRemoveQueued ? /* @__PURE__ */ (0,
|
|
10232
|
+
return /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "fdc-composer" + (disabled ? " is-disabled" : "") + (narrow ? " is-narrow" : ""), children: [
|
|
10233
|
+
queue.length ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "fdc-queue", "aria-label": "Queued messages", children: queue.map((q) => /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "fdc-queue-row", children: [
|
|
10234
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-clock-countdown", "aria-hidden": "true" }),
|
|
10235
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { className: "fdc-queue-text", title: truncationTitle(q.text || (q.attachments ? q.attachments.length + " file(s)" : "")), children: q.text || (q.attachments ? q.attachments.length + " file(s)" : "") }),
|
|
10236
|
+
onRemoveQueued ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("button", { type: "button", className: "fdc-queue-x", onClick: () => onRemoveQueued(q.id), "aria-label": "Remove queued message", children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-x", "aria-hidden": "true" }) }) : null
|
|
9877
10237
|
] }, q.id)) }) : null,
|
|
9878
10238
|
sessionBar,
|
|
9879
|
-
/* @__PURE__ */ (0,
|
|
10239
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
|
|
9880
10240
|
Dropzone,
|
|
9881
10241
|
{
|
|
9882
10242
|
className: "fdc-field",
|
|
@@ -9890,9 +10250,9 @@ function ChatComposer({
|
|
|
9890
10250
|
disabled: !fileUploadHandler || disabled,
|
|
9891
10251
|
label: "Drop to attach",
|
|
9892
10252
|
hint: acceptFiles ? acceptFiles.replace(/,/g, " \xB7 ") : void 0,
|
|
9893
|
-
children: /* @__PURE__ */ (0,
|
|
9894
|
-
staged.items.length ? /* @__PURE__ */ (0,
|
|
9895
|
-
/* @__PURE__ */ (0,
|
|
10253
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { ref: wrap, className: "fdc-field-inner", children: [
|
|
10254
|
+
staged.items.length ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "fdc-staged", children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(FileStrip, { files: staged.items, size: narrow ? 60 : 68, onRemove: staged.remove, onRetry: staged.retry, onOpen: onOpenAttachment }) }) : null,
|
|
10255
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
|
|
9896
10256
|
MarkdownEditor,
|
|
9897
10257
|
{
|
|
9898
10258
|
ref: editor,
|
|
@@ -9911,33 +10271,33 @@ function ChatComposer({
|
|
|
9911
10271
|
ariaLabel: "Message"
|
|
9912
10272
|
}
|
|
9913
10273
|
),
|
|
9914
|
-
/* @__PURE__ */ (0,
|
|
9915
|
-
fileUploadHandler ? /* @__PURE__ */ (0,
|
|
9916
|
-
voiceHandler ? /* @__PURE__ */ (0,
|
|
10274
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "fdc-tools", children: [
|
|
10275
|
+
fileUploadHandler ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(FilePickButton, { onFiles: staged.add, onReject: (r) => flash(r[0].message), accept: acceptFiles, maxFileSize, label: "Attach files", icon: "plus" }) : null,
|
|
10276
|
+
voiceHandler ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("button", { type: "button", className: "fd-attachbtn" + (listening ? " is-live" : ""), onClick: voice, "aria-pressed": listening, "aria-label": listening ? "Stop dictation" : "Dictate", children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-" + (listening ? "waveform" : "microphone"), "aria-hidden": "true" }) }) : null,
|
|
9917
10277
|
toolbarExtras,
|
|
9918
|
-
/* @__PURE__ */ (0,
|
|
9919
|
-
maxLength && text.length > maxLength * 0.6 ? /* @__PURE__ */ (0,
|
|
10278
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { className: "fdc-tools-gap" }),
|
|
10279
|
+
maxLength && text.length > maxLength * 0.6 ? /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("span", { className: "fdc-count fd-tabular" + (text.length > maxLength * 0.95 ? " is-hot" : ""), children: [
|
|
9920
10280
|
text.length,
|
|
9921
10281
|
"/",
|
|
9922
10282
|
maxLength
|
|
9923
10283
|
] }) : null,
|
|
9924
|
-
busy ? /* @__PURE__ */ (0,
|
|
9925
|
-
/* @__PURE__ */ (0,
|
|
9926
|
-
/* @__PURE__ */ (0,
|
|
9927
|
-
] }) : /* @__PURE__ */ (0,
|
|
9928
|
-
/* @__PURE__ */ (0,
|
|
9929
|
-
/* @__PURE__ */ (0,
|
|
10284
|
+
busy ? /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("button", { type: "button", className: "fdc-stop", onClick: onStop, "aria-label": "Stop generating", children: [
|
|
10285
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-stop-circle", "aria-hidden": "true" }),
|
|
10286
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { children: "Stop" })
|
|
10287
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("button", { type: "button", className: "fdc-send", onClick: submit, disabled: !canSend, "aria-label": "Send message", children: [
|
|
10288
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-paper-plane-right", "aria-hidden": "true" }),
|
|
10289
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)("span", { className: "fdc-send-label", children: "Send" })
|
|
9930
10290
|
] })
|
|
9931
10291
|
] })
|
|
9932
10292
|
] })
|
|
9933
10293
|
}
|
|
9934
10294
|
),
|
|
9935
|
-
notice ? /* @__PURE__ */ (0,
|
|
9936
|
-
/* @__PURE__ */ (0,
|
|
10295
|
+
notice ? /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "fdc-notice", role: "status", children: [
|
|
10296
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
|
|
9937
10297
|
notice
|
|
9938
10298
|
] }) : null,
|
|
9939
|
-
hint && !notice ? /* @__PURE__ */ (0,
|
|
9940
|
-
/* @__PURE__ */ (0,
|
|
10299
|
+
hint && !notice ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "fdc-hint", children: hint }) : null,
|
|
10300
|
+
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
|
|
9941
10301
|
Popover,
|
|
9942
10302
|
{
|
|
9943
10303
|
open: menuOpen,
|
|
@@ -9951,12 +10311,12 @@ function ChatComposer({
|
|
|
9951
10311
|
returnFocus: false,
|
|
9952
10312
|
closeOnOutside: true,
|
|
9953
10313
|
label: trigger && trigger.type === "slash" ? "Commands" : "Mentions",
|
|
9954
|
-
children: /* @__PURE__ */ (0,
|
|
10314
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
|
|
9955
10315
|
Menu,
|
|
9956
10316
|
{
|
|
9957
10317
|
items: menuItems,
|
|
9958
10318
|
autoFocus: false,
|
|
9959
|
-
header: /* @__PURE__ */ (0,
|
|
10319
|
+
header: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "fd-pop-group", children: trigger && trigger.type === "slash" ? "Commands" : "Attach context" }),
|
|
9960
10320
|
onClose: () => setTrigger(null)
|
|
9961
10321
|
}
|
|
9962
10322
|
)
|
|
@@ -9966,12 +10326,12 @@ function ChatComposer({
|
|
|
9966
10326
|
}
|
|
9967
10327
|
|
|
9968
10328
|
// src/components/chat/ChatSessionBar.tsx
|
|
9969
|
-
var
|
|
9970
|
-
var
|
|
10329
|
+
var React49 = __toESM(require("react"), 1);
|
|
10330
|
+
var import_jsx_runtime78 = require("react/jsx-runtime");
|
|
9971
10331
|
var compact2 = meterFormats.compact;
|
|
9972
10332
|
function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }) {
|
|
9973
|
-
const [open, setOpen] =
|
|
9974
|
-
const anchor =
|
|
10333
|
+
const [open, setOpen] = React49.useState(false);
|
|
10334
|
+
const anchor = React49.useRef(null);
|
|
9975
10335
|
const stats = sessionStats || null;
|
|
9976
10336
|
const cu = contextUsage || null;
|
|
9977
10337
|
const limits = usageLimits || null;
|
|
@@ -9986,9 +10346,9 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
|
|
|
9986
10346
|
if (stats && stats.runningTasks) bits.push(stats.runningTasks + " running task" + (stats.runningTasks === 1 ? "" : "s"));
|
|
9987
10347
|
if (cu) bits.push(pct + "% context");
|
|
9988
10348
|
const expandable = !!(cu || limits);
|
|
9989
|
-
return /* @__PURE__ */ (0,
|
|
9990
|
-
/* @__PURE__ */ (0,
|
|
9991
|
-
/* @__PURE__ */ (0,
|
|
10349
|
+
return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(React49.Fragment, { children: [
|
|
10350
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "fdc-bar", children: [
|
|
10351
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(
|
|
9992
10352
|
"button",
|
|
9993
10353
|
{
|
|
9994
10354
|
type: "button",
|
|
@@ -9999,15 +10359,15 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
|
|
|
9999
10359
|
"aria-expanded": expandable ? open : void 0,
|
|
10000
10360
|
"aria-label": expandable ? "Usage details" : void 0,
|
|
10001
10361
|
children: [
|
|
10002
|
-
cu ? /* @__PURE__ */ (0,
|
|
10003
|
-
/* @__PURE__ */ (0,
|
|
10004
|
-
expandable ? /* @__PURE__ */ (0,
|
|
10362
|
+
cu ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fdc-bar-mini", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(SegmentedMeter, { total, segments: cu.segments, height: 4, showTotal: false }) }) : null,
|
|
10363
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fdc-bar-text", title: truncationTitle(bits.join(" \xB7 ")), children: bits.join(" \xB7 ") }),
|
|
10364
|
+
expandable ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("i", { className: "ph ph-caret-right fdc-bar-caret", "aria-hidden": "true" }) : null
|
|
10005
10365
|
]
|
|
10006
10366
|
}
|
|
10007
10367
|
),
|
|
10008
10368
|
extras
|
|
10009
10369
|
] }),
|
|
10010
|
-
/* @__PURE__ */ (0,
|
|
10370
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
10011
10371
|
Popover,
|
|
10012
10372
|
{
|
|
10013
10373
|
open,
|
|
@@ -10018,15 +10378,15 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
|
|
|
10018
10378
|
maxHeight: 460,
|
|
10019
10379
|
padded: true,
|
|
10020
10380
|
label: "Usage",
|
|
10021
|
-
footer: onClear ? /* @__PURE__ */ (0,
|
|
10381
|
+
footer: onClear ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("button", { type: "button", className: "fdc-usage-clear", onClick: () => {
|
|
10022
10382
|
setOpen(false);
|
|
10023
10383
|
onClear();
|
|
10024
10384
|
}, children: [
|
|
10025
|
-
/* @__PURE__ */ (0,
|
|
10385
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("i", { className: "ph ph-trash", "aria-hidden": "true" }),
|
|
10026
10386
|
"Clear conversation"
|
|
10027
10387
|
] }) : void 0,
|
|
10028
|
-
children: /* @__PURE__ */ (0,
|
|
10029
|
-
cu ? /* @__PURE__ */ (0,
|
|
10388
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "fdc-usage", children: [
|
|
10389
|
+
cu ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("section", { className: "fdc-usage-sec", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
10030
10390
|
SegmentedMeter,
|
|
10031
10391
|
{
|
|
10032
10392
|
total,
|
|
@@ -10038,9 +10398,9 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
|
|
|
10038
10398
|
remainderLabel: "Free"
|
|
10039
10399
|
}
|
|
10040
10400
|
) }) : null,
|
|
10041
|
-
limits && limits.length ? /* @__PURE__ */ (0,
|
|
10042
|
-
/* @__PURE__ */ (0,
|
|
10043
|
-
/* @__PURE__ */ (0,
|
|
10401
|
+
limits && limits.length ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("section", { className: "fdc-usage-sec", children: [
|
|
10402
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("h4", { className: "fdc-usage-h", children: "Usage limits" }),
|
|
10403
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("div", { className: "fdc-usage-rows", children: limits.map((l) => /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
10044
10404
|
QuotaRow,
|
|
10045
10405
|
{
|
|
10046
10406
|
label: l.label,
|
|
@@ -10050,25 +10410,25 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
|
|
|
10050
10410
|
l.id
|
|
10051
10411
|
)) })
|
|
10052
10412
|
] }) : null,
|
|
10053
|
-
stats ? /* @__PURE__ */ (0,
|
|
10054
|
-
stats.elapsedMs ? /* @__PURE__ */ (0,
|
|
10055
|
-
/* @__PURE__ */ (0,
|
|
10056
|
-
/* @__PURE__ */ (0,
|
|
10413
|
+
stats ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("section", { className: "fdc-usage-sec fdc-usage-stats", children: [
|
|
10414
|
+
stats.elapsedMs ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { children: [
|
|
10415
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: "Session" }),
|
|
10416
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("b", { className: "fd-tabular", children: formatDuration(stats.elapsedMs) })
|
|
10057
10417
|
] }) : null,
|
|
10058
|
-
stats.tokens ? /* @__PURE__ */ (0,
|
|
10059
|
-
/* @__PURE__ */ (0,
|
|
10060
|
-
/* @__PURE__ */ (0,
|
|
10418
|
+
stats.tokens ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { children: [
|
|
10419
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: "Tokens" }),
|
|
10420
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("b", { className: "fd-tabular", children: stats.tokens.toLocaleString() })
|
|
10061
10421
|
] }) : null,
|
|
10062
|
-
stats.costUsd != null ? /* @__PURE__ */ (0,
|
|
10063
|
-
/* @__PURE__ */ (0,
|
|
10064
|
-
/* @__PURE__ */ (0,
|
|
10422
|
+
stats.costUsd != null ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { children: [
|
|
10423
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: "Cost" }),
|
|
10424
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("b", { className: "fd-tabular", children: [
|
|
10065
10425
|
"$",
|
|
10066
10426
|
Number(stats.costUsd).toFixed(3)
|
|
10067
10427
|
] })
|
|
10068
10428
|
] }) : null,
|
|
10069
|
-
stats.turns ? /* @__PURE__ */ (0,
|
|
10070
|
-
/* @__PURE__ */ (0,
|
|
10071
|
-
/* @__PURE__ */ (0,
|
|
10429
|
+
stats.turns ? /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { children: [
|
|
10430
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: "Turns" }),
|
|
10431
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("b", { className: "fd-tabular", children: stats.turns })
|
|
10072
10432
|
] }) : null
|
|
10073
10433
|
] }) : null
|
|
10074
10434
|
] })
|
|
@@ -10107,7 +10467,7 @@ function ModelControls({
|
|
|
10107
10467
|
disabled: m.disabled,
|
|
10108
10468
|
checked: m.id === (current2 && current2.id),
|
|
10109
10469
|
meta: m.meta,
|
|
10110
|
-
shortcut: m.shortcut ? /* @__PURE__ */ (0,
|
|
10470
|
+
shortcut: m.shortcut ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(KeyHint, { keys: m.shortcut, size: "sm" }) : void 0,
|
|
10111
10471
|
onSelect: () => onModelChange && onModelChange(m.id)
|
|
10112
10472
|
});
|
|
10113
10473
|
const items = [{ kind: "section", label: "Models" }].concat(flat.map(item));
|
|
@@ -10120,15 +10480,15 @@ function ModelControls({
|
|
|
10120
10480
|
items.push({ kind: "section", label: "Fast mode" });
|
|
10121
10481
|
items.push({
|
|
10122
10482
|
kind: "custom",
|
|
10123
|
-
render: () => /* @__PURE__ */ (0,
|
|
10124
|
-
/* @__PURE__ */ (0,
|
|
10125
|
-
/* @__PURE__ */ (0,
|
|
10126
|
-
/* @__PURE__ */ (0,
|
|
10483
|
+
render: () => /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("label", { className: "fdc-switchrow", children: [
|
|
10484
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: fastModeLabel }),
|
|
10485
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("input", { type: "checkbox", className: "fd-sr", checked: !!fastMode, onChange: (e) => onFastModeChange(e.target.checked) }),
|
|
10486
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fd-switch-track" + (fastMode ? " is-on" : ""), "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "fd-switch-thumb" }) })
|
|
10127
10487
|
] })
|
|
10128
10488
|
});
|
|
10129
10489
|
}
|
|
10130
|
-
return /* @__PURE__ */ (0,
|
|
10131
|
-
/* @__PURE__ */ (0,
|
|
10490
|
+
return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(React49.Fragment, { children: [
|
|
10491
|
+
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
10132
10492
|
MenuButton,
|
|
10133
10493
|
{
|
|
10134
10494
|
items,
|
|
@@ -10139,7 +10499,7 @@ function ModelControls({
|
|
|
10139
10499
|
title: "Choose a model"
|
|
10140
10500
|
}
|
|
10141
10501
|
),
|
|
10142
|
-
effortLevels && effortLevels.length ? /* @__PURE__ */ (0,
|
|
10502
|
+
effortLevels && effortLevels.length ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
10143
10503
|
MenuButton,
|
|
10144
10504
|
{
|
|
10145
10505
|
placement: "top-end",
|
|
@@ -10160,7 +10520,7 @@ function ModelControls({
|
|
|
10160
10520
|
}
|
|
10161
10521
|
|
|
10162
10522
|
// src/components/chat/AgentChatPanel.tsx
|
|
10163
|
-
var
|
|
10523
|
+
var import_jsx_runtime79 = require("react/jsx-runtime");
|
|
10164
10524
|
var SURFACES = { sidebar: "is-sidebar", inline: "is-inline", page: "is-page", modal: "is-modal", sheet: "is-sheet" };
|
|
10165
10525
|
function AgentChatPanel({
|
|
10166
10526
|
/* required */
|
|
@@ -10244,11 +10604,11 @@ function AgentChatPanel({
|
|
|
10244
10604
|
onFeedback,
|
|
10245
10605
|
onEditMessage
|
|
10246
10606
|
}) {
|
|
10247
|
-
const [panelWidth, setPanelWidth] =
|
|
10248
|
-
const [applied, setApplied] =
|
|
10249
|
-
const [draft, setDraft] =
|
|
10250
|
-
const dragging =
|
|
10251
|
-
|
|
10607
|
+
const [panelWidth, setPanelWidth] = React50.useState(width);
|
|
10608
|
+
const [applied, setApplied] = React50.useState({});
|
|
10609
|
+
const [draft, setDraft] = React50.useState("");
|
|
10610
|
+
const dragging = React50.useRef(null);
|
|
10611
|
+
React50.useEffect(() => setPanelWidth(width), [width]);
|
|
10252
10612
|
const engine = useChatEngine({
|
|
10253
10613
|
contextType,
|
|
10254
10614
|
contextId,
|
|
@@ -10314,7 +10674,7 @@ function AgentChatPanel({
|
|
|
10314
10674
|
onReconnect: engine.reload,
|
|
10315
10675
|
deadTitle: "The assistant isn\u2019t reachable"
|
|
10316
10676
|
};
|
|
10317
|
-
const sessionBar = /* @__PURE__ */ (0,
|
|
10677
|
+
const sessionBar = /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10318
10678
|
ChatSessionBar,
|
|
10319
10679
|
{
|
|
10320
10680
|
sessionStats,
|
|
@@ -10323,7 +10683,7 @@ function AgentChatPanel({
|
|
|
10323
10683
|
onClear: engine.visible.length ? engine.clear : void 0
|
|
10324
10684
|
}
|
|
10325
10685
|
);
|
|
10326
|
-
const modelControls = /* @__PURE__ */ (0,
|
|
10686
|
+
const modelControls = /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10327
10687
|
ModelControls,
|
|
10328
10688
|
{
|
|
10329
10689
|
models,
|
|
@@ -10338,7 +10698,7 @@ function AgentChatPanel({
|
|
|
10338
10698
|
narrow
|
|
10339
10699
|
}
|
|
10340
10700
|
);
|
|
10341
|
-
const threadMenu = threads && threads.length ? /* @__PURE__ */ (0,
|
|
10701
|
+
const threadMenu = threads && threads.length ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10342
10702
|
MenuButton,
|
|
10343
10703
|
{
|
|
10344
10704
|
variant: "ghost",
|
|
@@ -10365,7 +10725,7 @@ function AgentChatPanel({
|
|
|
10365
10725
|
})))
|
|
10366
10726
|
}
|
|
10367
10727
|
) : null;
|
|
10368
|
-
return /* @__PURE__ */ (0,
|
|
10728
|
+
return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
|
|
10369
10729
|
"aside",
|
|
10370
10730
|
{
|
|
10371
10731
|
className: ["fdc-panel", SURFACES[surface] || SURFACES.sidebar, narrow ? "is-narrow" : "", className].filter(Boolean).join(" "),
|
|
@@ -10376,7 +10736,7 @@ function AgentChatPanel({
|
|
|
10376
10736
|
},
|
|
10377
10737
|
"aria-label": title,
|
|
10378
10738
|
children: [
|
|
10379
|
-
surface === "sidebar" && resizable ? /* @__PURE__ */ (0,
|
|
10739
|
+
surface === "sidebar" && resizable ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10380
10740
|
"div",
|
|
10381
10741
|
{
|
|
10382
10742
|
className: "fdc-grip",
|
|
@@ -10391,20 +10751,20 @@ function AgentChatPanel({
|
|
|
10391
10751
|
}
|
|
10392
10752
|
}
|
|
10393
10753
|
) : null,
|
|
10394
|
-
showHeader ? /* @__PURE__ */ (0,
|
|
10395
|
-
/* @__PURE__ */ (0,
|
|
10396
|
-
/* @__PURE__ */ (0,
|
|
10397
|
-
/* @__PURE__ */ (0,
|
|
10398
|
-
subtitle ? /* @__PURE__ */ (0,
|
|
10754
|
+
showHeader ? /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("header", { className: "fdc-head", children: [
|
|
10755
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fdc-head-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-" + assistantIcon }) }),
|
|
10756
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fdc-head-titles", children: [
|
|
10757
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fdc-head-title", title: truncationTitle(title), children: title }),
|
|
10758
|
+
subtitle ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fdc-head-sub", title: truncationTitle(subtitle), children: subtitle }) : null
|
|
10399
10759
|
] }),
|
|
10400
|
-
/* @__PURE__ */ (0,
|
|
10760
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fdc-head-acts", children: [
|
|
10401
10761
|
headerActions,
|
|
10402
10762
|
threadMenu,
|
|
10403
|
-
onNewThread ? /* @__PURE__ */ (0,
|
|
10404
|
-
onClose ? /* @__PURE__ */ (0,
|
|
10763
|
+
onNewThread ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("button", { type: "button", className: "fdc-iconbtn", onClick: onNewThread, "aria-label": "New conversation", title: "New conversation", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-plus", "aria-hidden": "true" }) }) : null,
|
|
10764
|
+
onClose ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("button", { type: "button", className: "fdc-iconbtn", onClick: onClose, "aria-label": "Close panel", title: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-x", "aria-hidden": "true" }) }) : null
|
|
10405
10765
|
] })
|
|
10406
10766
|
] }) : null,
|
|
10407
|
-
/* @__PURE__ */ (0,
|
|
10767
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10408
10768
|
ChatTranscript,
|
|
10409
10769
|
{
|
|
10410
10770
|
messages: engine.visible,
|
|
@@ -10419,7 +10779,7 @@ function AgentChatPanel({
|
|
|
10419
10779
|
renderEmpty
|
|
10420
10780
|
}
|
|
10421
10781
|
),
|
|
10422
|
-
/* @__PURE__ */ (0,
|
|
10782
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10423
10783
|
ChatComposer,
|
|
10424
10784
|
{
|
|
10425
10785
|
onSubmit: (text, atts) => engine.send(text, atts),
|
|
@@ -10452,8 +10812,8 @@ function AgentChatPanel({
|
|
|
10452
10812
|
}
|
|
10453
10813
|
|
|
10454
10814
|
// src/components/files/FileViewer.tsx
|
|
10455
|
-
var
|
|
10456
|
-
var
|
|
10815
|
+
var React51 = __toESM(require("react"), 1);
|
|
10816
|
+
var import_jsx_runtime80 = require("react/jsx-runtime");
|
|
10457
10817
|
function previewKindOf(file) {
|
|
10458
10818
|
const mime = (file.mime || "").toLowerCase();
|
|
10459
10819
|
const ext = extensionOf(file.name || "");
|
|
@@ -10509,13 +10869,13 @@ function parseDelimited(text, delimiter) {
|
|
|
10509
10869
|
return rows;
|
|
10510
10870
|
}
|
|
10511
10871
|
function usePreview(file, kind, resolveUrl) {
|
|
10512
|
-
const [state, setState] =
|
|
10513
|
-
const [attempt, setAttempt] =
|
|
10514
|
-
const resolveRef =
|
|
10515
|
-
|
|
10872
|
+
const [state, setState] = React51.useState({ status: "loading" });
|
|
10873
|
+
const [attempt, setAttempt] = React51.useState(0);
|
|
10874
|
+
const resolveRef = React51.useRef(resolveUrl);
|
|
10875
|
+
React51.useEffect(() => {
|
|
10516
10876
|
resolveRef.current = resolveUrl;
|
|
10517
10877
|
}, [resolveUrl]);
|
|
10518
|
-
|
|
10878
|
+
React51.useEffect(() => {
|
|
10519
10879
|
if (kind === "none") {
|
|
10520
10880
|
setState({ status: "ready" });
|
|
10521
10881
|
return;
|
|
@@ -10545,19 +10905,19 @@ function usePreview(file, kind, resolveUrl) {
|
|
|
10545
10905
|
if (ac) ac.abort();
|
|
10546
10906
|
};
|
|
10547
10907
|
}, [file.id, kind, attempt]);
|
|
10548
|
-
return { ...state, retry:
|
|
10908
|
+
return { ...state, retry: React51.useCallback(() => setAttempt((a) => a + 1), []) };
|
|
10549
10909
|
}
|
|
10550
10910
|
function PreviewBody({ file, kind, state }) {
|
|
10551
|
-
if (kind === "image") return /* @__PURE__ */ (0,
|
|
10552
|
-
if (kind === "pdf") return /* @__PURE__ */ (0,
|
|
10553
|
-
if (kind === "markdown") return /* @__PURE__ */ (0,
|
|
10911
|
+
if (kind === "image") return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("img", { className: "fd-fv-image", src: state.url, alt: file.name || "Preview" });
|
|
10912
|
+
if (kind === "pdf") return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("iframe", { className: "fd-fv-pdf", src: state.url, title: file.name || "PDF preview" });
|
|
10913
|
+
if (kind === "markdown") return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Markdown, { className: "fd-fv-markdown", source: state.text || "" });
|
|
10554
10914
|
if (kind === "json") {
|
|
10555
10915
|
let pretty = state.text || "";
|
|
10556
10916
|
try {
|
|
10557
10917
|
pretty = JSON.stringify(JSON.parse(pretty), null, 2);
|
|
10558
10918
|
} catch {
|
|
10559
10919
|
}
|
|
10560
|
-
return /* @__PURE__ */ (0,
|
|
10920
|
+
return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(CodeBlock, { code: pretty, language: "json" });
|
|
10561
10921
|
}
|
|
10562
10922
|
if (kind === "csv") {
|
|
10563
10923
|
const tsv = extensionOf(file.name || "") === "tsv" || (file.mime || "").toLowerCase().includes("tab-separated");
|
|
@@ -10565,12 +10925,12 @@ function PreviewBody({ file, kind, state }) {
|
|
|
10565
10925
|
const head = rows[0] || [];
|
|
10566
10926
|
const dataRows = rows.slice(1);
|
|
10567
10927
|
const shown = dataRows.slice(0, FILE_VIEWER_CSV_ROW_LIMIT);
|
|
10568
|
-
return /* @__PURE__ */ (0,
|
|
10569
|
-
/* @__PURE__ */ (0,
|
|
10570
|
-
/* @__PURE__ */ (0,
|
|
10571
|
-
/* @__PURE__ */ (0,
|
|
10928
|
+
return /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "fd-fv-csv", children: [
|
|
10929
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("table", { className: "fd-fv-csv-table", children: [
|
|
10930
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("tr", { children: head.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("th", { children: c }, i)) }) }),
|
|
10931
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("tbody", { children: shown.map((r, ri) => /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("tr", { children: head.map((_, ci) => /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("td", { children: r[ci] ?? "" }, ci)) }, ri)) })
|
|
10572
10932
|
] }),
|
|
10573
|
-
dataRows.length > FILE_VIEWER_CSV_ROW_LIMIT ? /* @__PURE__ */ (0,
|
|
10933
|
+
dataRows.length > FILE_VIEWER_CSV_ROW_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("p", { className: "fd-fv-csv-note fd-body-sm fd-muted", children: [
|
|
10574
10934
|
"Showing the first ",
|
|
10575
10935
|
FILE_VIEWER_CSV_ROW_LIMIT.toLocaleString(),
|
|
10576
10936
|
" of ",
|
|
@@ -10579,24 +10939,24 @@ function PreviewBody({ file, kind, state }) {
|
|
|
10579
10939
|
] }) : null
|
|
10580
10940
|
] });
|
|
10581
10941
|
}
|
|
10582
|
-
if (kind === "text") return /* @__PURE__ */ (0,
|
|
10583
|
-
return /* @__PURE__ */ (0,
|
|
10584
|
-
/* @__PURE__ */ (0,
|
|
10585
|
-
/* @__PURE__ */ (0,
|
|
10586
|
-
/* @__PURE__ */ (0,
|
|
10942
|
+
if (kind === "text") return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(CodeBlock, { code: state.text || "", language: extensionOf(file.name || "") || void 0 });
|
|
10943
|
+
return /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "fd-fv-fallback", children: [
|
|
10944
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("span", { className: "fd-fv-fallback-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("i", { className: "ph ph-" + iconForMime(file.mime, file.name) }) }),
|
|
10945
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("span", { className: "fd-fv-fallback-title", children: "No preview available" }),
|
|
10946
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("span", { className: "fd-fv-fallback-meta", children: [
|
|
10587
10947
|
file.mime || "Unknown type",
|
|
10588
10948
|
file.size ? " \xB7 " + formatBytes(file.size) : ""
|
|
10589
10949
|
] })
|
|
10590
10950
|
] });
|
|
10591
10951
|
}
|
|
10592
10952
|
function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", onClose, actions, className = "" }) {
|
|
10593
|
-
const kind =
|
|
10953
|
+
const kind = React51.useMemo(() => previewKindOf(file), [file]);
|
|
10594
10954
|
const preview = usePreview(file, kind, resolveUrl);
|
|
10595
|
-
const [tab, setTab] =
|
|
10596
|
-
const [downloading, setDownloading] =
|
|
10597
|
-
const [downloadError, setDownloadError] =
|
|
10955
|
+
const [tab, setTab] = React51.useState("preview");
|
|
10956
|
+
const [downloading, setDownloading] = React51.useState(false);
|
|
10957
|
+
const [downloadError, setDownloadError] = React51.useState(null);
|
|
10598
10958
|
const hasSide = sidePanel != null;
|
|
10599
|
-
|
|
10959
|
+
React51.useEffect(() => {
|
|
10600
10960
|
setTab("preview");
|
|
10601
10961
|
}, [file.id]);
|
|
10602
10962
|
const download = () => {
|
|
@@ -10612,29 +10972,29 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
|
|
|
10612
10972
|
a.remove();
|
|
10613
10973
|
}).catch((err) => setDownloadError(err && err.message || "Download failed.")).finally(() => setDownloading(false));
|
|
10614
10974
|
};
|
|
10615
|
-
return /* @__PURE__ */ (0,
|
|
10975
|
+
return /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(
|
|
10616
10976
|
"section",
|
|
10617
10977
|
{
|
|
10618
10978
|
className: ["fd-fv", hasSide && tab === "side" ? "is-tab-side" : "is-tab-preview", className].filter(Boolean).join(" "),
|
|
10619
10979
|
"aria-label": file.name || "File",
|
|
10620
10980
|
children: [
|
|
10621
|
-
/* @__PURE__ */ (0,
|
|
10622
|
-
/* @__PURE__ */ (0,
|
|
10623
|
-
/* @__PURE__ */ (0,
|
|
10624
|
-
/* @__PURE__ */ (0,
|
|
10625
|
-
/* @__PURE__ */ (0,
|
|
10981
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("header", { className: "fd-fv-head", children: [
|
|
10982
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("span", { className: "fd-fv-head-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("i", { className: "ph ph-" + iconForMime(file.mime, file.name) }) }),
|
|
10983
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("span", { className: "fd-fv-head-titles", children: [
|
|
10984
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("span", { className: "fd-fv-head-title", title: file.name, children: file.name }),
|
|
10985
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("span", { className: "fd-fv-head-meta", children: [
|
|
10626
10986
|
file.mime || "Unknown type",
|
|
10627
10987
|
file.size ? " \xB7 " + formatBytes(file.size) : ""
|
|
10628
10988
|
] })
|
|
10629
10989
|
] }),
|
|
10630
|
-
/* @__PURE__ */ (0,
|
|
10990
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("span", { className: "fd-fv-head-actions", children: [
|
|
10631
10991
|
actions,
|
|
10632
|
-
/* @__PURE__ */ (0,
|
|
10633
|
-
onClose ? /* @__PURE__ */ (0,
|
|
10992
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Button, { variant: "secondary", size: "sm", icon: "download-simple", loading: downloading, onClick: download, children: "Download" }),
|
|
10993
|
+
onClose ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(IconButton, { icon: "x", label: "Close", onClick: onClose }) : null
|
|
10634
10994
|
] })
|
|
10635
10995
|
] }),
|
|
10636
|
-
downloadError ? /* @__PURE__ */ (0,
|
|
10637
|
-
hasSide ? /* @__PURE__ */ (0,
|
|
10996
|
+
downloadError ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("div", { className: "fd-fv-download-error", role: "alert", children: downloadError }) : null,
|
|
10997
|
+
hasSide ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
|
|
10638
10998
|
Tabs,
|
|
10639
10999
|
{
|
|
10640
11000
|
className: "fd-fv-tabs",
|
|
@@ -10643,19 +11003,19 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
|
|
|
10643
11003
|
onChange: (v) => setTab(v)
|
|
10644
11004
|
}
|
|
10645
11005
|
) : null,
|
|
10646
|
-
/* @__PURE__ */ (0,
|
|
10647
|
-
/* @__PURE__ */ (0,
|
|
11006
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "fd-fv-body", children: [
|
|
11007
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("div", { className: "fd-fv-preview", role: "region", "aria-label": "Preview of " + (file.name || "file"), children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
|
|
10648
11008
|
LoadingRegion,
|
|
10649
11009
|
{
|
|
10650
11010
|
state: preview.status === "loading" ? "loading" : preview.status === "error" ? "error" : "ready",
|
|
10651
11011
|
error: preview.error,
|
|
10652
11012
|
label: "Loading preview of " + (file.name || "file"),
|
|
10653
11013
|
onRetry: preview.status === "error" ? preview.retry : void 0,
|
|
10654
|
-
skeleton: /* @__PURE__ */ (0,
|
|
10655
|
-
children: /* @__PURE__ */ (0,
|
|
11014
|
+
skeleton: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Skeleton, { shape: "card", h: 220 }),
|
|
11015
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(PreviewBody, { file, kind, state: preview })
|
|
10656
11016
|
}
|
|
10657
11017
|
) }),
|
|
10658
|
-
hasSide ? /* @__PURE__ */ (0,
|
|
11018
|
+
hasSide ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("div", { className: "fd-fv-side", role: "region", "aria-label": sidePanelLabel, children: sidePanel }) : null
|
|
10659
11019
|
] })
|
|
10660
11020
|
]
|
|
10661
11021
|
}
|
|
@@ -10663,8 +11023,8 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
|
|
|
10663
11023
|
}
|
|
10664
11024
|
|
|
10665
11025
|
// src/components/files/FileBrowser.tsx
|
|
10666
|
-
var
|
|
10667
|
-
var
|
|
11026
|
+
var React52 = __toESM(require("react"), 1);
|
|
11027
|
+
var import_jsx_runtime81 = require("react/jsx-runtime");
|
|
10668
11028
|
function childrenOf(folders, parentId) {
|
|
10669
11029
|
return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
|
|
10670
11030
|
}
|
|
@@ -10734,10 +11094,10 @@ function FolderPickerMenu({
|
|
|
10734
11094
|
destinations,
|
|
10735
11095
|
onPick
|
|
10736
11096
|
}) {
|
|
10737
|
-
const [open, setOpen] =
|
|
11097
|
+
const [open, setOpen] = React52.useState(false);
|
|
10738
11098
|
const zIndex = useOverlayLayer(open);
|
|
10739
|
-
const ref =
|
|
10740
|
-
|
|
11099
|
+
const ref = React52.useRef(null);
|
|
11100
|
+
React52.useEffect(() => {
|
|
10741
11101
|
if (!open) return;
|
|
10742
11102
|
const away = (e) => {
|
|
10743
11103
|
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
@@ -10752,8 +11112,8 @@ function FolderPickerMenu({
|
|
|
10752
11112
|
document.removeEventListener("keydown", esc);
|
|
10753
11113
|
};
|
|
10754
11114
|
}, [open]);
|
|
10755
|
-
return /* @__PURE__ */ (0,
|
|
10756
|
-
/* @__PURE__ */ (0,
|
|
11115
|
+
return /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("span", { className: "fd-fb-menu-anchor", ref, children: [
|
|
11116
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
10757
11117
|
IconButton,
|
|
10758
11118
|
{
|
|
10759
11119
|
icon: "arrows-out-cardinal",
|
|
@@ -10765,9 +11125,9 @@ function FolderPickerMenu({
|
|
|
10765
11125
|
onClick: () => setOpen((v) => !v)
|
|
10766
11126
|
}
|
|
10767
11127
|
),
|
|
10768
|
-
open ? /* @__PURE__ */ (0,
|
|
10769
|
-
/* @__PURE__ */ (0,
|
|
10770
|
-
destinations.map((d) => /* @__PURE__ */ (0,
|
|
11128
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-menu", role: "menu", "aria-label": label, style: { zIndex }, children: [
|
|
11129
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-overline fd-muted fd-fb-menu-title", children: "Move to\u2026" }),
|
|
11130
|
+
destinations.map((d) => /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(
|
|
10771
11131
|
"button",
|
|
10772
11132
|
{
|
|
10773
11133
|
type: "button",
|
|
@@ -10779,7 +11139,7 @@ function FolderPickerMenu({
|
|
|
10779
11139
|
setOpen(false);
|
|
10780
11140
|
},
|
|
10781
11141
|
children: [
|
|
10782
|
-
/* @__PURE__ */ (0,
|
|
11142
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)("i", { className: "ph ph-" + (d.id == null ? "house-simple" : "folder-simple"), "aria-hidden": "true" }),
|
|
10783
11143
|
d.name
|
|
10784
11144
|
]
|
|
10785
11145
|
},
|
|
@@ -10789,8 +11149,8 @@ function FolderPickerMenu({
|
|
|
10789
11149
|
] });
|
|
10790
11150
|
}
|
|
10791
11151
|
function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
|
|
10792
|
-
const [over, setOver] =
|
|
10793
|
-
const dragCounter =
|
|
11152
|
+
const [over, setOver] = React52.useState(false);
|
|
11153
|
+
const dragCounter = React52.useRef(0);
|
|
10794
11154
|
const dropHandlers = isDropTarget ? {
|
|
10795
11155
|
onDragEnter: (e) => {
|
|
10796
11156
|
if (!hasInternalPayload(e)) return;
|
|
@@ -10815,8 +11175,8 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
|
|
|
10815
11175
|
if (payload && onDropPayload) onDropPayload(payload);
|
|
10816
11176
|
}
|
|
10817
11177
|
} : {};
|
|
10818
|
-
return /* @__PURE__ */ (0,
|
|
10819
|
-
/* @__PURE__ */ (0,
|
|
11178
|
+
return /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: ["fd-fb-row", over ? "is-drop-target" : "", className].filter(Boolean).join(" "), ...dropHandlers, children: [
|
|
11179
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(
|
|
10820
11180
|
"button",
|
|
10821
11181
|
{
|
|
10822
11182
|
type: "button",
|
|
@@ -10825,26 +11185,26 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
|
|
|
10825
11185
|
draggable: !!draggableId,
|
|
10826
11186
|
onDragStart: draggableId ? (e) => writeDragPayload(e, draggableId) : void 0,
|
|
10827
11187
|
children: [
|
|
10828
|
-
/* @__PURE__ */ (0,
|
|
10829
|
-
/* @__PURE__ */ (0,
|
|
10830
|
-
/* @__PURE__ */ (0,
|
|
10831
|
-
meta ? /* @__PURE__ */ (0,
|
|
11188
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-fb-row-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("i", { className: "ph ph-" + icon }) }),
|
|
11189
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("span", { className: "fd-fb-row-text", children: [
|
|
11190
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-fb-row-name", title: name, children: name }),
|
|
11191
|
+
meta ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-fb-row-meta", children: meta }) : null
|
|
10832
11192
|
] })
|
|
10833
11193
|
]
|
|
10834
11194
|
}
|
|
10835
11195
|
),
|
|
10836
|
-
actions ? /* @__PURE__ */ (0,
|
|
11196
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-fb-row-actions", children: actions }) : null
|
|
10837
11197
|
] });
|
|
10838
11198
|
}
|
|
10839
11199
|
function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
|
|
10840
11200
|
const kids = childrenOf(folders, folder.id);
|
|
10841
|
-
const [expanded, setExpanded] =
|
|
10842
|
-
|
|
11201
|
+
const [expanded, setExpanded] = React52.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
|
|
11202
|
+
React52.useEffect(() => {
|
|
10843
11203
|
if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
|
|
10844
11204
|
}, [currentFolderId, folder.id, folders]);
|
|
10845
11205
|
const isCurrent = folder.id === currentFolderId;
|
|
10846
|
-
return /* @__PURE__ */ (0,
|
|
10847
|
-
/* @__PURE__ */ (0,
|
|
11206
|
+
return /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-tree-node", children: [
|
|
11207
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
10848
11208
|
RowShell,
|
|
10849
11209
|
{
|
|
10850
11210
|
icon: folder.id === currentFolderId ? "folder-open" : "folder-simple",
|
|
@@ -10854,7 +11214,7 @@ function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveI
|
|
|
10854
11214
|
isDropTarget: canMove,
|
|
10855
11215
|
onDropPayload: (payload) => onMoveInto(payload, folder.id),
|
|
10856
11216
|
className: ["fd-fb-tree-row", isCurrent ? "is-current" : ""].filter(Boolean).join(" "),
|
|
10857
|
-
actions: kids.length ? /* @__PURE__ */ (0,
|
|
11217
|
+
actions: kids.length ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
10858
11218
|
IconButton,
|
|
10859
11219
|
{
|
|
10860
11220
|
icon: expanded ? "caret-down" : "caret-right",
|
|
@@ -10866,7 +11226,7 @@ function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveI
|
|
|
10866
11226
|
) : void 0
|
|
10867
11227
|
}
|
|
10868
11228
|
),
|
|
10869
|
-
expanded && kids.length ? /* @__PURE__ */ (0,
|
|
11229
|
+
expanded && kids.length ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("div", { className: "fd-fb-tree-children", style: { paddingLeft: 14 }, children: kids.map((k) => /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
10870
11230
|
TreeNode,
|
|
10871
11231
|
{
|
|
10872
11232
|
folder: k,
|
|
@@ -10905,15 +11265,15 @@ function FileBrowser({
|
|
|
10905
11265
|
const canUpload = perm.canUpload !== false && !!onUpload;
|
|
10906
11266
|
const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
|
|
10907
11267
|
const canDeleteFolder = perm.canDeleteFolder !== false && !!onDeleteFolder;
|
|
10908
|
-
const [createOpen, setCreateOpen] =
|
|
10909
|
-
const [createName, setCreateName] =
|
|
10910
|
-
const [renameTarget, setRenameTarget] =
|
|
10911
|
-
const [renameValue, setRenameValue] =
|
|
10912
|
-
const [deleteTarget, setDeleteTarget] =
|
|
10913
|
-
const uploadInputRef =
|
|
10914
|
-
const crumbFolders =
|
|
10915
|
-
const subfolders =
|
|
10916
|
-
const currentFiles =
|
|
11268
|
+
const [createOpen, setCreateOpen] = React52.useState(false);
|
|
11269
|
+
const [createName, setCreateName] = React52.useState("");
|
|
11270
|
+
const [renameTarget, setRenameTarget] = React52.useState(null);
|
|
11271
|
+
const [renameValue, setRenameValue] = React52.useState("");
|
|
11272
|
+
const [deleteTarget, setDeleteTarget] = React52.useState(null);
|
|
11273
|
+
const uploadInputRef = React52.useRef(null);
|
|
11274
|
+
const crumbFolders = React52.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
|
|
11275
|
+
const subfolders = React52.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
|
|
11276
|
+
const currentFiles = React52.useMemo(
|
|
10917
11277
|
() => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
|
|
10918
11278
|
[files, currentFolderId]
|
|
10919
11279
|
);
|
|
@@ -10952,10 +11312,10 @@ function FileBrowser({
|
|
|
10952
11312
|
if (deleteTarget && onDeleteFolder) onDeleteFolder(deleteTarget);
|
|
10953
11313
|
setDeleteTarget(null);
|
|
10954
11314
|
};
|
|
10955
|
-
const content = /* @__PURE__ */ (0,
|
|
10956
|
-
/* @__PURE__ */ (0,
|
|
10957
|
-
/* @__PURE__ */ (0,
|
|
10958
|
-
/* @__PURE__ */ (0,
|
|
11315
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: ["fd-fb", className].filter(Boolean).join(" "), children: [
|
|
11316
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-head", children: [
|
|
11317
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("nav", { className: "fd-fb-crumbs", "aria-label": "Folder path", children: [
|
|
11318
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
10959
11319
|
BreadcrumbCrumb,
|
|
10960
11320
|
{
|
|
10961
11321
|
label: "Home",
|
|
@@ -10965,7 +11325,7 @@ function FileBrowser({
|
|
|
10965
11325
|
onDropPayload: (p) => moveIntoFolder(p, null)
|
|
10966
11326
|
}
|
|
10967
11327
|
),
|
|
10968
|
-
crumbFolders.map((f, i) => /* @__PURE__ */ (0,
|
|
11328
|
+
crumbFolders.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
10969
11329
|
BreadcrumbCrumb,
|
|
10970
11330
|
{
|
|
10971
11331
|
label: f.name,
|
|
@@ -10977,11 +11337,11 @@ function FileBrowser({
|
|
|
10977
11337
|
f.id
|
|
10978
11338
|
))
|
|
10979
11339
|
] }),
|
|
10980
|
-
/* @__PURE__ */ (0,
|
|
10981
|
-
canCreateFolder ? /* @__PURE__ */ (0,
|
|
10982
|
-
canUpload ? /* @__PURE__ */ (0,
|
|
10983
|
-
/* @__PURE__ */ (0,
|
|
10984
|
-
/* @__PURE__ */ (0,
|
|
11340
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("span", { className: "fd-fb-actions", children: [
|
|
11341
|
+
canCreateFolder ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "secondary", size: "sm", icon: "folder-simple-plus", onClick: openCreate, children: "New folder" }) : null,
|
|
11342
|
+
canUpload ? /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
|
|
11343
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "secondary", size: "sm", icon: "upload-simple", onClick: () => uploadInputRef.current && uploadInputRef.current.click(), children: "Upload" }),
|
|
11344
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
10985
11345
|
"input",
|
|
10986
11346
|
{
|
|
10987
11347
|
ref: uploadInputRef,
|
|
@@ -10999,9 +11359,9 @@ function FileBrowser({
|
|
|
10999
11359
|
] }) : null
|
|
11000
11360
|
] })
|
|
11001
11361
|
] }),
|
|
11002
|
-
/* @__PURE__ */ (0,
|
|
11003
|
-
/* @__PURE__ */ (0,
|
|
11004
|
-
/* @__PURE__ */ (0,
|
|
11362
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-body", children: [
|
|
11363
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-tree", role: "region", "aria-label": "Folder tree", children: [
|
|
11364
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
11005
11365
|
RowShell,
|
|
11006
11366
|
{
|
|
11007
11367
|
icon: "house-simple",
|
|
@@ -11012,7 +11372,7 @@ function FileBrowser({
|
|
|
11012
11372
|
className: ["fd-fb-tree-row", currentFolderId == null ? "is-current" : ""].filter(Boolean).join(" ")
|
|
11013
11373
|
}
|
|
11014
11374
|
),
|
|
11015
|
-
childrenOf(folders, null).map((f) => /* @__PURE__ */ (0,
|
|
11375
|
+
childrenOf(folders, null).map((f) => /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
11016
11376
|
TreeNode,
|
|
11017
11377
|
{
|
|
11018
11378
|
folder: f,
|
|
@@ -11026,18 +11386,18 @@ function FileBrowser({
|
|
|
11026
11386
|
f.id
|
|
11027
11387
|
))
|
|
11028
11388
|
] }),
|
|
11029
|
-
/* @__PURE__ */ (0,
|
|
11389
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)("div", { className: "fd-fb-main", role: "region", "aria-label": "Folder contents", children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
11030
11390
|
LoadingRegion,
|
|
11031
11391
|
{
|
|
11032
11392
|
state: loading ? "loading" : "ready",
|
|
11033
11393
|
label: "Loading folder contents",
|
|
11034
|
-
skeleton: /* @__PURE__ */ (0,
|
|
11035
|
-
/* @__PURE__ */ (0,
|
|
11036
|
-
/* @__PURE__ */ (0,
|
|
11037
|
-
/* @__PURE__ */ (0,
|
|
11394
|
+
skeleton: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-stack", style: { gap: 8, padding: 12 }, children: [
|
|
11395
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Skeleton, { h: 44, radius: "10px" }),
|
|
11396
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Skeleton, { h: 44, radius: "10px" }),
|
|
11397
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Skeleton, { h: 44, radius: "10px" })
|
|
11038
11398
|
] }),
|
|
11039
|
-
children: isEmpty ? /* @__PURE__ */ (0,
|
|
11040
|
-
subfolders.map((f) => /* @__PURE__ */ (0,
|
|
11399
|
+
children: isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(EmptyState, { icon: "folder-open", title: "This folder is empty", children: canUpload ? "Drop files here, or use Upload above." : "Nothing has been filed here yet." }) : /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "fd-fb-list", children: [
|
|
11400
|
+
subfolders.map((f) => /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
11041
11401
|
RowShell,
|
|
11042
11402
|
{
|
|
11043
11403
|
icon: "folder-simple",
|
|
@@ -11046,9 +11406,9 @@ function FileBrowser({
|
|
|
11046
11406
|
draggableId: canMove ? { kind: "folder", id: f.id } : void 0,
|
|
11047
11407
|
isDropTarget: canMove,
|
|
11048
11408
|
onDropPayload: (p) => moveIntoFolder(p, f.id),
|
|
11049
|
-
actions: /* @__PURE__ */ (0,
|
|
11050
|
-
canRenameFolder ? /* @__PURE__ */ (0,
|
|
11051
|
-
canMove ? /* @__PURE__ */ (0,
|
|
11409
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
|
|
11410
|
+
canRenameFolder ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(IconButton, { icon: "pencil-simple", label: "Rename " + f.name, size: "sm", variant: "ghost", onClick: () => openRename(f) }) : null,
|
|
11411
|
+
canMove ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
11052
11412
|
FolderPickerMenu,
|
|
11053
11413
|
{
|
|
11054
11414
|
label: "Move " + f.name,
|
|
@@ -11056,12 +11416,12 @@ function FileBrowser({
|
|
|
11056
11416
|
onPick: (target) => moveIntoFolder({ kind: "folder", id: f.id }, target)
|
|
11057
11417
|
}
|
|
11058
11418
|
) : null,
|
|
11059
|
-
canDeleteFolder ? /* @__PURE__ */ (0,
|
|
11419
|
+
canDeleteFolder ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(IconButton, { icon: "trash-simple", label: "Delete " + f.name, size: "sm", variant: "ghost", onClick: () => openDelete(f) }) : null
|
|
11060
11420
|
] })
|
|
11061
11421
|
},
|
|
11062
11422
|
f.id
|
|
11063
11423
|
)),
|
|
11064
|
-
currentFiles.map((file) => /* @__PURE__ */ (0,
|
|
11424
|
+
currentFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
11065
11425
|
RowShell,
|
|
11066
11426
|
{
|
|
11067
11427
|
icon: iconForMime(file.mime, file.name),
|
|
@@ -11069,8 +11429,8 @@ function FileBrowser({
|
|
|
11069
11429
|
meta: file.size ? formatBytes(file.size) : void 0,
|
|
11070
11430
|
onOpen: onOpenFile ? () => onOpenFile(file) : void 0,
|
|
11071
11431
|
draggableId: canMove ? { kind: "file", id: file.id } : void 0,
|
|
11072
|
-
actions: /* @__PURE__ */ (0,
|
|
11073
|
-
canMove ? /* @__PURE__ */ (0,
|
|
11432
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
|
|
11433
|
+
canMove ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
11074
11434
|
FolderPickerMenu,
|
|
11075
11435
|
{
|
|
11076
11436
|
label: "Move " + file.name,
|
|
@@ -11078,7 +11438,7 @@ function FileBrowser({
|
|
|
11078
11438
|
onPick: (target) => onMoveFile && onMoveFile(file.id, target)
|
|
11079
11439
|
}
|
|
11080
11440
|
) : null,
|
|
11081
|
-
canRemoveFile ? /* @__PURE__ */ (0,
|
|
11441
|
+
canRemoveFile ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(IconButton, { icon: "trash-simple", label: "Remove " + file.name, size: "sm", variant: "ghost", onClick: () => onRemoveFile && onRemoveFile(file) }) : null
|
|
11082
11442
|
] })
|
|
11083
11443
|
},
|
|
11084
11444
|
file.id
|
|
@@ -11087,10 +11447,10 @@ function FileBrowser({
|
|
|
11087
11447
|
}
|
|
11088
11448
|
) })
|
|
11089
11449
|
] }),
|
|
11090
|
-
/* @__PURE__ */ (0,
|
|
11091
|
-
/* @__PURE__ */ (0,
|
|
11092
|
-
/* @__PURE__ */ (0,
|
|
11093
|
-
] }), children: /* @__PURE__ */ (0,
|
|
11450
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Modal, { open: createOpen, title: "New folder", onClose: () => setCreateOpen(false), footer: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
|
|
11451
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "secondary", onClick: () => setCreateOpen(false), children: "Cancel" }),
|
|
11452
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { onClick: submitCreate, disabled: !createName.trim(), children: "Create" })
|
|
11453
|
+
] }), children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
11094
11454
|
"input",
|
|
11095
11455
|
{
|
|
11096
11456
|
className: "fd-fb-input",
|
|
@@ -11104,10 +11464,10 @@ function FileBrowser({
|
|
|
11104
11464
|
autoFocus: true
|
|
11105
11465
|
}
|
|
11106
11466
|
) }),
|
|
11107
|
-
/* @__PURE__ */ (0,
|
|
11108
|
-
/* @__PURE__ */ (0,
|
|
11109
|
-
/* @__PURE__ */ (0,
|
|
11110
|
-
] }), children: /* @__PURE__ */ (0,
|
|
11467
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Modal, { open: renameTarget != null, title: "Rename folder", onClose: () => setRenameTarget(null), footer: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
|
|
11468
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "secondary", onClick: () => setRenameTarget(null), children: "Cancel" }),
|
|
11469
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { onClick: submitRename, disabled: !renameValue.trim(), children: "Save" })
|
|
11470
|
+
] }), children: /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
11111
11471
|
"input",
|
|
11112
11472
|
{
|
|
11113
11473
|
className: "fd-fb-input",
|
|
@@ -11121,22 +11481,22 @@ function FileBrowser({
|
|
|
11121
11481
|
autoFocus: true
|
|
11122
11482
|
}
|
|
11123
11483
|
) }),
|
|
11124
|
-
/* @__PURE__ */ (0,
|
|
11484
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
11125
11485
|
Modal,
|
|
11126
11486
|
{
|
|
11127
11487
|
open: deleteTarget != null,
|
|
11128
11488
|
title: deleteTarget ? "Delete \u201C" + deleteTarget.name + "\u201D?" : "Delete folder?",
|
|
11129
11489
|
description: "This can't be undone. The folder must be empty.",
|
|
11130
11490
|
onClose: () => setDeleteTarget(null),
|
|
11131
|
-
footer: /* @__PURE__ */ (0,
|
|
11132
|
-
/* @__PURE__ */ (0,
|
|
11133
|
-
/* @__PURE__ */ (0,
|
|
11491
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(import_jsx_runtime81.Fragment, { children: [
|
|
11492
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "secondary", onClick: () => setDeleteTarget(null), children: "Cancel" }),
|
|
11493
|
+
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Button, { variant: "danger", onClick: confirmDelete, children: "Delete" })
|
|
11134
11494
|
] })
|
|
11135
11495
|
}
|
|
11136
11496
|
)
|
|
11137
11497
|
] });
|
|
11138
11498
|
if (!canUpload) return content;
|
|
11139
|
-
return /* @__PURE__ */ (0,
|
|
11499
|
+
return /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Dropzone, { className: "fd-fb-dropzone", onFiles: (picked) => onUpload && onUpload(picked, currentFolderId), label: "Drop files to upload", children: content });
|
|
11140
11500
|
}
|
|
11141
11501
|
function BreadcrumbCrumb({
|
|
11142
11502
|
label,
|
|
@@ -11145,8 +11505,8 @@ function BreadcrumbCrumb({
|
|
|
11145
11505
|
isDropTarget,
|
|
11146
11506
|
onDropPayload
|
|
11147
11507
|
}) {
|
|
11148
|
-
const [over, setOver] =
|
|
11149
|
-
const dragCounter =
|
|
11508
|
+
const [over, setOver] = React52.useState(false);
|
|
11509
|
+
const dragCounter = React52.useRef(0);
|
|
11150
11510
|
const dropHandlers = isDropTarget ? {
|
|
11151
11511
|
onDragEnter: (e) => {
|
|
11152
11512
|
if (!hasInternalPayload(e)) return;
|
|
@@ -11171,11 +11531,11 @@ function BreadcrumbCrumb({
|
|
|
11171
11531
|
if (payload && onDropPayload) onDropPayload(payload);
|
|
11172
11532
|
}
|
|
11173
11533
|
} : {};
|
|
11174
|
-
return /* @__PURE__ */ (0,
|
|
11534
|
+
return /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: ["fd-fb-crumb", over ? "is-drop-target" : ""].filter(Boolean).join(" "), ...dropHandlers, children: isCurrent ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: "fd-fb-crumb-current", "aria-current": "page", children: label }) : /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("button", { type: "button", className: "fd-fb-crumb-link", onClick, children: label }) });
|
|
11175
11535
|
}
|
|
11176
11536
|
|
|
11177
11537
|
// src/kits/query.ts
|
|
11178
|
-
var
|
|
11538
|
+
var React53 = __toESM(require("react"), 1);
|
|
11179
11539
|
function eqFilter(get2) {
|
|
11180
11540
|
return (row, value) => Array.isArray(value) ? value.includes(get2(row)) : get2(row) === value;
|
|
11181
11541
|
}
|
|
@@ -11207,22 +11567,22 @@ function compare(a, b, dir) {
|
|
|
11207
11567
|
var API = null;
|
|
11208
11568
|
var PREFS = null;
|
|
11209
11569
|
function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
|
|
11210
|
-
const [query, setQuery] =
|
|
11570
|
+
const [query, setQuery] = React53.useState(() => {
|
|
11211
11571
|
const store = PREFS || window.PlannerPrefs;
|
|
11212
11572
|
const saved = prefsKey && store ? store.getTable(prefsKey) : {};
|
|
11213
11573
|
return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
|
|
11214
11574
|
});
|
|
11215
|
-
const savePref =
|
|
11575
|
+
const savePref = React53.useCallback((patch2) => {
|
|
11216
11576
|
const store = PREFS || window.PlannerPrefs;
|
|
11217
11577
|
if (prefsKey && store) store.setTable(prefsKey, patch2);
|
|
11218
11578
|
}, [prefsKey]);
|
|
11219
|
-
const [res, setRes] =
|
|
11220
|
-
const [loading, setLoading] =
|
|
11221
|
-
const seq2 =
|
|
11579
|
+
const [res, setRes] = React53.useState(null);
|
|
11580
|
+
const [loading, setLoading] = React53.useState(true);
|
|
11581
|
+
const seq2 = React53.useRef(0);
|
|
11222
11582
|
const depKey = (deps || []).join("|");
|
|
11223
11583
|
const paramKey = JSON.stringify(params || {});
|
|
11224
11584
|
const queryKey = JSON.stringify(query);
|
|
11225
|
-
|
|
11585
|
+
React53.useEffect(() => {
|
|
11226
11586
|
const id = ++seq2.current;
|
|
11227
11587
|
setLoading(true);
|
|
11228
11588
|
const t = setTimeout(() => {
|
|
@@ -11524,6 +11884,8 @@ function createVersionStore(initialState, options) {
|
|
|
11524
11884
|
AccountMenu,
|
|
11525
11885
|
AgentChatPanel,
|
|
11526
11886
|
ApiSpecBrowser,
|
|
11887
|
+
AppSwitcher,
|
|
11888
|
+
AppearanceMenu,
|
|
11527
11889
|
Avatar,
|
|
11528
11890
|
AvatarStack,
|
|
11529
11891
|
Badge,
|
|
@@ -11554,8 +11916,11 @@ function createVersionStore(initialState, options) {
|
|
|
11554
11916
|
CsiBadge,
|
|
11555
11917
|
CsiHero,
|
|
11556
11918
|
CsiMeter,
|
|
11919
|
+
DEFAULT_PREFS,
|
|
11920
|
+
DEFAULT_STORAGE_KEY,
|
|
11557
11921
|
DataTable,
|
|
11558
11922
|
DatePicker,
|
|
11923
|
+
DemoSessionProvider,
|
|
11559
11924
|
DiffBlock,
|
|
11560
11925
|
Drawer,
|
|
11561
11926
|
Dropzone,
|
|
@@ -11625,6 +11990,7 @@ function createVersionStore(initialState, options) {
|
|
|
11625
11990
|
SegmentedMeter,
|
|
11626
11991
|
Select,
|
|
11627
11992
|
SessionKit,
|
|
11993
|
+
SessionProvider,
|
|
11628
11994
|
SidebarNav,
|
|
11629
11995
|
Skeleton,
|
|
11630
11996
|
SkeletonText,
|
|
@@ -11653,9 +12019,11 @@ function createVersionStore(initialState, options) {
|
|
|
11653
12019
|
VirtualList,
|
|
11654
12020
|
acceptMatches,
|
|
11655
12021
|
anyOfFilter,
|
|
12022
|
+
applyPrefs,
|
|
11656
12023
|
channelWeightOf,
|
|
11657
12024
|
computeNeedMore,
|
|
11658
12025
|
computeVirtualWindow,
|
|
12026
|
+
createLocalStorageAppearanceStore,
|
|
11659
12027
|
createVersionStore,
|
|
11660
12028
|
eqFilter,
|
|
11661
12029
|
extensionOf,
|
|
@@ -11676,6 +12044,7 @@ function createVersionStore(initialState, options) {
|
|
|
11676
12044
|
isSystemMessage,
|
|
11677
12045
|
jobBucket,
|
|
11678
12046
|
languageLabel,
|
|
12047
|
+
loadPrefs,
|
|
11679
12048
|
markdownToText,
|
|
11680
12049
|
meterFormats,
|
|
11681
12050
|
modifierLabel,
|
|
@@ -11685,14 +12054,18 @@ function createVersionStore(initialState, options) {
|
|
|
11685
12054
|
preferenceScore,
|
|
11686
12055
|
preferenceSegments,
|
|
11687
12056
|
rangeFilter,
|
|
12057
|
+
resolveTheme,
|
|
11688
12058
|
revokeAttachment,
|
|
12059
|
+
savePrefs,
|
|
11689
12060
|
toAttachment,
|
|
11690
12061
|
tokenize,
|
|
12062
|
+
useAppearancePrefs,
|
|
11691
12063
|
useChatEngine,
|
|
11692
12064
|
useFeatureStatus,
|
|
11693
12065
|
usePopoverPosition,
|
|
11694
12066
|
useRuntimeMode,
|
|
11695
12067
|
useServerTable,
|
|
12068
|
+
useSessionContext,
|
|
11696
12069
|
useStagedFiles,
|
|
11697
12070
|
visibleMessages
|
|
11698
12071
|
});
|