@brokr/sdk 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/feature.js +22 -2
- package/dist/feature.mjs +22 -2
- package/dist/index.js +22 -2
- package/dist/index.mjs +22 -2
- package/dist/next.js +22 -2
- package/dist/next.mjs +22 -2
- package/dist/react-notifications.js +63 -50
- package/dist/react-notifications.mjs +63 -50
- package/dist/react-styles.js +386 -169
- package/dist/react-styles.mjs +386 -169
- package/dist/react-theme.js +6 -4
- package/dist/react-theme.mjs +6 -4
- package/dist/react.js +644 -317
- package/dist/react.mjs +682 -355
- package/dist/runtime.js +22 -2
- package/dist/runtime.mjs +22 -2
- package/dist/src/chat/config.d.ts +2 -0
- package/dist/src/chat/config.d.ts.map +1 -1
- package/dist/src/gateway.d.ts.map +1 -1
- package/dist/src/models.d.ts +2 -0
- package/dist/src/models.d.ts.map +1 -1
- package/dist/src/react/account/AccountPanel.d.ts.map +1 -1
- package/dist/src/react/account/UserButton.d.ts.map +1 -1
- package/dist/src/react/chat/AIChat.d.ts.map +1 -1
- package/dist/src/react/chat/ChatContext.d.ts +3 -6
- package/dist/src/react/chat/ChatContext.d.ts.map +1 -1
- package/dist/src/react/chat/ChatInput.d.ts.map +1 -1
- package/dist/src/react/chat/MarkdownRenderer.d.ts.map +1 -1
- package/dist/src/react/chat/MessagePane.d.ts.map +1 -1
- package/dist/src/react/chat/ModelSelector.d.ts +1 -1
- package/dist/src/react/chat/ModelSelector.d.ts.map +1 -1
- package/dist/src/react/chat/ThreadSidebar.d.ts.map +1 -1
- package/dist/src/react/chat/memory.d.ts +12 -0
- package/dist/src/react/chat/memory.d.ts.map +1 -0
- package/dist/src/react/chat/types.d.ts +6 -0
- package/dist/src/react/chat/types.d.ts.map +1 -1
- package/dist/src/react/chat/useChat.d.ts +8 -6
- package/dist/src/react/chat/useChat.d.ts.map +1 -1
- package/dist/src/react/composites/FabAI.d.ts +6 -1
- package/dist/src/react/composites/FabAI.d.ts.map +1 -1
- package/dist/src/react/composites/fab-context.d.ts +26 -0
- package/dist/src/react/composites/fab-context.d.ts.map +1 -0
- package/dist/src/react/css/account.d.ts +1 -1
- package/dist/src/react/css/account.d.ts.map +1 -1
- package/dist/src/react/css/auth.d.ts +1 -1
- package/dist/src/react/css/auth.d.ts.map +1 -1
- package/dist/src/react/css/chat-extras.d.ts +1 -1
- package/dist/src/react/css/chat-extras.d.ts.map +1 -1
- package/dist/src/react/css/chat.d.ts +1 -1
- package/dist/src/react/css/chat.d.ts.map +1 -1
- package/dist/src/react/css/composites.d.ts +1 -1
- package/dist/src/react/css/composites.d.ts.map +1 -1
- package/dist/src/react/css/gates.d.ts +1 -1
- package/dist/src/react/css/gates.d.ts.map +1 -1
- package/dist/src/react/css/markdown.d.ts +1 -1
- package/dist/src/react/css/markdown.d.ts.map +1 -1
- package/dist/src/react/css/notifications.d.ts +1 -1
- package/dist/src/react/css/notifications.d.ts.map +1 -1
- package/dist/src/react/css/primitives.d.ts +1 -1
- package/dist/src/react/css/primitives.d.ts.map +1 -1
- package/dist/src/react/css/reset.d.ts +1 -1
- package/dist/src/react/css/reset.d.ts.map +1 -1
- package/dist/src/react/css/responsive.d.ts +1 -1
- package/dist/src/react/css/responsive.d.ts.map +1 -1
- package/dist/src/react/css/skeleton.d.ts +1 -1
- package/dist/src/react/css/skeleton.d.ts.map +1 -1
- package/dist/src/react/css/tokens.d.ts +1 -1
- package/dist/src/react/css/tokens.d.ts.map +1 -1
- package/dist/src/react/notifications/NotificationBell.d.ts.map +1 -1
- package/dist/src/react/notifications/NotificationList.d.ts.map +1 -1
- package/dist/src/react/notifications/Toast.d.ts.map +1 -1
- package/dist/src/react/payments/Plans.d.ts.map +1 -1
- package/dist/src/react/theme.d.ts.map +1 -1
- package/dist/src/react/types.d.ts +4 -4
- package/dist/src/react/types.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -4237,10 +4237,12 @@ function getBrokrRootStyle(theme) {
|
|
|
4237
4237
|
...theme.colors.error ? { ["--brokr-error"]: theme.colors.error } : {},
|
|
4238
4238
|
...theme.colors.success ? { ["--brokr-success"]: theme.colors.success } : {},
|
|
4239
4239
|
...theme.colors.warning ? { ["--brokr-warning"]: theme.colors.warning } : {},
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4240
|
+
// Toast vars always emitted with smart defaults — toasts stay light in dark mode.
|
|
4241
|
+
// Users can override via theme.colors.toastBg etc.
|
|
4242
|
+
["--brokr-toast-bg"]: theme.colors.toastBg ?? "#ffffff",
|
|
4243
|
+
["--brokr-toast-text"]: theme.colors.toastText ?? "#0a0a0a",
|
|
4244
|
+
["--brokr-toast-text-secondary"]: theme.colors.toastTextSecondary ?? "#52525b",
|
|
4245
|
+
["--brokr-toast-border"]: theme.colors.toastBorder ?? "#e4e4e7",
|
|
4244
4246
|
["--brokr-radius-card"]: `${theme.radii.card}px`,
|
|
4245
4247
|
["--brokr-radius-input"]: `${theme.radii.input}px`,
|
|
4246
4248
|
["--brokr-radius-button"]: `${theme.radii.button}px`
|
|
@@ -4252,6 +4254,7 @@ function getBrokrRootStyle(theme) {
|
|
|
4252
4254
|
var import_react3 = __toESM(require("react"));
|
|
4253
4255
|
|
|
4254
4256
|
// src/models.ts
|
|
4257
|
+
var STACK_DEFAULT = "__stack_default__";
|
|
4255
4258
|
var providers = [
|
|
4256
4259
|
{ id: "deepseek", label: "Deepseek", model: "deepseek-chat", color: "#0EA5E9", free: true, logo: "https://assets.brokr.sh/sdk/deepseek_logo.png" },
|
|
4257
4260
|
{ id: "openai", label: "ChatGPT", model: "gpt-5.4-mini", color: "#10B981", free: false, logo: "https://assets.brokr.sh/sdk/gpt_logo.png" },
|
|
@@ -4355,6 +4358,37 @@ function resolveNotificationType(registry, type, data) {
|
|
|
4355
4358
|
|
|
4356
4359
|
// src/react/notifications/Toast.tsx
|
|
4357
4360
|
var import_react2 = __toESM(require("react"));
|
|
4361
|
+
function ToastItem({
|
|
4362
|
+
notification,
|
|
4363
|
+
exiting,
|
|
4364
|
+
registry,
|
|
4365
|
+
onDismiss
|
|
4366
|
+
}) {
|
|
4367
|
+
const handleDismiss = (0, import_react2.useCallback)(() => onDismiss(notification.id), [notification.id, onDismiss]);
|
|
4368
|
+
const notifData = notification.data ?? {};
|
|
4369
|
+
const notifType = notifData.type ?? "default";
|
|
4370
|
+
const resolved = resolveNotificationType(registry, notifType, notifData);
|
|
4371
|
+
return /* @__PURE__ */ import_react2.default.createElement(
|
|
4372
|
+
"div",
|
|
4373
|
+
{
|
|
4374
|
+
className: `brokr-toast brokr-toast--${notification.variant}${exiting ? " brokr-toast--exit" : ""}`,
|
|
4375
|
+
role: "alert",
|
|
4376
|
+
"aria-live": "polite"
|
|
4377
|
+
},
|
|
4378
|
+
resolved.image ? /* @__PURE__ */ import_react2.default.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-toast-provider-logo" }) : /* @__PURE__ */ import_react2.default.createElement("span", { className: `brokr-toast-icon brokr-toast-icon--${notification.variant}` }),
|
|
4379
|
+
/* @__PURE__ */ import_react2.default.createElement("div", { className: "brokr-toast-content" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "brokr-toast-title" }, notification.title), /* @__PURE__ */ import_react2.default.createElement("span", { className: "brokr-toast-message" }, notification.message)),
|
|
4380
|
+
/* @__PURE__ */ import_react2.default.createElement(
|
|
4381
|
+
"button",
|
|
4382
|
+
{
|
|
4383
|
+
type: "button",
|
|
4384
|
+
className: "brokr-toast-dismiss",
|
|
4385
|
+
onClick: handleDismiss,
|
|
4386
|
+
"aria-label": "Dismiss notification"
|
|
4387
|
+
},
|
|
4388
|
+
/* @__PURE__ */ import_react2.default.createElement("svg", { "aria-hidden": "true", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ import_react2.default.createElement("path", { d: "M18 6 6 18M6 6l12 12" }))
|
|
4389
|
+
)
|
|
4390
|
+
);
|
|
4391
|
+
}
|
|
4358
4392
|
var DEFAULT_DURATION = 4e3;
|
|
4359
4393
|
var DEFAULT_MAX_VISIBLE = 3;
|
|
4360
4394
|
var EXIT_ANIMATION_MS = 300;
|
|
@@ -4365,6 +4399,26 @@ function ToastLayer({ toasts, config, registry, onDismiss }) {
|
|
|
4365
4399
|
const duration = config.toast?.duration ?? DEFAULT_DURATION;
|
|
4366
4400
|
const maxVisible = config.toast?.maxVisible ?? DEFAULT_MAX_VISIBLE;
|
|
4367
4401
|
const position = config.toast?.position ?? "top-right";
|
|
4402
|
+
const onDismissRef = (0, import_react2.useRef)(onDismiss);
|
|
4403
|
+
onDismissRef.current = onDismiss;
|
|
4404
|
+
const exitingRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
4405
|
+
const dismissToast = (0, import_react2.useCallback)((id) => {
|
|
4406
|
+
if (exitingRef.current.has(id)) return;
|
|
4407
|
+
exitingRef.current.add(id);
|
|
4408
|
+
setEntries(
|
|
4409
|
+
(prev) => prev.map((e) => e.notification.id === id ? { ...e, exiting: true } : e)
|
|
4410
|
+
);
|
|
4411
|
+
setTimeout(() => {
|
|
4412
|
+
setEntries((prev) => prev.filter((e) => e.notification.id !== id));
|
|
4413
|
+
onDismissRef.current(id);
|
|
4414
|
+
exitingRef.current.delete(id);
|
|
4415
|
+
}, EXIT_ANIMATION_MS);
|
|
4416
|
+
const timer = timersRef.current.get(id);
|
|
4417
|
+
if (timer) {
|
|
4418
|
+
clearTimeout(timer);
|
|
4419
|
+
timersRef.current.delete(id);
|
|
4420
|
+
}
|
|
4421
|
+
}, []);
|
|
4368
4422
|
(0, import_react2.useEffect)(() => {
|
|
4369
4423
|
for (const toast of toasts) {
|
|
4370
4424
|
if (processedRef.current.has(toast.id)) continue;
|
|
@@ -4383,21 +4437,7 @@ function ToastLayer({ toasts, config, registry, onDismiss }) {
|
|
|
4383
4437
|
timersRef.current.set(toast.id, timer);
|
|
4384
4438
|
}
|
|
4385
4439
|
}
|
|
4386
|
-
}, [toasts, duration, maxVisible]);
|
|
4387
|
-
const dismissToast = (0, import_react2.useCallback)((id) => {
|
|
4388
|
-
setEntries(
|
|
4389
|
-
(prev) => prev.map((e) => e.notification.id === id ? { ...e, exiting: true } : e)
|
|
4390
|
-
);
|
|
4391
|
-
setTimeout(() => {
|
|
4392
|
-
setEntries((prev) => prev.filter((e) => e.notification.id !== id));
|
|
4393
|
-
onDismiss(id);
|
|
4394
|
-
}, EXIT_ANIMATION_MS);
|
|
4395
|
-
const timer = timersRef.current.get(id);
|
|
4396
|
-
if (timer) {
|
|
4397
|
-
clearTimeout(timer);
|
|
4398
|
-
timersRef.current.delete(id);
|
|
4399
|
-
}
|
|
4400
|
-
}, [onDismiss]);
|
|
4440
|
+
}, [toasts, duration, maxVisible, dismissToast]);
|
|
4401
4441
|
(0, import_react2.useEffect)(() => {
|
|
4402
4442
|
return () => {
|
|
4403
4443
|
for (const timer of timersRef.current.values()) {
|
|
@@ -4417,39 +4457,16 @@ function ToastLayer({ toasts, config, registry, onDismiss }) {
|
|
|
4417
4457
|
return map[position] ?? "brokr-toast-layer--bottom-right";
|
|
4418
4458
|
}, [position]);
|
|
4419
4459
|
if (entries.length === 0) return null;
|
|
4420
|
-
return /* @__PURE__ */ import_react2.default.createElement("div", { className: `brokr-toast-layer ${positionClass}` }, entries.map(({ notification, exiting }) =>
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
"aria-live": "polite"
|
|
4431
|
-
},
|
|
4432
|
-
resolved.image ? /* @__PURE__ */ import_react2.default.createElement(
|
|
4433
|
-
"img",
|
|
4434
|
-
{
|
|
4435
|
-
src: resolved.image.url,
|
|
4436
|
-
alt: resolved.image.alt,
|
|
4437
|
-
className: "brokr-toast-provider-logo"
|
|
4438
|
-
}
|
|
4439
|
-
) : /* @__PURE__ */ import_react2.default.createElement("span", { className: `brokr-toast-icon brokr-toast-icon--${notification.variant}` }),
|
|
4440
|
-
/* @__PURE__ */ import_react2.default.createElement("div", { className: "brokr-toast-content" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "brokr-toast-title" }, notification.title), /* @__PURE__ */ import_react2.default.createElement("span", { className: "brokr-toast-message" }, notification.message)),
|
|
4441
|
-
/* @__PURE__ */ import_react2.default.createElement(
|
|
4442
|
-
"button",
|
|
4443
|
-
{
|
|
4444
|
-
type: "button",
|
|
4445
|
-
className: "brokr-toast-dismiss",
|
|
4446
|
-
onClick: () => dismissToast(notification.id),
|
|
4447
|
-
"aria-label": "Dismiss notification"
|
|
4448
|
-
},
|
|
4449
|
-
/* @__PURE__ */ import_react2.default.createElement("svg", { "aria-hidden": "true", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ import_react2.default.createElement("path", { d: "M18 6 6 18M6 6l12 12" }))
|
|
4450
|
-
)
|
|
4451
|
-
);
|
|
4452
|
-
}));
|
|
4460
|
+
return /* @__PURE__ */ import_react2.default.createElement("div", { className: `brokr-toast-layer ${positionClass}` }, entries.map(({ notification, exiting }) => /* @__PURE__ */ import_react2.default.createElement(
|
|
4461
|
+
ToastItem,
|
|
4462
|
+
{
|
|
4463
|
+
key: notification.id,
|
|
4464
|
+
notification,
|
|
4465
|
+
exiting,
|
|
4466
|
+
registry,
|
|
4467
|
+
onDismiss: dismissToast
|
|
4468
|
+
}
|
|
4469
|
+
)));
|
|
4453
4470
|
}
|
|
4454
4471
|
|
|
4455
4472
|
// src/react/notifications/provider.tsx
|
|
@@ -5565,6 +5582,26 @@ function filterDefined(defaults, overrides) {
|
|
|
5565
5582
|
}
|
|
5566
5583
|
|
|
5567
5584
|
// src/react/account/AccountPanel.tsx
|
|
5585
|
+
function AccountTabButton({
|
|
5586
|
+
tab,
|
|
5587
|
+
active,
|
|
5588
|
+
icon,
|
|
5589
|
+
label,
|
|
5590
|
+
onSelect
|
|
5591
|
+
}) {
|
|
5592
|
+
const handleClick = (0, import_react11.useCallback)(() => onSelect(tab), [tab, onSelect]);
|
|
5593
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
5594
|
+
"button",
|
|
5595
|
+
{
|
|
5596
|
+
className: "brokr-account-tab",
|
|
5597
|
+
"data-active": active,
|
|
5598
|
+
onClick: handleClick,
|
|
5599
|
+
type: "button"
|
|
5600
|
+
},
|
|
5601
|
+
/* @__PURE__ */ import_react11.default.createElement("span", { className: "brokr-account-tab-icon" }, icon),
|
|
5602
|
+
/* @__PURE__ */ import_react11.default.createElement("span", null, label)
|
|
5603
|
+
);
|
|
5604
|
+
}
|
|
5568
5605
|
var TAB_META = {
|
|
5569
5606
|
general: {
|
|
5570
5607
|
label: "General"
|
|
@@ -5696,16 +5733,15 @@ function AccountPanel(inlineProps) {
|
|
|
5696
5733
|
}
|
|
5697
5734
|
}, []);
|
|
5698
5735
|
return /* @__PURE__ */ import_react11.default.createElement("section", { className: "brokr-account-panel", "data-density": density }, /* @__PURE__ */ import_react11.default.createElement("aside", { className: "brokr-account-sidebar" }, groupedTabs.map((group) => /* @__PURE__ */ import_react11.default.createElement("div", { className: "brokr-account-sidebar-group", key: group.label }, /* @__PURE__ */ import_react11.default.createElement("span", { className: "brokr-account-sidebar-kicker" }, group.label), /* @__PURE__ */ import_react11.default.createElement("nav", { className: "brokr-account-tab-list", "aria-label": `${group.label} settings` }, group.tabs.map((tab) => /* @__PURE__ */ import_react11.default.createElement(
|
|
5699
|
-
|
|
5736
|
+
AccountTabButton,
|
|
5700
5737
|
{
|
|
5701
|
-
className: "brokr-account-tab",
|
|
5702
|
-
"data-active": activeTab === tab,
|
|
5703
5738
|
key: tab,
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5739
|
+
tab,
|
|
5740
|
+
active: activeTab === tab,
|
|
5741
|
+
icon: getTabIcon(tab),
|
|
5742
|
+
label: TAB_META[tab].label,
|
|
5743
|
+
onSelect: setActiveTab
|
|
5744
|
+
}
|
|
5709
5745
|
)))))), /* @__PURE__ */ import_react11.default.createElement("div", { className: "brokr-account-surface" }, /* @__PURE__ */ import_react11.default.createElement("header", { className: "brokr-account-surface-header" }, /* @__PURE__ */ import_react11.default.createElement("h2", { className: "brokr-title" }, activeTabMeta.label)), activeTab === "general" ? /* @__PURE__ */ import_react11.default.createElement("form", { className: "brokr-account-form", onSubmit: handleSaveProfile }, /* @__PURE__ */ import_react11.default.createElement("section", { className: "brokr-account-card" }, /* @__PURE__ */ import_react11.default.createElement("div", { className: "brokr-account-photo-row" }, /* @__PURE__ */ import_react11.default.createElement(ProfilePhotoButton, { size: 88 }), /* @__PURE__ */ import_react11.default.createElement("div", { className: "brokr-account-photo-copy" }, /* @__PURE__ */ import_react11.default.createElement("strong", null, "Profile photo"), /* @__PURE__ */ import_react11.default.createElement("span", { className: "brokr-copy" }, "PNG, JPEG, and GIF under 10MB"), /* @__PURE__ */ import_react11.default.createElement("span", { className: "brokr-account-photo-hint" }, /* @__PURE__ */ import_react11.default.createElement(UploadIcon, { size: 14 }), "Upload new picture")))), /* @__PURE__ */ import_react11.default.createElement("section", { className: "brokr-account-card brokr-account-field-list" }, /* @__PURE__ */ import_react11.default.createElement("div", { className: "brokr-account-field-row" }, /* @__PURE__ */ import_react11.default.createElement("div", { className: "brokr-account-field-head" }, /* @__PURE__ */ import_react11.default.createElement("label", { className: "brokr-label", htmlFor: "brokr-account-name" }, "Name")), /* @__PURE__ */ import_react11.default.createElement("div", { className: "brokr-account-field-body" }, /* @__PURE__ */ import_react11.default.createElement(
|
|
5710
5746
|
"input",
|
|
5711
5747
|
{
|
|
@@ -5807,6 +5843,9 @@ function UserButton({
|
|
|
5807
5843
|
setOpen(false);
|
|
5808
5844
|
await signOut();
|
|
5809
5845
|
}, [signOut]);
|
|
5846
|
+
const stopPropagation = (0, import_react13.useCallback)((event) => {
|
|
5847
|
+
event.stopPropagation();
|
|
5848
|
+
}, []);
|
|
5810
5849
|
(0, import_react13.useEffect)(() => {
|
|
5811
5850
|
if (!open) return void 0;
|
|
5812
5851
|
const handlePointerDown = (event) => {
|
|
@@ -5840,13 +5879,13 @@ function UserButton({
|
|
|
5840
5879
|
"data-align": align,
|
|
5841
5880
|
role: "dialog"
|
|
5842
5881
|
},
|
|
5843
|
-
/* @__PURE__ */ import_react13.default.createElement("div", { className: "brokr-account-menu" }, /* @__PURE__ */ import_react13.default.createElement("div", { className: "brokr-account-menu-header" }, /* @__PURE__ */ import_react13.default.createElement(Avatar, { email: summary.email, name: summary.name, src: user.image, size: 44 }), /* @__PURE__ */ import_react13.default.createElement("div", { className: "brokr-account-menu-copy" }, /* @__PURE__ */ import_react13.default.createElement("strong", null, summary.name), /* @__PURE__ */ import_react13.default.createElement("span", null, summary.email))), /* @__PURE__ */ import_react13.default.createElement("div", { className: "brokr-account-menu-actions" }, /* @__PURE__ */ import_react13.default.createElement("button", { className: "brokr-account-menu-action", onClick: openSettings, type: "button" }, /* @__PURE__ */ import_react13.default.createElement("span", { className: "brokr-account-menu-action-icon" }, /* @__PURE__ */ import_react13.default.createElement(SettingsIcon, { size: 15 })), /* @__PURE__ */ import_react13.default.createElement("span", null, "Settings")), /* @__PURE__ */ import_react13.default.createElement("button", { className: "brokr-account-menu-action", onClick:
|
|
5882
|
+
/* @__PURE__ */ import_react13.default.createElement("div", { className: "brokr-account-menu" }, /* @__PURE__ */ import_react13.default.createElement("div", { className: "brokr-account-menu-header" }, /* @__PURE__ */ import_react13.default.createElement(Avatar, { email: summary.email, name: summary.name, src: user.image, size: 44 }), /* @__PURE__ */ import_react13.default.createElement("div", { className: "brokr-account-menu-copy" }, /* @__PURE__ */ import_react13.default.createElement("strong", null, summary.name), /* @__PURE__ */ import_react13.default.createElement("span", null, summary.email))), /* @__PURE__ */ import_react13.default.createElement("div", { className: "brokr-account-menu-actions" }, /* @__PURE__ */ import_react13.default.createElement("button", { className: "brokr-account-menu-action", onClick: openSettings, type: "button" }, /* @__PURE__ */ import_react13.default.createElement("span", { className: "brokr-account-menu-action-icon" }, /* @__PURE__ */ import_react13.default.createElement(SettingsIcon, { size: 15 })), /* @__PURE__ */ import_react13.default.createElement("span", null, "Settings")), /* @__PURE__ */ import_react13.default.createElement("button", { className: "brokr-account-menu-action", onClick: handleSignOut, type: "button" }, /* @__PURE__ */ import_react13.default.createElement("span", { className: "brokr-account-menu-action-icon" }, /* @__PURE__ */ import_react13.default.createElement(LogoutIcon, { size: 15 })), /* @__PURE__ */ import_react13.default.createElement("span", null, "Sign out"))))
|
|
5844
5883
|
) : null, settingsOpen ? /* @__PURE__ */ import_react13.default.createElement("div", { className: "brokr-modal-backdrop", role: "presentation", onClick: closeSettings }, /* @__PURE__ */ import_react13.default.createElement(
|
|
5845
5884
|
"div",
|
|
5846
5885
|
{
|
|
5847
5886
|
"aria-modal": "true",
|
|
5848
5887
|
className: "brokr-panel brokr-modal-dialog brokr-account-settings-dialog",
|
|
5849
|
-
onClick:
|
|
5888
|
+
onClick: stopPropagation,
|
|
5850
5889
|
role: "dialog"
|
|
5851
5890
|
},
|
|
5852
5891
|
/* @__PURE__ */ import_react13.default.createElement("div", { className: "brokr-modal-toolbar" }, /* @__PURE__ */ import_react13.default.createElement("button", { className: "brokr-button-ghost", onClick: closeSettings, type: "button" }, /* @__PURE__ */ import_react13.default.createElement(CloseIcon, { size: 16 }))),
|
|
@@ -6042,6 +6081,28 @@ function AuthWall({ children }) {
|
|
|
6042
6081
|
|
|
6043
6082
|
// src/react/payments/Plans.tsx
|
|
6044
6083
|
var import_react22 = __toESM(require("react"));
|
|
6084
|
+
function PlanCard({
|
|
6085
|
+
plan,
|
|
6086
|
+
highlight,
|
|
6087
|
+
isPending,
|
|
6088
|
+
onSelect
|
|
6089
|
+
}) {
|
|
6090
|
+
const isHighlighted = highlight === plan.slug;
|
|
6091
|
+
const features = Object.entries(plan.features);
|
|
6092
|
+
const handleClick = (0, import_react22.useCallback)(() => {
|
|
6093
|
+
void onSelect(plan.slug);
|
|
6094
|
+
}, [plan.slug, onSelect]);
|
|
6095
|
+
return /* @__PURE__ */ import_react22.default.createElement("article", { className: "brokr-card brokr-plan-card", "data-highlight": isHighlighted, key: plan.slug }, /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-3)" } }, /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ import_react22.default.createElement("strong", null, plan.name), plan.isCurrent ? /* @__PURE__ */ import_react22.default.createElement("span", { className: "brokr-badge" }, "Current") : null, isHighlighted && !plan.isCurrent ? /* @__PURE__ */ import_react22.default.createElement("span", { className: "brokr-badge" }, "Recommended") : null), /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-plan-price" }, /* @__PURE__ */ import_react22.default.createElement("span", { className: "brokr-plan-value" }, plan.price === 0 ? "$0" : `$${(plan.price / 100).toFixed(0)}`), /* @__PURE__ */ import_react22.default.createElement("span", { className: "brokr-copy" }, "/", plan.interval)), plan.trialDays ? /* @__PURE__ */ import_react22.default.createElement("span", { className: "brokr-copy" }, plan.trialDays, " day trial included.") : null), /* @__PURE__ */ import_react22.default.createElement("ul", { className: "brokr-feature-list" }, features.map(([key, value]) => /* @__PURE__ */ import_react22.default.createElement("li", { className: "brokr-feature-item", key }, /* @__PURE__ */ import_react22.default.createElement(CheckIcon, { size: 14 }), /* @__PURE__ */ import_react22.default.createElement("span", null, featureLabel(key), " \xB7 ", featureValueLabel(value))))), /* @__PURE__ */ import_react22.default.createElement(
|
|
6096
|
+
"button",
|
|
6097
|
+
{
|
|
6098
|
+
className: plan.isCurrent ? "brokr-button-secondary" : "brokr-button",
|
|
6099
|
+
disabled: plan.isCurrent || isPending,
|
|
6100
|
+
onClick: handleClick,
|
|
6101
|
+
type: "button"
|
|
6102
|
+
},
|
|
6103
|
+
plan.isCurrent ? "Current plan" : isPending ? "Redirecting" : "Choose plan"
|
|
6104
|
+
));
|
|
6105
|
+
}
|
|
6045
6106
|
function Plans({
|
|
6046
6107
|
columns,
|
|
6047
6108
|
highlight,
|
|
@@ -6072,34 +6133,16 @@ function Plans({
|
|
|
6072
6133
|
setPendingPlan(null);
|
|
6073
6134
|
}
|
|
6074
6135
|
}, [checkout, onSelect]);
|
|
6075
|
-
return /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-section" }, paymentsMode === "sandbox" ? /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-inline-message" }, "Sandbox mode \u2014 test cards only.") : null, error ? /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null, /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-grid-auto", style: layoutStyle }, plans.map((plan) =>
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
className: "brokr-card brokr-plan-card",
|
|
6086
|
-
"data-highlight": isHighlighted,
|
|
6087
|
-
key: plan.slug
|
|
6088
|
-
},
|
|
6089
|
-
/* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-section", style: { gap: "0.75rem" } }, /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ import_react22.default.createElement("strong", null, plan.name), plan.isCurrent ? /* @__PURE__ */ import_react22.default.createElement("span", { className: "brokr-badge" }, "Current") : null, isHighlighted && !plan.isCurrent ? /* @__PURE__ */ import_react22.default.createElement("span", { className: "brokr-badge" }, "Recommended") : null), /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-plan-price" }, /* @__PURE__ */ import_react22.default.createElement("span", { className: "brokr-plan-value" }, plan.price === 0 ? "$0" : `$${(plan.price / 100).toFixed(0)}`), /* @__PURE__ */ import_react22.default.createElement("span", { className: "brokr-copy" }, "/", plan.interval)), plan.trialDays ? /* @__PURE__ */ import_react22.default.createElement("span", { className: "brokr-copy" }, plan.trialDays, " day trial included.") : null),
|
|
6090
|
-
/* @__PURE__ */ import_react22.default.createElement(
|
|
6091
|
-
"button",
|
|
6092
|
-
{
|
|
6093
|
-
className: plan.isCurrent ? "brokr-button-secondary" : "brokr-button",
|
|
6094
|
-
disabled: plan.isCurrent || isPending,
|
|
6095
|
-
onClick: handleClick,
|
|
6096
|
-
type: "button"
|
|
6097
|
-
},
|
|
6098
|
-
plan.isCurrent ? "Current plan" : isPending ? "Redirecting" : "Choose plan"
|
|
6099
|
-
),
|
|
6100
|
-
/* @__PURE__ */ import_react22.default.createElement("ul", { className: "brokr-feature-list" }, features.map(([key, value]) => /* @__PURE__ */ import_react22.default.createElement("li", { className: "brokr-feature-item", key }, /* @__PURE__ */ import_react22.default.createElement(CheckIcon, { size: 14 }), /* @__PURE__ */ import_react22.default.createElement("span", null, featureLabel(key), " \xB7 ", featureValueLabel(value)))))
|
|
6101
|
-
);
|
|
6102
|
-
})));
|
|
6136
|
+
return /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-section" }, paymentsMode === "sandbox" ? /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-inline-message" }, "Sandbox mode \u2014 test cards only.") : null, error ? /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null, /* @__PURE__ */ import_react22.default.createElement("div", { className: "brokr-grid-auto", style: layoutStyle }, plans.map((plan) => /* @__PURE__ */ import_react22.default.createElement(
|
|
6137
|
+
PlanCard,
|
|
6138
|
+
{
|
|
6139
|
+
key: plan.slug,
|
|
6140
|
+
plan,
|
|
6141
|
+
highlight,
|
|
6142
|
+
isPending: pendingPlan === plan.slug,
|
|
6143
|
+
onSelect: handleSelect
|
|
6144
|
+
}
|
|
6145
|
+
))));
|
|
6103
6146
|
}
|
|
6104
6147
|
|
|
6105
6148
|
// src/react/payments/FeatureMeter.tsx
|
|
@@ -6237,7 +6280,7 @@ function AutoReloadToggle({ onChange }) {
|
|
|
6237
6280
|
setIsPending(false);
|
|
6238
6281
|
}
|
|
6239
6282
|
}, [enabled, onChange]);
|
|
6240
|
-
return /* @__PURE__ */ import_react27.default.createElement("div", { className: "brokr-card brokr-gate-card" }, /* @__PURE__ */ import_react27.default.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ import_react27.default.createElement("div", { className: "brokr-section", style: { gap: "
|
|
6283
|
+
return /* @__PURE__ */ import_react27.default.createElement("div", { className: "brokr-card brokr-gate-card" }, /* @__PURE__ */ import_react27.default.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ import_react27.default.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ import_react27.default.createElement("strong", null, "Auto reload"), /* @__PURE__ */ import_react27.default.createElement("span", { className: "brokr-copy" }, helperText)), /* @__PURE__ */ import_react27.default.createElement(
|
|
6241
6284
|
"button",
|
|
6242
6285
|
{
|
|
6243
6286
|
className: "brokr-button-secondary",
|
|
@@ -6406,6 +6449,39 @@ function isSSEResponse(response) {
|
|
|
6406
6449
|
return ct.includes("text/event-stream") && response.body !== null;
|
|
6407
6450
|
}
|
|
6408
6451
|
|
|
6452
|
+
// src/react/chat/memory.ts
|
|
6453
|
+
var MAX_MEMORY_CHARS = 2e3;
|
|
6454
|
+
function serializeMemory(obj, prefix = "") {
|
|
6455
|
+
const lines = [];
|
|
6456
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
6457
|
+
if (value === null || value === void 0) continue;
|
|
6458
|
+
const label = prefix ? `${prefix}.${key}` : key;
|
|
6459
|
+
if (Array.isArray(value)) {
|
|
6460
|
+
lines.push(`${label}: ${value.join(", ")}`);
|
|
6461
|
+
} else if (typeof value === "object") {
|
|
6462
|
+
lines.push(...serializeMemory(value, label));
|
|
6463
|
+
} else {
|
|
6464
|
+
lines.push(`${label}: ${String(value)}`);
|
|
6465
|
+
}
|
|
6466
|
+
}
|
|
6467
|
+
return lines;
|
|
6468
|
+
}
|
|
6469
|
+
function buildEffectivePrompt(prompt, memory) {
|
|
6470
|
+
if (!memory || Object.keys(memory).length === 0) return prompt;
|
|
6471
|
+
let block = serializeMemory(memory).join("\n");
|
|
6472
|
+
if (block.length > MAX_MEMORY_CHARS) {
|
|
6473
|
+
block = block.slice(0, MAX_MEMORY_CHARS);
|
|
6474
|
+
if (typeof console !== "undefined") {
|
|
6475
|
+
console.warn("[brokr] memory exceeded 2000 char limit, truncated");
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6478
|
+
const memSection = `[User context]
|
|
6479
|
+
${block}`;
|
|
6480
|
+
return prompt ? `${memSection}
|
|
6481
|
+
|
|
6482
|
+
${prompt}` : memSection;
|
|
6483
|
+
}
|
|
6484
|
+
|
|
6409
6485
|
// src/react/chat/useChat.ts
|
|
6410
6486
|
function makeId(prefix) {
|
|
6411
6487
|
return `${prefix}_${crypto.randomUUID()}`;
|
|
@@ -6423,7 +6499,9 @@ function useChat(config) {
|
|
|
6423
6499
|
const {
|
|
6424
6500
|
endpoint,
|
|
6425
6501
|
prompt,
|
|
6426
|
-
|
|
6502
|
+
explicitModel,
|
|
6503
|
+
displayModel: activeModel,
|
|
6504
|
+
memory,
|
|
6427
6505
|
persist,
|
|
6428
6506
|
surface,
|
|
6429
6507
|
subject,
|
|
@@ -6459,9 +6537,25 @@ function useChat(config) {
|
|
|
6459
6537
|
const scrollContainerRef = (0, import_react31.useRef)(null);
|
|
6460
6538
|
const sentinelRef = (0, import_react31.useRef)(null);
|
|
6461
6539
|
const displaySidebarItems = (0, import_react31.useMemo)(() => {
|
|
6462
|
-
if (isControlled)
|
|
6463
|
-
|
|
6464
|
-
|
|
6540
|
+
if (isControlled) {
|
|
6541
|
+
return threadsProp?.map((t) => ({
|
|
6542
|
+
id: t.id,
|
|
6543
|
+
title: t.title,
|
|
6544
|
+
updatedAt: t.updatedAt ?? null
|
|
6545
|
+
})) ?? [];
|
|
6546
|
+
}
|
|
6547
|
+
if (isPersist) {
|
|
6548
|
+
return serverThreads.map((t) => ({
|
|
6549
|
+
id: t.id,
|
|
6550
|
+
title: t.title,
|
|
6551
|
+
updatedAt: t.updatedAt ?? null
|
|
6552
|
+
}));
|
|
6553
|
+
}
|
|
6554
|
+
return memConversations.map((c) => ({
|
|
6555
|
+
id: c.id,
|
|
6556
|
+
title: c.title || "New chat",
|
|
6557
|
+
updatedAt: new Date(c.updatedAt).toISOString()
|
|
6558
|
+
}));
|
|
6465
6559
|
}, [isControlled, isPersist, threadsProp, serverThreads, memConversations]);
|
|
6466
6560
|
const activeId = (0, import_react31.useMemo)(() => {
|
|
6467
6561
|
if (isControlled) return activeThreadIdProp ?? null;
|
|
@@ -6642,8 +6736,11 @@ function useChat(config) {
|
|
|
6642
6736
|
messages: trimToTokenBudget(
|
|
6643
6737
|
nextMessages.filter((m) => m.role === "user" || m.role === "assistant" && m.content).map(({ content: mc, role }) => ({ role, content: mc }))
|
|
6644
6738
|
),
|
|
6645
|
-
model:
|
|
6646
|
-
...
|
|
6739
|
+
...explicitModel ? { model: explicitModel } : {},
|
|
6740
|
+
...(() => {
|
|
6741
|
+
const ep = buildEffectivePrompt(prompt, memory);
|
|
6742
|
+
return ep ? { systemPrompt: ep } : {};
|
|
6743
|
+
})()
|
|
6647
6744
|
})
|
|
6648
6745
|
});
|
|
6649
6746
|
if (!response.ok) {
|
|
@@ -6688,8 +6785,10 @@ function useChat(config) {
|
|
|
6688
6785
|
ensureMemConversation,
|
|
6689
6786
|
memConversations,
|
|
6690
6787
|
endpoint,
|
|
6788
|
+
explicitModel,
|
|
6691
6789
|
activeModel,
|
|
6692
6790
|
prompt,
|
|
6791
|
+
memory,
|
|
6693
6792
|
updateMemMessages,
|
|
6694
6793
|
appendMemDelta,
|
|
6695
6794
|
animateTitle,
|
|
@@ -6711,9 +6810,12 @@ function useChat(config) {
|
|
|
6711
6810
|
content,
|
|
6712
6811
|
surface,
|
|
6713
6812
|
subject: subject ?? null,
|
|
6714
|
-
model:
|
|
6813
|
+
...explicitModel ? { model: explicitModel } : {},
|
|
6715
6814
|
userId: userId ?? void 0,
|
|
6716
|
-
...
|
|
6815
|
+
...(() => {
|
|
6816
|
+
const ep = buildEffectivePrompt(prompt, memory);
|
|
6817
|
+
return ep ? { systemPrompt: ep } : {};
|
|
6818
|
+
})()
|
|
6717
6819
|
})
|
|
6718
6820
|
});
|
|
6719
6821
|
if (!response.ok) {
|
|
@@ -6768,8 +6870,10 @@ function useChat(config) {
|
|
|
6768
6870
|
endpoint,
|
|
6769
6871
|
surface,
|
|
6770
6872
|
subject,
|
|
6873
|
+
explicitModel,
|
|
6771
6874
|
activeModel,
|
|
6772
6875
|
prompt,
|
|
6876
|
+
memory,
|
|
6773
6877
|
userId,
|
|
6774
6878
|
setServerMessages,
|
|
6775
6879
|
setServerActiveId,
|
|
@@ -6927,6 +7031,37 @@ function useChat(config) {
|
|
|
6927
7031
|
|
|
6928
7032
|
// src/react/chat/ModelSelector.tsx
|
|
6929
7033
|
var import_react32 = __toESM(require("react"));
|
|
7034
|
+
function ModelOption({
|
|
7035
|
+
provider,
|
|
7036
|
+
isActive,
|
|
7037
|
+
isLocked,
|
|
7038
|
+
onSelect,
|
|
7039
|
+
onCheckout
|
|
7040
|
+
}) {
|
|
7041
|
+
const handleClick = (0, import_react32.useCallback)(() => {
|
|
7042
|
+
if (isLocked) {
|
|
7043
|
+
void onCheckout({ plan: "pro" });
|
|
7044
|
+
} else {
|
|
7045
|
+
onSelect(provider.model);
|
|
7046
|
+
}
|
|
7047
|
+
}, [isLocked, onCheckout, onSelect, provider.model]);
|
|
7048
|
+
return /* @__PURE__ */ import_react32.default.createElement(
|
|
7049
|
+
"button",
|
|
7050
|
+
{
|
|
7051
|
+
"aria-selected": isActive,
|
|
7052
|
+
className: "brokr-model-option",
|
|
7053
|
+
"data-active": isActive,
|
|
7054
|
+
"data-locked": isLocked,
|
|
7055
|
+
disabled: isLocked,
|
|
7056
|
+
onClick: handleClick,
|
|
7057
|
+
title: isLocked ? "Add credits to unlock" : void 0,
|
|
7058
|
+
type: "button"
|
|
7059
|
+
},
|
|
7060
|
+
/* @__PURE__ */ import_react32.default.createElement("img", { alt: "", className: "brokr-model-logo", src: provider.logo }),
|
|
7061
|
+
/* @__PURE__ */ import_react32.default.createElement("span", { className: "brokr-model-option-label" }, provider.label),
|
|
7062
|
+
isLocked ? /* @__PURE__ */ import_react32.default.createElement("span", { className: "brokr-model-lock", "aria-hidden": "true" }, /* @__PURE__ */ import_react32.default.createElement(LockIcon, { size: 13 })) : null
|
|
7063
|
+
);
|
|
7064
|
+
}
|
|
6930
7065
|
function ModelSelector({
|
|
6931
7066
|
activeModel,
|
|
6932
7067
|
setSelectedModel,
|
|
@@ -6935,10 +7070,19 @@ function ModelSelector({
|
|
|
6935
7070
|
}) {
|
|
6936
7071
|
const [selectorOpen, setSelectorOpen] = (0, import_react32.useState)(false);
|
|
6937
7072
|
const selectorRef = (0, import_react32.useRef)(null);
|
|
7073
|
+
const isAuto = activeModel === STACK_DEFAULT || !providers.some((p) => p.model === activeModel);
|
|
6938
7074
|
const activeProvider = (0, import_react32.useMemo)(
|
|
6939
|
-
() => providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
6940
|
-
[activeModel]
|
|
7075
|
+
() => isAuto ? void 0 : providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7076
|
+
[activeModel, isAuto]
|
|
6941
7077
|
);
|
|
7078
|
+
const handleModelSelect = (0, import_react32.useCallback)((model) => {
|
|
7079
|
+
setSelectedModel(model === STACK_DEFAULT ? null : model);
|
|
7080
|
+
setSelectorOpen(false);
|
|
7081
|
+
}, [setSelectedModel]);
|
|
7082
|
+
const handleAutoSelect = (0, import_react32.useCallback)(() => {
|
|
7083
|
+
setSelectedModel(null);
|
|
7084
|
+
setSelectorOpen(false);
|
|
7085
|
+
}, [setSelectedModel]);
|
|
6942
7086
|
(0, import_react32.useEffect)(() => {
|
|
6943
7087
|
if (!selectorOpen) return;
|
|
6944
7088
|
const onMouseDown = (e) => {
|
|
@@ -6966,40 +7110,79 @@ function ModelSelector({
|
|
|
6966
7110
|
onClick: handleToggle,
|
|
6967
7111
|
type: "button"
|
|
6968
7112
|
},
|
|
6969
|
-
activeProvider ? /* @__PURE__ */ import_react32.default.createElement("img", { alt: "", className: "brokr-model-logo", src: activeProvider.logo }) : /* @__PURE__ */ import_react32.default.createElement("span", { className: "brokr-model-dot", style: { background: "
|
|
6970
|
-
activeProvider?.label ?? "Model",
|
|
7113
|
+
activeProvider ? /* @__PURE__ */ import_react32.default.createElement("img", { alt: "", className: "brokr-model-logo", src: activeProvider.logo }) : /* @__PURE__ */ import_react32.default.createElement("span", { className: "brokr-model-dot", style: { background: "#6B7280" } }),
|
|
7114
|
+
isAuto ? "Auto" : activeProvider?.label ?? "Model",
|
|
6971
7115
|
/* @__PURE__ */ import_react32.default.createElement(ChevronDownIcon, { size: 13 })
|
|
6972
|
-
), selectorOpen ? /* @__PURE__ */ import_react32.default.createElement("div", { className: "brokr-model-dropdown", role: "listbox" },
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
"
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
/* @__PURE__ */ import_react32.default.createElement("img", { alt: "", className: "brokr-model-logo", src: p.logo }),
|
|
6995
|
-
/* @__PURE__ */ import_react32.default.createElement("span", { className: "brokr-model-option-label" }, p.label),
|
|
6996
|
-
locked ? /* @__PURE__ */ import_react32.default.createElement("span", { className: "brokr-model-lock", "aria-hidden": "true" }, /* @__PURE__ */ import_react32.default.createElement(LockIcon, { size: 13 })) : null
|
|
6997
|
-
);
|
|
6998
|
-
})) : null);
|
|
7116
|
+
), selectorOpen ? /* @__PURE__ */ import_react32.default.createElement("div", { className: "brokr-model-dropdown", role: "listbox" }, /* @__PURE__ */ import_react32.default.createElement(
|
|
7117
|
+
"button",
|
|
7118
|
+
{
|
|
7119
|
+
"aria-selected": isAuto,
|
|
7120
|
+
className: "brokr-model-option",
|
|
7121
|
+
"data-active": isAuto,
|
|
7122
|
+
onClick: handleAutoSelect,
|
|
7123
|
+
type: "button"
|
|
7124
|
+
},
|
|
7125
|
+
/* @__PURE__ */ import_react32.default.createElement("span", { className: "brokr-model-dot", style: { background: "#6B7280" } }),
|
|
7126
|
+
/* @__PURE__ */ import_react32.default.createElement("span", { className: "brokr-model-option-label" }, "Stack Default")
|
|
7127
|
+
), providers.map((p) => /* @__PURE__ */ import_react32.default.createElement(
|
|
7128
|
+
ModelOption,
|
|
7129
|
+
{
|
|
7130
|
+
key: p.id,
|
|
7131
|
+
provider: p,
|
|
7132
|
+
isActive: !isAuto && p.model === activeModel,
|
|
7133
|
+
isLocked: !availableProviders.some((a) => a.id === p.id),
|
|
7134
|
+
onSelect: handleModelSelect,
|
|
7135
|
+
onCheckout: checkout
|
|
7136
|
+
}
|
|
7137
|
+
))) : null);
|
|
6999
7138
|
}
|
|
7000
7139
|
|
|
7001
7140
|
// src/react/chat/ThreadSidebar.tsx
|
|
7002
7141
|
var import_react33 = __toESM(require("react"));
|
|
7142
|
+
function ThreadItemButton({ id, title, isActive, onSelect }) {
|
|
7143
|
+
const handleClick = (0, import_react33.useCallback)(() => onSelect(id), [id, onSelect]);
|
|
7144
|
+
return /* @__PURE__ */ import_react33.default.createElement(
|
|
7145
|
+
"button",
|
|
7146
|
+
{
|
|
7147
|
+
className: "brokr-ai-chat-conversation",
|
|
7148
|
+
"data-active": isActive,
|
|
7149
|
+
onClick: handleClick,
|
|
7150
|
+
type: "button"
|
|
7151
|
+
},
|
|
7152
|
+
/* @__PURE__ */ import_react33.default.createElement(MessageIcon, { size: 12 }),
|
|
7153
|
+
/* @__PURE__ */ import_react33.default.createElement("span", { className: "brokr-ai-chat-conversation-label" }, title)
|
|
7154
|
+
);
|
|
7155
|
+
}
|
|
7156
|
+
var SIDEBAR_GROUP_ORDER = [
|
|
7157
|
+
"today",
|
|
7158
|
+
"yesterday",
|
|
7159
|
+
"this_week",
|
|
7160
|
+
"this_month",
|
|
7161
|
+
"earlier",
|
|
7162
|
+
"undated"
|
|
7163
|
+
];
|
|
7164
|
+
var SIDEBAR_GROUP_LABELS = {
|
|
7165
|
+
today: "Today",
|
|
7166
|
+
yesterday: "Yesterday",
|
|
7167
|
+
this_week: "This Week",
|
|
7168
|
+
this_month: "This Month",
|
|
7169
|
+
earlier: "Earlier",
|
|
7170
|
+
undated: "Recent"
|
|
7171
|
+
};
|
|
7172
|
+
function resolveSidebarDateGroup(updatedAt) {
|
|
7173
|
+
if (!updatedAt) return "undated";
|
|
7174
|
+
const parsed = new Date(updatedAt);
|
|
7175
|
+
if (Number.isNaN(parsed.getTime())) return "undated";
|
|
7176
|
+
const now = /* @__PURE__ */ new Date();
|
|
7177
|
+
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
7178
|
+
const startOfTarget = new Date(parsed.getFullYear(), parsed.getMonth(), parsed.getDate());
|
|
7179
|
+
const diffDays = Math.floor((startOfToday.getTime() - startOfTarget.getTime()) / 864e5);
|
|
7180
|
+
if (diffDays <= 0) return "today";
|
|
7181
|
+
if (diffDays === 1) return "yesterday";
|
|
7182
|
+
if (diffDays < 7) return "this_week";
|
|
7183
|
+
if (diffDays < 30) return "this_month";
|
|
7184
|
+
return "earlier";
|
|
7185
|
+
}
|
|
7003
7186
|
function ThreadSidebar() {
|
|
7004
7187
|
const {
|
|
7005
7188
|
startNewChat,
|
|
@@ -7031,14 +7214,31 @@ function ThreadSidebar() {
|
|
|
7031
7214
|
const handleRenameChange = (0, import_react33.useCallback)((e) => {
|
|
7032
7215
|
setRenameValue(e.target.value);
|
|
7033
7216
|
}, [setRenameValue]);
|
|
7217
|
+
const groupedSidebarItems = (0, import_react33.useMemo)(() => {
|
|
7218
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
7219
|
+
for (const item of displaySidebarItems) {
|
|
7220
|
+
const key = resolveSidebarDateGroup(item.updatedAt);
|
|
7221
|
+
const bucket = grouped.get(key);
|
|
7222
|
+
if (bucket) {
|
|
7223
|
+
bucket.push(item);
|
|
7224
|
+
} else {
|
|
7225
|
+
grouped.set(key, [item]);
|
|
7226
|
+
}
|
|
7227
|
+
}
|
|
7228
|
+
return SIDEBAR_GROUP_ORDER.map((key) => ({
|
|
7229
|
+
key,
|
|
7230
|
+
label: SIDEBAR_GROUP_LABELS[key],
|
|
7231
|
+
items: grouped.get(key) ?? []
|
|
7232
|
+
})).filter((group) => group.items.length > 0);
|
|
7233
|
+
}, [displaySidebarItems]);
|
|
7034
7234
|
const content = (0, import_react33.useMemo)(() => {
|
|
7035
7235
|
if (threadsLoading && displaySidebarItems.length === 0) {
|
|
7036
|
-
return /* @__PURE__ */ import_react33.default.createElement("div", {
|
|
7236
|
+
return /* @__PURE__ */ import_react33.default.createElement("div", { className: "brokr-ai-chat-sidebar-skeleton" }, /* @__PURE__ */ import_react33.default.createElement(Skeleton, { width: "75%", height: 14, radius: 6 }), /* @__PURE__ */ import_react33.default.createElement(Skeleton, { width: "60%", height: 14, radius: 6 }), /* @__PURE__ */ import_react33.default.createElement(Skeleton, { width: "85%", height: 14, radius: 6 }));
|
|
7037
7237
|
}
|
|
7038
7238
|
if (displaySidebarItems.length === 0) {
|
|
7039
7239
|
return /* @__PURE__ */ import_react33.default.createElement("div", { className: "brokr-ai-chat-sidebar-empty" }, /* @__PURE__ */ import_react33.default.createElement("p", { className: "brokr-ai-chat-sidebar-empty-text" }, "No conversations yet. Start one above."));
|
|
7040
7240
|
}
|
|
7041
|
-
return /* @__PURE__ */ import_react33.default.createElement("div", { className: "brokr-ai-chat-conversations" },
|
|
7241
|
+
return /* @__PURE__ */ import_react33.default.createElement("div", { className: "brokr-ai-chat-sidebar-groups" }, groupedSidebarItems.map((group) => /* @__PURE__ */ import_react33.default.createElement("section", { className: "brokr-ai-chat-sidebar-group", key: group.key }, /* @__PURE__ */ import_react33.default.createElement("span", { className: "brokr-ai-chat-sidebar-kicker" }, group.label), /* @__PURE__ */ import_react33.default.createElement("div", { className: "brokr-ai-chat-conversations" }, group.items.map((item) => renamingId === item.id ? /* @__PURE__ */ import_react33.default.createElement(
|
|
7042
7242
|
"input",
|
|
7043
7243
|
{
|
|
7044
7244
|
autoFocus: true,
|
|
@@ -7050,28 +7250,28 @@ function ThreadSidebar() {
|
|
|
7050
7250
|
value: renameValue
|
|
7051
7251
|
}
|
|
7052
7252
|
) : /* @__PURE__ */ import_react33.default.createElement(
|
|
7053
|
-
|
|
7253
|
+
ThreadItemButton,
|
|
7054
7254
|
{
|
|
7055
|
-
className: "brokr-ai-chat-conversation",
|
|
7056
|
-
"data-active": item.id === activeId,
|
|
7057
7255
|
key: item.id,
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7256
|
+
id: item.id,
|
|
7257
|
+
title: item.title,
|
|
7258
|
+
isActive: item.id === activeId,
|
|
7259
|
+
onSelect: selectThreadAndCloseSidebar
|
|
7260
|
+
}
|
|
7261
|
+
))))));
|
|
7063
7262
|
}, [
|
|
7064
7263
|
threadsLoading,
|
|
7065
7264
|
displaySidebarItems,
|
|
7066
7265
|
renamingId,
|
|
7067
7266
|
renameValue,
|
|
7267
|
+
groupedSidebarItems,
|
|
7068
7268
|
activeId,
|
|
7069
7269
|
selectThreadAndCloseSidebar,
|
|
7070
7270
|
handleRenameBlur,
|
|
7071
7271
|
handleRenameChange,
|
|
7072
7272
|
handleRenameKeyDown
|
|
7073
7273
|
]);
|
|
7074
|
-
return /* @__PURE__ */ import_react33.default.createElement(import_react33.default.Fragment, null, /* @__PURE__ */ import_react33.default.createElement("button", { className: "brokr-ai-chat-sidebar-
|
|
7274
|
+
return /* @__PURE__ */ import_react33.default.createElement(import_react33.default.Fragment, null, /* @__PURE__ */ import_react33.default.createElement("button", { className: "brokr-ai-chat-sidebar-new-chat", onClick: handleNewChat, type: "button" }, /* @__PURE__ */ import_react33.default.createElement(MessageIcon, { size: 16 }), "New chat"), content);
|
|
7075
7275
|
}
|
|
7076
7276
|
|
|
7077
7277
|
// src/react/chat/MessagePane.tsx
|
|
@@ -7099,6 +7299,12 @@ function parseInline(text) {
|
|
|
7099
7299
|
remaining = remaining.slice(boldMatch[0].length);
|
|
7100
7300
|
continue;
|
|
7101
7301
|
}
|
|
7302
|
+
const strikethroughMatch = remaining.match(/^~~(.+?)~~/);
|
|
7303
|
+
if (strikethroughMatch) {
|
|
7304
|
+
nodes.push(/* @__PURE__ */ import_react34.default.createElement("del", { key: key++ }, strikethroughMatch[1]));
|
|
7305
|
+
remaining = remaining.slice(strikethroughMatch[0].length);
|
|
7306
|
+
continue;
|
|
7307
|
+
}
|
|
7102
7308
|
const italicMatch = remaining.match(/^\*(.+?)\*/);
|
|
7103
7309
|
if (italicMatch) {
|
|
7104
7310
|
nodes.push(/* @__PURE__ */ import_react34.default.createElement("em", { key: key++ }, italicMatch[1]));
|
|
@@ -7115,7 +7321,7 @@ function parseInline(text) {
|
|
|
7115
7321
|
remaining = remaining.slice(linkMatch[0].length);
|
|
7116
7322
|
continue;
|
|
7117
7323
|
}
|
|
7118
|
-
const nextSpecial = remaining.search(/[
|
|
7324
|
+
const nextSpecial = remaining.search(/[`*~\[]/);
|
|
7119
7325
|
if (nextSpecial === -1) {
|
|
7120
7326
|
nodes.push(remaining);
|
|
7121
7327
|
break;
|
|
@@ -7146,6 +7352,15 @@ function CodeBlock({ code, language }) {
|
|
|
7146
7352
|
"Copy"
|
|
7147
7353
|
)), /* @__PURE__ */ import_react34.default.createElement("pre", { className: "brokr-md-codeblock-pre" }, /* @__PURE__ */ import_react34.default.createElement("code", null, code)));
|
|
7148
7354
|
}
|
|
7355
|
+
function looksLikeTableSeparator(line) {
|
|
7356
|
+
const trimmed = line.trim();
|
|
7357
|
+
return /^\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?$/.test(trimmed);
|
|
7358
|
+
}
|
|
7359
|
+
function splitTableRow(line) {
|
|
7360
|
+
const trimmed = line.trim();
|
|
7361
|
+
const withoutEdges = trimmed.replace(/^\|/, "").replace(/\|$/, "");
|
|
7362
|
+
return withoutEdges.split("|").map((cell) => cell.trim());
|
|
7363
|
+
}
|
|
7149
7364
|
function parseBlocks(text) {
|
|
7150
7365
|
const blocks = [];
|
|
7151
7366
|
const lines = text.split("\n");
|
|
@@ -7175,22 +7390,44 @@ function parseBlocks(text) {
|
|
|
7175
7390
|
i++;
|
|
7176
7391
|
continue;
|
|
7177
7392
|
}
|
|
7393
|
+
if (line.includes("|") && i + 1 < lines.length && looksLikeTableSeparator(lines[i + 1] ?? "")) {
|
|
7394
|
+
const headers = splitTableRow(line);
|
|
7395
|
+
const rows = [];
|
|
7396
|
+
i += 2;
|
|
7397
|
+
while (i < lines.length) {
|
|
7398
|
+
const candidate = lines[i] ?? "";
|
|
7399
|
+
if (!candidate.trim() || !candidate.includes("|")) break;
|
|
7400
|
+
rows.push(splitTableRow(candidate));
|
|
7401
|
+
i++;
|
|
7402
|
+
}
|
|
7403
|
+
blocks.push({ type: "table", content: "", headers, rows });
|
|
7404
|
+
continue;
|
|
7405
|
+
}
|
|
7406
|
+
if (/^[\s]*>\s?/.test(line)) {
|
|
7407
|
+
const quoteLines = [];
|
|
7408
|
+
while (i < lines.length && /^[\s]*>\s?/.test(lines[i])) {
|
|
7409
|
+
quoteLines.push(lines[i].replace(/^[\s]*>\s?/, ""));
|
|
7410
|
+
i++;
|
|
7411
|
+
}
|
|
7412
|
+
blocks.push({ type: "quote", content: "", items: quoteLines });
|
|
7413
|
+
continue;
|
|
7414
|
+
}
|
|
7178
7415
|
if (/^[\s]*[-*]\s+/.test(line)) {
|
|
7179
7416
|
const items = [];
|
|
7180
7417
|
while (i < lines.length && /^[\s]*[-*]\s+/.test(lines[i])) {
|
|
7181
7418
|
items.push(lines[i].replace(/^[\s]*[-*]\s+/, ""));
|
|
7182
7419
|
i++;
|
|
7183
7420
|
}
|
|
7184
|
-
blocks.push({ type: "list", content: "", items });
|
|
7421
|
+
blocks.push({ type: "list", content: "", items, ordered: false });
|
|
7185
7422
|
continue;
|
|
7186
7423
|
}
|
|
7187
|
-
if (/^[\s]*\d
|
|
7424
|
+
if (/^[\s]*\d+[.)]\s+/.test(line)) {
|
|
7188
7425
|
const items = [];
|
|
7189
|
-
while (i < lines.length && /^[\s]*\d
|
|
7190
|
-
items.push(lines[i].replace(/^[\s]*\d
|
|
7426
|
+
while (i < lines.length && /^[\s]*\d+[.)]\s+/.test(lines[i])) {
|
|
7427
|
+
items.push(lines[i].replace(/^[\s]*\d+[.)]\s+/, ""));
|
|
7191
7428
|
i++;
|
|
7192
7429
|
}
|
|
7193
|
-
blocks.push({ type: "list", content: "", items });
|
|
7430
|
+
blocks.push({ type: "list", content: "", items, ordered: true });
|
|
7194
7431
|
continue;
|
|
7195
7432
|
}
|
|
7196
7433
|
if (!line.trim()) {
|
|
@@ -7199,7 +7436,7 @@ function parseBlocks(text) {
|
|
|
7199
7436
|
}
|
|
7200
7437
|
const paraLines = [line];
|
|
7201
7438
|
i++;
|
|
7202
|
-
while (i < lines.length && lines[i].trim() && !lines[i].trimStart().startsWith("```") && !lines[i].match(/^#{1,6}\s/) && !/^(-{3,}|\*{3,}|_{3,})\s*$/.test(lines[i].trim()) && !/^[\s]*[-*]\s+/.test(lines[i]) && !/^[\s]*\d
|
|
7439
|
+
while (i < lines.length && lines[i].trim() && !lines[i].trimStart().startsWith("```") && !lines[i].match(/^#{1,6}\s/) && !(lines[i].includes("|") && i + 1 < lines.length && looksLikeTableSeparator(lines[i + 1] ?? "")) && !/^(-{3,}|\*{3,}|_{3,})\s*$/.test(lines[i].trim()) && !/^[\s]*>\s?/.test(lines[i]) && !/^[\s]*[-*]\s+/.test(lines[i]) && !/^[\s]*\d+[.)]\s+/.test(lines[i])) {
|
|
7203
7440
|
paraLines.push(lines[i]);
|
|
7204
7441
|
i++;
|
|
7205
7442
|
}
|
|
@@ -7219,7 +7456,14 @@ function renderBlocks(blocks) {
|
|
|
7219
7456
|
return /* @__PURE__ */ import_react34.default.createElement(Tag, { className: `brokr-md-heading brokr-md-h${block.level}`, key: idx }, parseInline(block.content));
|
|
7220
7457
|
}
|
|
7221
7458
|
case "list":
|
|
7222
|
-
|
|
7459
|
+
if (block.ordered) {
|
|
7460
|
+
return /* @__PURE__ */ import_react34.default.createElement("ol", { className: "brokr-md-list brokr-md-list-ordered", key: idx }, block.items?.map((item, li) => /* @__PURE__ */ import_react34.default.createElement("li", { key: li }, parseInline(item))));
|
|
7461
|
+
}
|
|
7462
|
+
return /* @__PURE__ */ import_react34.default.createElement("ul", { className: "brokr-md-list brokr-md-list-unordered", key: idx }, block.items?.map((item, li) => /* @__PURE__ */ import_react34.default.createElement("li", { key: li }, parseInline(item))));
|
|
7463
|
+
case "quote":
|
|
7464
|
+
return /* @__PURE__ */ import_react34.default.createElement("blockquote", { className: "brokr-md-quote", key: idx }, block.items?.map((line, lineIndex) => /* @__PURE__ */ import_react34.default.createElement("p", { className: "brokr-md-quote-line", key: lineIndex }, parseInline(line))));
|
|
7465
|
+
case "table":
|
|
7466
|
+
return /* @__PURE__ */ import_react34.default.createElement("div", { className: "brokr-md-table-wrap", key: idx }, /* @__PURE__ */ import_react34.default.createElement("table", { className: "brokr-md-table" }, /* @__PURE__ */ import_react34.default.createElement("thead", null, /* @__PURE__ */ import_react34.default.createElement("tr", null, block.headers?.map((header, headerIndex) => /* @__PURE__ */ import_react34.default.createElement("th", { key: headerIndex }, parseInline(header))))), /* @__PURE__ */ import_react34.default.createElement("tbody", null, block.rows?.map((row, rowIndex) => /* @__PURE__ */ import_react34.default.createElement("tr", { key: rowIndex }, row.map((cell, cellIndex) => /* @__PURE__ */ import_react34.default.createElement("td", { key: cellIndex }, parseInline(cell))))))));
|
|
7223
7467
|
case "paragraph":
|
|
7224
7468
|
default:
|
|
7225
7469
|
return /* @__PURE__ */ import_react34.default.createElement("p", { className: "brokr-md-paragraph", key: idx }, parseInline(block.content));
|
|
@@ -7258,6 +7502,12 @@ function MessageBubble({ message, isTyping, user }) {
|
|
|
7258
7502
|
}
|
|
7259
7503
|
|
|
7260
7504
|
// src/react/chat/MessagePane.tsx
|
|
7505
|
+
function StarterPromptButton({ prompt, onSend }) {
|
|
7506
|
+
const handleClick = (0, import_react36.useCallback)(() => {
|
|
7507
|
+
void onSend(prompt);
|
|
7508
|
+
}, [prompt, onSend]);
|
|
7509
|
+
return /* @__PURE__ */ import_react36.default.createElement("button", { className: "brokr-ai-chat-starter", onClick: handleClick, type: "button" }, prompt);
|
|
7510
|
+
}
|
|
7261
7511
|
function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
7262
7512
|
const {
|
|
7263
7513
|
displayMessages,
|
|
@@ -7288,20 +7538,17 @@ function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
|
7288
7538
|
);
|
|
7289
7539
|
});
|
|
7290
7540
|
}, [visibleMessages, isSubmitting, user]);
|
|
7291
|
-
return /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-thread", "data-empty": isEmpty, ref: scrollContainerRef }, isEmpty ? /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-empty" }, /* @__PURE__ */ import_react36.default.createElement(SparkIcon, { size: 28 }), /* @__PURE__ */ import_react36.default.createElement("h2", { className: "brokr-title" }, emptyTitle), subtitle ?? emptyCopy ? /* @__PURE__ */ import_react36.default.createElement("p", { className: "brokr-copy" }, subtitle ?? emptyCopy) : null, starterPrompts.length > 0 ? /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-starters" }, starterPrompts.map((sp) => /* @__PURE__ */ import_react36.default.createElement(
|
|
7292
|
-
"button",
|
|
7293
|
-
{
|
|
7294
|
-
className: "brokr-ai-chat-starter",
|
|
7295
|
-
key: sp,
|
|
7296
|
-
onClick: () => void sendMessage(sp),
|
|
7297
|
-
type: "button"
|
|
7298
|
-
},
|
|
7299
|
-
sp
|
|
7300
|
-
))) : null) : /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-thread-inner" }, (isPersist ? hasMoreMessages : memHasMore) ? /* @__PURE__ */ import_react36.default.createElement("div", { ref: sentinelRef, className: "brokr-ai-chat-sentinel" }, loadingOlder ? /* @__PURE__ */ import_react36.default.createElement("div", { style: { display: "flex", justifyContent: "center", padding: "0.5rem" } }, /* @__PURE__ */ import_react36.default.createElement(Skeleton, { width: 120, height: 12, radius: 6 })) : null) : null, messageElements, /* @__PURE__ */ import_react36.default.createElement("div", { ref: bottomRef })));
|
|
7541
|
+
return /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-thread", "data-empty": isEmpty, ref: scrollContainerRef }, isEmpty ? /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-empty" }, /* @__PURE__ */ import_react36.default.createElement(SparkIcon, { size: 28 }), /* @__PURE__ */ import_react36.default.createElement("h2", { className: "brokr-title" }, emptyTitle), subtitle ?? emptyCopy ? /* @__PURE__ */ import_react36.default.createElement("p", { className: "brokr-copy" }, subtitle ?? emptyCopy) : null, starterPrompts.length > 0 ? /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-starters" }, starterPrompts.map((sp) => /* @__PURE__ */ import_react36.default.createElement(StarterPromptButton, { key: sp, prompt: sp, onSend: sendMessage }))) : null) : /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-thread-inner" }, (isPersist ? hasMoreMessages : memHasMore) ? /* @__PURE__ */ import_react36.default.createElement("div", { ref: sentinelRef, className: "brokr-ai-chat-sentinel" }, loadingOlder ? /* @__PURE__ */ import_react36.default.createElement("div", { style: { display: "flex", justifyContent: "center", padding: "0.5rem" } }, /* @__PURE__ */ import_react36.default.createElement(Skeleton, { width: 120, height: 12, radius: 6 })) : null) : null, messageElements, /* @__PURE__ */ import_react36.default.createElement("div", { ref: bottomRef })));
|
|
7301
7542
|
}
|
|
7302
7543
|
|
|
7303
7544
|
// src/react/chat/ChatInput.tsx
|
|
7304
7545
|
var import_react37 = __toESM(require("react"));
|
|
7546
|
+
function CommandButton({ cmd, chatContext }) {
|
|
7547
|
+
const handleClick = (0, import_react37.useCallback)(() => {
|
|
7548
|
+
void cmd.run(chatContext);
|
|
7549
|
+
}, [cmd, chatContext]);
|
|
7550
|
+
return /* @__PURE__ */ import_react37.default.createElement("button", { className: "brokr-ai-chat-sidebar-button", key: cmd.id, onClick: handleClick, type: "button" }, cmd.text);
|
|
7551
|
+
}
|
|
7305
7552
|
function ChatInput() {
|
|
7306
7553
|
const {
|
|
7307
7554
|
input,
|
|
@@ -7332,16 +7579,7 @@ function ChatInput() {
|
|
|
7332
7579
|
const handleChange = (0, import_react37.useCallback)((e) => {
|
|
7333
7580
|
setInput(e.target.value);
|
|
7334
7581
|
}, [setInput]);
|
|
7335
|
-
return /* @__PURE__ */ import_react37.default.createElement("form", { className: "brokr-ai-chat-input-area", onSubmit: handleSubmit }, /* @__PURE__ */ import_react37.default.createElement("div", { className: "brokr-ai-chat-input-container" }, composerCommands.length > 0 ? /* @__PURE__ */ import_react37.default.createElement("div", { className: "brokr-ai-chat-composer-actions" }, composerCommands.map((cmd) => /* @__PURE__ */ import_react37.default.createElement(
|
|
7336
|
-
"button",
|
|
7337
|
-
{
|
|
7338
|
-
className: "brokr-ai-chat-sidebar-button",
|
|
7339
|
-
key: cmd.id,
|
|
7340
|
-
onClick: () => void cmd.run(chatContext),
|
|
7341
|
-
type: "button"
|
|
7342
|
-
},
|
|
7343
|
-
cmd.text
|
|
7344
|
-
))) : null, /* @__PURE__ */ import_react37.default.createElement("div", { className: "brokr-ai-chat-input-row" }, /* @__PURE__ */ import_react37.default.createElement(
|
|
7582
|
+
return /* @__PURE__ */ import_react37.default.createElement("form", { className: "brokr-ai-chat-input-area", onSubmit: handleSubmit }, /* @__PURE__ */ import_react37.default.createElement("div", { className: "brokr-ai-chat-input-container" }, composerCommands.length > 0 ? /* @__PURE__ */ import_react37.default.createElement("div", { className: "brokr-ai-chat-composer-actions" }, composerCommands.map((cmd) => /* @__PURE__ */ import_react37.default.createElement(CommandButton, { key: cmd.id, cmd, chatContext }))) : null, /* @__PURE__ */ import_react37.default.createElement("div", { className: "brokr-ai-chat-input-row" }, /* @__PURE__ */ import_react37.default.createElement(
|
|
7345
7583
|
"textarea",
|
|
7346
7584
|
{
|
|
7347
7585
|
className: "brokr-ai-chat-textarea",
|
|
@@ -7382,6 +7620,7 @@ function AIChat(inlineProps) {
|
|
|
7382
7620
|
subtitle,
|
|
7383
7621
|
model: modelProp,
|
|
7384
7622
|
modelSelector,
|
|
7623
|
+
memory,
|
|
7385
7624
|
variant = 1,
|
|
7386
7625
|
sidebar: sidebarProp,
|
|
7387
7626
|
threadMenu: threadMenuProp,
|
|
@@ -7405,16 +7644,12 @@ function AIChat(inlineProps) {
|
|
|
7405
7644
|
const headerVisible = variant !== 3;
|
|
7406
7645
|
const threadMenuVisible = threadMenuProp !== void 0 ? threadMenuProp : variant !== 3;
|
|
7407
7646
|
const modelSelectorVisible = (modelSelector !== void 0 ? modelSelector : true) && !modelProp;
|
|
7408
|
-
const [
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
(0, import_react38.useEffect)(() => {
|
|
7413
|
-
if (modelProp) setSelectedModel(modelProp);
|
|
7414
|
-
}, [modelProp]);
|
|
7415
|
-
const activeModel = modelProp ?? selectedModel;
|
|
7647
|
+
const [userSelectedModel, setUserSelectedModel] = (0, import_react38.useState)(null);
|
|
7648
|
+
const explicitModel = modelProp ?? userSelectedModel ?? void 0;
|
|
7649
|
+
const displayModel = explicitModel ?? providers.find((p) => p.free)?.model ?? providers[0]?.model ?? "";
|
|
7650
|
+
const activeModel = explicitModel ?? STACK_DEFAULT;
|
|
7416
7651
|
const activeProvider = (0, import_react38.useMemo)(
|
|
7417
|
-
() => providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7652
|
+
() => activeModel === STACK_DEFAULT ? void 0 : providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7418
7653
|
[activeModel]
|
|
7419
7654
|
);
|
|
7420
7655
|
const hasBalance = (0, import_react38.useMemo)(
|
|
@@ -7428,7 +7663,9 @@ function AIChat(inlineProps) {
|
|
|
7428
7663
|
const chat = useChat({
|
|
7429
7664
|
endpoint,
|
|
7430
7665
|
prompt,
|
|
7431
|
-
|
|
7666
|
+
explicitModel,
|
|
7667
|
+
displayModel,
|
|
7668
|
+
memory,
|
|
7432
7669
|
persist,
|
|
7433
7670
|
surface,
|
|
7434
7671
|
subject,
|
|
@@ -7480,6 +7717,14 @@ function AIChat(inlineProps) {
|
|
|
7480
7717
|
chat.selectThread(id);
|
|
7481
7718
|
setSidebarOpen(false);
|
|
7482
7719
|
}, [chat.selectThread]);
|
|
7720
|
+
const handleCopy = (0, import_react38.useCallback)((content) => {
|
|
7721
|
+
navigator.clipboard.writeText(content).catch(() => {
|
|
7722
|
+
});
|
|
7723
|
+
}, []);
|
|
7724
|
+
const handleStartRename = (0, import_react38.useCallback)((threadId) => {
|
|
7725
|
+
setThreadMenuOpenId(null);
|
|
7726
|
+
chat.startRename(threadId);
|
|
7727
|
+
}, [chat.startRename]);
|
|
7483
7728
|
(0, import_react38.useEffect)(() => {
|
|
7484
7729
|
if (!threadMenuOpenId) return;
|
|
7485
7730
|
const onMouseDown = (e) => {
|
|
@@ -7528,26 +7773,20 @@ function AIChat(inlineProps) {
|
|
|
7528
7773
|
activeThread: chat.activeThread,
|
|
7529
7774
|
renderedTitle: finalTitle,
|
|
7530
7775
|
isTitleLoading: chat.isTitleLoading,
|
|
7531
|
-
activeModel,
|
|
7776
|
+
activeModel: displayModel,
|
|
7532
7777
|
activeProvider,
|
|
7533
|
-
selectedModel,
|
|
7534
|
-
setSelectedModel,
|
|
7778
|
+
selectedModel: displayModel,
|
|
7779
|
+
setSelectedModel: setUserSelectedModel,
|
|
7535
7780
|
availableProviders,
|
|
7536
7781
|
sendMessage: chat.sendMessage,
|
|
7537
7782
|
startNewChat: chat.startNewChat,
|
|
7538
7783
|
selectThread: chat.selectThread,
|
|
7539
7784
|
deleteThread: chat.deleteThread,
|
|
7540
|
-
handleCopy
|
|
7541
|
-
navigator.clipboard.writeText(content).catch(() => {
|
|
7542
|
-
});
|
|
7543
|
-
},
|
|
7785
|
+
handleCopy,
|
|
7544
7786
|
renamingId: chat.renamingId,
|
|
7545
7787
|
renameValue: chat.renameValue,
|
|
7546
7788
|
setRenameValue: chat.setRenameValue,
|
|
7547
|
-
startRename:
|
|
7548
|
-
setThreadMenuOpenId(null);
|
|
7549
|
-
chat.startRename(threadId);
|
|
7550
|
-
},
|
|
7789
|
+
startRename: handleStartRename,
|
|
7551
7790
|
submitRename: chat.submitRename,
|
|
7552
7791
|
displaySidebarItems: chat.displaySidebarItems,
|
|
7553
7792
|
threadsLoading: chat.threadsLoading,
|
|
@@ -7577,9 +7816,9 @@ function AIChat(inlineProps) {
|
|
|
7577
7816
|
}), [
|
|
7578
7817
|
chat,
|
|
7579
7818
|
finalTitle,
|
|
7580
|
-
|
|
7819
|
+
displayModel,
|
|
7581
7820
|
activeProvider,
|
|
7582
|
-
|
|
7821
|
+
userSelectedModel,
|
|
7583
7822
|
availableProviders,
|
|
7584
7823
|
sidebarOpen,
|
|
7585
7824
|
closeSidebar,
|
|
@@ -7616,8 +7855,8 @@ function AIChat(inlineProps) {
|
|
|
7616
7855
|
threadMenuRef,
|
|
7617
7856
|
threadMenuVisible,
|
|
7618
7857
|
setThreadMenuOpenId,
|
|
7619
|
-
activeModel,
|
|
7620
|
-
setSelectedModel,
|
|
7858
|
+
activeModel: displayModel,
|
|
7859
|
+
setSelectedModel: setUserSelectedModel,
|
|
7621
7860
|
availableProviders,
|
|
7622
7861
|
startRename: chatState.startRename,
|
|
7623
7862
|
deleteThread: chat.deleteThread
|
|
@@ -7633,6 +7872,19 @@ function AIChat(inlineProps) {
|
|
|
7633
7872
|
), /* @__PURE__ */ import_react38.default.createElement(ChatInput, null))
|
|
7634
7873
|
));
|
|
7635
7874
|
}
|
|
7875
|
+
function CommandButton2({ cmd, chatContext }) {
|
|
7876
|
+
const handleClick = (0, import_react38.useCallback)(() => {
|
|
7877
|
+
void cmd.run(chatContext);
|
|
7878
|
+
}, [cmd, chatContext]);
|
|
7879
|
+
return /* @__PURE__ */ import_react38.default.createElement("button", { className: "brokr-ai-chat-sidebar-button", onClick: handleClick, type: "button" }, cmd.text);
|
|
7880
|
+
}
|
|
7881
|
+
function MenuCommandItem({ cmd, chatContext, onClose }) {
|
|
7882
|
+
const handleClick = (0, import_react38.useCallback)(() => {
|
|
7883
|
+
onClose();
|
|
7884
|
+
void cmd.run(chatContext);
|
|
7885
|
+
}, [cmd, chatContext, onClose]);
|
|
7886
|
+
return /* @__PURE__ */ import_react38.default.createElement("button", { className: "brokr-ai-chat-thread-dropdown-item", onClick: handleClick, type: "button" }, cmd.text);
|
|
7887
|
+
}
|
|
7636
7888
|
function ChatHeader({
|
|
7637
7889
|
activeId,
|
|
7638
7890
|
sidebarVisible,
|
|
@@ -7656,6 +7908,16 @@ function ChatHeader({
|
|
|
7656
7908
|
const handleToggleMenu = (0, import_react38.useCallback)(() => {
|
|
7657
7909
|
setThreadMenuOpenId(threadMenuOpenId ? null : activeId);
|
|
7658
7910
|
}, [setThreadMenuOpenId, threadMenuOpenId, activeId]);
|
|
7911
|
+
const closeMenu = (0, import_react38.useCallback)(() => setThreadMenuOpenId(null), [setThreadMenuOpenId]);
|
|
7912
|
+
const handleRename = (0, import_react38.useCallback)(() => {
|
|
7913
|
+
if (activeId) startRename(activeId);
|
|
7914
|
+
}, [activeId, startRename]);
|
|
7915
|
+
const handleDelete = (0, import_react38.useCallback)(() => {
|
|
7916
|
+
if (activeId) {
|
|
7917
|
+
setThreadMenuOpenId(null);
|
|
7918
|
+
void deleteThread(activeId);
|
|
7919
|
+
}
|
|
7920
|
+
}, [activeId, deleteThread, setThreadMenuOpenId]);
|
|
7659
7921
|
return /* @__PURE__ */ import_react38.default.createElement("header", { className: "brokr-ai-chat-topbar" }, /* @__PURE__ */ import_react38.default.createElement("div", { className: "brokr-ai-chat-topbar-left" }, sidebarVisible ? /* @__PURE__ */ import_react38.default.createElement(
|
|
7660
7922
|
"button",
|
|
7661
7923
|
{
|
|
@@ -7665,16 +7927,7 @@ function ChatHeader({
|
|
|
7665
7927
|
type: "button"
|
|
7666
7928
|
},
|
|
7667
7929
|
/* @__PURE__ */ import_react38.default.createElement(MenuIcon, { size: 18 })
|
|
7668
|
-
) : null), /* @__PURE__ */ import_react38.default.createElement("div", { className: "brokr-ai-chat-topbar-actions" }, headerCommands.map((cmd) => /* @__PURE__ */ import_react38.default.createElement(
|
|
7669
|
-
"button",
|
|
7670
|
-
{
|
|
7671
|
-
className: "brokr-ai-chat-sidebar-button",
|
|
7672
|
-
key: cmd.id,
|
|
7673
|
-
onClick: () => void cmd.run(chatContext),
|
|
7674
|
-
type: "button"
|
|
7675
|
-
},
|
|
7676
|
-
cmd.text
|
|
7677
|
-
)), modelSelectorVisible ? /* @__PURE__ */ import_react38.default.createElement(
|
|
7930
|
+
) : null), /* @__PURE__ */ import_react38.default.createElement("div", { className: "brokr-ai-chat-topbar-actions" }, headerCommands.map((cmd) => /* @__PURE__ */ import_react38.default.createElement(CommandButton2, { key: cmd.id, cmd, chatContext })), modelSelectorVisible ? /* @__PURE__ */ import_react38.default.createElement(
|
|
7678
7931
|
ModelSelector,
|
|
7679
7932
|
{
|
|
7680
7933
|
activeModel,
|
|
@@ -7695,32 +7948,25 @@ function ChatHeader({
|
|
|
7695
7948
|
"button",
|
|
7696
7949
|
{
|
|
7697
7950
|
className: "brokr-ai-chat-thread-dropdown-item",
|
|
7698
|
-
onClick:
|
|
7951
|
+
onClick: handleRename,
|
|
7699
7952
|
type: "button"
|
|
7700
7953
|
},
|
|
7701
7954
|
/* @__PURE__ */ import_react38.default.createElement(PencilIcon, { size: 13 }),
|
|
7702
7955
|
"Rename"
|
|
7703
7956
|
), threadMenuCommands.map((cmd) => /* @__PURE__ */ import_react38.default.createElement(
|
|
7704
|
-
|
|
7957
|
+
MenuCommandItem,
|
|
7705
7958
|
{
|
|
7706
|
-
className: "brokr-ai-chat-thread-dropdown-item",
|
|
7707
7959
|
key: cmd.id,
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
type: "button"
|
|
7713
|
-
},
|
|
7714
|
-
cmd.text
|
|
7960
|
+
cmd,
|
|
7961
|
+
chatContext,
|
|
7962
|
+
onClose: closeMenu
|
|
7963
|
+
}
|
|
7715
7964
|
)), /* @__PURE__ */ import_react38.default.createElement(
|
|
7716
7965
|
"button",
|
|
7717
7966
|
{
|
|
7718
7967
|
className: "brokr-ai-chat-thread-dropdown-item",
|
|
7719
7968
|
"data-tone": "danger",
|
|
7720
|
-
onClick:
|
|
7721
|
-
setThreadMenuOpenId(null);
|
|
7722
|
-
void deleteThread(activeId);
|
|
7723
|
-
},
|
|
7969
|
+
onClick: handleDelete,
|
|
7724
7970
|
type: "button"
|
|
7725
7971
|
},
|
|
7726
7972
|
/* @__PURE__ */ import_react38.default.createElement(TrashIcon, { size: 13 }),
|
|
@@ -7730,19 +7976,62 @@ function ChatHeader({
|
|
|
7730
7976
|
|
|
7731
7977
|
// src/react/composites/FabAI.tsx
|
|
7732
7978
|
var import_react39 = __toESM(require("react"));
|
|
7979
|
+
|
|
7980
|
+
// src/react/composites/fab-context.ts
|
|
7981
|
+
function buildFabSystemPrompt(appContext, brandName, existingPrompt) {
|
|
7982
|
+
if (appContext === false) return existingPrompt;
|
|
7983
|
+
const name = appContext?.name ?? brandName;
|
|
7984
|
+
if (!name && !appContext) return existingPrompt;
|
|
7985
|
+
const parts = [];
|
|
7986
|
+
if (name) parts.push(`You are an AI assistant embedded in ${name}.`);
|
|
7987
|
+
if (appContext?.description) parts.push(`This app is ${appContext.description}.`);
|
|
7988
|
+
if (appContext?.currentPage) parts.push(`The user is currently on: ${appContext.currentPage}.`);
|
|
7989
|
+
if (appContext?.facts?.length) parts.push(...appContext.facts);
|
|
7990
|
+
const autoPrompt = parts.join(" ");
|
|
7991
|
+
return existingPrompt ? `${autoPrompt}
|
|
7992
|
+
|
|
7993
|
+
${existingPrompt}` : autoPrompt;
|
|
7994
|
+
}
|
|
7995
|
+
|
|
7996
|
+
// src/react/composites/FabAI.tsx
|
|
7997
|
+
function ensureAssistantReply(messages, content) {
|
|
7998
|
+
const normalized = content.trim() ? content : "No response received.";
|
|
7999
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
8000
|
+
if (messages[index]?.role !== "assistant") continue;
|
|
8001
|
+
const next = [...messages];
|
|
8002
|
+
next[index] = { ...next[index], content: normalized };
|
|
8003
|
+
return next;
|
|
8004
|
+
}
|
|
8005
|
+
return [...messages, { role: "assistant", content: normalized }];
|
|
8006
|
+
}
|
|
8007
|
+
function appendAssistantDelta(messages, delta) {
|
|
8008
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
8009
|
+
if (messages[index]?.role !== "assistant") continue;
|
|
8010
|
+
const next = [...messages];
|
|
8011
|
+
next[index] = {
|
|
8012
|
+
...next[index],
|
|
8013
|
+
content: `${contentToText(next[index].content)}${delta}`
|
|
8014
|
+
};
|
|
8015
|
+
return next;
|
|
8016
|
+
}
|
|
8017
|
+
return [...messages, { role: "assistant", content: delta }];
|
|
8018
|
+
}
|
|
7733
8019
|
function FabAI({
|
|
8020
|
+
appContext,
|
|
7734
8021
|
model,
|
|
8022
|
+
memory,
|
|
7735
8023
|
onSendMessage,
|
|
7736
8024
|
position = "bottom-right",
|
|
7737
|
-
starterPrompts = [],
|
|
7738
8025
|
systemPrompt
|
|
7739
8026
|
}) {
|
|
7740
|
-
const { can, user } = useBrokr();
|
|
8027
|
+
const { can, user, theme } = useBrokr();
|
|
8028
|
+
const brandName = theme?.brand?.name;
|
|
7741
8029
|
const [isOpen, setIsOpen] = (0, import_react39.useState)(false);
|
|
7742
8030
|
const [input, setInput] = (0, import_react39.useState)("");
|
|
7743
8031
|
const [error, setError] = (0, import_react39.useState)(null);
|
|
7744
8032
|
const [isSending, setIsSending] = (0, import_react39.useState)(false);
|
|
7745
8033
|
const [messages, setMessages] = (0, import_react39.useState)([]);
|
|
8034
|
+
const conversationIdRef = (0, import_react39.useRef)(`fab_${crypto.randomUUID()}`);
|
|
7746
8035
|
const launcherStyle = (0, import_react39.useMemo)(
|
|
7747
8036
|
() => ({
|
|
7748
8037
|
left: position === "bottom-left" ? "var(--brokr-space-6)" : void 0,
|
|
@@ -7766,37 +8055,75 @@ function FabAI({
|
|
|
7766
8055
|
}, []);
|
|
7767
8056
|
const sendPrompt = (0, import_react39.useCallback)(async (prompt) => {
|
|
7768
8057
|
const nextPrompt = prompt.trim();
|
|
7769
|
-
if (!nextPrompt) return;
|
|
7770
|
-
const
|
|
8058
|
+
if (!nextPrompt || isSending) return;
|
|
8059
|
+
const userMessage = { role: "user", content: nextPrompt };
|
|
8060
|
+
const nextMessages = [...messages, userMessage];
|
|
8061
|
+
const optimisticMessages = [...nextMessages, { role: "assistant", content: "" }];
|
|
7771
8062
|
try {
|
|
7772
8063
|
setError(null);
|
|
7773
8064
|
setIsSending(true);
|
|
7774
|
-
setMessages(
|
|
8065
|
+
setMessages(optimisticMessages);
|
|
7775
8066
|
setInput("");
|
|
7776
|
-
let responseText = "";
|
|
7777
8067
|
if (onSendMessage) {
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
8068
|
+
const responseText2 = await onSendMessage({ messages: nextMessages, model, systemPrompt });
|
|
8069
|
+
setMessages((current) => ensureAssistantReply(current, responseText2));
|
|
8070
|
+
return;
|
|
8071
|
+
}
|
|
8072
|
+
const response = await fetch("/api/brokr/chat", {
|
|
8073
|
+
method: "POST",
|
|
8074
|
+
credentials: "include",
|
|
8075
|
+
headers: { "Content-Type": "application/json" },
|
|
8076
|
+
body: JSON.stringify({
|
|
8077
|
+
conversationId: conversationIdRef.current,
|
|
8078
|
+
messages: trimToTokenBudget(
|
|
8079
|
+
nextMessages.filter((message) => message.role === "user" || message.role === "assistant" && Boolean(contentToText(message.content))).map((message) => ({
|
|
8080
|
+
role: message.role,
|
|
8081
|
+
content: contentToText(message.content)
|
|
8082
|
+
}))
|
|
8083
|
+
),
|
|
8084
|
+
...model !== void 0 ? { model } : {},
|
|
8085
|
+
...(() => {
|
|
8086
|
+
const withContext = buildFabSystemPrompt(appContext, brandName, systemPrompt);
|
|
8087
|
+
const ep = buildEffectivePrompt(withContext, memory);
|
|
8088
|
+
return ep ? { systemPrompt: ep } : {};
|
|
8089
|
+
})()
|
|
8090
|
+
})
|
|
8091
|
+
});
|
|
8092
|
+
if (!response.ok) {
|
|
8093
|
+
const payload2 = await response.json().catch(() => ({}));
|
|
8094
|
+
throw new Error(payload2.message ?? payload2.error ?? `Chat failed (${response.status})`);
|
|
8095
|
+
}
|
|
8096
|
+
if (isSSEResponse(response)) {
|
|
8097
|
+
let hasDelta = false;
|
|
8098
|
+
for await (const event of parseSSEStream(response)) {
|
|
8099
|
+
if (event.type === "conversation") {
|
|
8100
|
+
conversationIdRef.current = event.id;
|
|
8101
|
+
} else if (event.type === "delta") {
|
|
8102
|
+
hasDelta = true;
|
|
8103
|
+
setMessages((current) => appendAssistantDelta(current, event.delta));
|
|
8104
|
+
} else if (event.type === "error") {
|
|
8105
|
+
throw new Error(event.message);
|
|
7786
8106
|
}
|
|
7787
|
-
|
|
7788
|
-
|
|
8107
|
+
}
|
|
8108
|
+
if (!hasDelta) {
|
|
8109
|
+
setMessages((current) => ensureAssistantReply(current, "No response received."));
|
|
8110
|
+
}
|
|
8111
|
+
return;
|
|
8112
|
+
}
|
|
8113
|
+
const payload = await response.json().catch(() => ({}));
|
|
8114
|
+
if (payload.error || payload.message) {
|
|
8115
|
+
throw new Error(payload.message ?? payload.error ?? "AI request failed.");
|
|
7789
8116
|
}
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
content: responseText || "No response received."
|
|
7793
|
-
}]);
|
|
8117
|
+
const responseText = payload.content ?? payload.response ?? payload.text ?? "";
|
|
8118
|
+
setMessages((current) => ensureAssistantReply(current, responseText));
|
|
7794
8119
|
} catch (cause) {
|
|
7795
|
-
|
|
8120
|
+
const message = cause instanceof Error ? cause.message : "Could not send message.";
|
|
8121
|
+
setError(message);
|
|
8122
|
+
setMessages((current) => ensureAssistantReply(current, `Error: ${message}`));
|
|
7796
8123
|
} finally {
|
|
7797
8124
|
setIsSending(false);
|
|
7798
8125
|
}
|
|
7799
|
-
}, [messages, model, onSendMessage, systemPrompt]);
|
|
8126
|
+
}, [isSending, messages, model, onSendMessage, systemPrompt]);
|
|
7800
8127
|
const handleSubmit = (0, import_react39.useCallback)(async (event) => {
|
|
7801
8128
|
event.preventDefault();
|
|
7802
8129
|
await sendPrompt(input);
|
|
@@ -7807,44 +8134,31 @@ function FabAI({
|
|
|
7807
8134
|
void sendPrompt(input);
|
|
7808
8135
|
}
|
|
7809
8136
|
}, [input, sendPrompt]);
|
|
7810
|
-
const handleStarterPrompt = (0, import_react39.useCallback)((prompt) => {
|
|
7811
|
-
void sendPrompt(prompt);
|
|
7812
|
-
}, [sendPrompt]);
|
|
7813
8137
|
return /* @__PURE__ */ import_react39.default.createElement(import_react39.default.Fragment, null, /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-chat-fab", style: launcherStyle }, /* @__PURE__ */ import_react39.default.createElement(
|
|
7814
8138
|
"button",
|
|
7815
8139
|
{
|
|
8140
|
+
"aria-label": isOpen ? "Close AI chat" : "Open AI chat",
|
|
7816
8141
|
"aria-expanded": isOpen,
|
|
7817
8142
|
"aria-haspopup": "dialog",
|
|
7818
|
-
className: "brokr-
|
|
8143
|
+
className: "brokr-chat-fab-trigger",
|
|
7819
8144
|
onClick: toggleOpen,
|
|
7820
8145
|
type: "button"
|
|
7821
8146
|
},
|
|
7822
|
-
/* @__PURE__ */ import_react39.default.createElement(
|
|
7823
|
-
|
|
7824
|
-
)), isOpen ? /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-panel brokr-chat-panel", role: "dialog" }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-section", style: { gap: "0.25rem" } }, /* @__PURE__ */ import_react39.default.createElement("strong", null, "AI Chat"), user?.name ? /* @__PURE__ */ import_react39.default.createElement("span", { className: "brokr-copy" }, user.name) : null), /* @__PURE__ */ import_react39.default.createElement("button", { className: "brokr-button-ghost", onClick: handleClose, type: "button" }, /* @__PURE__ */ import_react39.default.createElement(CloseIcon, { size: 16 }))), /* @__PURE__ */ import_react39.default.createElement(
|
|
8147
|
+
/* @__PURE__ */ import_react39.default.createElement(SparkIcon, { size: 18 })
|
|
8148
|
+
)), isOpen ? /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-panel brokr-chat-panel", role: "dialog" }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ import_react39.default.createElement("strong", null, "AI Chat"), user?.name ? /* @__PURE__ */ import_react39.default.createElement("span", { className: "brokr-copy" }, user.name) : null), /* @__PURE__ */ import_react39.default.createElement("button", { className: "brokr-button-ghost", onClick: handleClose, type: "button" }, /* @__PURE__ */ import_react39.default.createElement(CloseIcon, { size: 16 }))), /* @__PURE__ */ import_react39.default.createElement(
|
|
7825
8149
|
"div",
|
|
7826
8150
|
{
|
|
7827
8151
|
className: "brokr-chat-messages",
|
|
7828
8152
|
"data-empty": messages.length === 0
|
|
7829
8153
|
},
|
|
7830
|
-
messages.length === 0 ? /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-chat-empty" }, /* @__PURE__ */ import_react39.default.createElement(SparkIcon, { size: 18 }), /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-section", style: { gap: "
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
return /* @__PURE__ */ import_react39.default.createElement(
|
|
7835
|
-
|
|
7836
|
-
{
|
|
7837
|
-
className: "brokr-chat-starter",
|
|
7838
|
-
key: prompt,
|
|
7839
|
-
onClick: handleClick,
|
|
7840
|
-
type: "button"
|
|
7841
|
-
},
|
|
7842
|
-
prompt
|
|
7843
|
-
);
|
|
7844
|
-
})) : null) : null,
|
|
7845
|
-
messages.map((message, index) => /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-chat-bubble", "data-role": message.role, key: `${message.role}-${index}` }, contentToText(message.content))),
|
|
8154
|
+
messages.length === 0 ? /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-chat-empty" }, /* @__PURE__ */ import_react39.default.createElement(SparkIcon, { size: 18 }), /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ import_react39.default.createElement("strong", null, "Send a message to chat with the AI."), /* @__PURE__ */ import_react39.default.createElement("span", { className: "brokr-copy" }, "Ask a question or drop in a starter prompt below."))) : null,
|
|
8155
|
+
messages.map((message, index) => {
|
|
8156
|
+
const text = contentToText(message.content);
|
|
8157
|
+
const isTyping = message.role === "assistant" && !text && isSending && index === messages.length - 1;
|
|
8158
|
+
return /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-chat-bubble", "data-role": message.role, key: `${message.role}-${index}` }, isTyping ? /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-ai-chat-typing", "aria-label": "AI is typing" }, /* @__PURE__ */ import_react39.default.createElement("span", null), /* @__PURE__ */ import_react39.default.createElement("span", null), /* @__PURE__ */ import_react39.default.createElement("span", null)) : message.role === "assistant" ? /* @__PURE__ */ import_react39.default.createElement(MarkdownRenderer, { content: text }) : text);
|
|
8159
|
+
}),
|
|
7846
8160
|
error ? /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null
|
|
7847
|
-
), /* @__PURE__ */ import_react39.default.createElement("form", { className: "brokr-section", onSubmit: handleSubmit, style: { gap: "
|
|
8161
|
+
), /* @__PURE__ */ import_react39.default.createElement("form", { className: "brokr-section", onSubmit: handleSubmit, style: { gap: "var(--brokr-space-3)" } }, /* @__PURE__ */ import_react39.default.createElement(
|
|
7848
8162
|
"textarea",
|
|
7849
8163
|
{
|
|
7850
8164
|
className: "brokr-textarea brokr-chat-input",
|
|
@@ -7854,7 +8168,7 @@ function FabAI({
|
|
|
7854
8168
|
rows: 2,
|
|
7855
8169
|
value: input
|
|
7856
8170
|
}
|
|
7857
|
-
), /* @__PURE__ */ import_react39.default.createElement("button", { className: "brokr-button", disabled: isSending, type: "submit" }, isSending ? "Thinking" : "Send"))) : null);
|
|
8171
|
+
), /* @__PURE__ */ import_react39.default.createElement("button", { className: "brokr-button", disabled: isSending || !input.trim(), type: "submit" }, isSending ? "Thinking" : "Send"))) : null);
|
|
7858
8172
|
}
|
|
7859
8173
|
|
|
7860
8174
|
// src/react/composites/SmartUpload.tsx
|
|
@@ -7937,7 +8251,7 @@ function SmartUpload({
|
|
|
7937
8251
|
const handleBrowse = (0, import_react40.useCallback)(() => {
|
|
7938
8252
|
inputRef.current?.click();
|
|
7939
8253
|
}, []);
|
|
7940
|
-
return /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-card brokr-upload-shell" }, /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-section", style: { gap: "
|
|
8254
|
+
return /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-card brokr-upload-shell" }, /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ import_react40.default.createElement("strong", null, "Upload files"), /* @__PURE__ */ import_react40.default.createElement("span", { className: "brokr-copy" }, "Drop a file and let Brokr handle the boring part.")), paymentsMode === "sandbox" ? /* @__PURE__ */ import_react40.default.createElement("span", { className: "brokr-badge brokr-badge-sandbox" }, "Sandbox") : null), /* @__PURE__ */ import_react40.default.createElement(
|
|
7941
8255
|
"label",
|
|
7942
8256
|
{
|
|
7943
8257
|
className: "brokr-upload-dropzone",
|
|
@@ -7962,7 +8276,7 @@ function SmartUpload({
|
|
|
7962
8276
|
ref: inputRef,
|
|
7963
8277
|
type: "file"
|
|
7964
8278
|
}
|
|
7965
|
-
), fileName ? /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-card brokr-upload-file" }, /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-section", style: { gap: "
|
|
8279
|
+
), fileName ? /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-card brokr-upload-file" }, /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ import_react40.default.createElement("strong", null, fileName), /* @__PURE__ */ import_react40.default.createElement("span", { className: "brokr-copy" }, isUploading ? `Uploading ${progress}%` : progress === 100 ? "Processed" : "Queued")), /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-meter-bar" }, /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-meter-fill", style: { width: `${progress}%` } }))) : null, error ? /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null);
|
|
7966
8280
|
}
|
|
7967
8281
|
|
|
7968
8282
|
// src/react/composites/FeedbackWidget.tsx
|
|
@@ -8327,6 +8641,28 @@ function timeAgo(iso) {
|
|
|
8327
8641
|
const days = Math.floor(hours / 24);
|
|
8328
8642
|
return `${days}d ago`;
|
|
8329
8643
|
}
|
|
8644
|
+
function NotifDropdownItem({
|
|
8645
|
+
notif,
|
|
8646
|
+
registry,
|
|
8647
|
+
onClick
|
|
8648
|
+
}) {
|
|
8649
|
+
const handleClick = (0, import_react44.useCallback)(() => onClick(notif), [notif, onClick]);
|
|
8650
|
+
const notifData = notif.data ?? {};
|
|
8651
|
+
const notifType = notifData.type ?? "default";
|
|
8652
|
+
const resolved = resolveNotificationType(registry, notifType, notifData);
|
|
8653
|
+
return /* @__PURE__ */ import_react44.default.createElement(
|
|
8654
|
+
"button",
|
|
8655
|
+
{
|
|
8656
|
+
type: "button",
|
|
8657
|
+
className: `brokr-notif-item${notif.read ? "" : " brokr-notif-item--unread"}`,
|
|
8658
|
+
onClick: handleClick,
|
|
8659
|
+
role: "menuitem"
|
|
8660
|
+
},
|
|
8661
|
+
resolved.image ? /* @__PURE__ */ import_react44.default.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-notif-item-logo" }) : /* @__PURE__ */ import_react44.default.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
|
|
8662
|
+
/* @__PURE__ */ import_react44.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
|
|
8663
|
+
/* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-item-time" }, timeAgo(notif.createdAt))
|
|
8664
|
+
);
|
|
8665
|
+
}
|
|
8330
8666
|
function NotificationBell() {
|
|
8331
8667
|
const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
|
|
8332
8668
|
const [open, setOpen] = (0, import_react44.useState)(false);
|
|
@@ -8391,7 +8727,8 @@ function NotificationBell() {
|
|
|
8391
8727
|
className: "brokr-notif-bell",
|
|
8392
8728
|
onClick: toggle,
|
|
8393
8729
|
"aria-label": `Notifications${unreadCount > 0 ? ` (${unreadCount} unread)` : ""}`,
|
|
8394
|
-
"aria-expanded": open
|
|
8730
|
+
"aria-expanded": open,
|
|
8731
|
+
"aria-haspopup": "menu"
|
|
8395
8732
|
},
|
|
8396
8733
|
/* @__PURE__ */ import_react44.default.createElement("svg", { "aria-hidden": "true", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react44.default.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ import_react44.default.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })),
|
|
8397
8734
|
unreadCount > 0 && /* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-badge" }, unreadCount > 99 ? "99+" : unreadCount)
|
|
@@ -8403,31 +8740,15 @@ function NotificationBell() {
|
|
|
8403
8740
|
onClick: markAllRead
|
|
8404
8741
|
},
|
|
8405
8742
|
"Mark all read"
|
|
8406
|
-
)), /* @__PURE__ */ import_react44.default.createElement("div", { className: "brokr-notif-dropdown-list" }, isLoading ? /* @__PURE__ */ import_react44.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-empty-text" }, "Loading\u2026")) : sorted.length === 0 ? /* @__PURE__ */ import_react44.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : sorted.map((notif) =>
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
className: `brokr-notif-item${notif.read ? "" : " brokr-notif-item--unread"}`,
|
|
8416
|
-
onClick: () => handleItemClick(notif),
|
|
8417
|
-
role: "menuitem"
|
|
8418
|
-
},
|
|
8419
|
-
resolved.image ? /* @__PURE__ */ import_react44.default.createElement(
|
|
8420
|
-
"img",
|
|
8421
|
-
{
|
|
8422
|
-
src: resolved.image.url,
|
|
8423
|
-
alt: resolved.image.alt,
|
|
8424
|
-
className: "brokr-notif-item-logo"
|
|
8425
|
-
}
|
|
8426
|
-
) : /* @__PURE__ */ import_react44.default.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
|
|
8427
|
-
/* @__PURE__ */ import_react44.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
|
|
8428
|
-
/* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-item-time" }, timeAgo(notif.createdAt))
|
|
8429
|
-
);
|
|
8430
|
-
}))));
|
|
8743
|
+
)), /* @__PURE__ */ import_react44.default.createElement("div", { className: "brokr-notif-dropdown-list" }, isLoading ? /* @__PURE__ */ import_react44.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-empty-text" }, "Loading\u2026")) : sorted.length === 0 ? /* @__PURE__ */ import_react44.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react44.default.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : sorted.map((notif) => /* @__PURE__ */ import_react44.default.createElement(
|
|
8744
|
+
NotifDropdownItem,
|
|
8745
|
+
{
|
|
8746
|
+
key: notif.id,
|
|
8747
|
+
notif,
|
|
8748
|
+
registry,
|
|
8749
|
+
onClick: handleItemClick
|
|
8750
|
+
}
|
|
8751
|
+
)))));
|
|
8431
8752
|
}
|
|
8432
8753
|
|
|
8433
8754
|
// src/react/notifications/NotificationList.tsx
|
|
@@ -8444,6 +8765,27 @@ function formatTimestamp(iso) {
|
|
|
8444
8765
|
function NotificationListSkeleton() {
|
|
8445
8766
|
return /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-notif-list-items" }, [1, 2, 3].map((i) => /* @__PURE__ */ import_react45.default.createElement("div", { key: i, className: "brokr-notif-list-row brokr-notif-list-row--skeleton" }, /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-item-dot brokr-notif-item-dot--skeleton" }), /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-item-title brokr-skeleton-line", style: { width: "60%" } }), /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-item-message brokr-skeleton-line", style: { width: "80%" } })), /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-item-time brokr-skeleton-line", style: { width: 48 } }))));
|
|
8446
8767
|
}
|
|
8768
|
+
function NotifListItem({
|
|
8769
|
+
notif,
|
|
8770
|
+
registry,
|
|
8771
|
+
onClick
|
|
8772
|
+
}) {
|
|
8773
|
+
const handleClick = (0, import_react45.useCallback)(() => onClick(notif), [notif, onClick]);
|
|
8774
|
+
const notifData = notif.data ?? {};
|
|
8775
|
+
const notifType = notif.type ?? notifData.type ?? "default";
|
|
8776
|
+
const resolved = resolveNotificationType(registry, notifType, notifData);
|
|
8777
|
+
return /* @__PURE__ */ import_react45.default.createElement(
|
|
8778
|
+
"button",
|
|
8779
|
+
{
|
|
8780
|
+
type: "button",
|
|
8781
|
+
className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
|
|
8782
|
+
onClick: handleClick
|
|
8783
|
+
},
|
|
8784
|
+
resolved.image ? /* @__PURE__ */ import_react45.default.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-notif-item-logo" }) : /* @__PURE__ */ import_react45.default.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
|
|
8785
|
+
/* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
|
|
8786
|
+
/* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-item-time" }, formatTimestamp(notif.createdAt))
|
|
8787
|
+
);
|
|
8788
|
+
}
|
|
8447
8789
|
function NotificationList() {
|
|
8448
8790
|
const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
|
|
8449
8791
|
const sorted = (0, import_react45.useMemo)(
|
|
@@ -8470,30 +8812,15 @@ function NotificationList() {
|
|
|
8470
8812
|
onClick: markAllRead
|
|
8471
8813
|
},
|
|
8472
8814
|
"Mark all read"
|
|
8473
|
-
)), isLoading ? /* @__PURE__ */ import_react45.default.createElement(NotificationListSkeleton, null) : sorted.length === 0 ? /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react45.default.createElement("svg", { "aria-hidden": "true", width: "40", height: "40", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", style: { opacity: 0.3 } }, /* @__PURE__ */ import_react45.default.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ import_react45.default.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })), /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-notif-list-items" }, sorted.map((notif) =>
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
|
|
8483
|
-
onClick: () => handleClick(notif)
|
|
8484
|
-
},
|
|
8485
|
-
resolved.image ? /* @__PURE__ */ import_react45.default.createElement(
|
|
8486
|
-
"img",
|
|
8487
|
-
{
|
|
8488
|
-
src: resolved.image.url,
|
|
8489
|
-
alt: resolved.image.alt,
|
|
8490
|
-
className: "brokr-notif-item-logo"
|
|
8491
|
-
}
|
|
8492
|
-
) : /* @__PURE__ */ import_react45.default.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
|
|
8493
|
-
/* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
|
|
8494
|
-
/* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-item-time" }, formatTimestamp(notif.createdAt))
|
|
8495
|
-
);
|
|
8496
|
-
})));
|
|
8815
|
+
)), isLoading ? /* @__PURE__ */ import_react45.default.createElement(NotificationListSkeleton, null) : sorted.length === 0 ? /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react45.default.createElement("svg", { "aria-hidden": "true", width: "40", height: "40", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", style: { opacity: 0.3 } }, /* @__PURE__ */ import_react45.default.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ import_react45.default.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })), /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-notif-list-items" }, sorted.map((notif) => /* @__PURE__ */ import_react45.default.createElement(
|
|
8816
|
+
NotifListItem,
|
|
8817
|
+
{
|
|
8818
|
+
key: notif.id,
|
|
8819
|
+
notif,
|
|
8820
|
+
registry,
|
|
8821
|
+
onClick: handleClick
|
|
8822
|
+
}
|
|
8823
|
+
))));
|
|
8497
8824
|
}
|
|
8498
8825
|
|
|
8499
8826
|
// src/react/hooks/use-user.ts
|