@brokr/sdk 2.1.0 → 2.1.2
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-styles.js +540 -95
- package/dist/react-styles.mjs +540 -95
- package/dist/react.js +665 -331
- package/dist/react.mjs +548 -217
- 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/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/MarkdownRenderer.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/activity-feed.d.ts +2 -0
- package/dist/src/react/css/activity-feed.d.ts.map +1 -0
- 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/data-table.d.ts +2 -0
- package/dist/src/react/css/data-table.d.ts.map +1 -0
- 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/index.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/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/stats-grid.d.ts +2 -0
- package/dist/src/react/css/stats-grid.d.ts.map +1 -0
- package/dist/src/react/css/usage-grid.d.ts +2 -0
- package/dist/src/react/css/usage-grid.d.ts.map +1 -0
- package/dist/src/react/index.d.ts +4 -1
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/react/notifications/ActivityFeed.d.ts +14 -0
- package/dist/src/react/notifications/ActivityFeed.d.ts.map +1 -0
- package/dist/src/react/payments/UsageGrid.d.ts +11 -0
- package/dist/src/react/payments/UsageGrid.d.ts.map +1 -0
- package/dist/src/react/primitives/DataTable.d.ts +28 -0
- package/dist/src/react/primitives/DataTable.d.ts.map +1 -0
- package/dist/src/react/primitives/StatsGrid.d.ts +18 -0
- package/dist/src/react/primitives/StatsGrid.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/react.mjs
CHANGED
|
@@ -4197,6 +4197,7 @@ import React2, {
|
|
|
4197
4197
|
} from "react";
|
|
4198
4198
|
|
|
4199
4199
|
// src/models.ts
|
|
4200
|
+
var STACK_DEFAULT = "__stack_default__";
|
|
4200
4201
|
var providers = [
|
|
4201
4202
|
{ id: "deepseek", label: "Deepseek", model: "deepseek-chat", color: "#0EA5E9", free: true, logo: "https://assets.brokr.sh/sdk/deepseek_logo.png" },
|
|
4202
4203
|
{ id: "openai", label: "ChatGPT", model: "gpt-5.4-mini", color: "#10B981", free: false, logo: "https://assets.brokr.sh/sdk/gpt_logo.png" },
|
|
@@ -6047,7 +6048,7 @@ function PlanCard({
|
|
|
6047
6048
|
const handleClick = useCallback12(() => {
|
|
6048
6049
|
void onSelect(plan.slug);
|
|
6049
6050
|
}, [plan.slug, onSelect]);
|
|
6050
|
-
return /* @__PURE__ */ React21.createElement("article", { className: "brokr-card brokr-plan-card", "data-highlight": isHighlighted, key: plan.slug }, /* @__PURE__ */ React21.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-3)" } }, /* @__PURE__ */ React21.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ React21.createElement("strong", null, plan.name), plan.isCurrent ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-badge" }, "Current") : null, isHighlighted && !plan.isCurrent ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-badge" }, "Recommended") : null), /* @__PURE__ */ React21.createElement("div", { className: "brokr-plan-price" }, /* @__PURE__ */ React21.createElement("span", { className: "brokr-plan-value" }, plan.price === 0 ? "$0" : `$${(plan.price / 100).toFixed(0)}`), /* @__PURE__ */ React21.createElement("span", { className: "brokr-copy" }, "/", plan.interval)), plan.trialDays ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-copy" }, plan.trialDays, " day trial included.") : null), /* @__PURE__ */ React21.createElement(
|
|
6051
|
+
return /* @__PURE__ */ React21.createElement("article", { className: "brokr-card brokr-plan-card", "data-highlight": isHighlighted, key: plan.slug }, /* @__PURE__ */ React21.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-3)" } }, /* @__PURE__ */ React21.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ React21.createElement("strong", null, plan.name), plan.isCurrent ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-badge" }, "Current") : null, isHighlighted && !plan.isCurrent ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-badge" }, "Recommended") : null), /* @__PURE__ */ React21.createElement("div", { className: "brokr-plan-price" }, /* @__PURE__ */ React21.createElement("span", { className: "brokr-plan-value" }, plan.price === 0 ? "$0" : `$${(plan.price / 100).toFixed(0)}`), /* @__PURE__ */ React21.createElement("span", { className: "brokr-copy" }, "/", plan.interval)), plan.trialDays ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-copy" }, plan.trialDays, " day trial included.") : null), /* @__PURE__ */ React21.createElement("ul", { className: "brokr-feature-list" }, features.map(([key, value]) => /* @__PURE__ */ React21.createElement("li", { className: "brokr-feature-item", key }, /* @__PURE__ */ React21.createElement(CheckIcon, { size: 14 }), /* @__PURE__ */ React21.createElement("span", null, featureLabel(key), " \xB7 ", featureValueLabel(value))))), /* @__PURE__ */ React21.createElement(
|
|
6051
6052
|
"button",
|
|
6052
6053
|
{
|
|
6053
6054
|
className: plan.isCurrent ? "brokr-button-secondary" : "brokr-button",
|
|
@@ -6056,7 +6057,7 @@ function PlanCard({
|
|
|
6056
6057
|
type: "button"
|
|
6057
6058
|
},
|
|
6058
6059
|
plan.isCurrent ? "Current plan" : isPending ? "Redirecting" : "Choose plan"
|
|
6059
|
-
)
|
|
6060
|
+
));
|
|
6060
6061
|
}
|
|
6061
6062
|
function Plans({
|
|
6062
6063
|
columns,
|
|
@@ -6283,11 +6284,28 @@ function CancelSubscription({ onCancel }) {
|
|
|
6283
6284
|
return /* @__PURE__ */ React28.createElement("div", { className: "brokr-section", style: { gap: "0.75rem" } }, confirming ? /* @__PURE__ */ React28.createElement("div", { className: "brokr-inline-message" }, "Subscription changes are confirmed in billing. Continue?") : null, /* @__PURE__ */ React28.createElement("div", { className: "brokr-brand-row" }, /* @__PURE__ */ React28.createElement("button", { className: "brokr-button-ghost", disabled: isPending, onClick: handlePrimaryClick, type: "button" }, isPending ? "Opening billing" : confirming ? "Continue" : "Cancel subscription"), confirming ? /* @__PURE__ */ React28.createElement("button", { className: "brokr-button-secondary", onClick: handleDismiss, type: "button" }, "Keep subscription") : null));
|
|
6284
6285
|
}
|
|
6285
6286
|
|
|
6287
|
+
// src/react/payments/UsageGrid.tsx
|
|
6288
|
+
import React29, { useMemo as useMemo18 } from "react";
|
|
6289
|
+
function UsageGrid() {
|
|
6290
|
+
const { entitlements, isLoaded } = useBrokr();
|
|
6291
|
+
const meteredFeatures = useMemo18(() => {
|
|
6292
|
+
if (!entitlements?.features) return [];
|
|
6293
|
+
return Object.entries(entitlements.features).filter(([_, check]) => check.limitType === "numeric").map(([slug]) => slug);
|
|
6294
|
+
}, [entitlements]);
|
|
6295
|
+
if (!isLoaded) {
|
|
6296
|
+
return /* @__PURE__ */ React29.createElement("div", { className: "brokr-usage-grid" }, /* @__PURE__ */ React29.createElement(Skeleton, { height: 72 }), /* @__PURE__ */ React29.createElement(Skeleton, { height: 72 }));
|
|
6297
|
+
}
|
|
6298
|
+
if (meteredFeatures.length === 0) {
|
|
6299
|
+
return null;
|
|
6300
|
+
}
|
|
6301
|
+
return /* @__PURE__ */ React29.createElement("div", { className: "brokr-usage-grid" }, meteredFeatures.map((slug) => /* @__PURE__ */ React29.createElement(FeatureMeter, { key: slug, feature: slug })));
|
|
6302
|
+
}
|
|
6303
|
+
|
|
6286
6304
|
// src/react/chat/AIChat.tsx
|
|
6287
|
-
import
|
|
6305
|
+
import React36, {
|
|
6288
6306
|
useCallback as useCallback23,
|
|
6289
6307
|
useEffect as useEffect12,
|
|
6290
|
-
useMemo as
|
|
6308
|
+
useMemo as useMemo24,
|
|
6291
6309
|
useRef as useRef7,
|
|
6292
6310
|
useState as useState18
|
|
6293
6311
|
} from "react";
|
|
@@ -6305,7 +6323,7 @@ function useChatState() {
|
|
|
6305
6323
|
}
|
|
6306
6324
|
|
|
6307
6325
|
// src/react/chat/useChat.ts
|
|
6308
|
-
import { useCallback as useCallback16, useEffect as useEffect9, useMemo as
|
|
6326
|
+
import { useCallback as useCallback16, useEffect as useEffect9, useMemo as useMemo19, useRef as useRef5, useState as useState16, useContext as useContext3 } from "react";
|
|
6309
6327
|
|
|
6310
6328
|
// src/ai/types.ts
|
|
6311
6329
|
function contentToText(content) {
|
|
@@ -6410,6 +6428,39 @@ function isSSEResponse(response) {
|
|
|
6410
6428
|
return ct.includes("text/event-stream") && response.body !== null;
|
|
6411
6429
|
}
|
|
6412
6430
|
|
|
6431
|
+
// src/react/chat/memory.ts
|
|
6432
|
+
var MAX_MEMORY_CHARS = 2e3;
|
|
6433
|
+
function serializeMemory(obj, prefix = "") {
|
|
6434
|
+
const lines = [];
|
|
6435
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
6436
|
+
if (value === null || value === void 0) continue;
|
|
6437
|
+
const label = prefix ? `${prefix}.${key}` : key;
|
|
6438
|
+
if (Array.isArray(value)) {
|
|
6439
|
+
lines.push(`${label}: ${value.join(", ")}`);
|
|
6440
|
+
} else if (typeof value === "object") {
|
|
6441
|
+
lines.push(...serializeMemory(value, label));
|
|
6442
|
+
} else {
|
|
6443
|
+
lines.push(`${label}: ${String(value)}`);
|
|
6444
|
+
}
|
|
6445
|
+
}
|
|
6446
|
+
return lines;
|
|
6447
|
+
}
|
|
6448
|
+
function buildEffectivePrompt(prompt, memory) {
|
|
6449
|
+
if (!memory || Object.keys(memory).length === 0) return prompt;
|
|
6450
|
+
let block = serializeMemory(memory).join("\n");
|
|
6451
|
+
if (block.length > MAX_MEMORY_CHARS) {
|
|
6452
|
+
block = block.slice(0, MAX_MEMORY_CHARS);
|
|
6453
|
+
if (typeof console !== "undefined") {
|
|
6454
|
+
console.warn("[brokr] memory exceeded 2000 char limit, truncated");
|
|
6455
|
+
}
|
|
6456
|
+
}
|
|
6457
|
+
const memSection = `[User context]
|
|
6458
|
+
${block}`;
|
|
6459
|
+
return prompt ? `${memSection}
|
|
6460
|
+
|
|
6461
|
+
${prompt}` : memSection;
|
|
6462
|
+
}
|
|
6463
|
+
|
|
6413
6464
|
// src/react/chat/useChat.ts
|
|
6414
6465
|
function makeId(prefix) {
|
|
6415
6466
|
return `${prefix}_${crypto.randomUUID()}`;
|
|
@@ -6427,7 +6478,9 @@ function useChat(config) {
|
|
|
6427
6478
|
const {
|
|
6428
6479
|
endpoint,
|
|
6429
6480
|
prompt,
|
|
6430
|
-
|
|
6481
|
+
explicitModel,
|
|
6482
|
+
displayModel: activeModel,
|
|
6483
|
+
memory,
|
|
6431
6484
|
persist,
|
|
6432
6485
|
surface,
|
|
6433
6486
|
subject,
|
|
@@ -6462,31 +6515,47 @@ function useChat(config) {
|
|
|
6462
6515
|
const bottomRef = useRef5(null);
|
|
6463
6516
|
const scrollContainerRef = useRef5(null);
|
|
6464
6517
|
const sentinelRef = useRef5(null);
|
|
6465
|
-
const displaySidebarItems =
|
|
6466
|
-
if (isControlled)
|
|
6467
|
-
|
|
6468
|
-
|
|
6518
|
+
const displaySidebarItems = useMemo19(() => {
|
|
6519
|
+
if (isControlled) {
|
|
6520
|
+
return threadsProp?.map((t) => ({
|
|
6521
|
+
id: t.id,
|
|
6522
|
+
title: t.title,
|
|
6523
|
+
updatedAt: t.updatedAt ?? null
|
|
6524
|
+
})) ?? [];
|
|
6525
|
+
}
|
|
6526
|
+
if (isPersist) {
|
|
6527
|
+
return serverThreads.map((t) => ({
|
|
6528
|
+
id: t.id,
|
|
6529
|
+
title: t.title,
|
|
6530
|
+
updatedAt: t.updatedAt ?? null
|
|
6531
|
+
}));
|
|
6532
|
+
}
|
|
6533
|
+
return memConversations.map((c) => ({
|
|
6534
|
+
id: c.id,
|
|
6535
|
+
title: c.title || "New chat",
|
|
6536
|
+
updatedAt: new Date(c.updatedAt).toISOString()
|
|
6537
|
+
}));
|
|
6469
6538
|
}, [isControlled, isPersist, threadsProp, serverThreads, memConversations]);
|
|
6470
|
-
const activeId =
|
|
6539
|
+
const activeId = useMemo19(() => {
|
|
6471
6540
|
if (isControlled) return activeThreadIdProp ?? null;
|
|
6472
6541
|
if (isPersist) return serverActiveId;
|
|
6473
6542
|
return memActiveId;
|
|
6474
6543
|
}, [isControlled, isPersist, activeThreadIdProp, serverActiveId, memActiveId]);
|
|
6475
|
-
const displayMessages =
|
|
6544
|
+
const displayMessages = useMemo19(() => {
|
|
6476
6545
|
if (isControlled) return messagesProp ?? [];
|
|
6477
6546
|
if (isPersist) return serverMessages;
|
|
6478
6547
|
return memConversations.find((c) => c.id === memActiveId)?.messages ?? [];
|
|
6479
6548
|
}, [isControlled, isPersist, messagesProp, serverMessages, memConversations, memActiveId]);
|
|
6480
|
-
const activeMemConv =
|
|
6549
|
+
const activeMemConv = useMemo19(
|
|
6481
6550
|
() => memConversations.find((c) => c.id === memActiveId) ?? null,
|
|
6482
6551
|
[memConversations, memActiveId]
|
|
6483
6552
|
);
|
|
6484
|
-
const activeThread =
|
|
6553
|
+
const activeThread = useMemo19(() => {
|
|
6485
6554
|
if (isControlled) return threadsProp?.find((t) => t.id === activeId) ?? null;
|
|
6486
6555
|
if (isPersist) return serverThreads.find((t) => t.id === activeId) ?? null;
|
|
6487
6556
|
return null;
|
|
6488
6557
|
}, [isControlled, isPersist, threadsProp, serverThreads, activeId]);
|
|
6489
|
-
const renderedTitle =
|
|
6558
|
+
const renderedTitle = useMemo19(() => {
|
|
6490
6559
|
if (isPersist && activeThread) return activeThread.title;
|
|
6491
6560
|
if (!isPersist && activeMemConv) {
|
|
6492
6561
|
if (activeMemConv.titleState === "loading") return "Naming conversation";
|
|
@@ -6646,8 +6715,11 @@ function useChat(config) {
|
|
|
6646
6715
|
messages: trimToTokenBudget(
|
|
6647
6716
|
nextMessages.filter((m) => m.role === "user" || m.role === "assistant" && m.content).map(({ content: mc, role }) => ({ role, content: mc }))
|
|
6648
6717
|
),
|
|
6649
|
-
model:
|
|
6650
|
-
...
|
|
6718
|
+
...explicitModel ? { model: explicitModel } : {},
|
|
6719
|
+
...(() => {
|
|
6720
|
+
const ep = buildEffectivePrompt(prompt, memory);
|
|
6721
|
+
return ep ? { systemPrompt: ep } : {};
|
|
6722
|
+
})()
|
|
6651
6723
|
})
|
|
6652
6724
|
});
|
|
6653
6725
|
if (!response.ok) {
|
|
@@ -6692,8 +6764,10 @@ function useChat(config) {
|
|
|
6692
6764
|
ensureMemConversation,
|
|
6693
6765
|
memConversations,
|
|
6694
6766
|
endpoint,
|
|
6767
|
+
explicitModel,
|
|
6695
6768
|
activeModel,
|
|
6696
6769
|
prompt,
|
|
6770
|
+
memory,
|
|
6697
6771
|
updateMemMessages,
|
|
6698
6772
|
appendMemDelta,
|
|
6699
6773
|
animateTitle,
|
|
@@ -6715,9 +6789,12 @@ function useChat(config) {
|
|
|
6715
6789
|
content,
|
|
6716
6790
|
surface,
|
|
6717
6791
|
subject: subject ?? null,
|
|
6718
|
-
model:
|
|
6792
|
+
...explicitModel ? { model: explicitModel } : {},
|
|
6719
6793
|
userId: userId ?? void 0,
|
|
6720
|
-
...
|
|
6794
|
+
...(() => {
|
|
6795
|
+
const ep = buildEffectivePrompt(prompt, memory);
|
|
6796
|
+
return ep ? { systemPrompt: ep } : {};
|
|
6797
|
+
})()
|
|
6721
6798
|
})
|
|
6722
6799
|
});
|
|
6723
6800
|
if (!response.ok) {
|
|
@@ -6772,8 +6849,10 @@ function useChat(config) {
|
|
|
6772
6849
|
endpoint,
|
|
6773
6850
|
surface,
|
|
6774
6851
|
subject,
|
|
6852
|
+
explicitModel,
|
|
6775
6853
|
activeModel,
|
|
6776
6854
|
prompt,
|
|
6855
|
+
memory,
|
|
6777
6856
|
userId,
|
|
6778
6857
|
setServerMessages,
|
|
6779
6858
|
setServerActiveId,
|
|
@@ -6930,7 +7009,7 @@ function useChat(config) {
|
|
|
6930
7009
|
}
|
|
6931
7010
|
|
|
6932
7011
|
// src/react/chat/ModelSelector.tsx
|
|
6933
|
-
import
|
|
7012
|
+
import React30, { useCallback as useCallback17, useEffect as useEffect10, useMemo as useMemo20, useRef as useRef6, useState as useState17 } from "react";
|
|
6934
7013
|
function ModelOption({
|
|
6935
7014
|
provider,
|
|
6936
7015
|
isActive,
|
|
@@ -6945,7 +7024,7 @@ function ModelOption({
|
|
|
6945
7024
|
onSelect(provider.model);
|
|
6946
7025
|
}
|
|
6947
7026
|
}, [isLocked, onCheckout, onSelect, provider.model]);
|
|
6948
|
-
return /* @__PURE__ */
|
|
7027
|
+
return /* @__PURE__ */ React30.createElement(
|
|
6949
7028
|
"button",
|
|
6950
7029
|
{
|
|
6951
7030
|
"aria-selected": isActive,
|
|
@@ -6957,9 +7036,9 @@ function ModelOption({
|
|
|
6957
7036
|
title: isLocked ? "Add credits to unlock" : void 0,
|
|
6958
7037
|
type: "button"
|
|
6959
7038
|
},
|
|
6960
|
-
/* @__PURE__ */
|
|
6961
|
-
/* @__PURE__ */
|
|
6962
|
-
isLocked ? /* @__PURE__ */
|
|
7039
|
+
/* @__PURE__ */ React30.createElement("img", { alt: "", className: "brokr-model-logo", src: provider.logo }),
|
|
7040
|
+
/* @__PURE__ */ React30.createElement("span", { className: "brokr-model-option-label" }, provider.label),
|
|
7041
|
+
isLocked ? /* @__PURE__ */ React30.createElement("span", { className: "brokr-model-lock", "aria-hidden": "true" }, /* @__PURE__ */ React30.createElement(LockIcon, { size: 13 })) : null
|
|
6963
7042
|
);
|
|
6964
7043
|
}
|
|
6965
7044
|
function ModelSelector({
|
|
@@ -6970,12 +7049,17 @@ function ModelSelector({
|
|
|
6970
7049
|
}) {
|
|
6971
7050
|
const [selectorOpen, setSelectorOpen] = useState17(false);
|
|
6972
7051
|
const selectorRef = useRef6(null);
|
|
6973
|
-
const
|
|
6974
|
-
|
|
6975
|
-
[
|
|
7052
|
+
const isAuto = activeModel === STACK_DEFAULT || !providers.some((p) => p.model === activeModel);
|
|
7053
|
+
const activeProvider = useMemo20(
|
|
7054
|
+
() => isAuto ? void 0 : providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7055
|
+
[activeModel, isAuto]
|
|
6976
7056
|
);
|
|
6977
7057
|
const handleModelSelect = useCallback17((model) => {
|
|
6978
|
-
setSelectedModel(model);
|
|
7058
|
+
setSelectedModel(model === STACK_DEFAULT ? null : model);
|
|
7059
|
+
setSelectorOpen(false);
|
|
7060
|
+
}, [setSelectedModel]);
|
|
7061
|
+
const handleAutoSelect = useCallback17(() => {
|
|
7062
|
+
setSelectedModel(null);
|
|
6979
7063
|
setSelectorOpen(false);
|
|
6980
7064
|
}, [setSelectedModel]);
|
|
6981
7065
|
useEffect10(() => {
|
|
@@ -6996,7 +7080,7 @@ function ModelSelector({
|
|
|
6996
7080
|
const handleToggle = useCallback17(() => {
|
|
6997
7081
|
setSelectorOpen((v) => !v);
|
|
6998
7082
|
}, []);
|
|
6999
|
-
return /* @__PURE__ */
|
|
7083
|
+
return /* @__PURE__ */ React30.createElement("div", { className: "brokr-model-selector", ref: selectorRef }, /* @__PURE__ */ React30.createElement(
|
|
7000
7084
|
"button",
|
|
7001
7085
|
{
|
|
7002
7086
|
"aria-expanded": selectorOpen,
|
|
@@ -7005,15 +7089,26 @@ function ModelSelector({
|
|
|
7005
7089
|
onClick: handleToggle,
|
|
7006
7090
|
type: "button"
|
|
7007
7091
|
},
|
|
7008
|
-
activeProvider ? /* @__PURE__ */
|
|
7009
|
-
activeProvider?.label ?? "Model",
|
|
7010
|
-
/* @__PURE__ */
|
|
7011
|
-
), selectorOpen ? /* @__PURE__ */
|
|
7092
|
+
activeProvider ? /* @__PURE__ */ React30.createElement("img", { alt: "", className: "brokr-model-logo", src: activeProvider.logo }) : /* @__PURE__ */ React30.createElement("span", { className: "brokr-model-dot", style: { background: "#6B7280" } }),
|
|
7093
|
+
isAuto ? "Auto" : activeProvider?.label ?? "Model",
|
|
7094
|
+
/* @__PURE__ */ React30.createElement(ChevronDownIcon, { size: 13 })
|
|
7095
|
+
), selectorOpen ? /* @__PURE__ */ React30.createElement("div", { className: "brokr-model-dropdown", role: "listbox" }, /* @__PURE__ */ React30.createElement(
|
|
7096
|
+
"button",
|
|
7097
|
+
{
|
|
7098
|
+
"aria-selected": isAuto,
|
|
7099
|
+
className: "brokr-model-option",
|
|
7100
|
+
"data-active": isAuto,
|
|
7101
|
+
onClick: handleAutoSelect,
|
|
7102
|
+
type: "button"
|
|
7103
|
+
},
|
|
7104
|
+
/* @__PURE__ */ React30.createElement("span", { className: "brokr-model-dot", style: { background: "#6B7280" } }),
|
|
7105
|
+
/* @__PURE__ */ React30.createElement("span", { className: "brokr-model-option-label" }, "Stack Default")
|
|
7106
|
+
), providers.map((p) => /* @__PURE__ */ React30.createElement(
|
|
7012
7107
|
ModelOption,
|
|
7013
7108
|
{
|
|
7014
7109
|
key: p.id,
|
|
7015
7110
|
provider: p,
|
|
7016
|
-
isActive: p.model === activeModel,
|
|
7111
|
+
isActive: !isAuto && p.model === activeModel,
|
|
7017
7112
|
isLocked: !availableProviders.some((a) => a.id === p.id),
|
|
7018
7113
|
onSelect: handleModelSelect,
|
|
7019
7114
|
onCheckout: checkout
|
|
@@ -7022,10 +7117,10 @@ function ModelSelector({
|
|
|
7022
7117
|
}
|
|
7023
7118
|
|
|
7024
7119
|
// src/react/chat/ThreadSidebar.tsx
|
|
7025
|
-
import
|
|
7120
|
+
import React31, { useCallback as useCallback18, useMemo as useMemo21 } from "react";
|
|
7026
7121
|
function ThreadItemButton({ id, title, isActive, onSelect }) {
|
|
7027
7122
|
const handleClick = useCallback18(() => onSelect(id), [id, onSelect]);
|
|
7028
|
-
return /* @__PURE__ */
|
|
7123
|
+
return /* @__PURE__ */ React31.createElement(
|
|
7029
7124
|
"button",
|
|
7030
7125
|
{
|
|
7031
7126
|
className: "brokr-ai-chat-conversation",
|
|
@@ -7033,9 +7128,40 @@ function ThreadItemButton({ id, title, isActive, onSelect }) {
|
|
|
7033
7128
|
onClick: handleClick,
|
|
7034
7129
|
type: "button"
|
|
7035
7130
|
},
|
|
7036
|
-
|
|
7131
|
+
/* @__PURE__ */ React31.createElement(MessageIcon, { size: 12 }),
|
|
7132
|
+
/* @__PURE__ */ React31.createElement("span", { className: "brokr-ai-chat-conversation-label" }, title)
|
|
7037
7133
|
);
|
|
7038
7134
|
}
|
|
7135
|
+
var SIDEBAR_GROUP_ORDER = [
|
|
7136
|
+
"today",
|
|
7137
|
+
"yesterday",
|
|
7138
|
+
"this_week",
|
|
7139
|
+
"this_month",
|
|
7140
|
+
"earlier",
|
|
7141
|
+
"undated"
|
|
7142
|
+
];
|
|
7143
|
+
var SIDEBAR_GROUP_LABELS = {
|
|
7144
|
+
today: "Today",
|
|
7145
|
+
yesterday: "Yesterday",
|
|
7146
|
+
this_week: "This Week",
|
|
7147
|
+
this_month: "This Month",
|
|
7148
|
+
earlier: "Earlier",
|
|
7149
|
+
undated: "Recent"
|
|
7150
|
+
};
|
|
7151
|
+
function resolveSidebarDateGroup(updatedAt) {
|
|
7152
|
+
if (!updatedAt) return "undated";
|
|
7153
|
+
const parsed = new Date(updatedAt);
|
|
7154
|
+
if (Number.isNaN(parsed.getTime())) return "undated";
|
|
7155
|
+
const now = /* @__PURE__ */ new Date();
|
|
7156
|
+
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
7157
|
+
const startOfTarget = new Date(parsed.getFullYear(), parsed.getMonth(), parsed.getDate());
|
|
7158
|
+
const diffDays = Math.floor((startOfToday.getTime() - startOfTarget.getTime()) / 864e5);
|
|
7159
|
+
if (diffDays <= 0) return "today";
|
|
7160
|
+
if (diffDays === 1) return "yesterday";
|
|
7161
|
+
if (diffDays < 7) return "this_week";
|
|
7162
|
+
if (diffDays < 30) return "this_month";
|
|
7163
|
+
return "earlier";
|
|
7164
|
+
}
|
|
7039
7165
|
function ThreadSidebar() {
|
|
7040
7166
|
const {
|
|
7041
7167
|
startNewChat,
|
|
@@ -7067,14 +7193,31 @@ function ThreadSidebar() {
|
|
|
7067
7193
|
const handleRenameChange = useCallback18((e) => {
|
|
7068
7194
|
setRenameValue(e.target.value);
|
|
7069
7195
|
}, [setRenameValue]);
|
|
7070
|
-
const
|
|
7196
|
+
const groupedSidebarItems = useMemo21(() => {
|
|
7197
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
7198
|
+
for (const item of displaySidebarItems) {
|
|
7199
|
+
const key = resolveSidebarDateGroup(item.updatedAt);
|
|
7200
|
+
const bucket = grouped.get(key);
|
|
7201
|
+
if (bucket) {
|
|
7202
|
+
bucket.push(item);
|
|
7203
|
+
} else {
|
|
7204
|
+
grouped.set(key, [item]);
|
|
7205
|
+
}
|
|
7206
|
+
}
|
|
7207
|
+
return SIDEBAR_GROUP_ORDER.map((key) => ({
|
|
7208
|
+
key,
|
|
7209
|
+
label: SIDEBAR_GROUP_LABELS[key],
|
|
7210
|
+
items: grouped.get(key) ?? []
|
|
7211
|
+
})).filter((group) => group.items.length > 0);
|
|
7212
|
+
}, [displaySidebarItems]);
|
|
7213
|
+
const content = useMemo21(() => {
|
|
7071
7214
|
if (threadsLoading && displaySidebarItems.length === 0) {
|
|
7072
|
-
return /* @__PURE__ */
|
|
7215
|
+
return /* @__PURE__ */ React31.createElement("div", { className: "brokr-ai-chat-sidebar-skeleton" }, /* @__PURE__ */ React31.createElement(Skeleton, { width: "75%", height: 14, radius: 6 }), /* @__PURE__ */ React31.createElement(Skeleton, { width: "60%", height: 14, radius: 6 }), /* @__PURE__ */ React31.createElement(Skeleton, { width: "85%", height: 14, radius: 6 }));
|
|
7073
7216
|
}
|
|
7074
7217
|
if (displaySidebarItems.length === 0) {
|
|
7075
|
-
return /* @__PURE__ */
|
|
7218
|
+
return /* @__PURE__ */ React31.createElement("div", { className: "brokr-ai-chat-sidebar-empty" }, /* @__PURE__ */ React31.createElement("p", { className: "brokr-ai-chat-sidebar-empty-text" }, "No conversations yet. Start one above."));
|
|
7076
7219
|
}
|
|
7077
|
-
return /* @__PURE__ */
|
|
7220
|
+
return /* @__PURE__ */ React31.createElement("div", { className: "brokr-ai-chat-sidebar-groups" }, groupedSidebarItems.map((group) => /* @__PURE__ */ React31.createElement("section", { className: "brokr-ai-chat-sidebar-group", key: group.key }, /* @__PURE__ */ React31.createElement("span", { className: "brokr-ai-chat-sidebar-kicker" }, group.label), /* @__PURE__ */ React31.createElement("div", { className: "brokr-ai-chat-conversations" }, group.items.map((item) => renamingId === item.id ? /* @__PURE__ */ React31.createElement(
|
|
7078
7221
|
"input",
|
|
7079
7222
|
{
|
|
7080
7223
|
autoFocus: true,
|
|
@@ -7085,7 +7228,7 @@ function ThreadSidebar() {
|
|
|
7085
7228
|
onKeyDown: handleRenameKeyDown,
|
|
7086
7229
|
value: renameValue
|
|
7087
7230
|
}
|
|
7088
|
-
) : /* @__PURE__ */
|
|
7231
|
+
) : /* @__PURE__ */ React31.createElement(
|
|
7089
7232
|
ThreadItemButton,
|
|
7090
7233
|
{
|
|
7091
7234
|
key: item.id,
|
|
@@ -7094,29 +7237,30 @@ function ThreadSidebar() {
|
|
|
7094
7237
|
isActive: item.id === activeId,
|
|
7095
7238
|
onSelect: selectThreadAndCloseSidebar
|
|
7096
7239
|
}
|
|
7097
|
-
)));
|
|
7240
|
+
))))));
|
|
7098
7241
|
}, [
|
|
7099
7242
|
threadsLoading,
|
|
7100
7243
|
displaySidebarItems,
|
|
7101
7244
|
renamingId,
|
|
7102
7245
|
renameValue,
|
|
7246
|
+
groupedSidebarItems,
|
|
7103
7247
|
activeId,
|
|
7104
7248
|
selectThreadAndCloseSidebar,
|
|
7105
7249
|
handleRenameBlur,
|
|
7106
7250
|
handleRenameChange,
|
|
7107
7251
|
handleRenameKeyDown
|
|
7108
7252
|
]);
|
|
7109
|
-
return /* @__PURE__ */
|
|
7253
|
+
return /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement("button", { className: "brokr-ai-chat-sidebar-new-chat", onClick: handleNewChat, type: "button" }, /* @__PURE__ */ React31.createElement(MessageIcon, { size: 16 }), "New chat"), content);
|
|
7110
7254
|
}
|
|
7111
7255
|
|
|
7112
7256
|
// src/react/chat/MessagePane.tsx
|
|
7113
|
-
import
|
|
7257
|
+
import React34, { useCallback as useCallback21, useMemo as useMemo23 } from "react";
|
|
7114
7258
|
|
|
7115
7259
|
// src/react/chat/MessageBubble.tsx
|
|
7116
|
-
import
|
|
7260
|
+
import React33, { useCallback as useCallback20 } from "react";
|
|
7117
7261
|
|
|
7118
7262
|
// src/react/chat/MarkdownRenderer.tsx
|
|
7119
|
-
import
|
|
7263
|
+
import React32, { useCallback as useCallback19, useMemo as useMemo22 } from "react";
|
|
7120
7264
|
function parseInline(text) {
|
|
7121
7265
|
const nodes = [];
|
|
7122
7266
|
let remaining = text;
|
|
@@ -7124,19 +7268,25 @@ function parseInline(text) {
|
|
|
7124
7268
|
while (remaining) {
|
|
7125
7269
|
const codeMatch = remaining.match(/^`([^`]+)`/);
|
|
7126
7270
|
if (codeMatch) {
|
|
7127
|
-
nodes.push(/* @__PURE__ */
|
|
7271
|
+
nodes.push(/* @__PURE__ */ React32.createElement("code", { className: "brokr-md-inline-code", key: key++ }, codeMatch[1]));
|
|
7128
7272
|
remaining = remaining.slice(codeMatch[0].length);
|
|
7129
7273
|
continue;
|
|
7130
7274
|
}
|
|
7131
7275
|
const boldMatch = remaining.match(/^\*\*(.+?)\*\*/);
|
|
7132
7276
|
if (boldMatch) {
|
|
7133
|
-
nodes.push(/* @__PURE__ */
|
|
7277
|
+
nodes.push(/* @__PURE__ */ React32.createElement("strong", { key: key++ }, boldMatch[1]));
|
|
7134
7278
|
remaining = remaining.slice(boldMatch[0].length);
|
|
7135
7279
|
continue;
|
|
7136
7280
|
}
|
|
7281
|
+
const strikethroughMatch = remaining.match(/^~~(.+?)~~/);
|
|
7282
|
+
if (strikethroughMatch) {
|
|
7283
|
+
nodes.push(/* @__PURE__ */ React32.createElement("del", { key: key++ }, strikethroughMatch[1]));
|
|
7284
|
+
remaining = remaining.slice(strikethroughMatch[0].length);
|
|
7285
|
+
continue;
|
|
7286
|
+
}
|
|
7137
7287
|
const italicMatch = remaining.match(/^\*(.+?)\*/);
|
|
7138
7288
|
if (italicMatch) {
|
|
7139
|
-
nodes.push(/* @__PURE__ */
|
|
7289
|
+
nodes.push(/* @__PURE__ */ React32.createElement("em", { key: key++ }, italicMatch[1]));
|
|
7140
7290
|
remaining = remaining.slice(italicMatch[0].length);
|
|
7141
7291
|
continue;
|
|
7142
7292
|
}
|
|
@@ -7145,12 +7295,12 @@ function parseInline(text) {
|
|
|
7145
7295
|
const rawHref = linkMatch[2];
|
|
7146
7296
|
const isSafe = /^https?:\/\//i.test(rawHref) || /^mailto:/i.test(rawHref);
|
|
7147
7297
|
nodes.push(
|
|
7148
|
-
/* @__PURE__ */
|
|
7298
|
+
/* @__PURE__ */ React32.createElement("a", { className: "brokr-md-link", href: isSafe ? rawHref : "#", key: key++, rel: "noopener noreferrer", target: "_blank" }, linkMatch[1])
|
|
7149
7299
|
);
|
|
7150
7300
|
remaining = remaining.slice(linkMatch[0].length);
|
|
7151
7301
|
continue;
|
|
7152
7302
|
}
|
|
7153
|
-
const nextSpecial = remaining.search(/[
|
|
7303
|
+
const nextSpecial = remaining.search(/[`*~\[]/);
|
|
7154
7304
|
if (nextSpecial === -1) {
|
|
7155
7305
|
nodes.push(remaining);
|
|
7156
7306
|
break;
|
|
@@ -7169,7 +7319,7 @@ function CodeBlock({ code, language }) {
|
|
|
7169
7319
|
const handleCopy = useCallback19(() => {
|
|
7170
7320
|
void navigator.clipboard?.writeText(code);
|
|
7171
7321
|
}, [code]);
|
|
7172
|
-
return /* @__PURE__ */
|
|
7322
|
+
return /* @__PURE__ */ React32.createElement("div", { className: "brokr-md-codeblock" }, /* @__PURE__ */ React32.createElement("div", { className: "brokr-md-codeblock-header" }, /* @__PURE__ */ React32.createElement("span", { className: "brokr-md-codeblock-lang" }, language || "code"), /* @__PURE__ */ React32.createElement(
|
|
7173
7323
|
"button",
|
|
7174
7324
|
{
|
|
7175
7325
|
"aria-label": "Copy code",
|
|
@@ -7177,9 +7327,18 @@ function CodeBlock({ code, language }) {
|
|
|
7177
7327
|
onClick: handleCopy,
|
|
7178
7328
|
type: "button"
|
|
7179
7329
|
},
|
|
7180
|
-
/* @__PURE__ */
|
|
7330
|
+
/* @__PURE__ */ React32.createElement(CopyIcon, { size: 13 }),
|
|
7181
7331
|
"Copy"
|
|
7182
|
-
)), /* @__PURE__ */
|
|
7332
|
+
)), /* @__PURE__ */ React32.createElement("pre", { className: "brokr-md-codeblock-pre" }, /* @__PURE__ */ React32.createElement("code", null, code)));
|
|
7333
|
+
}
|
|
7334
|
+
function looksLikeTableSeparator(line) {
|
|
7335
|
+
const trimmed = line.trim();
|
|
7336
|
+
return /^\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?$/.test(trimmed);
|
|
7337
|
+
}
|
|
7338
|
+
function splitTableRow(line) {
|
|
7339
|
+
const trimmed = line.trim();
|
|
7340
|
+
const withoutEdges = trimmed.replace(/^\|/, "").replace(/\|$/, "");
|
|
7341
|
+
return withoutEdges.split("|").map((cell) => cell.trim());
|
|
7183
7342
|
}
|
|
7184
7343
|
function parseBlocks(text) {
|
|
7185
7344
|
const blocks = [];
|
|
@@ -7210,22 +7369,44 @@ function parseBlocks(text) {
|
|
|
7210
7369
|
i++;
|
|
7211
7370
|
continue;
|
|
7212
7371
|
}
|
|
7372
|
+
if (line.includes("|") && i + 1 < lines.length && looksLikeTableSeparator(lines[i + 1] ?? "")) {
|
|
7373
|
+
const headers = splitTableRow(line);
|
|
7374
|
+
const rows = [];
|
|
7375
|
+
i += 2;
|
|
7376
|
+
while (i < lines.length) {
|
|
7377
|
+
const candidate = lines[i] ?? "";
|
|
7378
|
+
if (!candidate.trim() || !candidate.includes("|")) break;
|
|
7379
|
+
rows.push(splitTableRow(candidate));
|
|
7380
|
+
i++;
|
|
7381
|
+
}
|
|
7382
|
+
blocks.push({ type: "table", content: "", headers, rows });
|
|
7383
|
+
continue;
|
|
7384
|
+
}
|
|
7385
|
+
if (/^[\s]*>\s?/.test(line)) {
|
|
7386
|
+
const quoteLines = [];
|
|
7387
|
+
while (i < lines.length && /^[\s]*>\s?/.test(lines[i])) {
|
|
7388
|
+
quoteLines.push(lines[i].replace(/^[\s]*>\s?/, ""));
|
|
7389
|
+
i++;
|
|
7390
|
+
}
|
|
7391
|
+
blocks.push({ type: "quote", content: "", items: quoteLines });
|
|
7392
|
+
continue;
|
|
7393
|
+
}
|
|
7213
7394
|
if (/^[\s]*[-*]\s+/.test(line)) {
|
|
7214
7395
|
const items = [];
|
|
7215
7396
|
while (i < lines.length && /^[\s]*[-*]\s+/.test(lines[i])) {
|
|
7216
7397
|
items.push(lines[i].replace(/^[\s]*[-*]\s+/, ""));
|
|
7217
7398
|
i++;
|
|
7218
7399
|
}
|
|
7219
|
-
blocks.push({ type: "list", content: "", items });
|
|
7400
|
+
blocks.push({ type: "list", content: "", items, ordered: false });
|
|
7220
7401
|
continue;
|
|
7221
7402
|
}
|
|
7222
|
-
if (/^[\s]*\d
|
|
7403
|
+
if (/^[\s]*\d+[.)]\s+/.test(line)) {
|
|
7223
7404
|
const items = [];
|
|
7224
|
-
while (i < lines.length && /^[\s]*\d
|
|
7225
|
-
items.push(lines[i].replace(/^[\s]*\d
|
|
7405
|
+
while (i < lines.length && /^[\s]*\d+[.)]\s+/.test(lines[i])) {
|
|
7406
|
+
items.push(lines[i].replace(/^[\s]*\d+[.)]\s+/, ""));
|
|
7226
7407
|
i++;
|
|
7227
7408
|
}
|
|
7228
|
-
blocks.push({ type: "list", content: "", items });
|
|
7409
|
+
blocks.push({ type: "list", content: "", items, ordered: true });
|
|
7229
7410
|
continue;
|
|
7230
7411
|
}
|
|
7231
7412
|
if (!line.trim()) {
|
|
@@ -7234,7 +7415,7 @@ function parseBlocks(text) {
|
|
|
7234
7415
|
}
|
|
7235
7416
|
const paraLines = [line];
|
|
7236
7417
|
i++;
|
|
7237
|
-
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
|
|
7418
|
+
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])) {
|
|
7238
7419
|
paraLines.push(lines[i]);
|
|
7239
7420
|
i++;
|
|
7240
7421
|
}
|
|
@@ -7246,28 +7427,35 @@ function renderBlocks(blocks) {
|
|
|
7246
7427
|
return blocks.map((block, idx) => {
|
|
7247
7428
|
switch (block.type) {
|
|
7248
7429
|
case "code":
|
|
7249
|
-
return /* @__PURE__ */
|
|
7430
|
+
return /* @__PURE__ */ React32.createElement(CodeBlock, { code: block.content, key: idx, language: block.language });
|
|
7250
7431
|
case "divider":
|
|
7251
|
-
return /* @__PURE__ */
|
|
7432
|
+
return /* @__PURE__ */ React32.createElement("hr", { className: "brokr-md-divider", key: idx });
|
|
7252
7433
|
case "heading": {
|
|
7253
7434
|
const Tag = `h${Math.min(block.level ?? 3, 6)}`;
|
|
7254
|
-
return /* @__PURE__ */
|
|
7435
|
+
return /* @__PURE__ */ React32.createElement(Tag, { className: `brokr-md-heading brokr-md-h${block.level}`, key: idx }, parseInline(block.content));
|
|
7255
7436
|
}
|
|
7256
7437
|
case "list":
|
|
7257
|
-
|
|
7438
|
+
if (block.ordered) {
|
|
7439
|
+
return /* @__PURE__ */ React32.createElement("ol", { className: "brokr-md-list brokr-md-list-ordered", key: idx }, block.items?.map((item, li) => /* @__PURE__ */ React32.createElement("li", { key: li }, parseInline(item))));
|
|
7440
|
+
}
|
|
7441
|
+
return /* @__PURE__ */ React32.createElement("ul", { className: "brokr-md-list brokr-md-list-unordered", key: idx }, block.items?.map((item, li) => /* @__PURE__ */ React32.createElement("li", { key: li }, parseInline(item))));
|
|
7442
|
+
case "quote":
|
|
7443
|
+
return /* @__PURE__ */ React32.createElement("blockquote", { className: "brokr-md-quote", key: idx }, block.items?.map((line, lineIndex) => /* @__PURE__ */ React32.createElement("p", { className: "brokr-md-quote-line", key: lineIndex }, parseInline(line))));
|
|
7444
|
+
case "table":
|
|
7445
|
+
return /* @__PURE__ */ React32.createElement("div", { className: "brokr-md-table-wrap", key: idx }, /* @__PURE__ */ React32.createElement("table", { className: "brokr-md-table" }, /* @__PURE__ */ React32.createElement("thead", null, /* @__PURE__ */ React32.createElement("tr", null, block.headers?.map((header, headerIndex) => /* @__PURE__ */ React32.createElement("th", { key: headerIndex }, parseInline(header))))), /* @__PURE__ */ React32.createElement("tbody", null, block.rows?.map((row, rowIndex) => /* @__PURE__ */ React32.createElement("tr", { key: rowIndex }, row.map((cell, cellIndex) => /* @__PURE__ */ React32.createElement("td", { key: cellIndex }, parseInline(cell))))))));
|
|
7258
7446
|
case "paragraph":
|
|
7259
7447
|
default:
|
|
7260
|
-
return /* @__PURE__ */
|
|
7448
|
+
return /* @__PURE__ */ React32.createElement("p", { className: "brokr-md-paragraph", key: idx }, parseInline(block.content));
|
|
7261
7449
|
}
|
|
7262
7450
|
});
|
|
7263
7451
|
}
|
|
7264
7452
|
function MarkdownRenderer({ content }) {
|
|
7265
|
-
const rendered =
|
|
7453
|
+
const rendered = useMemo22(() => {
|
|
7266
7454
|
if (!content) return null;
|
|
7267
7455
|
const blocks = parseBlocks(content);
|
|
7268
7456
|
return renderBlocks(blocks);
|
|
7269
7457
|
}, [content]);
|
|
7270
|
-
return /* @__PURE__ */
|
|
7458
|
+
return /* @__PURE__ */ React32.createElement("div", { className: "brokr-md" }, rendered);
|
|
7271
7459
|
}
|
|
7272
7460
|
|
|
7273
7461
|
// src/react/chat/MessageBubble.tsx
|
|
@@ -7277,10 +7465,10 @@ function MessageBubble({ message, isTyping, user }) {
|
|
|
7277
7465
|
});
|
|
7278
7466
|
}, [message.content]);
|
|
7279
7467
|
if (message.role === "user") {
|
|
7280
|
-
return /* @__PURE__ */
|
|
7468
|
+
return /* @__PURE__ */ React33.createElement("article", { className: "brokr-ai-chat-message", "data-role": "user" }, /* @__PURE__ */ React33.createElement("div", { className: "brokr-ai-chat-message-row", "data-role": "user" }, /* @__PURE__ */ React33.createElement("div", { className: "brokr-ai-chat-message-bubble" }, /* @__PURE__ */ React33.createElement("div", { className: "brokr-ai-chat-message-content brokr-ai-chat-message-content-user" }, message.content)), /* @__PURE__ */ React33.createElement(Avatar, { email: user?.email, name: user?.name, src: user?.image })));
|
|
7281
7469
|
}
|
|
7282
7470
|
const mp = message.model ? resolveProviderByModel(message.model) : null;
|
|
7283
|
-
return /* @__PURE__ */
|
|
7471
|
+
return /* @__PURE__ */ React33.createElement("article", { className: "brokr-ai-chat-message", "data-role": "assistant" }, /* @__PURE__ */ React33.createElement("div", { className: "brokr-ai-chat-message-row", "data-role": "assistant" }, mp ? /* @__PURE__ */ React33.createElement("img", { alt: mp.label, className: "brokr-ai-chat-model-avatar", src: mp.logo }) : null, isTyping ? /* @__PURE__ */ React33.createElement("div", { className: "brokr-ai-chat-typing", "aria-label": "AI is typing" }, /* @__PURE__ */ React33.createElement("span", null), /* @__PURE__ */ React33.createElement("span", null), /* @__PURE__ */ React33.createElement("span", null)) : message.status === "error" && !message.content ? /* @__PURE__ */ React33.createElement("div", { className: "brokr-ai-chat-message-wrap" }, /* @__PURE__ */ React33.createElement("div", { className: "brokr-ai-chat-message-error" }, "Something went wrong generating a reply.")) : /* @__PURE__ */ React33.createElement("div", { className: "brokr-ai-chat-message-wrap" }, /* @__PURE__ */ React33.createElement("div", { className: "brokr-ai-chat-message-content" }, /* @__PURE__ */ React33.createElement(MarkdownRenderer, { content: message.content })), message.content ? /* @__PURE__ */ React33.createElement(
|
|
7284
7472
|
"button",
|
|
7285
7473
|
{
|
|
7286
7474
|
"aria-label": "Copy message",
|
|
@@ -7288,7 +7476,7 @@ function MessageBubble({ message, isTyping, user }) {
|
|
|
7288
7476
|
onClick: handleCopy,
|
|
7289
7477
|
type: "button"
|
|
7290
7478
|
},
|
|
7291
|
-
/* @__PURE__ */
|
|
7479
|
+
/* @__PURE__ */ React33.createElement(CopyIcon, { size: 13 })
|
|
7292
7480
|
) : null)));
|
|
7293
7481
|
}
|
|
7294
7482
|
|
|
@@ -7297,7 +7485,7 @@ function StarterPromptButton({ prompt, onSend }) {
|
|
|
7297
7485
|
const handleClick = useCallback21(() => {
|
|
7298
7486
|
void onSend(prompt);
|
|
7299
7487
|
}, [prompt, onSend]);
|
|
7300
|
-
return /* @__PURE__ */
|
|
7488
|
+
return /* @__PURE__ */ React34.createElement("button", { className: "brokr-ai-chat-starter", onClick: handleClick, type: "button" }, prompt);
|
|
7301
7489
|
}
|
|
7302
7490
|
function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
7303
7491
|
const {
|
|
@@ -7315,10 +7503,10 @@ function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
|
7315
7503
|
user
|
|
7316
7504
|
} = useChatState();
|
|
7317
7505
|
const isEmpty = displayMessages.length === 0;
|
|
7318
|
-
const messageElements =
|
|
7506
|
+
const messageElements = useMemo23(() => {
|
|
7319
7507
|
return visibleMessages.map((message, index) => {
|
|
7320
7508
|
const isTyping = message.role === "assistant" && !message.content && (isSubmitting && index === visibleMessages.length - 1 || message.status === "pending" || message.status === "streaming");
|
|
7321
|
-
return /* @__PURE__ */
|
|
7509
|
+
return /* @__PURE__ */ React34.createElement(
|
|
7322
7510
|
MessageBubble,
|
|
7323
7511
|
{
|
|
7324
7512
|
isTyping,
|
|
@@ -7329,16 +7517,16 @@ function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
|
7329
7517
|
);
|
|
7330
7518
|
});
|
|
7331
7519
|
}, [visibleMessages, isSubmitting, user]);
|
|
7332
|
-
return /* @__PURE__ */
|
|
7520
|
+
return /* @__PURE__ */ React34.createElement("div", { className: "brokr-ai-chat-thread", "data-empty": isEmpty, ref: scrollContainerRef }, isEmpty ? /* @__PURE__ */ React34.createElement("div", { className: "brokr-ai-chat-empty" }, /* @__PURE__ */ React34.createElement(SparkIcon, { size: 28 }), /* @__PURE__ */ React34.createElement("h2", { className: "brokr-title" }, emptyTitle), subtitle ?? emptyCopy ? /* @__PURE__ */ React34.createElement("p", { className: "brokr-copy" }, subtitle ?? emptyCopy) : null, starterPrompts.length > 0 ? /* @__PURE__ */ React34.createElement("div", { className: "brokr-ai-chat-starters" }, starterPrompts.map((sp) => /* @__PURE__ */ React34.createElement(StarterPromptButton, { key: sp, prompt: sp, onSend: sendMessage }))) : null) : /* @__PURE__ */ React34.createElement("div", { className: "brokr-ai-chat-thread-inner" }, (isPersist ? hasMoreMessages : memHasMore) ? /* @__PURE__ */ React34.createElement("div", { ref: sentinelRef, className: "brokr-ai-chat-sentinel" }, loadingOlder ? /* @__PURE__ */ React34.createElement("div", { style: { display: "flex", justifyContent: "center", padding: "0.5rem" } }, /* @__PURE__ */ React34.createElement(Skeleton, { width: 120, height: 12, radius: 6 })) : null) : null, messageElements, /* @__PURE__ */ React34.createElement("div", { ref: bottomRef })));
|
|
7333
7521
|
}
|
|
7334
7522
|
|
|
7335
7523
|
// src/react/chat/ChatInput.tsx
|
|
7336
|
-
import
|
|
7524
|
+
import React35, { useCallback as useCallback22, useEffect as useEffect11 } from "react";
|
|
7337
7525
|
function CommandButton({ cmd, chatContext }) {
|
|
7338
7526
|
const handleClick = useCallback22(() => {
|
|
7339
7527
|
void cmd.run(chatContext);
|
|
7340
7528
|
}, [cmd, chatContext]);
|
|
7341
|
-
return /* @__PURE__ */
|
|
7529
|
+
return /* @__PURE__ */ React35.createElement("button", { className: "brokr-ai-chat-sidebar-button", key: cmd.id, onClick: handleClick, type: "button" }, cmd.text);
|
|
7342
7530
|
}
|
|
7343
7531
|
function ChatInput() {
|
|
7344
7532
|
const {
|
|
@@ -7370,7 +7558,7 @@ function ChatInput() {
|
|
|
7370
7558
|
const handleChange = useCallback22((e) => {
|
|
7371
7559
|
setInput(e.target.value);
|
|
7372
7560
|
}, [setInput]);
|
|
7373
|
-
return /* @__PURE__ */
|
|
7561
|
+
return /* @__PURE__ */ React35.createElement("form", { className: "brokr-ai-chat-input-area", onSubmit: handleSubmit }, /* @__PURE__ */ React35.createElement("div", { className: "brokr-ai-chat-input-container" }, composerCommands.length > 0 ? /* @__PURE__ */ React35.createElement("div", { className: "brokr-ai-chat-composer-actions" }, composerCommands.map((cmd) => /* @__PURE__ */ React35.createElement(CommandButton, { key: cmd.id, cmd, chatContext }))) : null, /* @__PURE__ */ React35.createElement("div", { className: "brokr-ai-chat-input-row" }, /* @__PURE__ */ React35.createElement(
|
|
7374
7562
|
"textarea",
|
|
7375
7563
|
{
|
|
7376
7564
|
className: "brokr-ai-chat-textarea",
|
|
@@ -7381,15 +7569,15 @@ function ChatInput() {
|
|
|
7381
7569
|
rows: 1,
|
|
7382
7570
|
value: input
|
|
7383
7571
|
}
|
|
7384
|
-
), /* @__PURE__ */
|
|
7572
|
+
), /* @__PURE__ */ React35.createElement(
|
|
7385
7573
|
"button",
|
|
7386
7574
|
{
|
|
7387
7575
|
className: "brokr-ai-chat-send",
|
|
7388
7576
|
disabled: isSubmitting || !input.trim(),
|
|
7389
7577
|
type: "submit"
|
|
7390
7578
|
},
|
|
7391
|
-
isSubmitting ? /* @__PURE__ */
|
|
7392
|
-
))), error ? /* @__PURE__ */
|
|
7579
|
+
isSubmitting ? /* @__PURE__ */ React35.createElement(SparkIcon, { size: 16 }) : /* @__PURE__ */ React35.createElement(ArrowRightIcon, { size: 16 })
|
|
7580
|
+
))), error ? /* @__PURE__ */ React35.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null);
|
|
7393
7581
|
}
|
|
7394
7582
|
|
|
7395
7583
|
// src/react/chat/AIChat.tsx
|
|
@@ -7411,6 +7599,7 @@ function AIChat(inlineProps) {
|
|
|
7411
7599
|
subtitle,
|
|
7412
7600
|
model: modelProp,
|
|
7413
7601
|
modelSelector,
|
|
7602
|
+
memory,
|
|
7414
7603
|
variant = 1,
|
|
7415
7604
|
sidebar: sidebarProp,
|
|
7416
7605
|
threadMenu: threadMenuProp,
|
|
@@ -7434,30 +7623,28 @@ function AIChat(inlineProps) {
|
|
|
7434
7623
|
const headerVisible = variant !== 3;
|
|
7435
7624
|
const threadMenuVisible = threadMenuProp !== void 0 ? threadMenuProp : variant !== 3;
|
|
7436
7625
|
const modelSelectorVisible = (modelSelector !== void 0 ? modelSelector : true) && !modelProp;
|
|
7437
|
-
const [
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
}, [modelProp]);
|
|
7444
|
-
const activeModel = modelProp ?? selectedModel;
|
|
7445
|
-
const activeProvider = useMemo23(
|
|
7446
|
-
() => providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7626
|
+
const [userSelectedModel, setUserSelectedModel] = useState18(null);
|
|
7627
|
+
const explicitModel = modelProp ?? userSelectedModel ?? void 0;
|
|
7628
|
+
const displayModel = explicitModel ?? providers.find((p) => p.free)?.model ?? providers[0]?.model ?? "";
|
|
7629
|
+
const activeModel = explicitModel ?? STACK_DEFAULT;
|
|
7630
|
+
const activeProvider = useMemo24(
|
|
7631
|
+
() => activeModel === STACK_DEFAULT ? void 0 : providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7447
7632
|
[activeModel]
|
|
7448
7633
|
);
|
|
7449
|
-
const hasBalance =
|
|
7634
|
+
const hasBalance = useMemo24(
|
|
7450
7635
|
() => billing === null || billing.balanceCents === null || billing.balanceCents > 0 || billing.hasPaymentMethod,
|
|
7451
7636
|
[billing]
|
|
7452
7637
|
);
|
|
7453
|
-
const availableProviders =
|
|
7638
|
+
const availableProviders = useMemo24(
|
|
7454
7639
|
() => hasBalance ? providers : providers.filter((p) => p.free),
|
|
7455
7640
|
[hasBalance]
|
|
7456
7641
|
);
|
|
7457
7642
|
const chat = useChat({
|
|
7458
7643
|
endpoint,
|
|
7459
7644
|
prompt,
|
|
7460
|
-
|
|
7645
|
+
explicitModel,
|
|
7646
|
+
displayModel,
|
|
7647
|
+
memory,
|
|
7461
7648
|
persist,
|
|
7462
7649
|
surface,
|
|
7463
7650
|
subject,
|
|
@@ -7533,10 +7720,10 @@ function AIChat(inlineProps) {
|
|
|
7533
7720
|
document.addEventListener("keydown", onKeyDown);
|
|
7534
7721
|
return () => document.removeEventListener("keydown", onKeyDown);
|
|
7535
7722
|
}, [sidebarOpen]);
|
|
7536
|
-
const finalTitle =
|
|
7723
|
+
const finalTitle = useMemo24(() => {
|
|
7537
7724
|
return chat.renderedTitle || title;
|
|
7538
7725
|
}, [chat.renderedTitle, title]);
|
|
7539
|
-
const chatContext =
|
|
7726
|
+
const chatContext = useMemo24(() => ({
|
|
7540
7727
|
thread: chat.activeThread,
|
|
7541
7728
|
messages: chat.displayMessages,
|
|
7542
7729
|
user,
|
|
@@ -7544,19 +7731,19 @@ function AIChat(inlineProps) {
|
|
|
7544
7731
|
newThread: chat.startNewChat,
|
|
7545
7732
|
setThread: chat.selectThread
|
|
7546
7733
|
}), [chat.activeThread, chat.displayMessages, user, chat.sendMessage, chat.startNewChat, chat.selectThread]);
|
|
7547
|
-
const headerCommands =
|
|
7734
|
+
const headerCommands = useMemo24(
|
|
7548
7735
|
() => commands.filter((c) => c.location === "header" && (!c.show || c.show(chatContext))),
|
|
7549
7736
|
[commands, chatContext]
|
|
7550
7737
|
);
|
|
7551
|
-
const composerCommands =
|
|
7738
|
+
const composerCommands = useMemo24(
|
|
7552
7739
|
() => commands.filter((c) => c.location === "composer" && (!c.show || c.show(chatContext))),
|
|
7553
7740
|
[commands, chatContext]
|
|
7554
7741
|
);
|
|
7555
|
-
const threadMenuCommands =
|
|
7742
|
+
const threadMenuCommands = useMemo24(
|
|
7556
7743
|
() => commands.filter((c) => c.location === "threadMenu" && (!c.show || c.show(chatContext))),
|
|
7557
7744
|
[commands, chatContext]
|
|
7558
7745
|
);
|
|
7559
|
-
const chatState =
|
|
7746
|
+
const chatState = useMemo24(() => ({
|
|
7560
7747
|
displayMessages: chat.displayMessages,
|
|
7561
7748
|
visibleMessages: chat.displayMessages,
|
|
7562
7749
|
isSubmitting: chat.isSubmitting,
|
|
@@ -7565,10 +7752,10 @@ function AIChat(inlineProps) {
|
|
|
7565
7752
|
activeThread: chat.activeThread,
|
|
7566
7753
|
renderedTitle: finalTitle,
|
|
7567
7754
|
isTitleLoading: chat.isTitleLoading,
|
|
7568
|
-
activeModel,
|
|
7755
|
+
activeModel: displayModel,
|
|
7569
7756
|
activeProvider,
|
|
7570
|
-
selectedModel,
|
|
7571
|
-
setSelectedModel,
|
|
7757
|
+
selectedModel: displayModel,
|
|
7758
|
+
setSelectedModel: setUserSelectedModel,
|
|
7572
7759
|
availableProviders,
|
|
7573
7760
|
sendMessage: chat.sendMessage,
|
|
7574
7761
|
startNewChat: chat.startNewChat,
|
|
@@ -7608,9 +7795,9 @@ function AIChat(inlineProps) {
|
|
|
7608
7795
|
}), [
|
|
7609
7796
|
chat,
|
|
7610
7797
|
finalTitle,
|
|
7611
|
-
|
|
7798
|
+
displayModel,
|
|
7612
7799
|
activeProvider,
|
|
7613
|
-
|
|
7800
|
+
userSelectedModel,
|
|
7614
7801
|
availableProviders,
|
|
7615
7802
|
sidebarOpen,
|
|
7616
7803
|
closeSidebar,
|
|
@@ -7623,16 +7810,16 @@ function AIChat(inlineProps) {
|
|
|
7623
7810
|
checkout,
|
|
7624
7811
|
user
|
|
7625
7812
|
]);
|
|
7626
|
-
return /* @__PURE__ */
|
|
7813
|
+
return /* @__PURE__ */ React36.createElement(ChatProvider, { value: chatState }, /* @__PURE__ */ React36.createElement(
|
|
7627
7814
|
"section",
|
|
7628
7815
|
{
|
|
7629
7816
|
className: "brokr-ai-chat-shell",
|
|
7630
7817
|
"data-sidebar": sidebarVisible,
|
|
7631
7818
|
ref: shellRef
|
|
7632
7819
|
},
|
|
7633
|
-
sidebarVisible ? /* @__PURE__ */
|
|
7634
|
-
sidebarOpen ? /* @__PURE__ */
|
|
7635
|
-
/* @__PURE__ */
|
|
7820
|
+
sidebarVisible ? /* @__PURE__ */ React36.createElement("aside", { className: "brokr-ai-chat-sidebar brokr-ai-chat-sidebar-desktop" }, /* @__PURE__ */ React36.createElement(ThreadSidebar, null)) : null,
|
|
7821
|
+
sidebarOpen ? /* @__PURE__ */ React36.createElement(React36.Fragment, null, /* @__PURE__ */ React36.createElement("div", { className: "brokr-ai-chat-drawer-backdrop", onClick: closeSidebar }), /* @__PURE__ */ React36.createElement("aside", { className: "brokr-ai-chat-drawer" }, /* @__PURE__ */ React36.createElement(ThreadSidebar, null))) : null,
|
|
7822
|
+
/* @__PURE__ */ React36.createElement("div", { className: "brokr-ai-chat-stage", "data-noheader": !headerVisible }, headerVisible ? /* @__PURE__ */ React36.createElement(
|
|
7636
7823
|
ChatHeader,
|
|
7637
7824
|
{
|
|
7638
7825
|
activeId: chat.activeId,
|
|
@@ -7647,13 +7834,13 @@ function AIChat(inlineProps) {
|
|
|
7647
7834
|
threadMenuRef,
|
|
7648
7835
|
threadMenuVisible,
|
|
7649
7836
|
setThreadMenuOpenId,
|
|
7650
|
-
activeModel,
|
|
7651
|
-
setSelectedModel,
|
|
7837
|
+
activeModel: displayModel,
|
|
7838
|
+
setSelectedModel: setUserSelectedModel,
|
|
7652
7839
|
availableProviders,
|
|
7653
7840
|
startRename: chatState.startRename,
|
|
7654
7841
|
deleteThread: chat.deleteThread
|
|
7655
7842
|
}
|
|
7656
|
-
) : null, /* @__PURE__ */
|
|
7843
|
+
) : null, /* @__PURE__ */ React36.createElement(
|
|
7657
7844
|
MessagePane,
|
|
7658
7845
|
{
|
|
7659
7846
|
starterPrompts,
|
|
@@ -7661,21 +7848,21 @@ function AIChat(inlineProps) {
|
|
|
7661
7848
|
emptyCopy,
|
|
7662
7849
|
subtitle
|
|
7663
7850
|
}
|
|
7664
|
-
), /* @__PURE__ */
|
|
7851
|
+
), /* @__PURE__ */ React36.createElement(ChatInput, null))
|
|
7665
7852
|
));
|
|
7666
7853
|
}
|
|
7667
7854
|
function CommandButton2({ cmd, chatContext }) {
|
|
7668
7855
|
const handleClick = useCallback23(() => {
|
|
7669
7856
|
void cmd.run(chatContext);
|
|
7670
7857
|
}, [cmd, chatContext]);
|
|
7671
|
-
return /* @__PURE__ */
|
|
7858
|
+
return /* @__PURE__ */ React36.createElement("button", { className: "brokr-ai-chat-sidebar-button", onClick: handleClick, type: "button" }, cmd.text);
|
|
7672
7859
|
}
|
|
7673
7860
|
function MenuCommandItem({ cmd, chatContext, onClose }) {
|
|
7674
7861
|
const handleClick = useCallback23(() => {
|
|
7675
7862
|
onClose();
|
|
7676
7863
|
void cmd.run(chatContext);
|
|
7677
7864
|
}, [cmd, chatContext, onClose]);
|
|
7678
|
-
return /* @__PURE__ */
|
|
7865
|
+
return /* @__PURE__ */ React36.createElement("button", { className: "brokr-ai-chat-thread-dropdown-item", onClick: handleClick, type: "button" }, cmd.text);
|
|
7679
7866
|
}
|
|
7680
7867
|
function ChatHeader({
|
|
7681
7868
|
activeId,
|
|
@@ -7710,7 +7897,7 @@ function ChatHeader({
|
|
|
7710
7897
|
void deleteThread(activeId);
|
|
7711
7898
|
}
|
|
7712
7899
|
}, [activeId, deleteThread, setThreadMenuOpenId]);
|
|
7713
|
-
return /* @__PURE__ */
|
|
7900
|
+
return /* @__PURE__ */ React36.createElement("header", { className: "brokr-ai-chat-topbar" }, /* @__PURE__ */ React36.createElement("div", { className: "brokr-ai-chat-topbar-left" }, sidebarVisible ? /* @__PURE__ */ React36.createElement(
|
|
7714
7901
|
"button",
|
|
7715
7902
|
{
|
|
7716
7903
|
"aria-label": "Open sidebar",
|
|
@@ -7718,8 +7905,8 @@ function ChatHeader({
|
|
|
7718
7905
|
onClick: handleOpenSidebar,
|
|
7719
7906
|
type: "button"
|
|
7720
7907
|
},
|
|
7721
|
-
/* @__PURE__ */
|
|
7722
|
-
) : null), /* @__PURE__ */
|
|
7908
|
+
/* @__PURE__ */ React36.createElement(MenuIcon, { size: 18 })
|
|
7909
|
+
) : null), /* @__PURE__ */ React36.createElement("div", { className: "brokr-ai-chat-topbar-actions" }, headerCommands.map((cmd) => /* @__PURE__ */ React36.createElement(CommandButton2, { key: cmd.id, cmd, chatContext })), modelSelectorVisible ? /* @__PURE__ */ React36.createElement(
|
|
7723
7910
|
ModelSelector,
|
|
7724
7911
|
{
|
|
7725
7912
|
activeModel,
|
|
@@ -7727,7 +7914,7 @@ function ChatHeader({
|
|
|
7727
7914
|
checkout,
|
|
7728
7915
|
setSelectedModel
|
|
7729
7916
|
}
|
|
7730
|
-
) : null, activeId && threadMenuVisible ? /* @__PURE__ */
|
|
7917
|
+
) : null, activeId && threadMenuVisible ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-ai-chat-thread-menu-wrap", ref: threadMenuRef }, /* @__PURE__ */ React36.createElement(
|
|
7731
7918
|
"button",
|
|
7732
7919
|
{
|
|
7733
7920
|
"aria-label": "Thread actions",
|
|
@@ -7735,17 +7922,17 @@ function ChatHeader({
|
|
|
7735
7922
|
onClick: handleToggleMenu,
|
|
7736
7923
|
type: "button"
|
|
7737
7924
|
},
|
|
7738
|
-
/* @__PURE__ */
|
|
7739
|
-
), threadMenuOpenId ? /* @__PURE__ */
|
|
7925
|
+
/* @__PURE__ */ React36.createElement(MoreHorizontalIcon, { size: 14 })
|
|
7926
|
+
), threadMenuOpenId ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-ai-chat-thread-dropdown" }, /* @__PURE__ */ React36.createElement(
|
|
7740
7927
|
"button",
|
|
7741
7928
|
{
|
|
7742
7929
|
className: "brokr-ai-chat-thread-dropdown-item",
|
|
7743
7930
|
onClick: handleRename,
|
|
7744
7931
|
type: "button"
|
|
7745
7932
|
},
|
|
7746
|
-
/* @__PURE__ */
|
|
7933
|
+
/* @__PURE__ */ React36.createElement(PencilIcon, { size: 13 }),
|
|
7747
7934
|
"Rename"
|
|
7748
|
-
), threadMenuCommands.map((cmd) => /* @__PURE__ */
|
|
7935
|
+
), threadMenuCommands.map((cmd) => /* @__PURE__ */ React36.createElement(
|
|
7749
7936
|
MenuCommandItem,
|
|
7750
7937
|
{
|
|
7751
7938
|
key: cmd.id,
|
|
@@ -7753,7 +7940,7 @@ function ChatHeader({
|
|
|
7753
7940
|
chatContext,
|
|
7754
7941
|
onClose: closeMenu
|
|
7755
7942
|
}
|
|
7756
|
-
)), /* @__PURE__ */
|
|
7943
|
+
)), /* @__PURE__ */ React36.createElement(
|
|
7757
7944
|
"button",
|
|
7758
7945
|
{
|
|
7759
7946
|
className: "brokr-ai-chat-thread-dropdown-item",
|
|
@@ -7761,38 +7948,77 @@ function ChatHeader({
|
|
|
7761
7948
|
onClick: handleDelete,
|
|
7762
7949
|
type: "button"
|
|
7763
7950
|
},
|
|
7764
|
-
/* @__PURE__ */
|
|
7951
|
+
/* @__PURE__ */ React36.createElement(TrashIcon, { size: 13 }),
|
|
7765
7952
|
"Delete"
|
|
7766
7953
|
)) : null) : null));
|
|
7767
7954
|
}
|
|
7768
7955
|
|
|
7769
7956
|
// src/react/composites/FabAI.tsx
|
|
7770
|
-
import
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7957
|
+
import React37, { useCallback as useCallback24, useMemo as useMemo25, useRef as useRef8, useState as useState19 } from "react";
|
|
7958
|
+
|
|
7959
|
+
// src/react/composites/fab-context.ts
|
|
7960
|
+
function buildFabSystemPrompt(appContext, brandName, existingPrompt) {
|
|
7961
|
+
if (appContext === false) return existingPrompt;
|
|
7962
|
+
const name = appContext?.name ?? brandName;
|
|
7963
|
+
if (!name && !appContext) return existingPrompt;
|
|
7964
|
+
const parts = [];
|
|
7965
|
+
if (name) parts.push(`You are an AI assistant embedded in ${name}.`);
|
|
7966
|
+
if (appContext?.description) parts.push(`This app is ${appContext.description}.`);
|
|
7967
|
+
if (appContext?.currentPage) parts.push(`The user is currently on: ${appContext.currentPage}.`);
|
|
7968
|
+
if (appContext?.facts?.length) parts.push(...appContext.facts);
|
|
7969
|
+
const autoPrompt = parts.join(" ");
|
|
7970
|
+
return existingPrompt ? `${autoPrompt}
|
|
7971
|
+
|
|
7972
|
+
${existingPrompt}` : autoPrompt;
|
|
7973
|
+
}
|
|
7974
|
+
|
|
7975
|
+
// src/react/composites/FabAI.tsx
|
|
7976
|
+
function ensureAssistantReply(messages, content) {
|
|
7977
|
+
const normalized = content.trim() ? content : "No response received.";
|
|
7978
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
7979
|
+
if (messages[index]?.role !== "assistant") continue;
|
|
7980
|
+
const next = [...messages];
|
|
7981
|
+
next[index] = { ...next[index], content: normalized };
|
|
7982
|
+
return next;
|
|
7983
|
+
}
|
|
7984
|
+
return [...messages, { role: "assistant", content: normalized }];
|
|
7985
|
+
}
|
|
7986
|
+
function appendAssistantDelta(messages, delta) {
|
|
7987
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
7988
|
+
if (messages[index]?.role !== "assistant") continue;
|
|
7989
|
+
const next = [...messages];
|
|
7990
|
+
next[index] = {
|
|
7991
|
+
...next[index],
|
|
7992
|
+
content: `${contentToText(next[index].content)}${delta}`
|
|
7993
|
+
};
|
|
7994
|
+
return next;
|
|
7995
|
+
}
|
|
7996
|
+
return [...messages, { role: "assistant", content: delta }];
|
|
7774
7997
|
}
|
|
7775
7998
|
function FabAI({
|
|
7999
|
+
appContext,
|
|
7776
8000
|
model,
|
|
8001
|
+
memory,
|
|
7777
8002
|
onSendMessage,
|
|
7778
8003
|
position = "bottom-right",
|
|
7779
|
-
starterPrompts = [],
|
|
7780
8004
|
systemPrompt
|
|
7781
8005
|
}) {
|
|
7782
|
-
const { can, user } = useBrokr();
|
|
8006
|
+
const { can, user, theme } = useBrokr();
|
|
8007
|
+
const brandName = theme?.brand?.name;
|
|
7783
8008
|
const [isOpen, setIsOpen] = useState19(false);
|
|
7784
8009
|
const [input, setInput] = useState19("");
|
|
7785
8010
|
const [error, setError] = useState19(null);
|
|
7786
8011
|
const [isSending, setIsSending] = useState19(false);
|
|
7787
8012
|
const [messages, setMessages] = useState19([]);
|
|
7788
|
-
const
|
|
8013
|
+
const conversationIdRef = useRef8(`fab_${crypto.randomUUID()}`);
|
|
8014
|
+
const launcherStyle = useMemo25(
|
|
7789
8015
|
() => ({
|
|
7790
8016
|
left: position === "bottom-left" ? "var(--brokr-space-6)" : void 0,
|
|
7791
8017
|
right: position === "bottom-right" ? "var(--brokr-space-6)" : void 0
|
|
7792
8018
|
}),
|
|
7793
8019
|
[position]
|
|
7794
8020
|
);
|
|
7795
|
-
const canChat =
|
|
8021
|
+
const canChat = useMemo25(() => can("ai.chat"), [can]);
|
|
7796
8022
|
const toggleOpen = useCallback24(() => {
|
|
7797
8023
|
if (!canChat) {
|
|
7798
8024
|
redirectTo("/pricing");
|
|
@@ -7808,37 +8034,75 @@ function FabAI({
|
|
|
7808
8034
|
}, []);
|
|
7809
8035
|
const sendPrompt = useCallback24(async (prompt) => {
|
|
7810
8036
|
const nextPrompt = prompt.trim();
|
|
7811
|
-
if (!nextPrompt) return;
|
|
7812
|
-
const
|
|
8037
|
+
if (!nextPrompt || isSending) return;
|
|
8038
|
+
const userMessage = { role: "user", content: nextPrompt };
|
|
8039
|
+
const nextMessages = [...messages, userMessage];
|
|
8040
|
+
const optimisticMessages = [...nextMessages, { role: "assistant", content: "" }];
|
|
7813
8041
|
try {
|
|
7814
8042
|
setError(null);
|
|
7815
8043
|
setIsSending(true);
|
|
7816
|
-
setMessages(
|
|
8044
|
+
setMessages(optimisticMessages);
|
|
7817
8045
|
setInput("");
|
|
7818
|
-
let responseText = "";
|
|
7819
8046
|
if (onSendMessage) {
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
8047
|
+
const responseText2 = await onSendMessage({ messages: nextMessages, model, systemPrompt });
|
|
8048
|
+
setMessages((current) => ensureAssistantReply(current, responseText2));
|
|
8049
|
+
return;
|
|
8050
|
+
}
|
|
8051
|
+
const response = await fetch("/api/brokr/chat", {
|
|
8052
|
+
method: "POST",
|
|
8053
|
+
credentials: "include",
|
|
8054
|
+
headers: { "Content-Type": "application/json" },
|
|
8055
|
+
body: JSON.stringify({
|
|
8056
|
+
conversationId: conversationIdRef.current,
|
|
8057
|
+
messages: trimToTokenBudget(
|
|
8058
|
+
nextMessages.filter((message) => message.role === "user" || message.role === "assistant" && Boolean(contentToText(message.content))).map((message) => ({
|
|
8059
|
+
role: message.role,
|
|
8060
|
+
content: contentToText(message.content)
|
|
8061
|
+
}))
|
|
8062
|
+
),
|
|
8063
|
+
...model !== void 0 ? { model } : {},
|
|
8064
|
+
...(() => {
|
|
8065
|
+
const withContext = buildFabSystemPrompt(appContext, brandName, systemPrompt);
|
|
8066
|
+
const ep = buildEffectivePrompt(withContext, memory);
|
|
8067
|
+
return ep ? { systemPrompt: ep } : {};
|
|
8068
|
+
})()
|
|
8069
|
+
})
|
|
8070
|
+
});
|
|
8071
|
+
if (!response.ok) {
|
|
8072
|
+
const payload2 = await response.json().catch(() => ({}));
|
|
8073
|
+
throw new Error(payload2.message ?? payload2.error ?? `Chat failed (${response.status})`);
|
|
8074
|
+
}
|
|
8075
|
+
if (isSSEResponse(response)) {
|
|
8076
|
+
let hasDelta = false;
|
|
8077
|
+
for await (const event of parseSSEStream(response)) {
|
|
8078
|
+
if (event.type === "conversation") {
|
|
8079
|
+
conversationIdRef.current = event.id;
|
|
8080
|
+
} else if (event.type === "delta") {
|
|
8081
|
+
hasDelta = true;
|
|
8082
|
+
setMessages((current) => appendAssistantDelta(current, event.delta));
|
|
8083
|
+
} else if (event.type === "error") {
|
|
8084
|
+
throw new Error(event.message);
|
|
7828
8085
|
}
|
|
7829
|
-
|
|
7830
|
-
|
|
8086
|
+
}
|
|
8087
|
+
if (!hasDelta) {
|
|
8088
|
+
setMessages((current) => ensureAssistantReply(current, "No response received."));
|
|
8089
|
+
}
|
|
8090
|
+
return;
|
|
8091
|
+
}
|
|
8092
|
+
const payload = await response.json().catch(() => ({}));
|
|
8093
|
+
if (payload.error || payload.message) {
|
|
8094
|
+
throw new Error(payload.message ?? payload.error ?? "AI request failed.");
|
|
7831
8095
|
}
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
content: responseText || "No response received."
|
|
7835
|
-
}]);
|
|
8096
|
+
const responseText = payload.content ?? payload.response ?? payload.text ?? "";
|
|
8097
|
+
setMessages((current) => ensureAssistantReply(current, responseText));
|
|
7836
8098
|
} catch (cause) {
|
|
7837
|
-
|
|
8099
|
+
const message = cause instanceof Error ? cause.message : "Could not send message.";
|
|
8100
|
+
setError(message);
|
|
8101
|
+
setMessages((current) => ensureAssistantReply(current, `Error: ${message}`));
|
|
7838
8102
|
} finally {
|
|
7839
8103
|
setIsSending(false);
|
|
7840
8104
|
}
|
|
7841
|
-
}, [messages, model, onSendMessage, systemPrompt]);
|
|
8105
|
+
}, [isSending, messages, model, onSendMessage, systemPrompt]);
|
|
7842
8106
|
const handleSubmit = useCallback24(async (event) => {
|
|
7843
8107
|
event.preventDefault();
|
|
7844
8108
|
await sendPrompt(input);
|
|
@@ -7849,30 +8113,31 @@ function FabAI({
|
|
|
7849
8113
|
void sendPrompt(input);
|
|
7850
8114
|
}
|
|
7851
8115
|
}, [input, sendPrompt]);
|
|
7852
|
-
|
|
7853
|
-
void sendPrompt(prompt);
|
|
7854
|
-
}, [sendPrompt]);
|
|
7855
|
-
return /* @__PURE__ */ React36.createElement(React36.Fragment, null, /* @__PURE__ */ React36.createElement("div", { className: "brokr-chat-fab", style: launcherStyle }, /* @__PURE__ */ React36.createElement(
|
|
8116
|
+
return /* @__PURE__ */ React37.createElement(React37.Fragment, null, /* @__PURE__ */ React37.createElement("div", { className: "brokr-chat-fab", style: launcherStyle }, /* @__PURE__ */ React37.createElement(
|
|
7856
8117
|
"button",
|
|
7857
8118
|
{
|
|
8119
|
+
"aria-label": isOpen ? "Close AI chat" : "Open AI chat",
|
|
7858
8120
|
"aria-expanded": isOpen,
|
|
7859
8121
|
"aria-haspopup": "dialog",
|
|
7860
|
-
className: "brokr-
|
|
8122
|
+
className: "brokr-chat-fab-trigger",
|
|
7861
8123
|
onClick: toggleOpen,
|
|
7862
8124
|
type: "button"
|
|
7863
8125
|
},
|
|
7864
|
-
/* @__PURE__ */
|
|
7865
|
-
|
|
7866
|
-
)), isOpen ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-panel brokr-chat-panel", role: "dialog" }, /* @__PURE__ */ React36.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ React36.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ React36.createElement("strong", null, "AI Chat"), user?.name ? /* @__PURE__ */ React36.createElement("span", { className: "brokr-copy" }, user.name) : null), /* @__PURE__ */ React36.createElement("button", { className: "brokr-button-ghost", onClick: handleClose, type: "button" }, /* @__PURE__ */ React36.createElement(CloseIcon, { size: 16 }))), /* @__PURE__ */ React36.createElement(
|
|
8126
|
+
/* @__PURE__ */ React37.createElement(SparkIcon, { size: 18 })
|
|
8127
|
+
)), isOpen ? /* @__PURE__ */ React37.createElement("div", { className: "brokr-panel brokr-chat-panel", role: "dialog" }, /* @__PURE__ */ React37.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ React37.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ React37.createElement("strong", null, "AI Chat"), user?.name ? /* @__PURE__ */ React37.createElement("span", { className: "brokr-copy" }, user.name) : null), /* @__PURE__ */ React37.createElement("button", { className: "brokr-button-ghost", onClick: handleClose, type: "button" }, /* @__PURE__ */ React37.createElement(CloseIcon, { size: 16 }))), /* @__PURE__ */ React37.createElement(
|
|
7867
8128
|
"div",
|
|
7868
8129
|
{
|
|
7869
8130
|
className: "brokr-chat-messages",
|
|
7870
8131
|
"data-empty": messages.length === 0
|
|
7871
8132
|
},
|
|
7872
|
-
messages.length === 0 ? /* @__PURE__ */
|
|
7873
|
-
messages.map((message, index) =>
|
|
7874
|
-
|
|
7875
|
-
|
|
8133
|
+
messages.length === 0 ? /* @__PURE__ */ React37.createElement("div", { className: "brokr-chat-empty" }, /* @__PURE__ */ React37.createElement(SparkIcon, { size: 18 }), /* @__PURE__ */ React37.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ React37.createElement("strong", null, "Send a message to chat with the AI."), /* @__PURE__ */ React37.createElement("span", { className: "brokr-copy" }, "Ask a question or drop in a starter prompt below."))) : null,
|
|
8134
|
+
messages.map((message, index) => {
|
|
8135
|
+
const text = contentToText(message.content);
|
|
8136
|
+
const isTyping = message.role === "assistant" && !text && isSending && index === messages.length - 1;
|
|
8137
|
+
return /* @__PURE__ */ React37.createElement("div", { className: "brokr-chat-bubble", "data-role": message.role, key: `${message.role}-${index}` }, isTyping ? /* @__PURE__ */ React37.createElement("div", { className: "brokr-ai-chat-typing", "aria-label": "AI is typing" }, /* @__PURE__ */ React37.createElement("span", null), /* @__PURE__ */ React37.createElement("span", null), /* @__PURE__ */ React37.createElement("span", null)) : message.role === "assistant" ? /* @__PURE__ */ React37.createElement(MarkdownRenderer, { content: text }) : text);
|
|
8138
|
+
}),
|
|
8139
|
+
error ? /* @__PURE__ */ React37.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null
|
|
8140
|
+
), /* @__PURE__ */ React37.createElement("form", { className: "brokr-section", onSubmit: handleSubmit, style: { gap: "var(--brokr-space-3)" } }, /* @__PURE__ */ React37.createElement(
|
|
7876
8141
|
"textarea",
|
|
7877
8142
|
{
|
|
7878
8143
|
className: "brokr-textarea brokr-chat-input",
|
|
@@ -7882,15 +8147,15 @@ function FabAI({
|
|
|
7882
8147
|
rows: 2,
|
|
7883
8148
|
value: input
|
|
7884
8149
|
}
|
|
7885
|
-
), /* @__PURE__ */
|
|
8150
|
+
), /* @__PURE__ */ React37.createElement("button", { className: "brokr-button", disabled: isSending || !input.trim(), type: "submit" }, isSending ? "Thinking" : "Send"))) : null);
|
|
7886
8151
|
}
|
|
7887
8152
|
|
|
7888
8153
|
// src/react/composites/SmartUpload.tsx
|
|
7889
|
-
import
|
|
8154
|
+
import React38, {
|
|
7890
8155
|
useCallback as useCallback25,
|
|
7891
8156
|
useId as useId2,
|
|
7892
|
-
useMemo as
|
|
7893
|
-
useRef as
|
|
8157
|
+
useMemo as useMemo26,
|
|
8158
|
+
useRef as useRef9,
|
|
7894
8159
|
useState as useState20
|
|
7895
8160
|
} from "react";
|
|
7896
8161
|
function SmartUpload({
|
|
@@ -7901,13 +8166,13 @@ function SmartUpload({
|
|
|
7901
8166
|
}) {
|
|
7902
8167
|
const { paymentsMode } = useBrokr();
|
|
7903
8168
|
const inputId = useId2();
|
|
7904
|
-
const inputRef =
|
|
8169
|
+
const inputRef = useRef9(null);
|
|
7905
8170
|
const [dragActive, setDragActive] = useState20(false);
|
|
7906
8171
|
const [error, setError] = useState20(null);
|
|
7907
8172
|
const [fileName, setFileName] = useState20(null);
|
|
7908
8173
|
const [progress, setProgress] = useState20(0);
|
|
7909
8174
|
const [isUploading, setIsUploading] = useState20(false);
|
|
7910
|
-
const helperText =
|
|
8175
|
+
const helperText = useMemo26(() => {
|
|
7911
8176
|
return `${Math.round(maxSize / (1024 * 1024))} MB max file size.`;
|
|
7912
8177
|
}, [maxSize]);
|
|
7913
8178
|
const beginUpload = useCallback25((file) => {
|
|
@@ -7971,7 +8236,7 @@ function SmartUpload({
|
|
|
7971
8236
|
const handleBrowse = useCallback25(() => {
|
|
7972
8237
|
inputRef.current?.click();
|
|
7973
8238
|
}, []);
|
|
7974
|
-
return /* @__PURE__ */
|
|
8239
|
+
return /* @__PURE__ */ React38.createElement("div", { className: "brokr-card brokr-upload-shell" }, /* @__PURE__ */ React38.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ React38.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ React38.createElement("strong", null, "Upload files"), /* @__PURE__ */ React38.createElement("span", { className: "brokr-copy" }, "Drop a file and let Brokr handle the boring part.")), paymentsMode === "sandbox" ? /* @__PURE__ */ React38.createElement("span", { className: "brokr-badge brokr-badge-sandbox" }, "Sandbox") : null), /* @__PURE__ */ React38.createElement(
|
|
7975
8240
|
"label",
|
|
7976
8241
|
{
|
|
7977
8242
|
className: "brokr-upload-dropzone",
|
|
@@ -7982,11 +8247,11 @@ function SmartUpload({
|
|
|
7982
8247
|
onDragOver: handleDragEnter,
|
|
7983
8248
|
onDrop: handleDrop
|
|
7984
8249
|
},
|
|
7985
|
-
/* @__PURE__ */
|
|
7986
|
-
/* @__PURE__ */
|
|
7987
|
-
/* @__PURE__ */
|
|
7988
|
-
/* @__PURE__ */
|
|
7989
|
-
), /* @__PURE__ */
|
|
8250
|
+
/* @__PURE__ */ React38.createElement(UploadIcon, { size: 24 }),
|
|
8251
|
+
/* @__PURE__ */ React38.createElement("strong", null, "Drag and drop a file here or choose one"),
|
|
8252
|
+
/* @__PURE__ */ React38.createElement("span", { className: "brokr-copy" }, helperText),
|
|
8253
|
+
/* @__PURE__ */ React38.createElement("button", { className: "brokr-button-secondary", onClick: handleBrowse, type: "button" }, "Choose file")
|
|
8254
|
+
), /* @__PURE__ */ React38.createElement(
|
|
7990
8255
|
"input",
|
|
7991
8256
|
{
|
|
7992
8257
|
accept,
|
|
@@ -7996,11 +8261,11 @@ function SmartUpload({
|
|
|
7996
8261
|
ref: inputRef,
|
|
7997
8262
|
type: "file"
|
|
7998
8263
|
}
|
|
7999
|
-
), fileName ? /* @__PURE__ */
|
|
8264
|
+
), fileName ? /* @__PURE__ */ React38.createElement("div", { className: "brokr-card brokr-upload-file" }, /* @__PURE__ */ React38.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ React38.createElement("strong", null, fileName), /* @__PURE__ */ React38.createElement("span", { className: "brokr-copy" }, isUploading ? `Uploading ${progress}%` : progress === 100 ? "Processed" : "Queued")), /* @__PURE__ */ React38.createElement("div", { className: "brokr-meter-bar" }, /* @__PURE__ */ React38.createElement("div", { className: "brokr-meter-fill", style: { width: `${progress}%` } }))) : null, error ? /* @__PURE__ */ React38.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null);
|
|
8000
8265
|
}
|
|
8001
8266
|
|
|
8002
8267
|
// src/react/composites/FeedbackWidget.tsx
|
|
8003
|
-
import
|
|
8268
|
+
import React39, { useCallback as useCallback26, useState as useState21 } from "react";
|
|
8004
8269
|
function FeedbackWidget({
|
|
8005
8270
|
context,
|
|
8006
8271
|
onSubmit
|
|
@@ -8050,7 +8315,7 @@ function FeedbackWidget({
|
|
|
8050
8315
|
setIsPending(false);
|
|
8051
8316
|
}
|
|
8052
8317
|
}, [context, onSubmit, rating, text, user?.id]);
|
|
8053
|
-
return /* @__PURE__ */
|
|
8318
|
+
return /* @__PURE__ */ React39.createElement("form", { className: "brokr-card brokr-feedback-shell", onSubmit: handleSubmit }, /* @__PURE__ */ React39.createElement("div", { className: "brokr-section", style: { gap: "0.5rem" } }, /* @__PURE__ */ React39.createElement("strong", null, "How did this feel?"), /* @__PURE__ */ React39.createElement("p", { className: "brokr-copy" }, "Tight signal only. Tell us what helped or what felt off.")), /* @__PURE__ */ React39.createElement("div", { className: "brokr-feedback-rating" }, /* @__PURE__ */ React39.createElement(
|
|
8054
8319
|
"button",
|
|
8055
8320
|
{
|
|
8056
8321
|
className: "brokr-rating-button",
|
|
@@ -8059,7 +8324,7 @@ function FeedbackWidget({
|
|
|
8059
8324
|
type: "button"
|
|
8060
8325
|
},
|
|
8061
8326
|
"This worked"
|
|
8062
|
-
), /* @__PURE__ */
|
|
8327
|
+
), /* @__PURE__ */ React39.createElement(
|
|
8063
8328
|
"button",
|
|
8064
8329
|
{
|
|
8065
8330
|
className: "brokr-rating-button",
|
|
@@ -8068,7 +8333,7 @@ function FeedbackWidget({
|
|
|
8068
8333
|
type: "button"
|
|
8069
8334
|
},
|
|
8070
8335
|
"Needs work"
|
|
8071
|
-
)), /* @__PURE__ */
|
|
8336
|
+
)), /* @__PURE__ */ React39.createElement(
|
|
8072
8337
|
"textarea",
|
|
8073
8338
|
{
|
|
8074
8339
|
className: "brokr-textarea",
|
|
@@ -8077,11 +8342,11 @@ function FeedbackWidget({
|
|
|
8077
8342
|
rows: 4,
|
|
8078
8343
|
value: text
|
|
8079
8344
|
}
|
|
8080
|
-
), error ? /* @__PURE__ */
|
|
8345
|
+
), error ? /* @__PURE__ */ React39.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null, message ? /* @__PURE__ */ React39.createElement("div", { className: "brokr-inline-message" }, message) : null, /* @__PURE__ */ React39.createElement("button", { className: "brokr-button", disabled: isPending, type: "submit" }, isPending ? "Sending" : "Send feedback"));
|
|
8081
8346
|
}
|
|
8082
8347
|
|
|
8083
8348
|
// src/react/BrokrErrorBoundary.tsx
|
|
8084
|
-
import
|
|
8349
|
+
import React40 from "react";
|
|
8085
8350
|
|
|
8086
8351
|
// src/fix-registry.ts
|
|
8087
8352
|
var FIX_REGISTRY = {
|
|
@@ -8305,7 +8570,7 @@ var BrokrError = class extends Error {
|
|
|
8305
8570
|
};
|
|
8306
8571
|
|
|
8307
8572
|
// src/react/BrokrErrorBoundary.tsx
|
|
8308
|
-
var BrokrErrorBoundary = class extends
|
|
8573
|
+
var BrokrErrorBoundary = class extends React40.Component {
|
|
8309
8574
|
constructor() {
|
|
8310
8575
|
super(...arguments);
|
|
8311
8576
|
this.state = { hasError: false, error: null };
|
|
@@ -8317,11 +8582,11 @@ var BrokrErrorBoundary = class extends React39.Component {
|
|
|
8317
8582
|
if (this.state.hasError) {
|
|
8318
8583
|
if (this.props.fallback) return this.props.fallback;
|
|
8319
8584
|
const message = this.state.error instanceof BrokrError ? this.state.error.toUserMessage() : "Something went wrong.";
|
|
8320
|
-
return
|
|
8585
|
+
return React40.createElement(
|
|
8321
8586
|
"div",
|
|
8322
8587
|
{ style: { padding: 24, textAlign: "center" } },
|
|
8323
|
-
|
|
8324
|
-
|
|
8588
|
+
React40.createElement("p", null, message),
|
|
8589
|
+
React40.createElement(
|
|
8325
8590
|
"button",
|
|
8326
8591
|
{
|
|
8327
8592
|
onClick: () => this.setState({ hasError: false, error: null }),
|
|
@@ -8335,8 +8600,8 @@ var BrokrErrorBoundary = class extends React39.Component {
|
|
|
8335
8600
|
}
|
|
8336
8601
|
};
|
|
8337
8602
|
|
|
8338
|
-
// src/react/notifications/
|
|
8339
|
-
import
|
|
8603
|
+
// src/react/notifications/ActivityFeed.tsx
|
|
8604
|
+
import React41, { useCallback as useCallback27, useMemo as useMemo27 } from "react";
|
|
8340
8605
|
|
|
8341
8606
|
// src/react/notifications/use-notifications.ts
|
|
8342
8607
|
import { useContext as useContext4 } from "react";
|
|
@@ -8350,7 +8615,7 @@ function useNotifications() {
|
|
|
8350
8615
|
return ctx;
|
|
8351
8616
|
}
|
|
8352
8617
|
|
|
8353
|
-
// src/react/notifications/
|
|
8618
|
+
// src/react/notifications/ActivityFeed.tsx
|
|
8354
8619
|
function timeAgo(iso) {
|
|
8355
8620
|
const diff = Date.now() - new Date(iso).getTime();
|
|
8356
8621
|
const mins = Math.floor(diff / 6e4);
|
|
@@ -8361,16 +8626,79 @@ function timeAgo(iso) {
|
|
|
8361
8626
|
const days = Math.floor(hours / 24);
|
|
8362
8627
|
return `${days}d ago`;
|
|
8363
8628
|
}
|
|
8629
|
+
function FeedItem({
|
|
8630
|
+
item,
|
|
8631
|
+
formatter
|
|
8632
|
+
}) {
|
|
8633
|
+
if (formatter) {
|
|
8634
|
+
return /* @__PURE__ */ React41.createElement("div", { className: "brokr-feed-item" }, formatter(item));
|
|
8635
|
+
}
|
|
8636
|
+
return /* @__PURE__ */ React41.createElement("div", { className: "brokr-feed-item" }, /* @__PURE__ */ React41.createElement("span", { className: `brokr-feed-dot brokr-feed-dot--${item.variant}` }), /* @__PURE__ */ React41.createElement("div", { className: "brokr-feed-content" }, /* @__PURE__ */ React41.createElement("span", { className: "brokr-feed-title" }, item.title), item.message ? /* @__PURE__ */ React41.createElement("span", { className: "brokr-feed-message" }, item.message) : null), /* @__PURE__ */ React41.createElement("span", { className: "brokr-feed-time" }, timeAgo(item.createdAt)));
|
|
8637
|
+
}
|
|
8638
|
+
function ActivityFeed({
|
|
8639
|
+
filter,
|
|
8640
|
+
maxItems = 20,
|
|
8641
|
+
formatters,
|
|
8642
|
+
emptyState
|
|
8643
|
+
}) {
|
|
8644
|
+
const { notifications, isLoading } = useNotifications();
|
|
8645
|
+
const items = useMemo27(() => {
|
|
8646
|
+
let filtered = notifications;
|
|
8647
|
+
if (filter && filter.length > 0) {
|
|
8648
|
+
const set = new Set(filter);
|
|
8649
|
+
filtered = notifications.filter(
|
|
8650
|
+
(n) => set.has(n.variant) || n.type && set.has(n.type)
|
|
8651
|
+
);
|
|
8652
|
+
}
|
|
8653
|
+
return [...filtered].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, maxItems);
|
|
8654
|
+
}, [notifications, filter, maxItems]);
|
|
8655
|
+
const getFormatter = useCallback27(
|
|
8656
|
+
(item) => {
|
|
8657
|
+
if (!formatters) return void 0;
|
|
8658
|
+
if (item.type && formatters[item.type]) return formatters[item.type];
|
|
8659
|
+
if (formatters[item.variant]) return formatters[item.variant];
|
|
8660
|
+
return void 0;
|
|
8661
|
+
},
|
|
8662
|
+
[formatters]
|
|
8663
|
+
);
|
|
8664
|
+
if (isLoading) {
|
|
8665
|
+
return /* @__PURE__ */ React41.createElement("div", { className: "brokr-feed" }, [1, 2, 3].map((i) => /* @__PURE__ */ React41.createElement("div", { className: "brokr-feed-item", key: i }, /* @__PURE__ */ React41.createElement("span", { className: "brokr-feed-dot" }), /* @__PURE__ */ React41.createElement("div", { className: "brokr-feed-content" }, /* @__PURE__ */ React41.createElement("span", { className: "brokr-skeleton", style: { width: "60%", height: 12 } }), /* @__PURE__ */ React41.createElement("span", { className: "brokr-skeleton", style: { width: "80%", height: 12 } })), /* @__PURE__ */ React41.createElement("span", { className: "brokr-skeleton", style: { width: 40, height: 12 } }))));
|
|
8666
|
+
}
|
|
8667
|
+
if (items.length === 0) {
|
|
8668
|
+
return /* @__PURE__ */ React41.createElement("div", { className: "brokr-feed" }, /* @__PURE__ */ React41.createElement("div", { className: "brokr-feed-empty" }, emptyState ?? /* @__PURE__ */ React41.createElement("span", { className: "brokr-copy" }, "No activity yet.")));
|
|
8669
|
+
}
|
|
8670
|
+
return /* @__PURE__ */ React41.createElement("div", { className: "brokr-feed" }, items.map((item) => /* @__PURE__ */ React41.createElement(
|
|
8671
|
+
FeedItem,
|
|
8672
|
+
{
|
|
8673
|
+
key: item.id,
|
|
8674
|
+
item,
|
|
8675
|
+
formatter: getFormatter(item)
|
|
8676
|
+
}
|
|
8677
|
+
)));
|
|
8678
|
+
}
|
|
8679
|
+
|
|
8680
|
+
// src/react/notifications/NotificationBell.tsx
|
|
8681
|
+
import React42, { useCallback as useCallback28, useEffect as useEffect13, useMemo as useMemo28, useRef as useRef10, useState as useState22 } from "react";
|
|
8682
|
+
function timeAgo2(iso) {
|
|
8683
|
+
const diff = Date.now() - new Date(iso).getTime();
|
|
8684
|
+
const mins = Math.floor(diff / 6e4);
|
|
8685
|
+
if (mins < 1) return "just now";
|
|
8686
|
+
if (mins < 60) return `${mins}m ago`;
|
|
8687
|
+
const hours = Math.floor(mins / 60);
|
|
8688
|
+
if (hours < 24) return `${hours}h ago`;
|
|
8689
|
+
const days = Math.floor(hours / 24);
|
|
8690
|
+
return `${days}d ago`;
|
|
8691
|
+
}
|
|
8364
8692
|
function NotifDropdownItem({
|
|
8365
8693
|
notif,
|
|
8366
8694
|
registry,
|
|
8367
8695
|
onClick
|
|
8368
8696
|
}) {
|
|
8369
|
-
const handleClick =
|
|
8697
|
+
const handleClick = useCallback28(() => onClick(notif), [notif, onClick]);
|
|
8370
8698
|
const notifData = notif.data ?? {};
|
|
8371
8699
|
const notifType = notifData.type ?? "default";
|
|
8372
8700
|
const resolved = resolveNotificationType(registry, notifType, notifData);
|
|
8373
|
-
return /* @__PURE__ */
|
|
8701
|
+
return /* @__PURE__ */ React42.createElement(
|
|
8374
8702
|
"button",
|
|
8375
8703
|
{
|
|
8376
8704
|
type: "button",
|
|
@@ -8378,17 +8706,17 @@ function NotifDropdownItem({
|
|
|
8378
8706
|
onClick: handleClick,
|
|
8379
8707
|
role: "menuitem"
|
|
8380
8708
|
},
|
|
8381
|
-
resolved.image ? /* @__PURE__ */
|
|
8382
|
-
/* @__PURE__ */
|
|
8383
|
-
/* @__PURE__ */
|
|
8709
|
+
resolved.image ? /* @__PURE__ */ React42.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-notif-item-logo" }) : /* @__PURE__ */ React42.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
|
|
8710
|
+
/* @__PURE__ */ React42.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ React42.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ React42.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
|
|
8711
|
+
/* @__PURE__ */ React42.createElement("span", { className: "brokr-notif-item-time" }, timeAgo2(notif.createdAt))
|
|
8384
8712
|
);
|
|
8385
8713
|
}
|
|
8386
8714
|
function NotificationBell() {
|
|
8387
8715
|
const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
|
|
8388
8716
|
const [open, setOpen] = useState22(false);
|
|
8389
|
-
const containerRef =
|
|
8390
|
-
const markReadTimerRef =
|
|
8391
|
-
const toggle =
|
|
8717
|
+
const containerRef = useRef10(null);
|
|
8718
|
+
const markReadTimerRef = useRef10(null);
|
|
8719
|
+
const toggle = useCallback28(() => setOpen((o) => !o), []);
|
|
8392
8720
|
useEffect13(() => {
|
|
8393
8721
|
if (markReadTimerRef.current) {
|
|
8394
8722
|
clearTimeout(markReadTimerRef.current);
|
|
@@ -8424,13 +8752,13 @@ function NotificationBell() {
|
|
|
8424
8752
|
document.addEventListener("keydown", handleKey);
|
|
8425
8753
|
return () => document.removeEventListener("keydown", handleKey);
|
|
8426
8754
|
}, [open]);
|
|
8427
|
-
const sorted =
|
|
8755
|
+
const sorted = useMemo28(
|
|
8428
8756
|
() => [...notifications].sort(
|
|
8429
8757
|
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
8430
8758
|
),
|
|
8431
8759
|
[notifications]
|
|
8432
8760
|
);
|
|
8433
|
-
const handleItemClick =
|
|
8761
|
+
const handleItemClick = useCallback28((notif) => {
|
|
8434
8762
|
if (!notif.read) markRead(notif.id);
|
|
8435
8763
|
const notifData = notif.data ?? {};
|
|
8436
8764
|
const notifType = notif.type ?? notifData.type ?? "default";
|
|
@@ -8440,7 +8768,7 @@ function NotificationBell() {
|
|
|
8440
8768
|
window.location.assign(href);
|
|
8441
8769
|
}
|
|
8442
8770
|
}, [markRead, registry]);
|
|
8443
|
-
return /* @__PURE__ */
|
|
8771
|
+
return /* @__PURE__ */ React42.createElement("div", { className: "brokr-notif-bell-wrap", ref: containerRef }, /* @__PURE__ */ React42.createElement(
|
|
8444
8772
|
"button",
|
|
8445
8773
|
{
|
|
8446
8774
|
type: "button",
|
|
@@ -8450,9 +8778,9 @@ function NotificationBell() {
|
|
|
8450
8778
|
"aria-expanded": open,
|
|
8451
8779
|
"aria-haspopup": "menu"
|
|
8452
8780
|
},
|
|
8453
|
-
/* @__PURE__ */
|
|
8454
|
-
unreadCount > 0 && /* @__PURE__ */
|
|
8455
|
-
), open && /* @__PURE__ */
|
|
8781
|
+
/* @__PURE__ */ React42.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__ */ React42.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ React42.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })),
|
|
8782
|
+
unreadCount > 0 && /* @__PURE__ */ React42.createElement("span", { className: "brokr-notif-badge" }, unreadCount > 99 ? "99+" : unreadCount)
|
|
8783
|
+
), open && /* @__PURE__ */ React42.createElement("div", { className: "brokr-notif-dropdown", role: "menu" }, /* @__PURE__ */ React42.createElement("div", { className: "brokr-notif-dropdown-header" }, /* @__PURE__ */ React42.createElement("span", { className: "brokr-notif-dropdown-title" }, "Notifications"), unreadCount > 0 && /* @__PURE__ */ React42.createElement(
|
|
8456
8784
|
"button",
|
|
8457
8785
|
{
|
|
8458
8786
|
type: "button",
|
|
@@ -8460,7 +8788,7 @@ function NotificationBell() {
|
|
|
8460
8788
|
onClick: markAllRead
|
|
8461
8789
|
},
|
|
8462
8790
|
"Mark all read"
|
|
8463
|
-
)), /* @__PURE__ */
|
|
8791
|
+
)), /* @__PURE__ */ React42.createElement("div", { className: "brokr-notif-dropdown-list" }, isLoading ? /* @__PURE__ */ React42.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ React42.createElement("span", { className: "brokr-notif-empty-text" }, "Loading\u2026")) : sorted.length === 0 ? /* @__PURE__ */ React42.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ React42.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : sorted.map((notif) => /* @__PURE__ */ React42.createElement(
|
|
8464
8792
|
NotifDropdownItem,
|
|
8465
8793
|
{
|
|
8466
8794
|
key: notif.id,
|
|
@@ -8472,7 +8800,7 @@ function NotificationBell() {
|
|
|
8472
8800
|
}
|
|
8473
8801
|
|
|
8474
8802
|
// src/react/notifications/NotificationList.tsx
|
|
8475
|
-
import
|
|
8803
|
+
import React43, { useCallback as useCallback29, useMemo as useMemo29 } from "react";
|
|
8476
8804
|
function formatTimestamp(iso) {
|
|
8477
8805
|
const date = new Date(iso);
|
|
8478
8806
|
return new Intl.DateTimeFormat("en-US", {
|
|
@@ -8483,38 +8811,38 @@ function formatTimestamp(iso) {
|
|
|
8483
8811
|
}).format(date);
|
|
8484
8812
|
}
|
|
8485
8813
|
function NotificationListSkeleton() {
|
|
8486
|
-
return /* @__PURE__ */
|
|
8814
|
+
return /* @__PURE__ */ React43.createElement("div", { className: "brokr-notif-list-items" }, [1, 2, 3].map((i) => /* @__PURE__ */ React43.createElement("div", { key: i, className: "brokr-notif-list-row brokr-notif-list-row--skeleton" }, /* @__PURE__ */ React43.createElement("span", { className: "brokr-notif-item-dot brokr-notif-item-dot--skeleton" }), /* @__PURE__ */ React43.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ React43.createElement("span", { className: "brokr-notif-item-title brokr-skeleton-line", style: { width: "60%" } }), /* @__PURE__ */ React43.createElement("span", { className: "brokr-notif-item-message brokr-skeleton-line", style: { width: "80%" } })), /* @__PURE__ */ React43.createElement("span", { className: "brokr-notif-item-time brokr-skeleton-line", style: { width: 48 } }))));
|
|
8487
8815
|
}
|
|
8488
8816
|
function NotifListItem({
|
|
8489
8817
|
notif,
|
|
8490
8818
|
registry,
|
|
8491
8819
|
onClick
|
|
8492
8820
|
}) {
|
|
8493
|
-
const handleClick =
|
|
8821
|
+
const handleClick = useCallback29(() => onClick(notif), [notif, onClick]);
|
|
8494
8822
|
const notifData = notif.data ?? {};
|
|
8495
8823
|
const notifType = notif.type ?? notifData.type ?? "default";
|
|
8496
8824
|
const resolved = resolveNotificationType(registry, notifType, notifData);
|
|
8497
|
-
return /* @__PURE__ */
|
|
8825
|
+
return /* @__PURE__ */ React43.createElement(
|
|
8498
8826
|
"button",
|
|
8499
8827
|
{
|
|
8500
8828
|
type: "button",
|
|
8501
8829
|
className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
|
|
8502
8830
|
onClick: handleClick
|
|
8503
8831
|
},
|
|
8504
|
-
resolved.image ? /* @__PURE__ */
|
|
8505
|
-
/* @__PURE__ */
|
|
8506
|
-
/* @__PURE__ */
|
|
8832
|
+
resolved.image ? /* @__PURE__ */ React43.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-notif-item-logo" }) : /* @__PURE__ */ React43.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
|
|
8833
|
+
/* @__PURE__ */ React43.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ React43.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ React43.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
|
|
8834
|
+
/* @__PURE__ */ React43.createElement("span", { className: "brokr-notif-item-time" }, formatTimestamp(notif.createdAt))
|
|
8507
8835
|
);
|
|
8508
8836
|
}
|
|
8509
8837
|
function NotificationList() {
|
|
8510
8838
|
const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
|
|
8511
|
-
const sorted =
|
|
8839
|
+
const sorted = useMemo29(
|
|
8512
8840
|
() => [...notifications].sort(
|
|
8513
8841
|
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
8514
8842
|
),
|
|
8515
8843
|
[notifications]
|
|
8516
8844
|
);
|
|
8517
|
-
const handleClick =
|
|
8845
|
+
const handleClick = useCallback29((notif) => {
|
|
8518
8846
|
if (!notif.read) markRead(notif.id);
|
|
8519
8847
|
const notifData = notif.data ?? {};
|
|
8520
8848
|
const notifType = notif.type ?? notifData.type ?? "default";
|
|
@@ -8524,7 +8852,7 @@ function NotificationList() {
|
|
|
8524
8852
|
window.location.assign(href);
|
|
8525
8853
|
}
|
|
8526
8854
|
}, [markRead, registry]);
|
|
8527
|
-
return /* @__PURE__ */
|
|
8855
|
+
return /* @__PURE__ */ React43.createElement("div", { className: "brokr-notif-list" }, /* @__PURE__ */ React43.createElement("div", { className: "brokr-notif-list-header" }, /* @__PURE__ */ React43.createElement("span", { className: "brokr-notif-list-title" }, "Notifications"), unreadCount > 0 && /* @__PURE__ */ React43.createElement(
|
|
8528
8856
|
"button",
|
|
8529
8857
|
{
|
|
8530
8858
|
type: "button",
|
|
@@ -8532,7 +8860,7 @@ function NotificationList() {
|
|
|
8532
8860
|
onClick: markAllRead
|
|
8533
8861
|
},
|
|
8534
8862
|
"Mark all read"
|
|
8535
|
-
)), isLoading ? /* @__PURE__ */
|
|
8863
|
+
)), isLoading ? /* @__PURE__ */ React43.createElement(NotificationListSkeleton, null) : sorted.length === 0 ? /* @__PURE__ */ React43.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ React43.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__ */ React43.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ React43.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })), /* @__PURE__ */ React43.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : /* @__PURE__ */ React43.createElement("div", { className: "brokr-notif-list-items" }, sorted.map((notif) => /* @__PURE__ */ React43.createElement(
|
|
8536
8864
|
NotifListItem,
|
|
8537
8865
|
{
|
|
8538
8866
|
key: notif.id,
|
|
@@ -8572,6 +8900,7 @@ function defineAccount(config) {
|
|
|
8572
8900
|
export {
|
|
8573
8901
|
AIChat,
|
|
8574
8902
|
AccountPanel,
|
|
8903
|
+
ActivityFeed,
|
|
8575
8904
|
AuthPageShell,
|
|
8576
8905
|
AuthWall,
|
|
8577
8906
|
AutoReloadToggle,
|
|
@@ -8601,8 +8930,10 @@ export {
|
|
|
8601
8930
|
UpdateBilling,
|
|
8602
8931
|
UpgradePrompt,
|
|
8603
8932
|
UsageGate,
|
|
8933
|
+
UsageGrid,
|
|
8604
8934
|
UserButton,
|
|
8605
8935
|
defineAccount,
|
|
8936
|
+
defineBrokrTheme,
|
|
8606
8937
|
defineChat,
|
|
8607
8938
|
useBrokr,
|
|
8608
8939
|
useBrokrTheme,
|