@brokr/sdk 2.1.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/feature.js +22 -2
- package/dist/feature.mjs +22 -2
- package/dist/index.js +22 -2
- package/dist/index.mjs +22 -2
- package/dist/next.js +22 -2
- package/dist/next.mjs +22 -2
- package/dist/react-styles.js +273 -94
- package/dist/react-styles.mjs +273 -94
- package/dist/react.js +329 -81
- package/dist/react.mjs +335 -87
- 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/auth.d.ts +1 -1
- package/dist/src/react/css/auth.d.ts.map +1 -1
- package/dist/src/react/css/chat-extras.d.ts +1 -1
- package/dist/src/react/css/chat-extras.d.ts.map +1 -1
- package/dist/src/react/css/chat.d.ts +1 -1
- package/dist/src/react/css/chat.d.ts.map +1 -1
- package/dist/src/react/css/composites.d.ts +1 -1
- package/dist/src/react/css/composites.d.ts.map +1 -1
- package/dist/src/react/css/gates.d.ts +1 -1
- package/dist/src/react/css/gates.d.ts.map +1 -1
- package/dist/src/react/css/markdown.d.ts +1 -1
- package/dist/src/react/css/markdown.d.ts.map +1 -1
- package/dist/src/react/css/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/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,
|
|
@@ -6410,6 +6411,39 @@ function isSSEResponse(response) {
|
|
|
6410
6411
|
return ct.includes("text/event-stream") && response.body !== null;
|
|
6411
6412
|
}
|
|
6412
6413
|
|
|
6414
|
+
// src/react/chat/memory.ts
|
|
6415
|
+
var MAX_MEMORY_CHARS = 2e3;
|
|
6416
|
+
function serializeMemory(obj, prefix = "") {
|
|
6417
|
+
const lines = [];
|
|
6418
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
6419
|
+
if (value === null || value === void 0) continue;
|
|
6420
|
+
const label = prefix ? `${prefix}.${key}` : key;
|
|
6421
|
+
if (Array.isArray(value)) {
|
|
6422
|
+
lines.push(`${label}: ${value.join(", ")}`);
|
|
6423
|
+
} else if (typeof value === "object") {
|
|
6424
|
+
lines.push(...serializeMemory(value, label));
|
|
6425
|
+
} else {
|
|
6426
|
+
lines.push(`${label}: ${String(value)}`);
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
return lines;
|
|
6430
|
+
}
|
|
6431
|
+
function buildEffectivePrompt(prompt, memory) {
|
|
6432
|
+
if (!memory || Object.keys(memory).length === 0) return prompt;
|
|
6433
|
+
let block = serializeMemory(memory).join("\n");
|
|
6434
|
+
if (block.length > MAX_MEMORY_CHARS) {
|
|
6435
|
+
block = block.slice(0, MAX_MEMORY_CHARS);
|
|
6436
|
+
if (typeof console !== "undefined") {
|
|
6437
|
+
console.warn("[brokr] memory exceeded 2000 char limit, truncated");
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6440
|
+
const memSection = `[User context]
|
|
6441
|
+
${block}`;
|
|
6442
|
+
return prompt ? `${memSection}
|
|
6443
|
+
|
|
6444
|
+
${prompt}` : memSection;
|
|
6445
|
+
}
|
|
6446
|
+
|
|
6413
6447
|
// src/react/chat/useChat.ts
|
|
6414
6448
|
function makeId(prefix) {
|
|
6415
6449
|
return `${prefix}_${crypto.randomUUID()}`;
|
|
@@ -6427,7 +6461,9 @@ function useChat(config) {
|
|
|
6427
6461
|
const {
|
|
6428
6462
|
endpoint,
|
|
6429
6463
|
prompt,
|
|
6430
|
-
|
|
6464
|
+
explicitModel,
|
|
6465
|
+
displayModel: activeModel,
|
|
6466
|
+
memory,
|
|
6431
6467
|
persist,
|
|
6432
6468
|
surface,
|
|
6433
6469
|
subject,
|
|
@@ -6463,9 +6499,25 @@ function useChat(config) {
|
|
|
6463
6499
|
const scrollContainerRef = useRef5(null);
|
|
6464
6500
|
const sentinelRef = useRef5(null);
|
|
6465
6501
|
const displaySidebarItems = useMemo18(() => {
|
|
6466
|
-
if (isControlled)
|
|
6467
|
-
|
|
6468
|
-
|
|
6502
|
+
if (isControlled) {
|
|
6503
|
+
return threadsProp?.map((t) => ({
|
|
6504
|
+
id: t.id,
|
|
6505
|
+
title: t.title,
|
|
6506
|
+
updatedAt: t.updatedAt ?? null
|
|
6507
|
+
})) ?? [];
|
|
6508
|
+
}
|
|
6509
|
+
if (isPersist) {
|
|
6510
|
+
return serverThreads.map((t) => ({
|
|
6511
|
+
id: t.id,
|
|
6512
|
+
title: t.title,
|
|
6513
|
+
updatedAt: t.updatedAt ?? null
|
|
6514
|
+
}));
|
|
6515
|
+
}
|
|
6516
|
+
return memConversations.map((c) => ({
|
|
6517
|
+
id: c.id,
|
|
6518
|
+
title: c.title || "New chat",
|
|
6519
|
+
updatedAt: new Date(c.updatedAt).toISOString()
|
|
6520
|
+
}));
|
|
6469
6521
|
}, [isControlled, isPersist, threadsProp, serverThreads, memConversations]);
|
|
6470
6522
|
const activeId = useMemo18(() => {
|
|
6471
6523
|
if (isControlled) return activeThreadIdProp ?? null;
|
|
@@ -6646,8 +6698,11 @@ function useChat(config) {
|
|
|
6646
6698
|
messages: trimToTokenBudget(
|
|
6647
6699
|
nextMessages.filter((m) => m.role === "user" || m.role === "assistant" && m.content).map(({ content: mc, role }) => ({ role, content: mc }))
|
|
6648
6700
|
),
|
|
6649
|
-
model:
|
|
6650
|
-
...
|
|
6701
|
+
...explicitModel ? { model: explicitModel } : {},
|
|
6702
|
+
...(() => {
|
|
6703
|
+
const ep = buildEffectivePrompt(prompt, memory);
|
|
6704
|
+
return ep ? { systemPrompt: ep } : {};
|
|
6705
|
+
})()
|
|
6651
6706
|
})
|
|
6652
6707
|
});
|
|
6653
6708
|
if (!response.ok) {
|
|
@@ -6692,8 +6747,10 @@ function useChat(config) {
|
|
|
6692
6747
|
ensureMemConversation,
|
|
6693
6748
|
memConversations,
|
|
6694
6749
|
endpoint,
|
|
6750
|
+
explicitModel,
|
|
6695
6751
|
activeModel,
|
|
6696
6752
|
prompt,
|
|
6753
|
+
memory,
|
|
6697
6754
|
updateMemMessages,
|
|
6698
6755
|
appendMemDelta,
|
|
6699
6756
|
animateTitle,
|
|
@@ -6715,9 +6772,12 @@ function useChat(config) {
|
|
|
6715
6772
|
content,
|
|
6716
6773
|
surface,
|
|
6717
6774
|
subject: subject ?? null,
|
|
6718
|
-
model:
|
|
6775
|
+
...explicitModel ? { model: explicitModel } : {},
|
|
6719
6776
|
userId: userId ?? void 0,
|
|
6720
|
-
...
|
|
6777
|
+
...(() => {
|
|
6778
|
+
const ep = buildEffectivePrompt(prompt, memory);
|
|
6779
|
+
return ep ? { systemPrompt: ep } : {};
|
|
6780
|
+
})()
|
|
6721
6781
|
})
|
|
6722
6782
|
});
|
|
6723
6783
|
if (!response.ok) {
|
|
@@ -6772,8 +6832,10 @@ function useChat(config) {
|
|
|
6772
6832
|
endpoint,
|
|
6773
6833
|
surface,
|
|
6774
6834
|
subject,
|
|
6835
|
+
explicitModel,
|
|
6775
6836
|
activeModel,
|
|
6776
6837
|
prompt,
|
|
6838
|
+
memory,
|
|
6777
6839
|
userId,
|
|
6778
6840
|
setServerMessages,
|
|
6779
6841
|
setServerActiveId,
|
|
@@ -6970,12 +7032,17 @@ function ModelSelector({
|
|
|
6970
7032
|
}) {
|
|
6971
7033
|
const [selectorOpen, setSelectorOpen] = useState17(false);
|
|
6972
7034
|
const selectorRef = useRef6(null);
|
|
7035
|
+
const isAuto = activeModel === STACK_DEFAULT || !providers.some((p) => p.model === activeModel);
|
|
6973
7036
|
const activeProvider = useMemo19(
|
|
6974
|
-
() => providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
6975
|
-
[activeModel]
|
|
7037
|
+
() => isAuto ? void 0 : providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7038
|
+
[activeModel, isAuto]
|
|
6976
7039
|
);
|
|
6977
7040
|
const handleModelSelect = useCallback17((model) => {
|
|
6978
|
-
setSelectedModel(model);
|
|
7041
|
+
setSelectedModel(model === STACK_DEFAULT ? null : model);
|
|
7042
|
+
setSelectorOpen(false);
|
|
7043
|
+
}, [setSelectedModel]);
|
|
7044
|
+
const handleAutoSelect = useCallback17(() => {
|
|
7045
|
+
setSelectedModel(null);
|
|
6979
7046
|
setSelectorOpen(false);
|
|
6980
7047
|
}, [setSelectedModel]);
|
|
6981
7048
|
useEffect10(() => {
|
|
@@ -7005,15 +7072,26 @@ function ModelSelector({
|
|
|
7005
7072
|
onClick: handleToggle,
|
|
7006
7073
|
type: "button"
|
|
7007
7074
|
},
|
|
7008
|
-
activeProvider ? /* @__PURE__ */ React29.createElement("img", { alt: "", className: "brokr-model-logo", src: activeProvider.logo }) : /* @__PURE__ */ React29.createElement("span", { className: "brokr-model-dot", style: { background: "
|
|
7009
|
-
activeProvider?.label ?? "Model",
|
|
7075
|
+
activeProvider ? /* @__PURE__ */ React29.createElement("img", { alt: "", className: "brokr-model-logo", src: activeProvider.logo }) : /* @__PURE__ */ React29.createElement("span", { className: "brokr-model-dot", style: { background: "#6B7280" } }),
|
|
7076
|
+
isAuto ? "Auto" : activeProvider?.label ?? "Model",
|
|
7010
7077
|
/* @__PURE__ */ React29.createElement(ChevronDownIcon, { size: 13 })
|
|
7011
|
-
), selectorOpen ? /* @__PURE__ */ React29.createElement("div", { className: "brokr-model-dropdown", role: "listbox" },
|
|
7078
|
+
), selectorOpen ? /* @__PURE__ */ React29.createElement("div", { className: "brokr-model-dropdown", role: "listbox" }, /* @__PURE__ */ React29.createElement(
|
|
7079
|
+
"button",
|
|
7080
|
+
{
|
|
7081
|
+
"aria-selected": isAuto,
|
|
7082
|
+
className: "brokr-model-option",
|
|
7083
|
+
"data-active": isAuto,
|
|
7084
|
+
onClick: handleAutoSelect,
|
|
7085
|
+
type: "button"
|
|
7086
|
+
},
|
|
7087
|
+
/* @__PURE__ */ React29.createElement("span", { className: "brokr-model-dot", style: { background: "#6B7280" } }),
|
|
7088
|
+
/* @__PURE__ */ React29.createElement("span", { className: "brokr-model-option-label" }, "Stack Default")
|
|
7089
|
+
), providers.map((p) => /* @__PURE__ */ React29.createElement(
|
|
7012
7090
|
ModelOption,
|
|
7013
7091
|
{
|
|
7014
7092
|
key: p.id,
|
|
7015
7093
|
provider: p,
|
|
7016
|
-
isActive: p.model === activeModel,
|
|
7094
|
+
isActive: !isAuto && p.model === activeModel,
|
|
7017
7095
|
isLocked: !availableProviders.some((a) => a.id === p.id),
|
|
7018
7096
|
onSelect: handleModelSelect,
|
|
7019
7097
|
onCheckout: checkout
|
|
@@ -7033,9 +7111,40 @@ function ThreadItemButton({ id, title, isActive, onSelect }) {
|
|
|
7033
7111
|
onClick: handleClick,
|
|
7034
7112
|
type: "button"
|
|
7035
7113
|
},
|
|
7036
|
-
|
|
7114
|
+
/* @__PURE__ */ React30.createElement(MessageIcon, { size: 12 }),
|
|
7115
|
+
/* @__PURE__ */ React30.createElement("span", { className: "brokr-ai-chat-conversation-label" }, title)
|
|
7037
7116
|
);
|
|
7038
7117
|
}
|
|
7118
|
+
var SIDEBAR_GROUP_ORDER = [
|
|
7119
|
+
"today",
|
|
7120
|
+
"yesterday",
|
|
7121
|
+
"this_week",
|
|
7122
|
+
"this_month",
|
|
7123
|
+
"earlier",
|
|
7124
|
+
"undated"
|
|
7125
|
+
];
|
|
7126
|
+
var SIDEBAR_GROUP_LABELS = {
|
|
7127
|
+
today: "Today",
|
|
7128
|
+
yesterday: "Yesterday",
|
|
7129
|
+
this_week: "This Week",
|
|
7130
|
+
this_month: "This Month",
|
|
7131
|
+
earlier: "Earlier",
|
|
7132
|
+
undated: "Recent"
|
|
7133
|
+
};
|
|
7134
|
+
function resolveSidebarDateGroup(updatedAt) {
|
|
7135
|
+
if (!updatedAt) return "undated";
|
|
7136
|
+
const parsed = new Date(updatedAt);
|
|
7137
|
+
if (Number.isNaN(parsed.getTime())) return "undated";
|
|
7138
|
+
const now = /* @__PURE__ */ new Date();
|
|
7139
|
+
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
7140
|
+
const startOfTarget = new Date(parsed.getFullYear(), parsed.getMonth(), parsed.getDate());
|
|
7141
|
+
const diffDays = Math.floor((startOfToday.getTime() - startOfTarget.getTime()) / 864e5);
|
|
7142
|
+
if (diffDays <= 0) return "today";
|
|
7143
|
+
if (diffDays === 1) return "yesterday";
|
|
7144
|
+
if (diffDays < 7) return "this_week";
|
|
7145
|
+
if (diffDays < 30) return "this_month";
|
|
7146
|
+
return "earlier";
|
|
7147
|
+
}
|
|
7039
7148
|
function ThreadSidebar() {
|
|
7040
7149
|
const {
|
|
7041
7150
|
startNewChat,
|
|
@@ -7067,14 +7176,31 @@ function ThreadSidebar() {
|
|
|
7067
7176
|
const handleRenameChange = useCallback18((e) => {
|
|
7068
7177
|
setRenameValue(e.target.value);
|
|
7069
7178
|
}, [setRenameValue]);
|
|
7179
|
+
const groupedSidebarItems = useMemo20(() => {
|
|
7180
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
7181
|
+
for (const item of displaySidebarItems) {
|
|
7182
|
+
const key = resolveSidebarDateGroup(item.updatedAt);
|
|
7183
|
+
const bucket = grouped.get(key);
|
|
7184
|
+
if (bucket) {
|
|
7185
|
+
bucket.push(item);
|
|
7186
|
+
} else {
|
|
7187
|
+
grouped.set(key, [item]);
|
|
7188
|
+
}
|
|
7189
|
+
}
|
|
7190
|
+
return SIDEBAR_GROUP_ORDER.map((key) => ({
|
|
7191
|
+
key,
|
|
7192
|
+
label: SIDEBAR_GROUP_LABELS[key],
|
|
7193
|
+
items: grouped.get(key) ?? []
|
|
7194
|
+
})).filter((group) => group.items.length > 0);
|
|
7195
|
+
}, [displaySidebarItems]);
|
|
7070
7196
|
const content = useMemo20(() => {
|
|
7071
7197
|
if (threadsLoading && displaySidebarItems.length === 0) {
|
|
7072
|
-
return /* @__PURE__ */ React30.createElement("div", {
|
|
7198
|
+
return /* @__PURE__ */ React30.createElement("div", { className: "brokr-ai-chat-sidebar-skeleton" }, /* @__PURE__ */ React30.createElement(Skeleton, { width: "75%", height: 14, radius: 6 }), /* @__PURE__ */ React30.createElement(Skeleton, { width: "60%", height: 14, radius: 6 }), /* @__PURE__ */ React30.createElement(Skeleton, { width: "85%", height: 14, radius: 6 }));
|
|
7073
7199
|
}
|
|
7074
7200
|
if (displaySidebarItems.length === 0) {
|
|
7075
7201
|
return /* @__PURE__ */ React30.createElement("div", { className: "brokr-ai-chat-sidebar-empty" }, /* @__PURE__ */ React30.createElement("p", { className: "brokr-ai-chat-sidebar-empty-text" }, "No conversations yet. Start one above."));
|
|
7076
7202
|
}
|
|
7077
|
-
return /* @__PURE__ */ React30.createElement("div", { className: "brokr-ai-chat-conversations" },
|
|
7203
|
+
return /* @__PURE__ */ React30.createElement("div", { className: "brokr-ai-chat-sidebar-groups" }, groupedSidebarItems.map((group) => /* @__PURE__ */ React30.createElement("section", { className: "brokr-ai-chat-sidebar-group", key: group.key }, /* @__PURE__ */ React30.createElement("span", { className: "brokr-ai-chat-sidebar-kicker" }, group.label), /* @__PURE__ */ React30.createElement("div", { className: "brokr-ai-chat-conversations" }, group.items.map((item) => renamingId === item.id ? /* @__PURE__ */ React30.createElement(
|
|
7078
7204
|
"input",
|
|
7079
7205
|
{
|
|
7080
7206
|
autoFocus: true,
|
|
@@ -7094,19 +7220,20 @@ function ThreadSidebar() {
|
|
|
7094
7220
|
isActive: item.id === activeId,
|
|
7095
7221
|
onSelect: selectThreadAndCloseSidebar
|
|
7096
7222
|
}
|
|
7097
|
-
)));
|
|
7223
|
+
))))));
|
|
7098
7224
|
}, [
|
|
7099
7225
|
threadsLoading,
|
|
7100
7226
|
displaySidebarItems,
|
|
7101
7227
|
renamingId,
|
|
7102
7228
|
renameValue,
|
|
7229
|
+
groupedSidebarItems,
|
|
7103
7230
|
activeId,
|
|
7104
7231
|
selectThreadAndCloseSidebar,
|
|
7105
7232
|
handleRenameBlur,
|
|
7106
7233
|
handleRenameChange,
|
|
7107
7234
|
handleRenameKeyDown
|
|
7108
7235
|
]);
|
|
7109
|
-
return /* @__PURE__ */ React30.createElement(React30.Fragment, null, /* @__PURE__ */ React30.createElement("button", { className: "brokr-ai-chat-sidebar-
|
|
7236
|
+
return /* @__PURE__ */ React30.createElement(React30.Fragment, null, /* @__PURE__ */ React30.createElement("button", { className: "brokr-ai-chat-sidebar-new-chat", onClick: handleNewChat, type: "button" }, /* @__PURE__ */ React30.createElement(MessageIcon, { size: 16 }), "New chat"), content);
|
|
7110
7237
|
}
|
|
7111
7238
|
|
|
7112
7239
|
// src/react/chat/MessagePane.tsx
|
|
@@ -7134,6 +7261,12 @@ function parseInline(text) {
|
|
|
7134
7261
|
remaining = remaining.slice(boldMatch[0].length);
|
|
7135
7262
|
continue;
|
|
7136
7263
|
}
|
|
7264
|
+
const strikethroughMatch = remaining.match(/^~~(.+?)~~/);
|
|
7265
|
+
if (strikethroughMatch) {
|
|
7266
|
+
nodes.push(/* @__PURE__ */ React31.createElement("del", { key: key++ }, strikethroughMatch[1]));
|
|
7267
|
+
remaining = remaining.slice(strikethroughMatch[0].length);
|
|
7268
|
+
continue;
|
|
7269
|
+
}
|
|
7137
7270
|
const italicMatch = remaining.match(/^\*(.+?)\*/);
|
|
7138
7271
|
if (italicMatch) {
|
|
7139
7272
|
nodes.push(/* @__PURE__ */ React31.createElement("em", { key: key++ }, italicMatch[1]));
|
|
@@ -7150,7 +7283,7 @@ function parseInline(text) {
|
|
|
7150
7283
|
remaining = remaining.slice(linkMatch[0].length);
|
|
7151
7284
|
continue;
|
|
7152
7285
|
}
|
|
7153
|
-
const nextSpecial = remaining.search(/[
|
|
7286
|
+
const nextSpecial = remaining.search(/[`*~\[]/);
|
|
7154
7287
|
if (nextSpecial === -1) {
|
|
7155
7288
|
nodes.push(remaining);
|
|
7156
7289
|
break;
|
|
@@ -7181,6 +7314,15 @@ function CodeBlock({ code, language }) {
|
|
|
7181
7314
|
"Copy"
|
|
7182
7315
|
)), /* @__PURE__ */ React31.createElement("pre", { className: "brokr-md-codeblock-pre" }, /* @__PURE__ */ React31.createElement("code", null, code)));
|
|
7183
7316
|
}
|
|
7317
|
+
function looksLikeTableSeparator(line) {
|
|
7318
|
+
const trimmed = line.trim();
|
|
7319
|
+
return /^\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?$/.test(trimmed);
|
|
7320
|
+
}
|
|
7321
|
+
function splitTableRow(line) {
|
|
7322
|
+
const trimmed = line.trim();
|
|
7323
|
+
const withoutEdges = trimmed.replace(/^\|/, "").replace(/\|$/, "");
|
|
7324
|
+
return withoutEdges.split("|").map((cell) => cell.trim());
|
|
7325
|
+
}
|
|
7184
7326
|
function parseBlocks(text) {
|
|
7185
7327
|
const blocks = [];
|
|
7186
7328
|
const lines = text.split("\n");
|
|
@@ -7210,22 +7352,44 @@ function parseBlocks(text) {
|
|
|
7210
7352
|
i++;
|
|
7211
7353
|
continue;
|
|
7212
7354
|
}
|
|
7355
|
+
if (line.includes("|") && i + 1 < lines.length && looksLikeTableSeparator(lines[i + 1] ?? "")) {
|
|
7356
|
+
const headers = splitTableRow(line);
|
|
7357
|
+
const rows = [];
|
|
7358
|
+
i += 2;
|
|
7359
|
+
while (i < lines.length) {
|
|
7360
|
+
const candidate = lines[i] ?? "";
|
|
7361
|
+
if (!candidate.trim() || !candidate.includes("|")) break;
|
|
7362
|
+
rows.push(splitTableRow(candidate));
|
|
7363
|
+
i++;
|
|
7364
|
+
}
|
|
7365
|
+
blocks.push({ type: "table", content: "", headers, rows });
|
|
7366
|
+
continue;
|
|
7367
|
+
}
|
|
7368
|
+
if (/^[\s]*>\s?/.test(line)) {
|
|
7369
|
+
const quoteLines = [];
|
|
7370
|
+
while (i < lines.length && /^[\s]*>\s?/.test(lines[i])) {
|
|
7371
|
+
quoteLines.push(lines[i].replace(/^[\s]*>\s?/, ""));
|
|
7372
|
+
i++;
|
|
7373
|
+
}
|
|
7374
|
+
blocks.push({ type: "quote", content: "", items: quoteLines });
|
|
7375
|
+
continue;
|
|
7376
|
+
}
|
|
7213
7377
|
if (/^[\s]*[-*]\s+/.test(line)) {
|
|
7214
7378
|
const items = [];
|
|
7215
7379
|
while (i < lines.length && /^[\s]*[-*]\s+/.test(lines[i])) {
|
|
7216
7380
|
items.push(lines[i].replace(/^[\s]*[-*]\s+/, ""));
|
|
7217
7381
|
i++;
|
|
7218
7382
|
}
|
|
7219
|
-
blocks.push({ type: "list", content: "", items });
|
|
7383
|
+
blocks.push({ type: "list", content: "", items, ordered: false });
|
|
7220
7384
|
continue;
|
|
7221
7385
|
}
|
|
7222
|
-
if (/^[\s]*\d
|
|
7386
|
+
if (/^[\s]*\d+[.)]\s+/.test(line)) {
|
|
7223
7387
|
const items = [];
|
|
7224
|
-
while (i < lines.length && /^[\s]*\d
|
|
7225
|
-
items.push(lines[i].replace(/^[\s]*\d
|
|
7388
|
+
while (i < lines.length && /^[\s]*\d+[.)]\s+/.test(lines[i])) {
|
|
7389
|
+
items.push(lines[i].replace(/^[\s]*\d+[.)]\s+/, ""));
|
|
7226
7390
|
i++;
|
|
7227
7391
|
}
|
|
7228
|
-
blocks.push({ type: "list", content: "", items });
|
|
7392
|
+
blocks.push({ type: "list", content: "", items, ordered: true });
|
|
7229
7393
|
continue;
|
|
7230
7394
|
}
|
|
7231
7395
|
if (!line.trim()) {
|
|
@@ -7234,7 +7398,7 @@ function parseBlocks(text) {
|
|
|
7234
7398
|
}
|
|
7235
7399
|
const paraLines = [line];
|
|
7236
7400
|
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
|
|
7401
|
+
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
7402
|
paraLines.push(lines[i]);
|
|
7239
7403
|
i++;
|
|
7240
7404
|
}
|
|
@@ -7254,7 +7418,14 @@ function renderBlocks(blocks) {
|
|
|
7254
7418
|
return /* @__PURE__ */ React31.createElement(Tag, { className: `brokr-md-heading brokr-md-h${block.level}`, key: idx }, parseInline(block.content));
|
|
7255
7419
|
}
|
|
7256
7420
|
case "list":
|
|
7257
|
-
|
|
7421
|
+
if (block.ordered) {
|
|
7422
|
+
return /* @__PURE__ */ React31.createElement("ol", { className: "brokr-md-list brokr-md-list-ordered", key: idx }, block.items?.map((item, li) => /* @__PURE__ */ React31.createElement("li", { key: li }, parseInline(item))));
|
|
7423
|
+
}
|
|
7424
|
+
return /* @__PURE__ */ React31.createElement("ul", { className: "brokr-md-list brokr-md-list-unordered", key: idx }, block.items?.map((item, li) => /* @__PURE__ */ React31.createElement("li", { key: li }, parseInline(item))));
|
|
7425
|
+
case "quote":
|
|
7426
|
+
return /* @__PURE__ */ React31.createElement("blockquote", { className: "brokr-md-quote", key: idx }, block.items?.map((line, lineIndex) => /* @__PURE__ */ React31.createElement("p", { className: "brokr-md-quote-line", key: lineIndex }, parseInline(line))));
|
|
7427
|
+
case "table":
|
|
7428
|
+
return /* @__PURE__ */ React31.createElement("div", { className: "brokr-md-table-wrap", key: idx }, /* @__PURE__ */ React31.createElement("table", { className: "brokr-md-table" }, /* @__PURE__ */ React31.createElement("thead", null, /* @__PURE__ */ React31.createElement("tr", null, block.headers?.map((header, headerIndex) => /* @__PURE__ */ React31.createElement("th", { key: headerIndex }, parseInline(header))))), /* @__PURE__ */ React31.createElement("tbody", null, block.rows?.map((row, rowIndex) => /* @__PURE__ */ React31.createElement("tr", { key: rowIndex }, row.map((cell, cellIndex) => /* @__PURE__ */ React31.createElement("td", { key: cellIndex }, parseInline(cell))))))));
|
|
7258
7429
|
case "paragraph":
|
|
7259
7430
|
default:
|
|
7260
7431
|
return /* @__PURE__ */ React31.createElement("p", { className: "brokr-md-paragraph", key: idx }, parseInline(block.content));
|
|
@@ -7411,6 +7582,7 @@ function AIChat(inlineProps) {
|
|
|
7411
7582
|
subtitle,
|
|
7412
7583
|
model: modelProp,
|
|
7413
7584
|
modelSelector,
|
|
7585
|
+
memory,
|
|
7414
7586
|
variant = 1,
|
|
7415
7587
|
sidebar: sidebarProp,
|
|
7416
7588
|
threadMenu: threadMenuProp,
|
|
@@ -7434,16 +7606,12 @@ function AIChat(inlineProps) {
|
|
|
7434
7606
|
const headerVisible = variant !== 3;
|
|
7435
7607
|
const threadMenuVisible = threadMenuProp !== void 0 ? threadMenuProp : variant !== 3;
|
|
7436
7608
|
const modelSelectorVisible = (modelSelector !== void 0 ? modelSelector : true) && !modelProp;
|
|
7437
|
-
const [
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
useEffect12(() => {
|
|
7442
|
-
if (modelProp) setSelectedModel(modelProp);
|
|
7443
|
-
}, [modelProp]);
|
|
7444
|
-
const activeModel = modelProp ?? selectedModel;
|
|
7609
|
+
const [userSelectedModel, setUserSelectedModel] = useState18(null);
|
|
7610
|
+
const explicitModel = modelProp ?? userSelectedModel ?? void 0;
|
|
7611
|
+
const displayModel = explicitModel ?? providers.find((p) => p.free)?.model ?? providers[0]?.model ?? "";
|
|
7612
|
+
const activeModel = explicitModel ?? STACK_DEFAULT;
|
|
7445
7613
|
const activeProvider = useMemo23(
|
|
7446
|
-
() => providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7614
|
+
() => activeModel === STACK_DEFAULT ? void 0 : providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7447
7615
|
[activeModel]
|
|
7448
7616
|
);
|
|
7449
7617
|
const hasBalance = useMemo23(
|
|
@@ -7457,7 +7625,9 @@ function AIChat(inlineProps) {
|
|
|
7457
7625
|
const chat = useChat({
|
|
7458
7626
|
endpoint,
|
|
7459
7627
|
prompt,
|
|
7460
|
-
|
|
7628
|
+
explicitModel,
|
|
7629
|
+
displayModel,
|
|
7630
|
+
memory,
|
|
7461
7631
|
persist,
|
|
7462
7632
|
surface,
|
|
7463
7633
|
subject,
|
|
@@ -7565,10 +7735,10 @@ function AIChat(inlineProps) {
|
|
|
7565
7735
|
activeThread: chat.activeThread,
|
|
7566
7736
|
renderedTitle: finalTitle,
|
|
7567
7737
|
isTitleLoading: chat.isTitleLoading,
|
|
7568
|
-
activeModel,
|
|
7738
|
+
activeModel: displayModel,
|
|
7569
7739
|
activeProvider,
|
|
7570
|
-
selectedModel,
|
|
7571
|
-
setSelectedModel,
|
|
7740
|
+
selectedModel: displayModel,
|
|
7741
|
+
setSelectedModel: setUserSelectedModel,
|
|
7572
7742
|
availableProviders,
|
|
7573
7743
|
sendMessage: chat.sendMessage,
|
|
7574
7744
|
startNewChat: chat.startNewChat,
|
|
@@ -7608,9 +7778,9 @@ function AIChat(inlineProps) {
|
|
|
7608
7778
|
}), [
|
|
7609
7779
|
chat,
|
|
7610
7780
|
finalTitle,
|
|
7611
|
-
|
|
7781
|
+
displayModel,
|
|
7612
7782
|
activeProvider,
|
|
7613
|
-
|
|
7783
|
+
userSelectedModel,
|
|
7614
7784
|
availableProviders,
|
|
7615
7785
|
sidebarOpen,
|
|
7616
7786
|
closeSidebar,
|
|
@@ -7647,8 +7817,8 @@ function AIChat(inlineProps) {
|
|
|
7647
7817
|
threadMenuRef,
|
|
7648
7818
|
threadMenuVisible,
|
|
7649
7819
|
setThreadMenuOpenId,
|
|
7650
|
-
activeModel,
|
|
7651
|
-
setSelectedModel,
|
|
7820
|
+
activeModel: displayModel,
|
|
7821
|
+
setSelectedModel: setUserSelectedModel,
|
|
7652
7822
|
availableProviders,
|
|
7653
7823
|
startRename: chatState.startRename,
|
|
7654
7824
|
deleteThread: chat.deleteThread
|
|
@@ -7767,24 +7937,63 @@ function ChatHeader({
|
|
|
7767
7937
|
}
|
|
7768
7938
|
|
|
7769
7939
|
// src/react/composites/FabAI.tsx
|
|
7770
|
-
import React36, { useCallback as useCallback24, useMemo as useMemo24, useState as useState19 } from "react";
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7940
|
+
import React36, { useCallback as useCallback24, useMemo as useMemo24, useRef as useRef8, useState as useState19 } from "react";
|
|
7941
|
+
|
|
7942
|
+
// src/react/composites/fab-context.ts
|
|
7943
|
+
function buildFabSystemPrompt(appContext, brandName, existingPrompt) {
|
|
7944
|
+
if (appContext === false) return existingPrompt;
|
|
7945
|
+
const name = appContext?.name ?? brandName;
|
|
7946
|
+
if (!name && !appContext) return existingPrompt;
|
|
7947
|
+
const parts = [];
|
|
7948
|
+
if (name) parts.push(`You are an AI assistant embedded in ${name}.`);
|
|
7949
|
+
if (appContext?.description) parts.push(`This app is ${appContext.description}.`);
|
|
7950
|
+
if (appContext?.currentPage) parts.push(`The user is currently on: ${appContext.currentPage}.`);
|
|
7951
|
+
if (appContext?.facts?.length) parts.push(...appContext.facts);
|
|
7952
|
+
const autoPrompt = parts.join(" ");
|
|
7953
|
+
return existingPrompt ? `${autoPrompt}
|
|
7954
|
+
|
|
7955
|
+
${existingPrompt}` : autoPrompt;
|
|
7956
|
+
}
|
|
7957
|
+
|
|
7958
|
+
// src/react/composites/FabAI.tsx
|
|
7959
|
+
function ensureAssistantReply(messages, content) {
|
|
7960
|
+
const normalized = content.trim() ? content : "No response received.";
|
|
7961
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
7962
|
+
if (messages[index]?.role !== "assistant") continue;
|
|
7963
|
+
const next = [...messages];
|
|
7964
|
+
next[index] = { ...next[index], content: normalized };
|
|
7965
|
+
return next;
|
|
7966
|
+
}
|
|
7967
|
+
return [...messages, { role: "assistant", content: normalized }];
|
|
7968
|
+
}
|
|
7969
|
+
function appendAssistantDelta(messages, delta) {
|
|
7970
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
7971
|
+
if (messages[index]?.role !== "assistant") continue;
|
|
7972
|
+
const next = [...messages];
|
|
7973
|
+
next[index] = {
|
|
7974
|
+
...next[index],
|
|
7975
|
+
content: `${contentToText(next[index].content)}${delta}`
|
|
7976
|
+
};
|
|
7977
|
+
return next;
|
|
7978
|
+
}
|
|
7979
|
+
return [...messages, { role: "assistant", content: delta }];
|
|
7774
7980
|
}
|
|
7775
7981
|
function FabAI({
|
|
7982
|
+
appContext,
|
|
7776
7983
|
model,
|
|
7984
|
+
memory,
|
|
7777
7985
|
onSendMessage,
|
|
7778
7986
|
position = "bottom-right",
|
|
7779
|
-
starterPrompts = [],
|
|
7780
7987
|
systemPrompt
|
|
7781
7988
|
}) {
|
|
7782
|
-
const { can, user } = useBrokr();
|
|
7989
|
+
const { can, user, theme } = useBrokr();
|
|
7990
|
+
const brandName = theme?.brand?.name;
|
|
7783
7991
|
const [isOpen, setIsOpen] = useState19(false);
|
|
7784
7992
|
const [input, setInput] = useState19("");
|
|
7785
7993
|
const [error, setError] = useState19(null);
|
|
7786
7994
|
const [isSending, setIsSending] = useState19(false);
|
|
7787
7995
|
const [messages, setMessages] = useState19([]);
|
|
7996
|
+
const conversationIdRef = useRef8(`fab_${crypto.randomUUID()}`);
|
|
7788
7997
|
const launcherStyle = useMemo24(
|
|
7789
7998
|
() => ({
|
|
7790
7999
|
left: position === "bottom-left" ? "var(--brokr-space-6)" : void 0,
|
|
@@ -7808,37 +8017,75 @@ function FabAI({
|
|
|
7808
8017
|
}, []);
|
|
7809
8018
|
const sendPrompt = useCallback24(async (prompt) => {
|
|
7810
8019
|
const nextPrompt = prompt.trim();
|
|
7811
|
-
if (!nextPrompt) return;
|
|
7812
|
-
const
|
|
8020
|
+
if (!nextPrompt || isSending) return;
|
|
8021
|
+
const userMessage = { role: "user", content: nextPrompt };
|
|
8022
|
+
const nextMessages = [...messages, userMessage];
|
|
8023
|
+
const optimisticMessages = [...nextMessages, { role: "assistant", content: "" }];
|
|
7813
8024
|
try {
|
|
7814
8025
|
setError(null);
|
|
7815
8026
|
setIsSending(true);
|
|
7816
|
-
setMessages(
|
|
8027
|
+
setMessages(optimisticMessages);
|
|
7817
8028
|
setInput("");
|
|
7818
|
-
let responseText = "";
|
|
7819
8029
|
if (onSendMessage) {
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
8030
|
+
const responseText2 = await onSendMessage({ messages: nextMessages, model, systemPrompt });
|
|
8031
|
+
setMessages((current) => ensureAssistantReply(current, responseText2));
|
|
8032
|
+
return;
|
|
8033
|
+
}
|
|
8034
|
+
const response = await fetch("/api/brokr/chat", {
|
|
8035
|
+
method: "POST",
|
|
8036
|
+
credentials: "include",
|
|
8037
|
+
headers: { "Content-Type": "application/json" },
|
|
8038
|
+
body: JSON.stringify({
|
|
8039
|
+
conversationId: conversationIdRef.current,
|
|
8040
|
+
messages: trimToTokenBudget(
|
|
8041
|
+
nextMessages.filter((message) => message.role === "user" || message.role === "assistant" && Boolean(contentToText(message.content))).map((message) => ({
|
|
8042
|
+
role: message.role,
|
|
8043
|
+
content: contentToText(message.content)
|
|
8044
|
+
}))
|
|
8045
|
+
),
|
|
8046
|
+
...model !== void 0 ? { model } : {},
|
|
8047
|
+
...(() => {
|
|
8048
|
+
const withContext = buildFabSystemPrompt(appContext, brandName, systemPrompt);
|
|
8049
|
+
const ep = buildEffectivePrompt(withContext, memory);
|
|
8050
|
+
return ep ? { systemPrompt: ep } : {};
|
|
8051
|
+
})()
|
|
8052
|
+
})
|
|
8053
|
+
});
|
|
8054
|
+
if (!response.ok) {
|
|
8055
|
+
const payload2 = await response.json().catch(() => ({}));
|
|
8056
|
+
throw new Error(payload2.message ?? payload2.error ?? `Chat failed (${response.status})`);
|
|
8057
|
+
}
|
|
8058
|
+
if (isSSEResponse(response)) {
|
|
8059
|
+
let hasDelta = false;
|
|
8060
|
+
for await (const event of parseSSEStream(response)) {
|
|
8061
|
+
if (event.type === "conversation") {
|
|
8062
|
+
conversationIdRef.current = event.id;
|
|
8063
|
+
} else if (event.type === "delta") {
|
|
8064
|
+
hasDelta = true;
|
|
8065
|
+
setMessages((current) => appendAssistantDelta(current, event.delta));
|
|
8066
|
+
} else if (event.type === "error") {
|
|
8067
|
+
throw new Error(event.message);
|
|
7828
8068
|
}
|
|
7829
|
-
|
|
7830
|
-
|
|
8069
|
+
}
|
|
8070
|
+
if (!hasDelta) {
|
|
8071
|
+
setMessages((current) => ensureAssistantReply(current, "No response received."));
|
|
8072
|
+
}
|
|
8073
|
+
return;
|
|
7831
8074
|
}
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
}
|
|
8075
|
+
const payload = await response.json().catch(() => ({}));
|
|
8076
|
+
if (payload.error || payload.message) {
|
|
8077
|
+
throw new Error(payload.message ?? payload.error ?? "AI request failed.");
|
|
8078
|
+
}
|
|
8079
|
+
const responseText = payload.content ?? payload.response ?? payload.text ?? "";
|
|
8080
|
+
setMessages((current) => ensureAssistantReply(current, responseText));
|
|
7836
8081
|
} catch (cause) {
|
|
7837
|
-
|
|
8082
|
+
const message = cause instanceof Error ? cause.message : "Could not send message.";
|
|
8083
|
+
setError(message);
|
|
8084
|
+
setMessages((current) => ensureAssistantReply(current, `Error: ${message}`));
|
|
7838
8085
|
} finally {
|
|
7839
8086
|
setIsSending(false);
|
|
7840
8087
|
}
|
|
7841
|
-
}, [messages, model, onSendMessage, systemPrompt]);
|
|
8088
|
+
}, [isSending, messages, model, onSendMessage, systemPrompt]);
|
|
7842
8089
|
const handleSubmit = useCallback24(async (event) => {
|
|
7843
8090
|
event.preventDefault();
|
|
7844
8091
|
await sendPrompt(input);
|
|
@@ -7849,28 +8096,29 @@ function FabAI({
|
|
|
7849
8096
|
void sendPrompt(input);
|
|
7850
8097
|
}
|
|
7851
8098
|
}, [input, sendPrompt]);
|
|
7852
|
-
const handleStarterPrompt = useCallback24((prompt) => {
|
|
7853
|
-
void sendPrompt(prompt);
|
|
7854
|
-
}, [sendPrompt]);
|
|
7855
8099
|
return /* @__PURE__ */ React36.createElement(React36.Fragment, null, /* @__PURE__ */ React36.createElement("div", { className: "brokr-chat-fab", style: launcherStyle }, /* @__PURE__ */ React36.createElement(
|
|
7856
8100
|
"button",
|
|
7857
8101
|
{
|
|
8102
|
+
"aria-label": isOpen ? "Close AI chat" : "Open AI chat",
|
|
7858
8103
|
"aria-expanded": isOpen,
|
|
7859
8104
|
"aria-haspopup": "dialog",
|
|
7860
|
-
className: "brokr-
|
|
8105
|
+
className: "brokr-chat-fab-trigger",
|
|
7861
8106
|
onClick: toggleOpen,
|
|
7862
8107
|
type: "button"
|
|
7863
8108
|
},
|
|
7864
|
-
/* @__PURE__ */ React36.createElement(
|
|
7865
|
-
"Ask AI"
|
|
8109
|
+
/* @__PURE__ */ React36.createElement(SparkIcon, { size: 18 })
|
|
7866
8110
|
)), 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(
|
|
7867
8111
|
"div",
|
|
7868
8112
|
{
|
|
7869
8113
|
className: "brokr-chat-messages",
|
|
7870
8114
|
"data-empty": messages.length === 0
|
|
7871
8115
|
},
|
|
7872
|
-
messages.length === 0 ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-chat-empty" }, /* @__PURE__ */ React36.createElement(SparkIcon, { size: 18 }), /* @__PURE__ */ React36.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ React36.createElement("strong", null, "Send a message to chat with the AI."), /* @__PURE__ */ React36.createElement("span", { className: "brokr-copy" }, "Ask a question or drop in a starter prompt below."))
|
|
7873
|
-
messages.map((message, index) =>
|
|
8116
|
+
messages.length === 0 ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-chat-empty" }, /* @__PURE__ */ React36.createElement(SparkIcon, { size: 18 }), /* @__PURE__ */ React36.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ React36.createElement("strong", null, "Send a message to chat with the AI."), /* @__PURE__ */ React36.createElement("span", { className: "brokr-copy" }, "Ask a question or drop in a starter prompt below."))) : null,
|
|
8117
|
+
messages.map((message, index) => {
|
|
8118
|
+
const text = contentToText(message.content);
|
|
8119
|
+
const isTyping = message.role === "assistant" && !text && isSending && index === messages.length - 1;
|
|
8120
|
+
return /* @__PURE__ */ React36.createElement("div", { className: "brokr-chat-bubble", "data-role": message.role, key: `${message.role}-${index}` }, isTyping ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-ai-chat-typing", "aria-label": "AI is typing" }, /* @__PURE__ */ React36.createElement("span", null), /* @__PURE__ */ React36.createElement("span", null), /* @__PURE__ */ React36.createElement("span", null)) : message.role === "assistant" ? /* @__PURE__ */ React36.createElement(MarkdownRenderer, { content: text }) : text);
|
|
8121
|
+
}),
|
|
7874
8122
|
error ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null
|
|
7875
8123
|
), /* @__PURE__ */ React36.createElement("form", { className: "brokr-section", onSubmit: handleSubmit, style: { gap: "var(--brokr-space-3)" } }, /* @__PURE__ */ React36.createElement(
|
|
7876
8124
|
"textarea",
|
|
@@ -7882,7 +8130,7 @@ function FabAI({
|
|
|
7882
8130
|
rows: 2,
|
|
7883
8131
|
value: input
|
|
7884
8132
|
}
|
|
7885
|
-
), /* @__PURE__ */ React36.createElement("button", { className: "brokr-button", disabled: isSending, type: "submit" }, isSending ? "Thinking" : "Send"))) : null);
|
|
8133
|
+
), /* @__PURE__ */ React36.createElement("button", { className: "brokr-button", disabled: isSending || !input.trim(), type: "submit" }, isSending ? "Thinking" : "Send"))) : null);
|
|
7886
8134
|
}
|
|
7887
8135
|
|
|
7888
8136
|
// src/react/composites/SmartUpload.tsx
|
|
@@ -7890,7 +8138,7 @@ import React37, {
|
|
|
7890
8138
|
useCallback as useCallback25,
|
|
7891
8139
|
useId as useId2,
|
|
7892
8140
|
useMemo as useMemo25,
|
|
7893
|
-
useRef as
|
|
8141
|
+
useRef as useRef9,
|
|
7894
8142
|
useState as useState20
|
|
7895
8143
|
} from "react";
|
|
7896
8144
|
function SmartUpload({
|
|
@@ -7901,7 +8149,7 @@ function SmartUpload({
|
|
|
7901
8149
|
}) {
|
|
7902
8150
|
const { paymentsMode } = useBrokr();
|
|
7903
8151
|
const inputId = useId2();
|
|
7904
|
-
const inputRef =
|
|
8152
|
+
const inputRef = useRef9(null);
|
|
7905
8153
|
const [dragActive, setDragActive] = useState20(false);
|
|
7906
8154
|
const [error, setError] = useState20(null);
|
|
7907
8155
|
const [fileName, setFileName] = useState20(null);
|
|
@@ -8336,7 +8584,7 @@ var BrokrErrorBoundary = class extends React39.Component {
|
|
|
8336
8584
|
};
|
|
8337
8585
|
|
|
8338
8586
|
// src/react/notifications/NotificationBell.tsx
|
|
8339
|
-
import React40, { useCallback as useCallback27, useEffect as useEffect13, useMemo as useMemo26, useRef as
|
|
8587
|
+
import React40, { useCallback as useCallback27, useEffect as useEffect13, useMemo as useMemo26, useRef as useRef10, useState as useState22 } from "react";
|
|
8340
8588
|
|
|
8341
8589
|
// src/react/notifications/use-notifications.ts
|
|
8342
8590
|
import { useContext as useContext4 } from "react";
|
|
@@ -8386,8 +8634,8 @@ function NotifDropdownItem({
|
|
|
8386
8634
|
function NotificationBell() {
|
|
8387
8635
|
const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
|
|
8388
8636
|
const [open, setOpen] = useState22(false);
|
|
8389
|
-
const containerRef =
|
|
8390
|
-
const markReadTimerRef =
|
|
8637
|
+
const containerRef = useRef10(null);
|
|
8638
|
+
const markReadTimerRef = useRef10(null);
|
|
8391
8639
|
const toggle = useCallback27(() => setOpen((o) => !o), []);
|
|
8392
8640
|
useEffect13(() => {
|
|
8393
8641
|
if (markReadTimerRef.current) {
|