@brokr/sdk 2.1.1 → 2.1.3
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/react-styles.js +273 -1
- package/dist/react-styles.mjs +273 -1
- package/dist/react.js +359 -273
- package/dist/react.mjs +235 -152
- 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/data-table.d.ts +2 -0
- package/dist/src/react/css/data-table.d.ts.map +1 -0
- package/dist/src/react/css/index.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/tokens.d.ts +1 -1
- package/dist/src/react/css/tokens.d.ts.map +1 -1
- 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
|
@@ -6284,11 +6284,28 @@ function CancelSubscription({ onCancel }) {
|
|
|
6284
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));
|
|
6285
6285
|
}
|
|
6286
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
|
+
|
|
6287
6304
|
// src/react/chat/AIChat.tsx
|
|
6288
|
-
import
|
|
6305
|
+
import React36, {
|
|
6289
6306
|
useCallback as useCallback23,
|
|
6290
6307
|
useEffect as useEffect12,
|
|
6291
|
-
useMemo as
|
|
6308
|
+
useMemo as useMemo24,
|
|
6292
6309
|
useRef as useRef7,
|
|
6293
6310
|
useState as useState18
|
|
6294
6311
|
} from "react";
|
|
@@ -6306,7 +6323,7 @@ function useChatState() {
|
|
|
6306
6323
|
}
|
|
6307
6324
|
|
|
6308
6325
|
// src/react/chat/useChat.ts
|
|
6309
|
-
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";
|
|
6310
6327
|
|
|
6311
6328
|
// src/ai/types.ts
|
|
6312
6329
|
function contentToText(content) {
|
|
@@ -6498,7 +6515,7 @@ function useChat(config) {
|
|
|
6498
6515
|
const bottomRef = useRef5(null);
|
|
6499
6516
|
const scrollContainerRef = useRef5(null);
|
|
6500
6517
|
const sentinelRef = useRef5(null);
|
|
6501
|
-
const displaySidebarItems =
|
|
6518
|
+
const displaySidebarItems = useMemo19(() => {
|
|
6502
6519
|
if (isControlled) {
|
|
6503
6520
|
return threadsProp?.map((t) => ({
|
|
6504
6521
|
id: t.id,
|
|
@@ -6519,26 +6536,26 @@ function useChat(config) {
|
|
|
6519
6536
|
updatedAt: new Date(c.updatedAt).toISOString()
|
|
6520
6537
|
}));
|
|
6521
6538
|
}, [isControlled, isPersist, threadsProp, serverThreads, memConversations]);
|
|
6522
|
-
const activeId =
|
|
6539
|
+
const activeId = useMemo19(() => {
|
|
6523
6540
|
if (isControlled) return activeThreadIdProp ?? null;
|
|
6524
6541
|
if (isPersist) return serverActiveId;
|
|
6525
6542
|
return memActiveId;
|
|
6526
6543
|
}, [isControlled, isPersist, activeThreadIdProp, serverActiveId, memActiveId]);
|
|
6527
|
-
const displayMessages =
|
|
6544
|
+
const displayMessages = useMemo19(() => {
|
|
6528
6545
|
if (isControlled) return messagesProp ?? [];
|
|
6529
6546
|
if (isPersist) return serverMessages;
|
|
6530
6547
|
return memConversations.find((c) => c.id === memActiveId)?.messages ?? [];
|
|
6531
6548
|
}, [isControlled, isPersist, messagesProp, serverMessages, memConversations, memActiveId]);
|
|
6532
|
-
const activeMemConv =
|
|
6549
|
+
const activeMemConv = useMemo19(
|
|
6533
6550
|
() => memConversations.find((c) => c.id === memActiveId) ?? null,
|
|
6534
6551
|
[memConversations, memActiveId]
|
|
6535
6552
|
);
|
|
6536
|
-
const activeThread =
|
|
6553
|
+
const activeThread = useMemo19(() => {
|
|
6537
6554
|
if (isControlled) return threadsProp?.find((t) => t.id === activeId) ?? null;
|
|
6538
6555
|
if (isPersist) return serverThreads.find((t) => t.id === activeId) ?? null;
|
|
6539
6556
|
return null;
|
|
6540
6557
|
}, [isControlled, isPersist, threadsProp, serverThreads, activeId]);
|
|
6541
|
-
const renderedTitle =
|
|
6558
|
+
const renderedTitle = useMemo19(() => {
|
|
6542
6559
|
if (isPersist && activeThread) return activeThread.title;
|
|
6543
6560
|
if (!isPersist && activeMemConv) {
|
|
6544
6561
|
if (activeMemConv.titleState === "loading") return "Naming conversation";
|
|
@@ -6992,7 +7009,7 @@ function useChat(config) {
|
|
|
6992
7009
|
}
|
|
6993
7010
|
|
|
6994
7011
|
// src/react/chat/ModelSelector.tsx
|
|
6995
|
-
import
|
|
7012
|
+
import React30, { useCallback as useCallback17, useEffect as useEffect10, useMemo as useMemo20, useRef as useRef6, useState as useState17 } from "react";
|
|
6996
7013
|
function ModelOption({
|
|
6997
7014
|
provider,
|
|
6998
7015
|
isActive,
|
|
@@ -7007,7 +7024,7 @@ function ModelOption({
|
|
|
7007
7024
|
onSelect(provider.model);
|
|
7008
7025
|
}
|
|
7009
7026
|
}, [isLocked, onCheckout, onSelect, provider.model]);
|
|
7010
|
-
return /* @__PURE__ */
|
|
7027
|
+
return /* @__PURE__ */ React30.createElement(
|
|
7011
7028
|
"button",
|
|
7012
7029
|
{
|
|
7013
7030
|
"aria-selected": isActive,
|
|
@@ -7019,9 +7036,9 @@ function ModelOption({
|
|
|
7019
7036
|
title: isLocked ? "Add credits to unlock" : void 0,
|
|
7020
7037
|
type: "button"
|
|
7021
7038
|
},
|
|
7022
|
-
/* @__PURE__ */
|
|
7023
|
-
/* @__PURE__ */
|
|
7024
|
-
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
|
|
7025
7042
|
);
|
|
7026
7043
|
}
|
|
7027
7044
|
function ModelSelector({
|
|
@@ -7033,7 +7050,7 @@ function ModelSelector({
|
|
|
7033
7050
|
const [selectorOpen, setSelectorOpen] = useState17(false);
|
|
7034
7051
|
const selectorRef = useRef6(null);
|
|
7035
7052
|
const isAuto = activeModel === STACK_DEFAULT || !providers.some((p) => p.model === activeModel);
|
|
7036
|
-
const activeProvider =
|
|
7053
|
+
const activeProvider = useMemo20(
|
|
7037
7054
|
() => isAuto ? void 0 : providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7038
7055
|
[activeModel, isAuto]
|
|
7039
7056
|
);
|
|
@@ -7063,7 +7080,7 @@ function ModelSelector({
|
|
|
7063
7080
|
const handleToggle = useCallback17(() => {
|
|
7064
7081
|
setSelectorOpen((v) => !v);
|
|
7065
7082
|
}, []);
|
|
7066
|
-
return /* @__PURE__ */
|
|
7083
|
+
return /* @__PURE__ */ React30.createElement("div", { className: "brokr-model-selector", ref: selectorRef }, /* @__PURE__ */ React30.createElement(
|
|
7067
7084
|
"button",
|
|
7068
7085
|
{
|
|
7069
7086
|
"aria-expanded": selectorOpen,
|
|
@@ -7072,10 +7089,10 @@ function ModelSelector({
|
|
|
7072
7089
|
onClick: handleToggle,
|
|
7073
7090
|
type: "button"
|
|
7074
7091
|
},
|
|
7075
|
-
activeProvider ? /* @__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" } }),
|
|
7076
7093
|
isAuto ? "Auto" : activeProvider?.label ?? "Model",
|
|
7077
|
-
/* @__PURE__ */
|
|
7078
|
-
), selectorOpen ? /* @__PURE__ */
|
|
7094
|
+
/* @__PURE__ */ React30.createElement(ChevronDownIcon, { size: 13 })
|
|
7095
|
+
), selectorOpen ? /* @__PURE__ */ React30.createElement("div", { className: "brokr-model-dropdown", role: "listbox" }, /* @__PURE__ */ React30.createElement(
|
|
7079
7096
|
"button",
|
|
7080
7097
|
{
|
|
7081
7098
|
"aria-selected": isAuto,
|
|
@@ -7084,9 +7101,9 @@ function ModelSelector({
|
|
|
7084
7101
|
onClick: handleAutoSelect,
|
|
7085
7102
|
type: "button"
|
|
7086
7103
|
},
|
|
7087
|
-
/* @__PURE__ */
|
|
7088
|
-
/* @__PURE__ */
|
|
7089
|
-
), providers.map((p) => /* @__PURE__ */
|
|
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(
|
|
7090
7107
|
ModelOption,
|
|
7091
7108
|
{
|
|
7092
7109
|
key: p.id,
|
|
@@ -7100,10 +7117,10 @@ function ModelSelector({
|
|
|
7100
7117
|
}
|
|
7101
7118
|
|
|
7102
7119
|
// src/react/chat/ThreadSidebar.tsx
|
|
7103
|
-
import
|
|
7120
|
+
import React31, { useCallback as useCallback18, useMemo as useMemo21 } from "react";
|
|
7104
7121
|
function ThreadItemButton({ id, title, isActive, onSelect }) {
|
|
7105
7122
|
const handleClick = useCallback18(() => onSelect(id), [id, onSelect]);
|
|
7106
|
-
return /* @__PURE__ */
|
|
7123
|
+
return /* @__PURE__ */ React31.createElement(
|
|
7107
7124
|
"button",
|
|
7108
7125
|
{
|
|
7109
7126
|
className: "brokr-ai-chat-conversation",
|
|
@@ -7111,8 +7128,8 @@ function ThreadItemButton({ id, title, isActive, onSelect }) {
|
|
|
7111
7128
|
onClick: handleClick,
|
|
7112
7129
|
type: "button"
|
|
7113
7130
|
},
|
|
7114
|
-
/* @__PURE__ */
|
|
7115
|
-
/* @__PURE__ */
|
|
7131
|
+
/* @__PURE__ */ React31.createElement(MessageIcon, { size: 12 }),
|
|
7132
|
+
/* @__PURE__ */ React31.createElement("span", { className: "brokr-ai-chat-conversation-label" }, title)
|
|
7116
7133
|
);
|
|
7117
7134
|
}
|
|
7118
7135
|
var SIDEBAR_GROUP_ORDER = [
|
|
@@ -7176,7 +7193,7 @@ function ThreadSidebar() {
|
|
|
7176
7193
|
const handleRenameChange = useCallback18((e) => {
|
|
7177
7194
|
setRenameValue(e.target.value);
|
|
7178
7195
|
}, [setRenameValue]);
|
|
7179
|
-
const groupedSidebarItems =
|
|
7196
|
+
const groupedSidebarItems = useMemo21(() => {
|
|
7180
7197
|
const grouped = /* @__PURE__ */ new Map();
|
|
7181
7198
|
for (const item of displaySidebarItems) {
|
|
7182
7199
|
const key = resolveSidebarDateGroup(item.updatedAt);
|
|
@@ -7193,14 +7210,14 @@ function ThreadSidebar() {
|
|
|
7193
7210
|
items: grouped.get(key) ?? []
|
|
7194
7211
|
})).filter((group) => group.items.length > 0);
|
|
7195
7212
|
}, [displaySidebarItems]);
|
|
7196
|
-
const content =
|
|
7213
|
+
const content = useMemo21(() => {
|
|
7197
7214
|
if (threadsLoading && displaySidebarItems.length === 0) {
|
|
7198
|
-
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 }));
|
|
7199
7216
|
}
|
|
7200
7217
|
if (displaySidebarItems.length === 0) {
|
|
7201
|
-
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."));
|
|
7202
7219
|
}
|
|
7203
|
-
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(
|
|
7204
7221
|
"input",
|
|
7205
7222
|
{
|
|
7206
7223
|
autoFocus: true,
|
|
@@ -7211,7 +7228,7 @@ function ThreadSidebar() {
|
|
|
7211
7228
|
onKeyDown: handleRenameKeyDown,
|
|
7212
7229
|
value: renameValue
|
|
7213
7230
|
}
|
|
7214
|
-
) : /* @__PURE__ */
|
|
7231
|
+
) : /* @__PURE__ */ React31.createElement(
|
|
7215
7232
|
ThreadItemButton,
|
|
7216
7233
|
{
|
|
7217
7234
|
key: item.id,
|
|
@@ -7233,17 +7250,17 @@ function ThreadSidebar() {
|
|
|
7233
7250
|
handleRenameChange,
|
|
7234
7251
|
handleRenameKeyDown
|
|
7235
7252
|
]);
|
|
7236
|
-
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);
|
|
7237
7254
|
}
|
|
7238
7255
|
|
|
7239
7256
|
// src/react/chat/MessagePane.tsx
|
|
7240
|
-
import
|
|
7257
|
+
import React34, { useCallback as useCallback21, useMemo as useMemo23 } from "react";
|
|
7241
7258
|
|
|
7242
7259
|
// src/react/chat/MessageBubble.tsx
|
|
7243
|
-
import
|
|
7260
|
+
import React33, { useCallback as useCallback20 } from "react";
|
|
7244
7261
|
|
|
7245
7262
|
// src/react/chat/MarkdownRenderer.tsx
|
|
7246
|
-
import
|
|
7263
|
+
import React32, { useCallback as useCallback19, useMemo as useMemo22 } from "react";
|
|
7247
7264
|
function parseInline(text) {
|
|
7248
7265
|
const nodes = [];
|
|
7249
7266
|
let remaining = text;
|
|
@@ -7251,25 +7268,25 @@ function parseInline(text) {
|
|
|
7251
7268
|
while (remaining) {
|
|
7252
7269
|
const codeMatch = remaining.match(/^`([^`]+)`/);
|
|
7253
7270
|
if (codeMatch) {
|
|
7254
|
-
nodes.push(/* @__PURE__ */
|
|
7271
|
+
nodes.push(/* @__PURE__ */ React32.createElement("code", { className: "brokr-md-inline-code", key: key++ }, codeMatch[1]));
|
|
7255
7272
|
remaining = remaining.slice(codeMatch[0].length);
|
|
7256
7273
|
continue;
|
|
7257
7274
|
}
|
|
7258
7275
|
const boldMatch = remaining.match(/^\*\*(.+?)\*\*/);
|
|
7259
7276
|
if (boldMatch) {
|
|
7260
|
-
nodes.push(/* @__PURE__ */
|
|
7277
|
+
nodes.push(/* @__PURE__ */ React32.createElement("strong", { key: key++ }, boldMatch[1]));
|
|
7261
7278
|
remaining = remaining.slice(boldMatch[0].length);
|
|
7262
7279
|
continue;
|
|
7263
7280
|
}
|
|
7264
7281
|
const strikethroughMatch = remaining.match(/^~~(.+?)~~/);
|
|
7265
7282
|
if (strikethroughMatch) {
|
|
7266
|
-
nodes.push(/* @__PURE__ */
|
|
7283
|
+
nodes.push(/* @__PURE__ */ React32.createElement("del", { key: key++ }, strikethroughMatch[1]));
|
|
7267
7284
|
remaining = remaining.slice(strikethroughMatch[0].length);
|
|
7268
7285
|
continue;
|
|
7269
7286
|
}
|
|
7270
7287
|
const italicMatch = remaining.match(/^\*(.+?)\*/);
|
|
7271
7288
|
if (italicMatch) {
|
|
7272
|
-
nodes.push(/* @__PURE__ */
|
|
7289
|
+
nodes.push(/* @__PURE__ */ React32.createElement("em", { key: key++ }, italicMatch[1]));
|
|
7273
7290
|
remaining = remaining.slice(italicMatch[0].length);
|
|
7274
7291
|
continue;
|
|
7275
7292
|
}
|
|
@@ -7278,7 +7295,7 @@ function parseInline(text) {
|
|
|
7278
7295
|
const rawHref = linkMatch[2];
|
|
7279
7296
|
const isSafe = /^https?:\/\//i.test(rawHref) || /^mailto:/i.test(rawHref);
|
|
7280
7297
|
nodes.push(
|
|
7281
|
-
/* @__PURE__ */
|
|
7298
|
+
/* @__PURE__ */ React32.createElement("a", { className: "brokr-md-link", href: isSafe ? rawHref : "#", key: key++, rel: "noopener noreferrer", target: "_blank" }, linkMatch[1])
|
|
7282
7299
|
);
|
|
7283
7300
|
remaining = remaining.slice(linkMatch[0].length);
|
|
7284
7301
|
continue;
|
|
@@ -7302,7 +7319,7 @@ function CodeBlock({ code, language }) {
|
|
|
7302
7319
|
const handleCopy = useCallback19(() => {
|
|
7303
7320
|
void navigator.clipboard?.writeText(code);
|
|
7304
7321
|
}, [code]);
|
|
7305
|
-
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(
|
|
7306
7323
|
"button",
|
|
7307
7324
|
{
|
|
7308
7325
|
"aria-label": "Copy code",
|
|
@@ -7310,9 +7327,9 @@ function CodeBlock({ code, language }) {
|
|
|
7310
7327
|
onClick: handleCopy,
|
|
7311
7328
|
type: "button"
|
|
7312
7329
|
},
|
|
7313
|
-
/* @__PURE__ */
|
|
7330
|
+
/* @__PURE__ */ React32.createElement(CopyIcon, { size: 13 }),
|
|
7314
7331
|
"Copy"
|
|
7315
|
-
)), /* @__PURE__ */
|
|
7332
|
+
)), /* @__PURE__ */ React32.createElement("pre", { className: "brokr-md-codeblock-pre" }, /* @__PURE__ */ React32.createElement("code", null, code)));
|
|
7316
7333
|
}
|
|
7317
7334
|
function looksLikeTableSeparator(line) {
|
|
7318
7335
|
const trimmed = line.trim();
|
|
@@ -7410,35 +7427,35 @@ function renderBlocks(blocks) {
|
|
|
7410
7427
|
return blocks.map((block, idx) => {
|
|
7411
7428
|
switch (block.type) {
|
|
7412
7429
|
case "code":
|
|
7413
|
-
return /* @__PURE__ */
|
|
7430
|
+
return /* @__PURE__ */ React32.createElement(CodeBlock, { code: block.content, key: idx, language: block.language });
|
|
7414
7431
|
case "divider":
|
|
7415
|
-
return /* @__PURE__ */
|
|
7432
|
+
return /* @__PURE__ */ React32.createElement("hr", { className: "brokr-md-divider", key: idx });
|
|
7416
7433
|
case "heading": {
|
|
7417
7434
|
const Tag = `h${Math.min(block.level ?? 3, 6)}`;
|
|
7418
|
-
return /* @__PURE__ */
|
|
7435
|
+
return /* @__PURE__ */ React32.createElement(Tag, { className: `brokr-md-heading brokr-md-h${block.level}`, key: idx }, parseInline(block.content));
|
|
7419
7436
|
}
|
|
7420
7437
|
case "list":
|
|
7421
7438
|
if (block.ordered) {
|
|
7422
|
-
return /* @__PURE__ */
|
|
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))));
|
|
7423
7440
|
}
|
|
7424
|
-
return /* @__PURE__ */
|
|
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))));
|
|
7425
7442
|
case "quote":
|
|
7426
|
-
return /* @__PURE__ */
|
|
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))));
|
|
7427
7444
|
case "table":
|
|
7428
|
-
return /* @__PURE__ */
|
|
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))))))));
|
|
7429
7446
|
case "paragraph":
|
|
7430
7447
|
default:
|
|
7431
|
-
return /* @__PURE__ */
|
|
7448
|
+
return /* @__PURE__ */ React32.createElement("p", { className: "brokr-md-paragraph", key: idx }, parseInline(block.content));
|
|
7432
7449
|
}
|
|
7433
7450
|
});
|
|
7434
7451
|
}
|
|
7435
7452
|
function MarkdownRenderer({ content }) {
|
|
7436
|
-
const rendered =
|
|
7453
|
+
const rendered = useMemo22(() => {
|
|
7437
7454
|
if (!content) return null;
|
|
7438
7455
|
const blocks = parseBlocks(content);
|
|
7439
7456
|
return renderBlocks(blocks);
|
|
7440
7457
|
}, [content]);
|
|
7441
|
-
return /* @__PURE__ */
|
|
7458
|
+
return /* @__PURE__ */ React32.createElement("div", { className: "brokr-md" }, rendered);
|
|
7442
7459
|
}
|
|
7443
7460
|
|
|
7444
7461
|
// src/react/chat/MessageBubble.tsx
|
|
@@ -7448,10 +7465,10 @@ function MessageBubble({ message, isTyping, user }) {
|
|
|
7448
7465
|
});
|
|
7449
7466
|
}, [message.content]);
|
|
7450
7467
|
if (message.role === "user") {
|
|
7451
|
-
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 })));
|
|
7452
7469
|
}
|
|
7453
7470
|
const mp = message.model ? resolveProviderByModel(message.model) : null;
|
|
7454
|
-
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(
|
|
7455
7472
|
"button",
|
|
7456
7473
|
{
|
|
7457
7474
|
"aria-label": "Copy message",
|
|
@@ -7459,7 +7476,7 @@ function MessageBubble({ message, isTyping, user }) {
|
|
|
7459
7476
|
onClick: handleCopy,
|
|
7460
7477
|
type: "button"
|
|
7461
7478
|
},
|
|
7462
|
-
/* @__PURE__ */
|
|
7479
|
+
/* @__PURE__ */ React33.createElement(CopyIcon, { size: 13 })
|
|
7463
7480
|
) : null)));
|
|
7464
7481
|
}
|
|
7465
7482
|
|
|
@@ -7468,7 +7485,7 @@ function StarterPromptButton({ prompt, onSend }) {
|
|
|
7468
7485
|
const handleClick = useCallback21(() => {
|
|
7469
7486
|
void onSend(prompt);
|
|
7470
7487
|
}, [prompt, onSend]);
|
|
7471
|
-
return /* @__PURE__ */
|
|
7488
|
+
return /* @__PURE__ */ React34.createElement("button", { className: "brokr-ai-chat-starter", onClick: handleClick, type: "button" }, prompt);
|
|
7472
7489
|
}
|
|
7473
7490
|
function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
7474
7491
|
const {
|
|
@@ -7486,10 +7503,10 @@ function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
|
7486
7503
|
user
|
|
7487
7504
|
} = useChatState();
|
|
7488
7505
|
const isEmpty = displayMessages.length === 0;
|
|
7489
|
-
const messageElements =
|
|
7506
|
+
const messageElements = useMemo23(() => {
|
|
7490
7507
|
return visibleMessages.map((message, index) => {
|
|
7491
7508
|
const isTyping = message.role === "assistant" && !message.content && (isSubmitting && index === visibleMessages.length - 1 || message.status === "pending" || message.status === "streaming");
|
|
7492
|
-
return /* @__PURE__ */
|
|
7509
|
+
return /* @__PURE__ */ React34.createElement(
|
|
7493
7510
|
MessageBubble,
|
|
7494
7511
|
{
|
|
7495
7512
|
isTyping,
|
|
@@ -7500,16 +7517,16 @@ function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
|
7500
7517
|
);
|
|
7501
7518
|
});
|
|
7502
7519
|
}, [visibleMessages, isSubmitting, user]);
|
|
7503
|
-
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 })));
|
|
7504
7521
|
}
|
|
7505
7522
|
|
|
7506
7523
|
// src/react/chat/ChatInput.tsx
|
|
7507
|
-
import
|
|
7524
|
+
import React35, { useCallback as useCallback22, useEffect as useEffect11 } from "react";
|
|
7508
7525
|
function CommandButton({ cmd, chatContext }) {
|
|
7509
7526
|
const handleClick = useCallback22(() => {
|
|
7510
7527
|
void cmd.run(chatContext);
|
|
7511
7528
|
}, [cmd, chatContext]);
|
|
7512
|
-
return /* @__PURE__ */
|
|
7529
|
+
return /* @__PURE__ */ React35.createElement("button", { className: "brokr-ai-chat-sidebar-button", key: cmd.id, onClick: handleClick, type: "button" }, cmd.text);
|
|
7513
7530
|
}
|
|
7514
7531
|
function ChatInput() {
|
|
7515
7532
|
const {
|
|
@@ -7541,7 +7558,7 @@ function ChatInput() {
|
|
|
7541
7558
|
const handleChange = useCallback22((e) => {
|
|
7542
7559
|
setInput(e.target.value);
|
|
7543
7560
|
}, [setInput]);
|
|
7544
|
-
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(
|
|
7545
7562
|
"textarea",
|
|
7546
7563
|
{
|
|
7547
7564
|
className: "brokr-ai-chat-textarea",
|
|
@@ -7552,15 +7569,15 @@ function ChatInput() {
|
|
|
7552
7569
|
rows: 1,
|
|
7553
7570
|
value: input
|
|
7554
7571
|
}
|
|
7555
|
-
), /* @__PURE__ */
|
|
7572
|
+
), /* @__PURE__ */ React35.createElement(
|
|
7556
7573
|
"button",
|
|
7557
7574
|
{
|
|
7558
7575
|
className: "brokr-ai-chat-send",
|
|
7559
7576
|
disabled: isSubmitting || !input.trim(),
|
|
7560
7577
|
type: "submit"
|
|
7561
7578
|
},
|
|
7562
|
-
isSubmitting ? /* @__PURE__ */
|
|
7563
|
-
))), 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);
|
|
7564
7581
|
}
|
|
7565
7582
|
|
|
7566
7583
|
// src/react/chat/AIChat.tsx
|
|
@@ -7610,15 +7627,15 @@ function AIChat(inlineProps) {
|
|
|
7610
7627
|
const explicitModel = modelProp ?? userSelectedModel ?? void 0;
|
|
7611
7628
|
const displayModel = explicitModel ?? providers.find((p) => p.free)?.model ?? providers[0]?.model ?? "";
|
|
7612
7629
|
const activeModel = explicitModel ?? STACK_DEFAULT;
|
|
7613
|
-
const activeProvider =
|
|
7630
|
+
const activeProvider = useMemo24(
|
|
7614
7631
|
() => activeModel === STACK_DEFAULT ? void 0 : providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7615
7632
|
[activeModel]
|
|
7616
7633
|
);
|
|
7617
|
-
const hasBalance =
|
|
7634
|
+
const hasBalance = useMemo24(
|
|
7618
7635
|
() => billing === null || billing.balanceCents === null || billing.balanceCents > 0 || billing.hasPaymentMethod,
|
|
7619
7636
|
[billing]
|
|
7620
7637
|
);
|
|
7621
|
-
const availableProviders =
|
|
7638
|
+
const availableProviders = useMemo24(
|
|
7622
7639
|
() => hasBalance ? providers : providers.filter((p) => p.free),
|
|
7623
7640
|
[hasBalance]
|
|
7624
7641
|
);
|
|
@@ -7703,10 +7720,10 @@ function AIChat(inlineProps) {
|
|
|
7703
7720
|
document.addEventListener("keydown", onKeyDown);
|
|
7704
7721
|
return () => document.removeEventListener("keydown", onKeyDown);
|
|
7705
7722
|
}, [sidebarOpen]);
|
|
7706
|
-
const finalTitle =
|
|
7723
|
+
const finalTitle = useMemo24(() => {
|
|
7707
7724
|
return chat.renderedTitle || title;
|
|
7708
7725
|
}, [chat.renderedTitle, title]);
|
|
7709
|
-
const chatContext =
|
|
7726
|
+
const chatContext = useMemo24(() => ({
|
|
7710
7727
|
thread: chat.activeThread,
|
|
7711
7728
|
messages: chat.displayMessages,
|
|
7712
7729
|
user,
|
|
@@ -7714,19 +7731,19 @@ function AIChat(inlineProps) {
|
|
|
7714
7731
|
newThread: chat.startNewChat,
|
|
7715
7732
|
setThread: chat.selectThread
|
|
7716
7733
|
}), [chat.activeThread, chat.displayMessages, user, chat.sendMessage, chat.startNewChat, chat.selectThread]);
|
|
7717
|
-
const headerCommands =
|
|
7734
|
+
const headerCommands = useMemo24(
|
|
7718
7735
|
() => commands.filter((c) => c.location === "header" && (!c.show || c.show(chatContext))),
|
|
7719
7736
|
[commands, chatContext]
|
|
7720
7737
|
);
|
|
7721
|
-
const composerCommands =
|
|
7738
|
+
const composerCommands = useMemo24(
|
|
7722
7739
|
() => commands.filter((c) => c.location === "composer" && (!c.show || c.show(chatContext))),
|
|
7723
7740
|
[commands, chatContext]
|
|
7724
7741
|
);
|
|
7725
|
-
const threadMenuCommands =
|
|
7742
|
+
const threadMenuCommands = useMemo24(
|
|
7726
7743
|
() => commands.filter((c) => c.location === "threadMenu" && (!c.show || c.show(chatContext))),
|
|
7727
7744
|
[commands, chatContext]
|
|
7728
7745
|
);
|
|
7729
|
-
const chatState =
|
|
7746
|
+
const chatState = useMemo24(() => ({
|
|
7730
7747
|
displayMessages: chat.displayMessages,
|
|
7731
7748
|
visibleMessages: chat.displayMessages,
|
|
7732
7749
|
isSubmitting: chat.isSubmitting,
|
|
@@ -7793,16 +7810,16 @@ function AIChat(inlineProps) {
|
|
|
7793
7810
|
checkout,
|
|
7794
7811
|
user
|
|
7795
7812
|
]);
|
|
7796
|
-
return /* @__PURE__ */
|
|
7813
|
+
return /* @__PURE__ */ React36.createElement(ChatProvider, { value: chatState }, /* @__PURE__ */ React36.createElement(
|
|
7797
7814
|
"section",
|
|
7798
7815
|
{
|
|
7799
7816
|
className: "brokr-ai-chat-shell",
|
|
7800
7817
|
"data-sidebar": sidebarVisible,
|
|
7801
7818
|
ref: shellRef
|
|
7802
7819
|
},
|
|
7803
|
-
sidebarVisible ? /* @__PURE__ */
|
|
7804
|
-
sidebarOpen ? /* @__PURE__ */
|
|
7805
|
-
/* @__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(
|
|
7806
7823
|
ChatHeader,
|
|
7807
7824
|
{
|
|
7808
7825
|
activeId: chat.activeId,
|
|
@@ -7823,7 +7840,7 @@ function AIChat(inlineProps) {
|
|
|
7823
7840
|
startRename: chatState.startRename,
|
|
7824
7841
|
deleteThread: chat.deleteThread
|
|
7825
7842
|
}
|
|
7826
|
-
) : null, /* @__PURE__ */
|
|
7843
|
+
) : null, /* @__PURE__ */ React36.createElement(
|
|
7827
7844
|
MessagePane,
|
|
7828
7845
|
{
|
|
7829
7846
|
starterPrompts,
|
|
@@ -7831,21 +7848,21 @@ function AIChat(inlineProps) {
|
|
|
7831
7848
|
emptyCopy,
|
|
7832
7849
|
subtitle
|
|
7833
7850
|
}
|
|
7834
|
-
), /* @__PURE__ */
|
|
7851
|
+
), /* @__PURE__ */ React36.createElement(ChatInput, null))
|
|
7835
7852
|
));
|
|
7836
7853
|
}
|
|
7837
7854
|
function CommandButton2({ cmd, chatContext }) {
|
|
7838
7855
|
const handleClick = useCallback23(() => {
|
|
7839
7856
|
void cmd.run(chatContext);
|
|
7840
7857
|
}, [cmd, chatContext]);
|
|
7841
|
-
return /* @__PURE__ */
|
|
7858
|
+
return /* @__PURE__ */ React36.createElement("button", { className: "brokr-ai-chat-sidebar-button", onClick: handleClick, type: "button" }, cmd.text);
|
|
7842
7859
|
}
|
|
7843
7860
|
function MenuCommandItem({ cmd, chatContext, onClose }) {
|
|
7844
7861
|
const handleClick = useCallback23(() => {
|
|
7845
7862
|
onClose();
|
|
7846
7863
|
void cmd.run(chatContext);
|
|
7847
7864
|
}, [cmd, chatContext, onClose]);
|
|
7848
|
-
return /* @__PURE__ */
|
|
7865
|
+
return /* @__PURE__ */ React36.createElement("button", { className: "brokr-ai-chat-thread-dropdown-item", onClick: handleClick, type: "button" }, cmd.text);
|
|
7849
7866
|
}
|
|
7850
7867
|
function ChatHeader({
|
|
7851
7868
|
activeId,
|
|
@@ -7880,7 +7897,7 @@ function ChatHeader({
|
|
|
7880
7897
|
void deleteThread(activeId);
|
|
7881
7898
|
}
|
|
7882
7899
|
}, [activeId, deleteThread, setThreadMenuOpenId]);
|
|
7883
|
-
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(
|
|
7884
7901
|
"button",
|
|
7885
7902
|
{
|
|
7886
7903
|
"aria-label": "Open sidebar",
|
|
@@ -7888,8 +7905,8 @@ function ChatHeader({
|
|
|
7888
7905
|
onClick: handleOpenSidebar,
|
|
7889
7906
|
type: "button"
|
|
7890
7907
|
},
|
|
7891
|
-
/* @__PURE__ */
|
|
7892
|
-
) : 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(
|
|
7893
7910
|
ModelSelector,
|
|
7894
7911
|
{
|
|
7895
7912
|
activeModel,
|
|
@@ -7897,7 +7914,7 @@ function ChatHeader({
|
|
|
7897
7914
|
checkout,
|
|
7898
7915
|
setSelectedModel
|
|
7899
7916
|
}
|
|
7900
|
-
) : null, activeId && threadMenuVisible ? /* @__PURE__ */
|
|
7917
|
+
) : null, activeId && threadMenuVisible ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-ai-chat-thread-menu-wrap", ref: threadMenuRef }, /* @__PURE__ */ React36.createElement(
|
|
7901
7918
|
"button",
|
|
7902
7919
|
{
|
|
7903
7920
|
"aria-label": "Thread actions",
|
|
@@ -7905,17 +7922,17 @@ function ChatHeader({
|
|
|
7905
7922
|
onClick: handleToggleMenu,
|
|
7906
7923
|
type: "button"
|
|
7907
7924
|
},
|
|
7908
|
-
/* @__PURE__ */
|
|
7909
|
-
), threadMenuOpenId ? /* @__PURE__ */
|
|
7925
|
+
/* @__PURE__ */ React36.createElement(MoreHorizontalIcon, { size: 14 })
|
|
7926
|
+
), threadMenuOpenId ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-ai-chat-thread-dropdown" }, /* @__PURE__ */ React36.createElement(
|
|
7910
7927
|
"button",
|
|
7911
7928
|
{
|
|
7912
7929
|
className: "brokr-ai-chat-thread-dropdown-item",
|
|
7913
7930
|
onClick: handleRename,
|
|
7914
7931
|
type: "button"
|
|
7915
7932
|
},
|
|
7916
|
-
/* @__PURE__ */
|
|
7933
|
+
/* @__PURE__ */ React36.createElement(PencilIcon, { size: 13 }),
|
|
7917
7934
|
"Rename"
|
|
7918
|
-
), threadMenuCommands.map((cmd) => /* @__PURE__ */
|
|
7935
|
+
), threadMenuCommands.map((cmd) => /* @__PURE__ */ React36.createElement(
|
|
7919
7936
|
MenuCommandItem,
|
|
7920
7937
|
{
|
|
7921
7938
|
key: cmd.id,
|
|
@@ -7923,7 +7940,7 @@ function ChatHeader({
|
|
|
7923
7940
|
chatContext,
|
|
7924
7941
|
onClose: closeMenu
|
|
7925
7942
|
}
|
|
7926
|
-
)), /* @__PURE__ */
|
|
7943
|
+
)), /* @__PURE__ */ React36.createElement(
|
|
7927
7944
|
"button",
|
|
7928
7945
|
{
|
|
7929
7946
|
className: "brokr-ai-chat-thread-dropdown-item",
|
|
@@ -7931,13 +7948,13 @@ function ChatHeader({
|
|
|
7931
7948
|
onClick: handleDelete,
|
|
7932
7949
|
type: "button"
|
|
7933
7950
|
},
|
|
7934
|
-
/* @__PURE__ */
|
|
7951
|
+
/* @__PURE__ */ React36.createElement(TrashIcon, { size: 13 }),
|
|
7935
7952
|
"Delete"
|
|
7936
7953
|
)) : null) : null));
|
|
7937
7954
|
}
|
|
7938
7955
|
|
|
7939
7956
|
// src/react/composites/FabAI.tsx
|
|
7940
|
-
import
|
|
7957
|
+
import React37, { useCallback as useCallback24, useMemo as useMemo25, useRef as useRef8, useState as useState19 } from "react";
|
|
7941
7958
|
|
|
7942
7959
|
// src/react/composites/fab-context.ts
|
|
7943
7960
|
function buildFabSystemPrompt(appContext, brandName, existingPrompt) {
|
|
@@ -7994,14 +8011,14 @@ function FabAI({
|
|
|
7994
8011
|
const [isSending, setIsSending] = useState19(false);
|
|
7995
8012
|
const [messages, setMessages] = useState19([]);
|
|
7996
8013
|
const conversationIdRef = useRef8(`fab_${crypto.randomUUID()}`);
|
|
7997
|
-
const launcherStyle =
|
|
8014
|
+
const launcherStyle = useMemo25(
|
|
7998
8015
|
() => ({
|
|
7999
8016
|
left: position === "bottom-left" ? "var(--brokr-space-6)" : void 0,
|
|
8000
8017
|
right: position === "bottom-right" ? "var(--brokr-space-6)" : void 0
|
|
8001
8018
|
}),
|
|
8002
8019
|
[position]
|
|
8003
8020
|
);
|
|
8004
|
-
const canChat =
|
|
8021
|
+
const canChat = useMemo25(() => can("ai.chat"), [can]);
|
|
8005
8022
|
const toggleOpen = useCallback24(() => {
|
|
8006
8023
|
if (!canChat) {
|
|
8007
8024
|
redirectTo("/pricing");
|
|
@@ -8096,7 +8113,7 @@ function FabAI({
|
|
|
8096
8113
|
void sendPrompt(input);
|
|
8097
8114
|
}
|
|
8098
8115
|
}, [input, sendPrompt]);
|
|
8099
|
-
return /* @__PURE__ */
|
|
8116
|
+
return /* @__PURE__ */ React37.createElement(React37.Fragment, null, /* @__PURE__ */ React37.createElement("div", { className: "brokr-chat-fab", style: launcherStyle }, /* @__PURE__ */ React37.createElement(
|
|
8100
8117
|
"button",
|
|
8101
8118
|
{
|
|
8102
8119
|
"aria-label": isOpen ? "Close AI chat" : "Open AI chat",
|
|
@@ -8106,21 +8123,21 @@ function FabAI({
|
|
|
8106
8123
|
onClick: toggleOpen,
|
|
8107
8124
|
type: "button"
|
|
8108
8125
|
},
|
|
8109
|
-
/* @__PURE__ */
|
|
8110
|
-
)), isOpen ? /* @__PURE__ */
|
|
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(
|
|
8111
8128
|
"div",
|
|
8112
8129
|
{
|
|
8113
8130
|
className: "brokr-chat-messages",
|
|
8114
8131
|
"data-empty": messages.length === 0
|
|
8115
8132
|
},
|
|
8116
|
-
messages.length === 0 ? /* @__PURE__ */
|
|
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,
|
|
8117
8134
|
messages.map((message, index) => {
|
|
8118
8135
|
const text = contentToText(message.content);
|
|
8119
8136
|
const isTyping = message.role === "assistant" && !text && isSending && index === messages.length - 1;
|
|
8120
|
-
return /* @__PURE__ */
|
|
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);
|
|
8121
8138
|
}),
|
|
8122
|
-
error ? /* @__PURE__ */
|
|
8123
|
-
), /* @__PURE__ */
|
|
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(
|
|
8124
8141
|
"textarea",
|
|
8125
8142
|
{
|
|
8126
8143
|
className: "brokr-textarea brokr-chat-input",
|
|
@@ -8130,14 +8147,14 @@ function FabAI({
|
|
|
8130
8147
|
rows: 2,
|
|
8131
8148
|
value: input
|
|
8132
8149
|
}
|
|
8133
|
-
), /* @__PURE__ */
|
|
8150
|
+
), /* @__PURE__ */ React37.createElement("button", { className: "brokr-button", disabled: isSending || !input.trim(), type: "submit" }, isSending ? "Thinking" : "Send"))) : null);
|
|
8134
8151
|
}
|
|
8135
8152
|
|
|
8136
8153
|
// src/react/composites/SmartUpload.tsx
|
|
8137
|
-
import
|
|
8154
|
+
import React38, {
|
|
8138
8155
|
useCallback as useCallback25,
|
|
8139
8156
|
useId as useId2,
|
|
8140
|
-
useMemo as
|
|
8157
|
+
useMemo as useMemo26,
|
|
8141
8158
|
useRef as useRef9,
|
|
8142
8159
|
useState as useState20
|
|
8143
8160
|
} from "react";
|
|
@@ -8155,7 +8172,7 @@ function SmartUpload({
|
|
|
8155
8172
|
const [fileName, setFileName] = useState20(null);
|
|
8156
8173
|
const [progress, setProgress] = useState20(0);
|
|
8157
8174
|
const [isUploading, setIsUploading] = useState20(false);
|
|
8158
|
-
const helperText =
|
|
8175
|
+
const helperText = useMemo26(() => {
|
|
8159
8176
|
return `${Math.round(maxSize / (1024 * 1024))} MB max file size.`;
|
|
8160
8177
|
}, [maxSize]);
|
|
8161
8178
|
const beginUpload = useCallback25((file) => {
|
|
@@ -8219,7 +8236,7 @@ function SmartUpload({
|
|
|
8219
8236
|
const handleBrowse = useCallback25(() => {
|
|
8220
8237
|
inputRef.current?.click();
|
|
8221
8238
|
}, []);
|
|
8222
|
-
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(
|
|
8223
8240
|
"label",
|
|
8224
8241
|
{
|
|
8225
8242
|
className: "brokr-upload-dropzone",
|
|
@@ -8230,11 +8247,11 @@ function SmartUpload({
|
|
|
8230
8247
|
onDragOver: handleDragEnter,
|
|
8231
8248
|
onDrop: handleDrop
|
|
8232
8249
|
},
|
|
8233
|
-
/* @__PURE__ */
|
|
8234
|
-
/* @__PURE__ */
|
|
8235
|
-
/* @__PURE__ */
|
|
8236
|
-
/* @__PURE__ */
|
|
8237
|
-
), /* @__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(
|
|
8238
8255
|
"input",
|
|
8239
8256
|
{
|
|
8240
8257
|
accept,
|
|
@@ -8244,11 +8261,11 @@ function SmartUpload({
|
|
|
8244
8261
|
ref: inputRef,
|
|
8245
8262
|
type: "file"
|
|
8246
8263
|
}
|
|
8247
|
-
), 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);
|
|
8248
8265
|
}
|
|
8249
8266
|
|
|
8250
8267
|
// src/react/composites/FeedbackWidget.tsx
|
|
8251
|
-
import
|
|
8268
|
+
import React39, { useCallback as useCallback26, useState as useState21 } from "react";
|
|
8252
8269
|
function FeedbackWidget({
|
|
8253
8270
|
context,
|
|
8254
8271
|
onSubmit
|
|
@@ -8298,7 +8315,7 @@ function FeedbackWidget({
|
|
|
8298
8315
|
setIsPending(false);
|
|
8299
8316
|
}
|
|
8300
8317
|
}, [context, onSubmit, rating, text, user?.id]);
|
|
8301
|
-
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(
|
|
8302
8319
|
"button",
|
|
8303
8320
|
{
|
|
8304
8321
|
className: "brokr-rating-button",
|
|
@@ -8307,7 +8324,7 @@ function FeedbackWidget({
|
|
|
8307
8324
|
type: "button"
|
|
8308
8325
|
},
|
|
8309
8326
|
"This worked"
|
|
8310
|
-
), /* @__PURE__ */
|
|
8327
|
+
), /* @__PURE__ */ React39.createElement(
|
|
8311
8328
|
"button",
|
|
8312
8329
|
{
|
|
8313
8330
|
className: "brokr-rating-button",
|
|
@@ -8316,7 +8333,7 @@ function FeedbackWidget({
|
|
|
8316
8333
|
type: "button"
|
|
8317
8334
|
},
|
|
8318
8335
|
"Needs work"
|
|
8319
|
-
)), /* @__PURE__ */
|
|
8336
|
+
)), /* @__PURE__ */ React39.createElement(
|
|
8320
8337
|
"textarea",
|
|
8321
8338
|
{
|
|
8322
8339
|
className: "brokr-textarea",
|
|
@@ -8325,11 +8342,11 @@ function FeedbackWidget({
|
|
|
8325
8342
|
rows: 4,
|
|
8326
8343
|
value: text
|
|
8327
8344
|
}
|
|
8328
|
-
), 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"));
|
|
8329
8346
|
}
|
|
8330
8347
|
|
|
8331
8348
|
// src/react/BrokrErrorBoundary.tsx
|
|
8332
|
-
import
|
|
8349
|
+
import React40 from "react";
|
|
8333
8350
|
|
|
8334
8351
|
// src/fix-registry.ts
|
|
8335
8352
|
var FIX_REGISTRY = {
|
|
@@ -8553,7 +8570,7 @@ var BrokrError = class extends Error {
|
|
|
8553
8570
|
};
|
|
8554
8571
|
|
|
8555
8572
|
// src/react/BrokrErrorBoundary.tsx
|
|
8556
|
-
var BrokrErrorBoundary = class extends
|
|
8573
|
+
var BrokrErrorBoundary = class extends React40.Component {
|
|
8557
8574
|
constructor() {
|
|
8558
8575
|
super(...arguments);
|
|
8559
8576
|
this.state = { hasError: false, error: null };
|
|
@@ -8565,11 +8582,11 @@ var BrokrErrorBoundary = class extends React39.Component {
|
|
|
8565
8582
|
if (this.state.hasError) {
|
|
8566
8583
|
if (this.props.fallback) return this.props.fallback;
|
|
8567
8584
|
const message = this.state.error instanceof BrokrError ? this.state.error.toUserMessage() : "Something went wrong.";
|
|
8568
|
-
return
|
|
8585
|
+
return React40.createElement(
|
|
8569
8586
|
"div",
|
|
8570
8587
|
{ style: { padding: 24, textAlign: "center" } },
|
|
8571
|
-
|
|
8572
|
-
|
|
8588
|
+
React40.createElement("p", null, message),
|
|
8589
|
+
React40.createElement(
|
|
8573
8590
|
"button",
|
|
8574
8591
|
{
|
|
8575
8592
|
onClick: () => this.setState({ hasError: false, error: null }),
|
|
@@ -8583,8 +8600,8 @@ var BrokrErrorBoundary = class extends React39.Component {
|
|
|
8583
8600
|
}
|
|
8584
8601
|
};
|
|
8585
8602
|
|
|
8586
|
-
// src/react/notifications/
|
|
8587
|
-
import
|
|
8603
|
+
// src/react/notifications/ActivityFeed.tsx
|
|
8604
|
+
import React41, { useCallback as useCallback27, useMemo as useMemo27 } from "react";
|
|
8588
8605
|
|
|
8589
8606
|
// src/react/notifications/use-notifications.ts
|
|
8590
8607
|
import { useContext as useContext4 } from "react";
|
|
@@ -8598,7 +8615,7 @@ function useNotifications() {
|
|
|
8598
8615
|
return ctx;
|
|
8599
8616
|
}
|
|
8600
8617
|
|
|
8601
|
-
// src/react/notifications/
|
|
8618
|
+
// src/react/notifications/ActivityFeed.tsx
|
|
8602
8619
|
function timeAgo(iso) {
|
|
8603
8620
|
const diff = Date.now() - new Date(iso).getTime();
|
|
8604
8621
|
const mins = Math.floor(diff / 6e4);
|
|
@@ -8609,16 +8626,79 @@ function timeAgo(iso) {
|
|
|
8609
8626
|
const days = Math.floor(hours / 24);
|
|
8610
8627
|
return `${days}d ago`;
|
|
8611
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
|
+
}
|
|
8612
8692
|
function NotifDropdownItem({
|
|
8613
8693
|
notif,
|
|
8614
8694
|
registry,
|
|
8615
8695
|
onClick
|
|
8616
8696
|
}) {
|
|
8617
|
-
const handleClick =
|
|
8697
|
+
const handleClick = useCallback28(() => onClick(notif), [notif, onClick]);
|
|
8618
8698
|
const notifData = notif.data ?? {};
|
|
8619
8699
|
const notifType = notifData.type ?? "default";
|
|
8620
8700
|
const resolved = resolveNotificationType(registry, notifType, notifData);
|
|
8621
|
-
return /* @__PURE__ */
|
|
8701
|
+
return /* @__PURE__ */ React42.createElement(
|
|
8622
8702
|
"button",
|
|
8623
8703
|
{
|
|
8624
8704
|
type: "button",
|
|
@@ -8626,9 +8706,9 @@ function NotifDropdownItem({
|
|
|
8626
8706
|
onClick: handleClick,
|
|
8627
8707
|
role: "menuitem"
|
|
8628
8708
|
},
|
|
8629
|
-
resolved.image ? /* @__PURE__ */
|
|
8630
|
-
/* @__PURE__ */
|
|
8631
|
-
/* @__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))
|
|
8632
8712
|
);
|
|
8633
8713
|
}
|
|
8634
8714
|
function NotificationBell() {
|
|
@@ -8636,7 +8716,7 @@ function NotificationBell() {
|
|
|
8636
8716
|
const [open, setOpen] = useState22(false);
|
|
8637
8717
|
const containerRef = useRef10(null);
|
|
8638
8718
|
const markReadTimerRef = useRef10(null);
|
|
8639
|
-
const toggle =
|
|
8719
|
+
const toggle = useCallback28(() => setOpen((o) => !o), []);
|
|
8640
8720
|
useEffect13(() => {
|
|
8641
8721
|
if (markReadTimerRef.current) {
|
|
8642
8722
|
clearTimeout(markReadTimerRef.current);
|
|
@@ -8672,13 +8752,13 @@ function NotificationBell() {
|
|
|
8672
8752
|
document.addEventListener("keydown", handleKey);
|
|
8673
8753
|
return () => document.removeEventListener("keydown", handleKey);
|
|
8674
8754
|
}, [open]);
|
|
8675
|
-
const sorted =
|
|
8755
|
+
const sorted = useMemo28(
|
|
8676
8756
|
() => [...notifications].sort(
|
|
8677
8757
|
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
8678
8758
|
),
|
|
8679
8759
|
[notifications]
|
|
8680
8760
|
);
|
|
8681
|
-
const handleItemClick =
|
|
8761
|
+
const handleItemClick = useCallback28((notif) => {
|
|
8682
8762
|
if (!notif.read) markRead(notif.id);
|
|
8683
8763
|
const notifData = notif.data ?? {};
|
|
8684
8764
|
const notifType = notif.type ?? notifData.type ?? "default";
|
|
@@ -8688,7 +8768,7 @@ function NotificationBell() {
|
|
|
8688
8768
|
window.location.assign(href);
|
|
8689
8769
|
}
|
|
8690
8770
|
}, [markRead, registry]);
|
|
8691
|
-
return /* @__PURE__ */
|
|
8771
|
+
return /* @__PURE__ */ React42.createElement("div", { className: "brokr-notif-bell-wrap", ref: containerRef }, /* @__PURE__ */ React42.createElement(
|
|
8692
8772
|
"button",
|
|
8693
8773
|
{
|
|
8694
8774
|
type: "button",
|
|
@@ -8698,9 +8778,9 @@ function NotificationBell() {
|
|
|
8698
8778
|
"aria-expanded": open,
|
|
8699
8779
|
"aria-haspopup": "menu"
|
|
8700
8780
|
},
|
|
8701
|
-
/* @__PURE__ */
|
|
8702
|
-
unreadCount > 0 && /* @__PURE__ */
|
|
8703
|
-
), 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(
|
|
8704
8784
|
"button",
|
|
8705
8785
|
{
|
|
8706
8786
|
type: "button",
|
|
@@ -8708,7 +8788,7 @@ function NotificationBell() {
|
|
|
8708
8788
|
onClick: markAllRead
|
|
8709
8789
|
},
|
|
8710
8790
|
"Mark all read"
|
|
8711
|
-
)), /* @__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(
|
|
8712
8792
|
NotifDropdownItem,
|
|
8713
8793
|
{
|
|
8714
8794
|
key: notif.id,
|
|
@@ -8720,7 +8800,7 @@ function NotificationBell() {
|
|
|
8720
8800
|
}
|
|
8721
8801
|
|
|
8722
8802
|
// src/react/notifications/NotificationList.tsx
|
|
8723
|
-
import
|
|
8803
|
+
import React43, { useCallback as useCallback29, useMemo as useMemo29 } from "react";
|
|
8724
8804
|
function formatTimestamp(iso) {
|
|
8725
8805
|
const date = new Date(iso);
|
|
8726
8806
|
return new Intl.DateTimeFormat("en-US", {
|
|
@@ -8731,38 +8811,38 @@ function formatTimestamp(iso) {
|
|
|
8731
8811
|
}).format(date);
|
|
8732
8812
|
}
|
|
8733
8813
|
function NotificationListSkeleton() {
|
|
8734
|
-
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 } }))));
|
|
8735
8815
|
}
|
|
8736
8816
|
function NotifListItem({
|
|
8737
8817
|
notif,
|
|
8738
8818
|
registry,
|
|
8739
8819
|
onClick
|
|
8740
8820
|
}) {
|
|
8741
|
-
const handleClick =
|
|
8821
|
+
const handleClick = useCallback29(() => onClick(notif), [notif, onClick]);
|
|
8742
8822
|
const notifData = notif.data ?? {};
|
|
8743
8823
|
const notifType = notif.type ?? notifData.type ?? "default";
|
|
8744
8824
|
const resolved = resolveNotificationType(registry, notifType, notifData);
|
|
8745
|
-
return /* @__PURE__ */
|
|
8825
|
+
return /* @__PURE__ */ React43.createElement(
|
|
8746
8826
|
"button",
|
|
8747
8827
|
{
|
|
8748
8828
|
type: "button",
|
|
8749
8829
|
className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
|
|
8750
8830
|
onClick: handleClick
|
|
8751
8831
|
},
|
|
8752
|
-
resolved.image ? /* @__PURE__ */
|
|
8753
|
-
/* @__PURE__ */
|
|
8754
|
-
/* @__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))
|
|
8755
8835
|
);
|
|
8756
8836
|
}
|
|
8757
8837
|
function NotificationList() {
|
|
8758
8838
|
const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
|
|
8759
|
-
const sorted =
|
|
8839
|
+
const sorted = useMemo29(
|
|
8760
8840
|
() => [...notifications].sort(
|
|
8761
8841
|
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
8762
8842
|
),
|
|
8763
8843
|
[notifications]
|
|
8764
8844
|
);
|
|
8765
|
-
const handleClick =
|
|
8845
|
+
const handleClick = useCallback29((notif) => {
|
|
8766
8846
|
if (!notif.read) markRead(notif.id);
|
|
8767
8847
|
const notifData = notif.data ?? {};
|
|
8768
8848
|
const notifType = notif.type ?? notifData.type ?? "default";
|
|
@@ -8772,7 +8852,7 @@ function NotificationList() {
|
|
|
8772
8852
|
window.location.assign(href);
|
|
8773
8853
|
}
|
|
8774
8854
|
}, [markRead, registry]);
|
|
8775
|
-
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(
|
|
8776
8856
|
"button",
|
|
8777
8857
|
{
|
|
8778
8858
|
type: "button",
|
|
@@ -8780,7 +8860,7 @@ function NotificationList() {
|
|
|
8780
8860
|
onClick: markAllRead
|
|
8781
8861
|
},
|
|
8782
8862
|
"Mark all read"
|
|
8783
|
-
)), 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(
|
|
8784
8864
|
NotifListItem,
|
|
8785
8865
|
{
|
|
8786
8866
|
key: notif.id,
|
|
@@ -8820,6 +8900,7 @@ function defineAccount(config) {
|
|
|
8820
8900
|
export {
|
|
8821
8901
|
AIChat,
|
|
8822
8902
|
AccountPanel,
|
|
8903
|
+
ActivityFeed,
|
|
8823
8904
|
AuthPageShell,
|
|
8824
8905
|
AuthWall,
|
|
8825
8906
|
AutoReloadToggle,
|
|
@@ -8849,8 +8930,10 @@ export {
|
|
|
8849
8930
|
UpdateBilling,
|
|
8850
8931
|
UpgradePrompt,
|
|
8851
8932
|
UsageGate,
|
|
8933
|
+
UsageGrid,
|
|
8852
8934
|
UserButton,
|
|
8853
8935
|
defineAccount,
|
|
8936
|
+
defineBrokrTheme,
|
|
8854
8937
|
defineChat,
|
|
8855
8938
|
useBrokr,
|
|
8856
8939
|
useBrokrTheme,
|