@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.js
CHANGED
|
@@ -34,6 +34,7 @@ var index_exports = {};
|
|
|
34
34
|
__export(index_exports, {
|
|
35
35
|
AIChat: () => AIChat,
|
|
36
36
|
AccountPanel: () => AccountPanel,
|
|
37
|
+
ActivityFeed: () => ActivityFeed,
|
|
37
38
|
AuthPageShell: () => AuthPageShell,
|
|
38
39
|
AuthWall: () => AuthWall,
|
|
39
40
|
AutoReloadToggle: () => AutoReloadToggle,
|
|
@@ -63,8 +64,10 @@ __export(index_exports, {
|
|
|
63
64
|
UpdateBilling: () => UpdateBilling,
|
|
64
65
|
UpgradePrompt: () => UpgradePrompt,
|
|
65
66
|
UsageGate: () => UsageGate,
|
|
67
|
+
UsageGrid: () => UsageGrid,
|
|
66
68
|
UserButton: () => UserButton,
|
|
67
69
|
defineAccount: () => defineAccount,
|
|
70
|
+
defineBrokrTheme: () => defineBrokrTheme,
|
|
68
71
|
defineChat: () => defineChat,
|
|
69
72
|
useBrokr: () => useBrokr,
|
|
70
73
|
useBrokrTheme: () => useBrokrTheme,
|
|
@@ -6328,15 +6331,32 @@ function CancelSubscription({ onCancel }) {
|
|
|
6328
6331
|
return /* @__PURE__ */ import_react29.default.createElement("div", { className: "brokr-section", style: { gap: "0.75rem" } }, confirming ? /* @__PURE__ */ import_react29.default.createElement("div", { className: "brokr-inline-message" }, "Subscription changes are confirmed in billing. Continue?") : null, /* @__PURE__ */ import_react29.default.createElement("div", { className: "brokr-brand-row" }, /* @__PURE__ */ import_react29.default.createElement("button", { className: "brokr-button-ghost", disabled: isPending, onClick: handlePrimaryClick, type: "button" }, isPending ? "Opening billing" : confirming ? "Continue" : "Cancel subscription"), confirming ? /* @__PURE__ */ import_react29.default.createElement("button", { className: "brokr-button-secondary", onClick: handleDismiss, type: "button" }, "Keep subscription") : null));
|
|
6329
6332
|
}
|
|
6330
6333
|
|
|
6334
|
+
// src/react/payments/UsageGrid.tsx
|
|
6335
|
+
var import_react30 = __toESM(require("react"));
|
|
6336
|
+
function UsageGrid() {
|
|
6337
|
+
const { entitlements, isLoaded } = useBrokr();
|
|
6338
|
+
const meteredFeatures = (0, import_react30.useMemo)(() => {
|
|
6339
|
+
if (!entitlements?.features) return [];
|
|
6340
|
+
return Object.entries(entitlements.features).filter(([_, check]) => check.limitType === "numeric").map(([slug]) => slug);
|
|
6341
|
+
}, [entitlements]);
|
|
6342
|
+
if (!isLoaded) {
|
|
6343
|
+
return /* @__PURE__ */ import_react30.default.createElement("div", { className: "brokr-usage-grid" }, /* @__PURE__ */ import_react30.default.createElement(Skeleton, { height: 72 }), /* @__PURE__ */ import_react30.default.createElement(Skeleton, { height: 72 }));
|
|
6344
|
+
}
|
|
6345
|
+
if (meteredFeatures.length === 0) {
|
|
6346
|
+
return null;
|
|
6347
|
+
}
|
|
6348
|
+
return /* @__PURE__ */ import_react30.default.createElement("div", { className: "brokr-usage-grid" }, meteredFeatures.map((slug) => /* @__PURE__ */ import_react30.default.createElement(FeatureMeter, { key: slug, feature: slug })));
|
|
6349
|
+
}
|
|
6350
|
+
|
|
6331
6351
|
// src/react/chat/AIChat.tsx
|
|
6332
|
-
var
|
|
6352
|
+
var import_react39 = __toESM(require("react"));
|
|
6333
6353
|
|
|
6334
6354
|
// src/react/chat/ChatContext.tsx
|
|
6335
|
-
var
|
|
6336
|
-
var InternalChatContext = (0,
|
|
6355
|
+
var import_react31 = require("react");
|
|
6356
|
+
var InternalChatContext = (0, import_react31.createContext)(null);
|
|
6337
6357
|
var ChatProvider = InternalChatContext.Provider;
|
|
6338
6358
|
function useChatState() {
|
|
6339
|
-
const ctx = (0,
|
|
6359
|
+
const ctx = (0, import_react31.useContext)(InternalChatContext);
|
|
6340
6360
|
if (!ctx) {
|
|
6341
6361
|
throw new Error("useChatState must be used within <AIChat>");
|
|
6342
6362
|
}
|
|
@@ -6344,7 +6364,7 @@ function useChatState() {
|
|
|
6344
6364
|
}
|
|
6345
6365
|
|
|
6346
6366
|
// src/react/chat/useChat.ts
|
|
6347
|
-
var
|
|
6367
|
+
var import_react32 = require("react");
|
|
6348
6368
|
|
|
6349
6369
|
// src/ai/types.ts
|
|
6350
6370
|
function contentToText(content) {
|
|
@@ -6519,24 +6539,24 @@ function useChat(config) {
|
|
|
6519
6539
|
userId
|
|
6520
6540
|
} = config;
|
|
6521
6541
|
const isPersist = persist && !isControlled;
|
|
6522
|
-
const [memConversations, setMemConversations] = (0,
|
|
6523
|
-
const [memActiveId, setMemActiveId] = (0,
|
|
6524
|
-
const [serverThreads, setServerThreads] = (0,
|
|
6525
|
-
const [serverActiveId, setServerActiveId] = (0,
|
|
6526
|
-
const [serverMessages, setServerMessages] = (0,
|
|
6527
|
-
const [input, setInput] = (0,
|
|
6528
|
-
const [isSubmitting, setIsSubmitting] = (0,
|
|
6529
|
-
const [error, setError] = (0,
|
|
6530
|
-
const [threadsLoading, setThreadsLoading] = (0,
|
|
6531
|
-
const [renamingId, setRenamingId] = (0,
|
|
6532
|
-
const [renameValue, setRenameValue] = (0,
|
|
6533
|
-
const [hasMoreMessages, setHasMoreMessages] = (0,
|
|
6534
|
-
const [loadingOlder, setLoadingOlder] = (0,
|
|
6535
|
-
const textareaRef = (0,
|
|
6536
|
-
const bottomRef = (0,
|
|
6537
|
-
const scrollContainerRef = (0,
|
|
6538
|
-
const sentinelRef = (0,
|
|
6539
|
-
const displaySidebarItems = (0,
|
|
6542
|
+
const [memConversations, setMemConversations] = (0, import_react32.useState)([]);
|
|
6543
|
+
const [memActiveId, setMemActiveId] = (0, import_react32.useState)(null);
|
|
6544
|
+
const [serverThreads, setServerThreads] = (0, import_react32.useState)([]);
|
|
6545
|
+
const [serverActiveId, setServerActiveId] = (0, import_react32.useState)(null);
|
|
6546
|
+
const [serverMessages, setServerMessages] = (0, import_react32.useState)([]);
|
|
6547
|
+
const [input, setInput] = (0, import_react32.useState)("");
|
|
6548
|
+
const [isSubmitting, setIsSubmitting] = (0, import_react32.useState)(false);
|
|
6549
|
+
const [error, setError] = (0, import_react32.useState)(null);
|
|
6550
|
+
const [threadsLoading, setThreadsLoading] = (0, import_react32.useState)(false);
|
|
6551
|
+
const [renamingId, setRenamingId] = (0, import_react32.useState)(null);
|
|
6552
|
+
const [renameValue, setRenameValue] = (0, import_react32.useState)("");
|
|
6553
|
+
const [hasMoreMessages, setHasMoreMessages] = (0, import_react32.useState)(false);
|
|
6554
|
+
const [loadingOlder, setLoadingOlder] = (0, import_react32.useState)(false);
|
|
6555
|
+
const textareaRef = (0, import_react32.useRef)(null);
|
|
6556
|
+
const bottomRef = (0, import_react32.useRef)(null);
|
|
6557
|
+
const scrollContainerRef = (0, import_react32.useRef)(null);
|
|
6558
|
+
const sentinelRef = (0, import_react32.useRef)(null);
|
|
6559
|
+
const displaySidebarItems = (0, import_react32.useMemo)(() => {
|
|
6540
6560
|
if (isControlled) {
|
|
6541
6561
|
return threadsProp?.map((t) => ({
|
|
6542
6562
|
id: t.id,
|
|
@@ -6557,26 +6577,26 @@ function useChat(config) {
|
|
|
6557
6577
|
updatedAt: new Date(c.updatedAt).toISOString()
|
|
6558
6578
|
}));
|
|
6559
6579
|
}, [isControlled, isPersist, threadsProp, serverThreads, memConversations]);
|
|
6560
|
-
const activeId = (0,
|
|
6580
|
+
const activeId = (0, import_react32.useMemo)(() => {
|
|
6561
6581
|
if (isControlled) return activeThreadIdProp ?? null;
|
|
6562
6582
|
if (isPersist) return serverActiveId;
|
|
6563
6583
|
return memActiveId;
|
|
6564
6584
|
}, [isControlled, isPersist, activeThreadIdProp, serverActiveId, memActiveId]);
|
|
6565
|
-
const displayMessages = (0,
|
|
6585
|
+
const displayMessages = (0, import_react32.useMemo)(() => {
|
|
6566
6586
|
if (isControlled) return messagesProp ?? [];
|
|
6567
6587
|
if (isPersist) return serverMessages;
|
|
6568
6588
|
return memConversations.find((c) => c.id === memActiveId)?.messages ?? [];
|
|
6569
6589
|
}, [isControlled, isPersist, messagesProp, serverMessages, memConversations, memActiveId]);
|
|
6570
|
-
const activeMemConv = (0,
|
|
6590
|
+
const activeMemConv = (0, import_react32.useMemo)(
|
|
6571
6591
|
() => memConversations.find((c) => c.id === memActiveId) ?? null,
|
|
6572
6592
|
[memConversations, memActiveId]
|
|
6573
6593
|
);
|
|
6574
|
-
const activeThread = (0,
|
|
6594
|
+
const activeThread = (0, import_react32.useMemo)(() => {
|
|
6575
6595
|
if (isControlled) return threadsProp?.find((t) => t.id === activeId) ?? null;
|
|
6576
6596
|
if (isPersist) return serverThreads.find((t) => t.id === activeId) ?? null;
|
|
6577
6597
|
return null;
|
|
6578
6598
|
}, [isControlled, isPersist, threadsProp, serverThreads, activeId]);
|
|
6579
|
-
const renderedTitle = (0,
|
|
6599
|
+
const renderedTitle = (0, import_react32.useMemo)(() => {
|
|
6580
6600
|
if (isPersist && activeThread) return activeThread.title;
|
|
6581
6601
|
if (!isPersist && activeMemConv) {
|
|
6582
6602
|
if (activeMemConv.titleState === "loading") return "Naming conversation";
|
|
@@ -6585,10 +6605,10 @@ function useChat(config) {
|
|
|
6585
6605
|
return "New chat";
|
|
6586
6606
|
}, [isPersist, activeThread, activeMemConv]);
|
|
6587
6607
|
const isTitleLoading = !isPersist && activeMemConv?.titleState === "loading";
|
|
6588
|
-
(0,
|
|
6608
|
+
(0, import_react32.useEffect)(() => {
|
|
6589
6609
|
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
6590
6610
|
}, [displayMessages, activeId]);
|
|
6591
|
-
(0,
|
|
6611
|
+
(0, import_react32.useEffect)(() => {
|
|
6592
6612
|
if (!isPersist) return;
|
|
6593
6613
|
setThreadsLoading(true);
|
|
6594
6614
|
const base = getBaseEndpoint(endpoint);
|
|
@@ -6597,7 +6617,7 @@ function useChat(config) {
|
|
|
6597
6617
|
fetch(`${base}/threads?${params}`, { credentials: "include" }).then((r) => r.json()).then((data) => setServerThreads(data.threads ?? [])).catch(() => {
|
|
6598
6618
|
}).finally(() => setThreadsLoading(false));
|
|
6599
6619
|
}, [isPersist, endpoint, surface, subject]);
|
|
6600
|
-
(0,
|
|
6620
|
+
(0, import_react32.useEffect)(() => {
|
|
6601
6621
|
if (!isPersist || !serverActiveId || isSubmitting) return;
|
|
6602
6622
|
const base = getBaseEndpoint(endpoint);
|
|
6603
6623
|
fetch(`${base}/threads/${serverActiveId}/messages?limit=30&offset=0`, { credentials: "include" }).then((r) => r.json()).then((data) => {
|
|
@@ -6606,7 +6626,7 @@ function useChat(config) {
|
|
|
6606
6626
|
}).catch(() => {
|
|
6607
6627
|
});
|
|
6608
6628
|
}, [isPersist, serverActiveId, endpoint, isSubmitting]);
|
|
6609
|
-
const loadOlderMessages = (0,
|
|
6629
|
+
const loadOlderMessages = (0, import_react32.useCallback)(async () => {
|
|
6610
6630
|
if (!isPersist || !serverActiveId || loadingOlder || !hasMoreMessages) return;
|
|
6611
6631
|
setLoadingOlder(true);
|
|
6612
6632
|
const base = getBaseEndpoint(endpoint);
|
|
@@ -6636,7 +6656,7 @@ function useChat(config) {
|
|
|
6636
6656
|
setLoadingOlder(false);
|
|
6637
6657
|
}
|
|
6638
6658
|
}, [isPersist, serverActiveId, loadingOlder, hasMoreMessages, endpoint, serverMessages.length]);
|
|
6639
|
-
(0,
|
|
6659
|
+
(0, import_react32.useEffect)(() => {
|
|
6640
6660
|
if (!isPersist || !hasMoreMessages) return;
|
|
6641
6661
|
const sentinel = sentinelRef.current;
|
|
6642
6662
|
if (!sentinel) return;
|
|
@@ -6649,15 +6669,15 @@ function useChat(config) {
|
|
|
6649
6669
|
observer.observe(sentinel);
|
|
6650
6670
|
return () => observer.disconnect();
|
|
6651
6671
|
}, [isPersist, hasMoreMessages, loadOlderMessages]);
|
|
6652
|
-
const notificationsCtx = (0,
|
|
6653
|
-
(0,
|
|
6672
|
+
const notificationsCtx = (0, import_react32.useContext)(NotificationsContext);
|
|
6673
|
+
(0, import_react32.useEffect)(() => {
|
|
6654
6674
|
if (!notificationsCtx || !serverActiveId) return;
|
|
6655
6675
|
notificationsCtx.registerViewingThread(serverActiveId);
|
|
6656
6676
|
return () => {
|
|
6657
6677
|
notificationsCtx.unregisterViewingThread(serverActiveId);
|
|
6658
6678
|
};
|
|
6659
6679
|
}, [notificationsCtx, serverActiveId]);
|
|
6660
|
-
(0,
|
|
6680
|
+
(0, import_react32.useEffect)(() => {
|
|
6661
6681
|
if (!onThreadChange) return;
|
|
6662
6682
|
if (isControlled || isPersist) {
|
|
6663
6683
|
onThreadChange(activeThread);
|
|
@@ -6678,23 +6698,23 @@ function useChat(config) {
|
|
|
6678
6698
|
}
|
|
6679
6699
|
}
|
|
6680
6700
|
}, [activeThread, onThreadChange, isControlled, isPersist, memConversations, memActiveId, surface, subject]);
|
|
6681
|
-
const updateMemMessages = (0,
|
|
6701
|
+
const updateMemMessages = (0, import_react32.useCallback)((convId, msgs) => {
|
|
6682
6702
|
setMemConversations((prev) => prev.map((c) => c.id === convId ? { ...c, messages: msgs, updatedAt: Date.now() } : c).sort((a, b) => b.updatedAt - a.updatedAt));
|
|
6683
6703
|
}, []);
|
|
6684
|
-
const updateMemTitle = (0,
|
|
6704
|
+
const updateMemTitle = (0, import_react32.useCallback)((convId, t) => {
|
|
6685
6705
|
setMemConversations((prev) => prev.map((c) => c.id === convId ? { ...c, title: t, titleState: t ? "ready" : c.titleState, updatedAt: Date.now() } : c));
|
|
6686
6706
|
}, []);
|
|
6687
|
-
const setMemTitleState = (0,
|
|
6707
|
+
const setMemTitleState = (0, import_react32.useCallback)((convId, state) => {
|
|
6688
6708
|
setMemConversations((prev) => prev.map((c) => c.id === convId ? { ...c, titleState: state, updatedAt: Date.now() } : c));
|
|
6689
6709
|
}, []);
|
|
6690
|
-
const appendMemDelta = (0,
|
|
6710
|
+
const appendMemDelta = (0, import_react32.useCallback)((convId, msgId, delta) => {
|
|
6691
6711
|
setMemConversations((prev) => prev.map((c) => c.id === convId ? {
|
|
6692
6712
|
...c,
|
|
6693
6713
|
messages: c.messages.map((m) => m.id === msgId ? { ...m, content: `${m.content}${delta}` } : m),
|
|
6694
6714
|
updatedAt: Date.now()
|
|
6695
6715
|
} : c));
|
|
6696
6716
|
}, []);
|
|
6697
|
-
const animateTitle = (0,
|
|
6717
|
+
const animateTitle = (0, import_react32.useCallback)(async (convId, raw) => {
|
|
6698
6718
|
const safe = sanitizeTitle(raw);
|
|
6699
6719
|
setMemTitleState(convId, "loading");
|
|
6700
6720
|
updateMemTitle(convId, "");
|
|
@@ -6706,7 +6726,7 @@ function useChat(config) {
|
|
|
6706
6726
|
}
|
|
6707
6727
|
setMemTitleState(convId, "ready");
|
|
6708
6728
|
}, [setMemTitleState, updateMemTitle]);
|
|
6709
|
-
const ensureMemConversation = (0,
|
|
6729
|
+
const ensureMemConversation = (0, import_react32.useCallback)(() => {
|
|
6710
6730
|
if (memActiveId) return memActiveId;
|
|
6711
6731
|
const id = makeId("conv");
|
|
6712
6732
|
setMemConversations((prev) => [{
|
|
@@ -6719,7 +6739,7 @@ function useChat(config) {
|
|
|
6719
6739
|
setMemActiveId(id);
|
|
6720
6740
|
return id;
|
|
6721
6741
|
}, [memActiveId]);
|
|
6722
|
-
const sendMemoryMessage = (0,
|
|
6742
|
+
const sendMemoryMessage = (0, import_react32.useCallback)(async (content) => {
|
|
6723
6743
|
const convId = ensureMemConversation();
|
|
6724
6744
|
const userMsg = { id: makeId("msg"), role: "user", content };
|
|
6725
6745
|
const assistantMsg = { id: makeId("msg"), role: "assistant", content: "", model: activeModel };
|
|
@@ -6796,7 +6816,7 @@ function useChat(config) {
|
|
|
6796
6816
|
setMemTitleState,
|
|
6797
6817
|
onMessage
|
|
6798
6818
|
]);
|
|
6799
|
-
const sendPersistMessage = (0,
|
|
6819
|
+
const sendPersistMessage = (0, import_react32.useCallback)(async (content) => {
|
|
6800
6820
|
const userMsg = { id: makeId("msg"), role: "user", content, status: "complete" };
|
|
6801
6821
|
const assistantMsg = { id: makeId("msg"), role: "assistant", content: "", model: activeModel, status: "pending" };
|
|
6802
6822
|
setServerMessages((prev) => [...prev, userMsg, assistantMsg]);
|
|
@@ -6881,7 +6901,7 @@ function useChat(config) {
|
|
|
6881
6901
|
onMessage,
|
|
6882
6902
|
onThreadCreate
|
|
6883
6903
|
]);
|
|
6884
|
-
const sendMessage = (0,
|
|
6904
|
+
const sendMessage = (0, import_react32.useCallback)(async (rawText) => {
|
|
6885
6905
|
const content = (rawText ?? input).trim();
|
|
6886
6906
|
if (!content || isSubmitting) return;
|
|
6887
6907
|
setInput("");
|
|
@@ -6921,7 +6941,7 @@ function useChat(config) {
|
|
|
6921
6941
|
setIsSubmitting(false);
|
|
6922
6942
|
}
|
|
6923
6943
|
}, [input, isSubmitting, isControlled, isPersist, onSendMessage, activeId, sendPersistMessage, sendMemoryMessage, memActiveId]);
|
|
6924
|
-
const startNewChat = (0,
|
|
6944
|
+
const startNewChat = (0, import_react32.useCallback)(() => {
|
|
6925
6945
|
if (isControlled) {
|
|
6926
6946
|
onThreadSelect?.(null);
|
|
6927
6947
|
} else if (isPersist) {
|
|
@@ -6936,7 +6956,7 @@ function useChat(config) {
|
|
|
6936
6956
|
textareaRef.current?.focus();
|
|
6937
6957
|
});
|
|
6938
6958
|
}, [isControlled, isPersist, onThreadSelect]);
|
|
6939
|
-
const selectThread = (0,
|
|
6959
|
+
const selectThread = (0, import_react32.useCallback)((id) => {
|
|
6940
6960
|
if (isControlled) {
|
|
6941
6961
|
onThreadSelect?.(id);
|
|
6942
6962
|
} else if (isPersist) {
|
|
@@ -6947,7 +6967,7 @@ function useChat(config) {
|
|
|
6947
6967
|
setError(null);
|
|
6948
6968
|
}
|
|
6949
6969
|
}, [isControlled, isPersist, onThreadSelect]);
|
|
6950
|
-
const deleteThread = (0,
|
|
6970
|
+
const deleteThread = (0, import_react32.useCallback)(async (threadId) => {
|
|
6951
6971
|
if (isControlled) {
|
|
6952
6972
|
onThreadDelete?.(threadId);
|
|
6953
6973
|
return;
|
|
@@ -6966,12 +6986,12 @@ function useChat(config) {
|
|
|
6966
6986
|
if (memActiveId === threadId) setMemActiveId(null);
|
|
6967
6987
|
}
|
|
6968
6988
|
}, [isControlled, isPersist, serverActiveId, endpoint, memActiveId, onThreadDelete]);
|
|
6969
|
-
const startRename = (0,
|
|
6989
|
+
const startRename = (0, import_react32.useCallback)((threadId) => {
|
|
6970
6990
|
const item = displaySidebarItems.find((t) => t.id === threadId);
|
|
6971
6991
|
setRenamingId(threadId);
|
|
6972
6992
|
setRenameValue(item?.title ?? "");
|
|
6973
6993
|
}, [displaySidebarItems]);
|
|
6974
|
-
const submitRename = (0,
|
|
6994
|
+
const submitRename = (0, import_react32.useCallback)(async () => {
|
|
6975
6995
|
if (!renamingId || !renameValue.trim()) {
|
|
6976
6996
|
setRenamingId(null);
|
|
6977
6997
|
return;
|
|
@@ -7030,7 +7050,7 @@ function useChat(config) {
|
|
|
7030
7050
|
}
|
|
7031
7051
|
|
|
7032
7052
|
// src/react/chat/ModelSelector.tsx
|
|
7033
|
-
var
|
|
7053
|
+
var import_react33 = __toESM(require("react"));
|
|
7034
7054
|
function ModelOption({
|
|
7035
7055
|
provider,
|
|
7036
7056
|
isActive,
|
|
@@ -7038,14 +7058,14 @@ function ModelOption({
|
|
|
7038
7058
|
onSelect,
|
|
7039
7059
|
onCheckout
|
|
7040
7060
|
}) {
|
|
7041
|
-
const handleClick = (0,
|
|
7061
|
+
const handleClick = (0, import_react33.useCallback)(() => {
|
|
7042
7062
|
if (isLocked) {
|
|
7043
7063
|
void onCheckout({ plan: "pro" });
|
|
7044
7064
|
} else {
|
|
7045
7065
|
onSelect(provider.model);
|
|
7046
7066
|
}
|
|
7047
7067
|
}, [isLocked, onCheckout, onSelect, provider.model]);
|
|
7048
|
-
return /* @__PURE__ */
|
|
7068
|
+
return /* @__PURE__ */ import_react33.default.createElement(
|
|
7049
7069
|
"button",
|
|
7050
7070
|
{
|
|
7051
7071
|
"aria-selected": isActive,
|
|
@@ -7057,9 +7077,9 @@ function ModelOption({
|
|
|
7057
7077
|
title: isLocked ? "Add credits to unlock" : void 0,
|
|
7058
7078
|
type: "button"
|
|
7059
7079
|
},
|
|
7060
|
-
/* @__PURE__ */
|
|
7061
|
-
/* @__PURE__ */
|
|
7062
|
-
isLocked ? /* @__PURE__ */
|
|
7080
|
+
/* @__PURE__ */ import_react33.default.createElement("img", { alt: "", className: "brokr-model-logo", src: provider.logo }),
|
|
7081
|
+
/* @__PURE__ */ import_react33.default.createElement("span", { className: "brokr-model-option-label" }, provider.label),
|
|
7082
|
+
isLocked ? /* @__PURE__ */ import_react33.default.createElement("span", { className: "brokr-model-lock", "aria-hidden": "true" }, /* @__PURE__ */ import_react33.default.createElement(LockIcon, { size: 13 })) : null
|
|
7063
7083
|
);
|
|
7064
7084
|
}
|
|
7065
7085
|
function ModelSelector({
|
|
@@ -7068,22 +7088,22 @@ function ModelSelector({
|
|
|
7068
7088
|
availableProviders,
|
|
7069
7089
|
checkout
|
|
7070
7090
|
}) {
|
|
7071
|
-
const [selectorOpen, setSelectorOpen] = (0,
|
|
7072
|
-
const selectorRef = (0,
|
|
7091
|
+
const [selectorOpen, setSelectorOpen] = (0, import_react33.useState)(false);
|
|
7092
|
+
const selectorRef = (0, import_react33.useRef)(null);
|
|
7073
7093
|
const isAuto = activeModel === STACK_DEFAULT || !providers.some((p) => p.model === activeModel);
|
|
7074
|
-
const activeProvider = (0,
|
|
7094
|
+
const activeProvider = (0, import_react33.useMemo)(
|
|
7075
7095
|
() => isAuto ? void 0 : providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7076
7096
|
[activeModel, isAuto]
|
|
7077
7097
|
);
|
|
7078
|
-
const handleModelSelect = (0,
|
|
7098
|
+
const handleModelSelect = (0, import_react33.useCallback)((model) => {
|
|
7079
7099
|
setSelectedModel(model === STACK_DEFAULT ? null : model);
|
|
7080
7100
|
setSelectorOpen(false);
|
|
7081
7101
|
}, [setSelectedModel]);
|
|
7082
|
-
const handleAutoSelect = (0,
|
|
7102
|
+
const handleAutoSelect = (0, import_react33.useCallback)(() => {
|
|
7083
7103
|
setSelectedModel(null);
|
|
7084
7104
|
setSelectorOpen(false);
|
|
7085
7105
|
}, [setSelectedModel]);
|
|
7086
|
-
(0,
|
|
7106
|
+
(0, import_react33.useEffect)(() => {
|
|
7087
7107
|
if (!selectorOpen) return;
|
|
7088
7108
|
const onMouseDown = (e) => {
|
|
7089
7109
|
if (!selectorRef.current?.contains(e.target)) setSelectorOpen(false);
|
|
@@ -7098,10 +7118,10 @@ function ModelSelector({
|
|
|
7098
7118
|
document.removeEventListener("keydown", onKeyDown);
|
|
7099
7119
|
};
|
|
7100
7120
|
}, [selectorOpen]);
|
|
7101
|
-
const handleToggle = (0,
|
|
7121
|
+
const handleToggle = (0, import_react33.useCallback)(() => {
|
|
7102
7122
|
setSelectorOpen((v) => !v);
|
|
7103
7123
|
}, []);
|
|
7104
|
-
return /* @__PURE__ */
|
|
7124
|
+
return /* @__PURE__ */ import_react33.default.createElement("div", { className: "brokr-model-selector", ref: selectorRef }, /* @__PURE__ */ import_react33.default.createElement(
|
|
7105
7125
|
"button",
|
|
7106
7126
|
{
|
|
7107
7127
|
"aria-expanded": selectorOpen,
|
|
@@ -7110,10 +7130,10 @@ function ModelSelector({
|
|
|
7110
7130
|
onClick: handleToggle,
|
|
7111
7131
|
type: "button"
|
|
7112
7132
|
},
|
|
7113
|
-
activeProvider ? /* @__PURE__ */
|
|
7133
|
+
activeProvider ? /* @__PURE__ */ import_react33.default.createElement("img", { alt: "", className: "brokr-model-logo", src: activeProvider.logo }) : /* @__PURE__ */ import_react33.default.createElement("span", { className: "brokr-model-dot", style: { background: "#6B7280" } }),
|
|
7114
7134
|
isAuto ? "Auto" : activeProvider?.label ?? "Model",
|
|
7115
|
-
/* @__PURE__ */
|
|
7116
|
-
), selectorOpen ? /* @__PURE__ */
|
|
7135
|
+
/* @__PURE__ */ import_react33.default.createElement(ChevronDownIcon, { size: 13 })
|
|
7136
|
+
), selectorOpen ? /* @__PURE__ */ import_react33.default.createElement("div", { className: "brokr-model-dropdown", role: "listbox" }, /* @__PURE__ */ import_react33.default.createElement(
|
|
7117
7137
|
"button",
|
|
7118
7138
|
{
|
|
7119
7139
|
"aria-selected": isAuto,
|
|
@@ -7122,9 +7142,9 @@ function ModelSelector({
|
|
|
7122
7142
|
onClick: handleAutoSelect,
|
|
7123
7143
|
type: "button"
|
|
7124
7144
|
},
|
|
7125
|
-
/* @__PURE__ */
|
|
7126
|
-
/* @__PURE__ */
|
|
7127
|
-
), providers.map((p) => /* @__PURE__ */
|
|
7145
|
+
/* @__PURE__ */ import_react33.default.createElement("span", { className: "brokr-model-dot", style: { background: "#6B7280" } }),
|
|
7146
|
+
/* @__PURE__ */ import_react33.default.createElement("span", { className: "brokr-model-option-label" }, "Stack Default")
|
|
7147
|
+
), providers.map((p) => /* @__PURE__ */ import_react33.default.createElement(
|
|
7128
7148
|
ModelOption,
|
|
7129
7149
|
{
|
|
7130
7150
|
key: p.id,
|
|
@@ -7138,10 +7158,10 @@ function ModelSelector({
|
|
|
7138
7158
|
}
|
|
7139
7159
|
|
|
7140
7160
|
// src/react/chat/ThreadSidebar.tsx
|
|
7141
|
-
var
|
|
7161
|
+
var import_react34 = __toESM(require("react"));
|
|
7142
7162
|
function ThreadItemButton({ id, title, isActive, onSelect }) {
|
|
7143
|
-
const handleClick = (0,
|
|
7144
|
-
return /* @__PURE__ */
|
|
7163
|
+
const handleClick = (0, import_react34.useCallback)(() => onSelect(id), [id, onSelect]);
|
|
7164
|
+
return /* @__PURE__ */ import_react34.default.createElement(
|
|
7145
7165
|
"button",
|
|
7146
7166
|
{
|
|
7147
7167
|
className: "brokr-ai-chat-conversation",
|
|
@@ -7149,8 +7169,8 @@ function ThreadItemButton({ id, title, isActive, onSelect }) {
|
|
|
7149
7169
|
onClick: handleClick,
|
|
7150
7170
|
type: "button"
|
|
7151
7171
|
},
|
|
7152
|
-
/* @__PURE__ */
|
|
7153
|
-
/* @__PURE__ */
|
|
7172
|
+
/* @__PURE__ */ import_react34.default.createElement(MessageIcon, { size: 12 }),
|
|
7173
|
+
/* @__PURE__ */ import_react34.default.createElement("span", { className: "brokr-ai-chat-conversation-label" }, title)
|
|
7154
7174
|
);
|
|
7155
7175
|
}
|
|
7156
7176
|
var SIDEBAR_GROUP_ORDER = [
|
|
@@ -7196,11 +7216,11 @@ function ThreadSidebar() {
|
|
|
7196
7216
|
activeId,
|
|
7197
7217
|
selectThreadAndCloseSidebar
|
|
7198
7218
|
} = useChatState();
|
|
7199
|
-
const handleNewChat = (0,
|
|
7219
|
+
const handleNewChat = (0, import_react34.useCallback)(() => {
|
|
7200
7220
|
startNewChat();
|
|
7201
7221
|
closeSidebar();
|
|
7202
7222
|
}, [startNewChat, closeSidebar]);
|
|
7203
|
-
const handleRenameKeyDown = (0,
|
|
7223
|
+
const handleRenameKeyDown = (0, import_react34.useCallback)((e) => {
|
|
7204
7224
|
if (e.key === "Enter") {
|
|
7205
7225
|
e.preventDefault();
|
|
7206
7226
|
void submitRename();
|
|
@@ -7208,13 +7228,13 @@ function ThreadSidebar() {
|
|
|
7208
7228
|
if (e.key === "Escape") {
|
|
7209
7229
|
}
|
|
7210
7230
|
}, [submitRename]);
|
|
7211
|
-
const handleRenameBlur = (0,
|
|
7231
|
+
const handleRenameBlur = (0, import_react34.useCallback)(() => {
|
|
7212
7232
|
void submitRename();
|
|
7213
7233
|
}, [submitRename]);
|
|
7214
|
-
const handleRenameChange = (0,
|
|
7234
|
+
const handleRenameChange = (0, import_react34.useCallback)((e) => {
|
|
7215
7235
|
setRenameValue(e.target.value);
|
|
7216
7236
|
}, [setRenameValue]);
|
|
7217
|
-
const groupedSidebarItems = (0,
|
|
7237
|
+
const groupedSidebarItems = (0, import_react34.useMemo)(() => {
|
|
7218
7238
|
const grouped = /* @__PURE__ */ new Map();
|
|
7219
7239
|
for (const item of displaySidebarItems) {
|
|
7220
7240
|
const key = resolveSidebarDateGroup(item.updatedAt);
|
|
@@ -7231,14 +7251,14 @@ function ThreadSidebar() {
|
|
|
7231
7251
|
items: grouped.get(key) ?? []
|
|
7232
7252
|
})).filter((group) => group.items.length > 0);
|
|
7233
7253
|
}, [displaySidebarItems]);
|
|
7234
|
-
const content = (0,
|
|
7254
|
+
const content = (0, import_react34.useMemo)(() => {
|
|
7235
7255
|
if (threadsLoading && displaySidebarItems.length === 0) {
|
|
7236
|
-
return /* @__PURE__ */
|
|
7256
|
+
return /* @__PURE__ */ import_react34.default.createElement("div", { className: "brokr-ai-chat-sidebar-skeleton" }, /* @__PURE__ */ import_react34.default.createElement(Skeleton, { width: "75%", height: 14, radius: 6 }), /* @__PURE__ */ import_react34.default.createElement(Skeleton, { width: "60%", height: 14, radius: 6 }), /* @__PURE__ */ import_react34.default.createElement(Skeleton, { width: "85%", height: 14, radius: 6 }));
|
|
7237
7257
|
}
|
|
7238
7258
|
if (displaySidebarItems.length === 0) {
|
|
7239
|
-
return /* @__PURE__ */
|
|
7259
|
+
return /* @__PURE__ */ import_react34.default.createElement("div", { className: "brokr-ai-chat-sidebar-empty" }, /* @__PURE__ */ import_react34.default.createElement("p", { className: "brokr-ai-chat-sidebar-empty-text" }, "No conversations yet. Start one above."));
|
|
7240
7260
|
}
|
|
7241
|
-
return /* @__PURE__ */
|
|
7261
|
+
return /* @__PURE__ */ import_react34.default.createElement("div", { className: "brokr-ai-chat-sidebar-groups" }, groupedSidebarItems.map((group) => /* @__PURE__ */ import_react34.default.createElement("section", { className: "brokr-ai-chat-sidebar-group", key: group.key }, /* @__PURE__ */ import_react34.default.createElement("span", { className: "brokr-ai-chat-sidebar-kicker" }, group.label), /* @__PURE__ */ import_react34.default.createElement("div", { className: "brokr-ai-chat-conversations" }, group.items.map((item) => renamingId === item.id ? /* @__PURE__ */ import_react34.default.createElement(
|
|
7242
7262
|
"input",
|
|
7243
7263
|
{
|
|
7244
7264
|
autoFocus: true,
|
|
@@ -7249,7 +7269,7 @@ function ThreadSidebar() {
|
|
|
7249
7269
|
onKeyDown: handleRenameKeyDown,
|
|
7250
7270
|
value: renameValue
|
|
7251
7271
|
}
|
|
7252
|
-
) : /* @__PURE__ */
|
|
7272
|
+
) : /* @__PURE__ */ import_react34.default.createElement(
|
|
7253
7273
|
ThreadItemButton,
|
|
7254
7274
|
{
|
|
7255
7275
|
key: item.id,
|
|
@@ -7271,17 +7291,17 @@ function ThreadSidebar() {
|
|
|
7271
7291
|
handleRenameChange,
|
|
7272
7292
|
handleRenameKeyDown
|
|
7273
7293
|
]);
|
|
7274
|
-
return /* @__PURE__ */
|
|
7294
|
+
return /* @__PURE__ */ import_react34.default.createElement(import_react34.default.Fragment, null, /* @__PURE__ */ import_react34.default.createElement("button", { className: "brokr-ai-chat-sidebar-new-chat", onClick: handleNewChat, type: "button" }, /* @__PURE__ */ import_react34.default.createElement(MessageIcon, { size: 16 }), "New chat"), content);
|
|
7275
7295
|
}
|
|
7276
7296
|
|
|
7277
7297
|
// src/react/chat/MessagePane.tsx
|
|
7278
|
-
var
|
|
7298
|
+
var import_react37 = __toESM(require("react"));
|
|
7279
7299
|
|
|
7280
7300
|
// src/react/chat/MessageBubble.tsx
|
|
7281
|
-
var
|
|
7301
|
+
var import_react36 = __toESM(require("react"));
|
|
7282
7302
|
|
|
7283
7303
|
// src/react/chat/MarkdownRenderer.tsx
|
|
7284
|
-
var
|
|
7304
|
+
var import_react35 = __toESM(require("react"));
|
|
7285
7305
|
function parseInline(text) {
|
|
7286
7306
|
const nodes = [];
|
|
7287
7307
|
let remaining = text;
|
|
@@ -7289,25 +7309,25 @@ function parseInline(text) {
|
|
|
7289
7309
|
while (remaining) {
|
|
7290
7310
|
const codeMatch = remaining.match(/^`([^`]+)`/);
|
|
7291
7311
|
if (codeMatch) {
|
|
7292
|
-
nodes.push(/* @__PURE__ */
|
|
7312
|
+
nodes.push(/* @__PURE__ */ import_react35.default.createElement("code", { className: "brokr-md-inline-code", key: key++ }, codeMatch[1]));
|
|
7293
7313
|
remaining = remaining.slice(codeMatch[0].length);
|
|
7294
7314
|
continue;
|
|
7295
7315
|
}
|
|
7296
7316
|
const boldMatch = remaining.match(/^\*\*(.+?)\*\*/);
|
|
7297
7317
|
if (boldMatch) {
|
|
7298
|
-
nodes.push(/* @__PURE__ */
|
|
7318
|
+
nodes.push(/* @__PURE__ */ import_react35.default.createElement("strong", { key: key++ }, boldMatch[1]));
|
|
7299
7319
|
remaining = remaining.slice(boldMatch[0].length);
|
|
7300
7320
|
continue;
|
|
7301
7321
|
}
|
|
7302
7322
|
const strikethroughMatch = remaining.match(/^~~(.+?)~~/);
|
|
7303
7323
|
if (strikethroughMatch) {
|
|
7304
|
-
nodes.push(/* @__PURE__ */
|
|
7324
|
+
nodes.push(/* @__PURE__ */ import_react35.default.createElement("del", { key: key++ }, strikethroughMatch[1]));
|
|
7305
7325
|
remaining = remaining.slice(strikethroughMatch[0].length);
|
|
7306
7326
|
continue;
|
|
7307
7327
|
}
|
|
7308
7328
|
const italicMatch = remaining.match(/^\*(.+?)\*/);
|
|
7309
7329
|
if (italicMatch) {
|
|
7310
|
-
nodes.push(/* @__PURE__ */
|
|
7330
|
+
nodes.push(/* @__PURE__ */ import_react35.default.createElement("em", { key: key++ }, italicMatch[1]));
|
|
7311
7331
|
remaining = remaining.slice(italicMatch[0].length);
|
|
7312
7332
|
continue;
|
|
7313
7333
|
}
|
|
@@ -7316,7 +7336,7 @@ function parseInline(text) {
|
|
|
7316
7336
|
const rawHref = linkMatch[2];
|
|
7317
7337
|
const isSafe = /^https?:\/\//i.test(rawHref) || /^mailto:/i.test(rawHref);
|
|
7318
7338
|
nodes.push(
|
|
7319
|
-
/* @__PURE__ */
|
|
7339
|
+
/* @__PURE__ */ import_react35.default.createElement("a", { className: "brokr-md-link", href: isSafe ? rawHref : "#", key: key++, rel: "noopener noreferrer", target: "_blank" }, linkMatch[1])
|
|
7320
7340
|
);
|
|
7321
7341
|
remaining = remaining.slice(linkMatch[0].length);
|
|
7322
7342
|
continue;
|
|
@@ -7337,10 +7357,10 @@ function parseInline(text) {
|
|
|
7337
7357
|
return nodes;
|
|
7338
7358
|
}
|
|
7339
7359
|
function CodeBlock({ code, language }) {
|
|
7340
|
-
const handleCopy = (0,
|
|
7360
|
+
const handleCopy = (0, import_react35.useCallback)(() => {
|
|
7341
7361
|
void navigator.clipboard?.writeText(code);
|
|
7342
7362
|
}, [code]);
|
|
7343
|
-
return /* @__PURE__ */
|
|
7363
|
+
return /* @__PURE__ */ import_react35.default.createElement("div", { className: "brokr-md-codeblock" }, /* @__PURE__ */ import_react35.default.createElement("div", { className: "brokr-md-codeblock-header" }, /* @__PURE__ */ import_react35.default.createElement("span", { className: "brokr-md-codeblock-lang" }, language || "code"), /* @__PURE__ */ import_react35.default.createElement(
|
|
7344
7364
|
"button",
|
|
7345
7365
|
{
|
|
7346
7366
|
"aria-label": "Copy code",
|
|
@@ -7348,9 +7368,9 @@ function CodeBlock({ code, language }) {
|
|
|
7348
7368
|
onClick: handleCopy,
|
|
7349
7369
|
type: "button"
|
|
7350
7370
|
},
|
|
7351
|
-
/* @__PURE__ */
|
|
7371
|
+
/* @__PURE__ */ import_react35.default.createElement(CopyIcon, { size: 13 }),
|
|
7352
7372
|
"Copy"
|
|
7353
|
-
)), /* @__PURE__ */
|
|
7373
|
+
)), /* @__PURE__ */ import_react35.default.createElement("pre", { className: "brokr-md-codeblock-pre" }, /* @__PURE__ */ import_react35.default.createElement("code", null, code)));
|
|
7354
7374
|
}
|
|
7355
7375
|
function looksLikeTableSeparator(line) {
|
|
7356
7376
|
const trimmed = line.trim();
|
|
@@ -7448,48 +7468,48 @@ function renderBlocks(blocks) {
|
|
|
7448
7468
|
return blocks.map((block, idx) => {
|
|
7449
7469
|
switch (block.type) {
|
|
7450
7470
|
case "code":
|
|
7451
|
-
return /* @__PURE__ */
|
|
7471
|
+
return /* @__PURE__ */ import_react35.default.createElement(CodeBlock, { code: block.content, key: idx, language: block.language });
|
|
7452
7472
|
case "divider":
|
|
7453
|
-
return /* @__PURE__ */
|
|
7473
|
+
return /* @__PURE__ */ import_react35.default.createElement("hr", { className: "brokr-md-divider", key: idx });
|
|
7454
7474
|
case "heading": {
|
|
7455
7475
|
const Tag = `h${Math.min(block.level ?? 3, 6)}`;
|
|
7456
|
-
return /* @__PURE__ */
|
|
7476
|
+
return /* @__PURE__ */ import_react35.default.createElement(Tag, { className: `brokr-md-heading brokr-md-h${block.level}`, key: idx }, parseInline(block.content));
|
|
7457
7477
|
}
|
|
7458
7478
|
case "list":
|
|
7459
7479
|
if (block.ordered) {
|
|
7460
|
-
return /* @__PURE__ */
|
|
7480
|
+
return /* @__PURE__ */ import_react35.default.createElement("ol", { className: "brokr-md-list brokr-md-list-ordered", key: idx }, block.items?.map((item, li) => /* @__PURE__ */ import_react35.default.createElement("li", { key: li }, parseInline(item))));
|
|
7461
7481
|
}
|
|
7462
|
-
return /* @__PURE__ */
|
|
7482
|
+
return /* @__PURE__ */ import_react35.default.createElement("ul", { className: "brokr-md-list brokr-md-list-unordered", key: idx }, block.items?.map((item, li) => /* @__PURE__ */ import_react35.default.createElement("li", { key: li }, parseInline(item))));
|
|
7463
7483
|
case "quote":
|
|
7464
|
-
return /* @__PURE__ */
|
|
7484
|
+
return /* @__PURE__ */ import_react35.default.createElement("blockquote", { className: "brokr-md-quote", key: idx }, block.items?.map((line, lineIndex) => /* @__PURE__ */ import_react35.default.createElement("p", { className: "brokr-md-quote-line", key: lineIndex }, parseInline(line))));
|
|
7465
7485
|
case "table":
|
|
7466
|
-
return /* @__PURE__ */
|
|
7486
|
+
return /* @__PURE__ */ import_react35.default.createElement("div", { className: "brokr-md-table-wrap", key: idx }, /* @__PURE__ */ import_react35.default.createElement("table", { className: "brokr-md-table" }, /* @__PURE__ */ import_react35.default.createElement("thead", null, /* @__PURE__ */ import_react35.default.createElement("tr", null, block.headers?.map((header, headerIndex) => /* @__PURE__ */ import_react35.default.createElement("th", { key: headerIndex }, parseInline(header))))), /* @__PURE__ */ import_react35.default.createElement("tbody", null, block.rows?.map((row, rowIndex) => /* @__PURE__ */ import_react35.default.createElement("tr", { key: rowIndex }, row.map((cell, cellIndex) => /* @__PURE__ */ import_react35.default.createElement("td", { key: cellIndex }, parseInline(cell))))))));
|
|
7467
7487
|
case "paragraph":
|
|
7468
7488
|
default:
|
|
7469
|
-
return /* @__PURE__ */
|
|
7489
|
+
return /* @__PURE__ */ import_react35.default.createElement("p", { className: "brokr-md-paragraph", key: idx }, parseInline(block.content));
|
|
7470
7490
|
}
|
|
7471
7491
|
});
|
|
7472
7492
|
}
|
|
7473
7493
|
function MarkdownRenderer({ content }) {
|
|
7474
|
-
const rendered = (0,
|
|
7494
|
+
const rendered = (0, import_react35.useMemo)(() => {
|
|
7475
7495
|
if (!content) return null;
|
|
7476
7496
|
const blocks = parseBlocks(content);
|
|
7477
7497
|
return renderBlocks(blocks);
|
|
7478
7498
|
}, [content]);
|
|
7479
|
-
return /* @__PURE__ */
|
|
7499
|
+
return /* @__PURE__ */ import_react35.default.createElement("div", { className: "brokr-md" }, rendered);
|
|
7480
7500
|
}
|
|
7481
7501
|
|
|
7482
7502
|
// src/react/chat/MessageBubble.tsx
|
|
7483
7503
|
function MessageBubble({ message, isTyping, user }) {
|
|
7484
|
-
const handleCopy = (0,
|
|
7504
|
+
const handleCopy = (0, import_react36.useCallback)(() => {
|
|
7485
7505
|
navigator.clipboard.writeText(message.content).catch(() => {
|
|
7486
7506
|
});
|
|
7487
7507
|
}, [message.content]);
|
|
7488
7508
|
if (message.role === "user") {
|
|
7489
|
-
return /* @__PURE__ */
|
|
7509
|
+
return /* @__PURE__ */ import_react36.default.createElement("article", { className: "brokr-ai-chat-message", "data-role": "user" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-message-row", "data-role": "user" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-message-bubble" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-message-content brokr-ai-chat-message-content-user" }, message.content)), /* @__PURE__ */ import_react36.default.createElement(Avatar, { email: user?.email, name: user?.name, src: user?.image })));
|
|
7490
7510
|
}
|
|
7491
7511
|
const mp = message.model ? resolveProviderByModel(message.model) : null;
|
|
7492
|
-
return /* @__PURE__ */
|
|
7512
|
+
return /* @__PURE__ */ import_react36.default.createElement("article", { className: "brokr-ai-chat-message", "data-role": "assistant" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-message-row", "data-role": "assistant" }, mp ? /* @__PURE__ */ import_react36.default.createElement("img", { alt: mp.label, className: "brokr-ai-chat-model-avatar", src: mp.logo }) : null, isTyping ? /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-typing", "aria-label": "AI is typing" }, /* @__PURE__ */ import_react36.default.createElement("span", null), /* @__PURE__ */ import_react36.default.createElement("span", null), /* @__PURE__ */ import_react36.default.createElement("span", null)) : message.status === "error" && !message.content ? /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-message-wrap" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-message-error" }, "Something went wrong generating a reply.")) : /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-message-wrap" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "brokr-ai-chat-message-content" }, /* @__PURE__ */ import_react36.default.createElement(MarkdownRenderer, { content: message.content })), message.content ? /* @__PURE__ */ import_react36.default.createElement(
|
|
7493
7513
|
"button",
|
|
7494
7514
|
{
|
|
7495
7515
|
"aria-label": "Copy message",
|
|
@@ -7497,16 +7517,16 @@ function MessageBubble({ message, isTyping, user }) {
|
|
|
7497
7517
|
onClick: handleCopy,
|
|
7498
7518
|
type: "button"
|
|
7499
7519
|
},
|
|
7500
|
-
/* @__PURE__ */
|
|
7520
|
+
/* @__PURE__ */ import_react36.default.createElement(CopyIcon, { size: 13 })
|
|
7501
7521
|
) : null)));
|
|
7502
7522
|
}
|
|
7503
7523
|
|
|
7504
7524
|
// src/react/chat/MessagePane.tsx
|
|
7505
7525
|
function StarterPromptButton({ prompt, onSend }) {
|
|
7506
|
-
const handleClick = (0,
|
|
7526
|
+
const handleClick = (0, import_react37.useCallback)(() => {
|
|
7507
7527
|
void onSend(prompt);
|
|
7508
7528
|
}, [prompt, onSend]);
|
|
7509
|
-
return /* @__PURE__ */
|
|
7529
|
+
return /* @__PURE__ */ import_react37.default.createElement("button", { className: "brokr-ai-chat-starter", onClick: handleClick, type: "button" }, prompt);
|
|
7510
7530
|
}
|
|
7511
7531
|
function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
7512
7532
|
const {
|
|
@@ -7524,10 +7544,10 @@ function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
|
7524
7544
|
user
|
|
7525
7545
|
} = useChatState();
|
|
7526
7546
|
const isEmpty = displayMessages.length === 0;
|
|
7527
|
-
const messageElements = (0,
|
|
7547
|
+
const messageElements = (0, import_react37.useMemo)(() => {
|
|
7528
7548
|
return visibleMessages.map((message, index) => {
|
|
7529
7549
|
const isTyping = message.role === "assistant" && !message.content && (isSubmitting && index === visibleMessages.length - 1 || message.status === "pending" || message.status === "streaming");
|
|
7530
|
-
return /* @__PURE__ */
|
|
7550
|
+
return /* @__PURE__ */ import_react37.default.createElement(
|
|
7531
7551
|
MessageBubble,
|
|
7532
7552
|
{
|
|
7533
7553
|
isTyping,
|
|
@@ -7538,16 +7558,16 @@ function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
|
|
|
7538
7558
|
);
|
|
7539
7559
|
});
|
|
7540
7560
|
}, [visibleMessages, isSubmitting, user]);
|
|
7541
|
-
return /* @__PURE__ */
|
|
7561
|
+
return /* @__PURE__ */ import_react37.default.createElement("div", { className: "brokr-ai-chat-thread", "data-empty": isEmpty, ref: scrollContainerRef }, isEmpty ? /* @__PURE__ */ import_react37.default.createElement("div", { className: "brokr-ai-chat-empty" }, /* @__PURE__ */ import_react37.default.createElement(SparkIcon, { size: 28 }), /* @__PURE__ */ import_react37.default.createElement("h2", { className: "brokr-title" }, emptyTitle), subtitle ?? emptyCopy ? /* @__PURE__ */ import_react37.default.createElement("p", { className: "brokr-copy" }, subtitle ?? emptyCopy) : null, starterPrompts.length > 0 ? /* @__PURE__ */ import_react37.default.createElement("div", { className: "brokr-ai-chat-starters" }, starterPrompts.map((sp) => /* @__PURE__ */ import_react37.default.createElement(StarterPromptButton, { key: sp, prompt: sp, onSend: sendMessage }))) : null) : /* @__PURE__ */ import_react37.default.createElement("div", { className: "brokr-ai-chat-thread-inner" }, (isPersist ? hasMoreMessages : memHasMore) ? /* @__PURE__ */ import_react37.default.createElement("div", { ref: sentinelRef, className: "brokr-ai-chat-sentinel" }, loadingOlder ? /* @__PURE__ */ import_react37.default.createElement("div", { style: { display: "flex", justifyContent: "center", padding: "0.5rem" } }, /* @__PURE__ */ import_react37.default.createElement(Skeleton, { width: 120, height: 12, radius: 6 })) : null) : null, messageElements, /* @__PURE__ */ import_react37.default.createElement("div", { ref: bottomRef })));
|
|
7542
7562
|
}
|
|
7543
7563
|
|
|
7544
7564
|
// src/react/chat/ChatInput.tsx
|
|
7545
|
-
var
|
|
7565
|
+
var import_react38 = __toESM(require("react"));
|
|
7546
7566
|
function CommandButton({ cmd, chatContext }) {
|
|
7547
|
-
const handleClick = (0,
|
|
7567
|
+
const handleClick = (0, import_react38.useCallback)(() => {
|
|
7548
7568
|
void cmd.run(chatContext);
|
|
7549
7569
|
}, [cmd, chatContext]);
|
|
7550
|
-
return /* @__PURE__ */
|
|
7570
|
+
return /* @__PURE__ */ import_react38.default.createElement("button", { className: "brokr-ai-chat-sidebar-button", key: cmd.id, onClick: handleClick, type: "button" }, cmd.text);
|
|
7551
7571
|
}
|
|
7552
7572
|
function ChatInput() {
|
|
7553
7573
|
const {
|
|
@@ -7560,26 +7580,26 @@ function ChatInput() {
|
|
|
7560
7580
|
composerCommands,
|
|
7561
7581
|
chatContext
|
|
7562
7582
|
} = useChatState();
|
|
7563
|
-
(0,
|
|
7583
|
+
(0, import_react38.useEffect)(() => {
|
|
7564
7584
|
const el = textareaRef.current;
|
|
7565
7585
|
if (!el) return;
|
|
7566
7586
|
el.style.height = "auto";
|
|
7567
7587
|
el.style.height = `${Math.min(el.scrollHeight, 168)}px`;
|
|
7568
7588
|
}, [input, textareaRef]);
|
|
7569
|
-
const handleSubmit = (0,
|
|
7589
|
+
const handleSubmit = (0, import_react38.useCallback)((e) => {
|
|
7570
7590
|
e.preventDefault();
|
|
7571
7591
|
void sendMessage();
|
|
7572
7592
|
}, [sendMessage]);
|
|
7573
|
-
const handleKeyDown = (0,
|
|
7593
|
+
const handleKeyDown = (0, import_react38.useCallback)((e) => {
|
|
7574
7594
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
7575
7595
|
e.preventDefault();
|
|
7576
7596
|
void sendMessage();
|
|
7577
7597
|
}
|
|
7578
7598
|
}, [sendMessage]);
|
|
7579
|
-
const handleChange = (0,
|
|
7599
|
+
const handleChange = (0, import_react38.useCallback)((e) => {
|
|
7580
7600
|
setInput(e.target.value);
|
|
7581
7601
|
}, [setInput]);
|
|
7582
|
-
return /* @__PURE__ */
|
|
7602
|
+
return /* @__PURE__ */ import_react38.default.createElement("form", { className: "brokr-ai-chat-input-area", onSubmit: handleSubmit }, /* @__PURE__ */ import_react38.default.createElement("div", { className: "brokr-ai-chat-input-container" }, composerCommands.length > 0 ? /* @__PURE__ */ import_react38.default.createElement("div", { className: "brokr-ai-chat-composer-actions" }, composerCommands.map((cmd) => /* @__PURE__ */ import_react38.default.createElement(CommandButton, { key: cmd.id, cmd, chatContext }))) : null, /* @__PURE__ */ import_react38.default.createElement("div", { className: "brokr-ai-chat-input-row" }, /* @__PURE__ */ import_react38.default.createElement(
|
|
7583
7603
|
"textarea",
|
|
7584
7604
|
{
|
|
7585
7605
|
className: "brokr-ai-chat-textarea",
|
|
@@ -7590,15 +7610,15 @@ function ChatInput() {
|
|
|
7590
7610
|
rows: 1,
|
|
7591
7611
|
value: input
|
|
7592
7612
|
}
|
|
7593
|
-
), /* @__PURE__ */
|
|
7613
|
+
), /* @__PURE__ */ import_react38.default.createElement(
|
|
7594
7614
|
"button",
|
|
7595
7615
|
{
|
|
7596
7616
|
className: "brokr-ai-chat-send",
|
|
7597
7617
|
disabled: isSubmitting || !input.trim(),
|
|
7598
7618
|
type: "submit"
|
|
7599
7619
|
},
|
|
7600
|
-
isSubmitting ? /* @__PURE__ */
|
|
7601
|
-
))), error ? /* @__PURE__ */
|
|
7620
|
+
isSubmitting ? /* @__PURE__ */ import_react38.default.createElement(SparkIcon, { size: 16 }) : /* @__PURE__ */ import_react38.default.createElement(ArrowRightIcon, { size: 16 })
|
|
7621
|
+
))), error ? /* @__PURE__ */ import_react38.default.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null);
|
|
7602
7622
|
}
|
|
7603
7623
|
|
|
7604
7624
|
// src/react/chat/AIChat.tsx
|
|
@@ -7644,19 +7664,19 @@ function AIChat(inlineProps) {
|
|
|
7644
7664
|
const headerVisible = variant !== 3;
|
|
7645
7665
|
const threadMenuVisible = threadMenuProp !== void 0 ? threadMenuProp : variant !== 3;
|
|
7646
7666
|
const modelSelectorVisible = (modelSelector !== void 0 ? modelSelector : true) && !modelProp;
|
|
7647
|
-
const [userSelectedModel, setUserSelectedModel] = (0,
|
|
7667
|
+
const [userSelectedModel, setUserSelectedModel] = (0, import_react39.useState)(null);
|
|
7648
7668
|
const explicitModel = modelProp ?? userSelectedModel ?? void 0;
|
|
7649
7669
|
const displayModel = explicitModel ?? providers.find((p) => p.free)?.model ?? providers[0]?.model ?? "";
|
|
7650
7670
|
const activeModel = explicitModel ?? STACK_DEFAULT;
|
|
7651
|
-
const activeProvider = (0,
|
|
7671
|
+
const activeProvider = (0, import_react39.useMemo)(
|
|
7652
7672
|
() => activeModel === STACK_DEFAULT ? void 0 : providers.find((p) => p.model === activeModel) ?? providers[0],
|
|
7653
7673
|
[activeModel]
|
|
7654
7674
|
);
|
|
7655
|
-
const hasBalance = (0,
|
|
7675
|
+
const hasBalance = (0, import_react39.useMemo)(
|
|
7656
7676
|
() => billing === null || billing.balanceCents === null || billing.balanceCents > 0 || billing.hasPaymentMethod,
|
|
7657
7677
|
[billing]
|
|
7658
7678
|
);
|
|
7659
|
-
const availableProviders = (0,
|
|
7679
|
+
const availableProviders = (0, import_react39.useMemo)(
|
|
7660
7680
|
() => hasBalance ? providers : providers.filter((p) => p.free),
|
|
7661
7681
|
[hasBalance]
|
|
7662
7682
|
);
|
|
@@ -7682,8 +7702,8 @@ function AIChat(inlineProps) {
|
|
|
7682
7702
|
onThreadChange,
|
|
7683
7703
|
userId: user?.id
|
|
7684
7704
|
});
|
|
7685
|
-
const shellRef = (0,
|
|
7686
|
-
(0,
|
|
7705
|
+
const shellRef = (0, import_react39.useRef)(null);
|
|
7706
|
+
(0, import_react39.useEffect)(() => {
|
|
7687
7707
|
const el = shellRef.current;
|
|
7688
7708
|
if (!el) return;
|
|
7689
7709
|
let raf = 0;
|
|
@@ -7709,23 +7729,23 @@ function AIChat(inlineProps) {
|
|
|
7709
7729
|
ro?.disconnect();
|
|
7710
7730
|
};
|
|
7711
7731
|
}, []);
|
|
7712
|
-
const [sidebarOpen, setSidebarOpen] = (0,
|
|
7713
|
-
const [threadMenuOpenId, setThreadMenuOpenId] = (0,
|
|
7714
|
-
const threadMenuRef = (0,
|
|
7715
|
-
const closeSidebar = (0,
|
|
7716
|
-
const selectThreadAndCloseSidebar = (0,
|
|
7732
|
+
const [sidebarOpen, setSidebarOpen] = (0, import_react39.useState)(false);
|
|
7733
|
+
const [threadMenuOpenId, setThreadMenuOpenId] = (0, import_react39.useState)(null);
|
|
7734
|
+
const threadMenuRef = (0, import_react39.useRef)(null);
|
|
7735
|
+
const closeSidebar = (0, import_react39.useCallback)(() => setSidebarOpen(false), []);
|
|
7736
|
+
const selectThreadAndCloseSidebar = (0, import_react39.useCallback)((id) => {
|
|
7717
7737
|
chat.selectThread(id);
|
|
7718
7738
|
setSidebarOpen(false);
|
|
7719
7739
|
}, [chat.selectThread]);
|
|
7720
|
-
const handleCopy = (0,
|
|
7740
|
+
const handleCopy = (0, import_react39.useCallback)((content) => {
|
|
7721
7741
|
navigator.clipboard.writeText(content).catch(() => {
|
|
7722
7742
|
});
|
|
7723
7743
|
}, []);
|
|
7724
|
-
const handleStartRename = (0,
|
|
7744
|
+
const handleStartRename = (0, import_react39.useCallback)((threadId) => {
|
|
7725
7745
|
setThreadMenuOpenId(null);
|
|
7726
7746
|
chat.startRename(threadId);
|
|
7727
7747
|
}, [chat.startRename]);
|
|
7728
|
-
(0,
|
|
7748
|
+
(0, import_react39.useEffect)(() => {
|
|
7729
7749
|
if (!threadMenuOpenId) return;
|
|
7730
7750
|
const onMouseDown = (e) => {
|
|
7731
7751
|
if (!threadMenuRef.current?.contains(e.target)) setThreadMenuOpenId(null);
|
|
@@ -7733,7 +7753,7 @@ function AIChat(inlineProps) {
|
|
|
7733
7753
|
document.addEventListener("mousedown", onMouseDown);
|
|
7734
7754
|
return () => document.removeEventListener("mousedown", onMouseDown);
|
|
7735
7755
|
}, [threadMenuOpenId]);
|
|
7736
|
-
(0,
|
|
7756
|
+
(0, import_react39.useEffect)(() => {
|
|
7737
7757
|
if (!sidebarOpen) return;
|
|
7738
7758
|
const onKeyDown = (e) => {
|
|
7739
7759
|
if (e.key === "Escape") setSidebarOpen(false);
|
|
@@ -7741,10 +7761,10 @@ function AIChat(inlineProps) {
|
|
|
7741
7761
|
document.addEventListener("keydown", onKeyDown);
|
|
7742
7762
|
return () => document.removeEventListener("keydown", onKeyDown);
|
|
7743
7763
|
}, [sidebarOpen]);
|
|
7744
|
-
const finalTitle = (0,
|
|
7764
|
+
const finalTitle = (0, import_react39.useMemo)(() => {
|
|
7745
7765
|
return chat.renderedTitle || title;
|
|
7746
7766
|
}, [chat.renderedTitle, title]);
|
|
7747
|
-
const chatContext = (0,
|
|
7767
|
+
const chatContext = (0, import_react39.useMemo)(() => ({
|
|
7748
7768
|
thread: chat.activeThread,
|
|
7749
7769
|
messages: chat.displayMessages,
|
|
7750
7770
|
user,
|
|
@@ -7752,19 +7772,19 @@ function AIChat(inlineProps) {
|
|
|
7752
7772
|
newThread: chat.startNewChat,
|
|
7753
7773
|
setThread: chat.selectThread
|
|
7754
7774
|
}), [chat.activeThread, chat.displayMessages, user, chat.sendMessage, chat.startNewChat, chat.selectThread]);
|
|
7755
|
-
const headerCommands = (0,
|
|
7775
|
+
const headerCommands = (0, import_react39.useMemo)(
|
|
7756
7776
|
() => commands.filter((c) => c.location === "header" && (!c.show || c.show(chatContext))),
|
|
7757
7777
|
[commands, chatContext]
|
|
7758
7778
|
);
|
|
7759
|
-
const composerCommands = (0,
|
|
7779
|
+
const composerCommands = (0, import_react39.useMemo)(
|
|
7760
7780
|
() => commands.filter((c) => c.location === "composer" && (!c.show || c.show(chatContext))),
|
|
7761
7781
|
[commands, chatContext]
|
|
7762
7782
|
);
|
|
7763
|
-
const threadMenuCommands = (0,
|
|
7783
|
+
const threadMenuCommands = (0, import_react39.useMemo)(
|
|
7764
7784
|
() => commands.filter((c) => c.location === "threadMenu" && (!c.show || c.show(chatContext))),
|
|
7765
7785
|
[commands, chatContext]
|
|
7766
7786
|
);
|
|
7767
|
-
const chatState = (0,
|
|
7787
|
+
const chatState = (0, import_react39.useMemo)(() => ({
|
|
7768
7788
|
displayMessages: chat.displayMessages,
|
|
7769
7789
|
visibleMessages: chat.displayMessages,
|
|
7770
7790
|
isSubmitting: chat.isSubmitting,
|
|
@@ -7831,16 +7851,16 @@ function AIChat(inlineProps) {
|
|
|
7831
7851
|
checkout,
|
|
7832
7852
|
user
|
|
7833
7853
|
]);
|
|
7834
|
-
return /* @__PURE__ */
|
|
7854
|
+
return /* @__PURE__ */ import_react39.default.createElement(ChatProvider, { value: chatState }, /* @__PURE__ */ import_react39.default.createElement(
|
|
7835
7855
|
"section",
|
|
7836
7856
|
{
|
|
7837
7857
|
className: "brokr-ai-chat-shell",
|
|
7838
7858
|
"data-sidebar": sidebarVisible,
|
|
7839
7859
|
ref: shellRef
|
|
7840
7860
|
},
|
|
7841
|
-
sidebarVisible ? /* @__PURE__ */
|
|
7842
|
-
sidebarOpen ? /* @__PURE__ */
|
|
7843
|
-
/* @__PURE__ */
|
|
7861
|
+
sidebarVisible ? /* @__PURE__ */ import_react39.default.createElement("aside", { className: "brokr-ai-chat-sidebar brokr-ai-chat-sidebar-desktop" }, /* @__PURE__ */ import_react39.default.createElement(ThreadSidebar, null)) : null,
|
|
7862
|
+
sidebarOpen ? /* @__PURE__ */ import_react39.default.createElement(import_react39.default.Fragment, null, /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-ai-chat-drawer-backdrop", onClick: closeSidebar }), /* @__PURE__ */ import_react39.default.createElement("aside", { className: "brokr-ai-chat-drawer" }, /* @__PURE__ */ import_react39.default.createElement(ThreadSidebar, null))) : null,
|
|
7863
|
+
/* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-ai-chat-stage", "data-noheader": !headerVisible }, headerVisible ? /* @__PURE__ */ import_react39.default.createElement(
|
|
7844
7864
|
ChatHeader,
|
|
7845
7865
|
{
|
|
7846
7866
|
activeId: chat.activeId,
|
|
@@ -7861,7 +7881,7 @@ function AIChat(inlineProps) {
|
|
|
7861
7881
|
startRename: chatState.startRename,
|
|
7862
7882
|
deleteThread: chat.deleteThread
|
|
7863
7883
|
}
|
|
7864
|
-
) : null, /* @__PURE__ */
|
|
7884
|
+
) : null, /* @__PURE__ */ import_react39.default.createElement(
|
|
7865
7885
|
MessagePane,
|
|
7866
7886
|
{
|
|
7867
7887
|
starterPrompts,
|
|
@@ -7869,21 +7889,21 @@ function AIChat(inlineProps) {
|
|
|
7869
7889
|
emptyCopy,
|
|
7870
7890
|
subtitle
|
|
7871
7891
|
}
|
|
7872
|
-
), /* @__PURE__ */
|
|
7892
|
+
), /* @__PURE__ */ import_react39.default.createElement(ChatInput, null))
|
|
7873
7893
|
));
|
|
7874
7894
|
}
|
|
7875
7895
|
function CommandButton2({ cmd, chatContext }) {
|
|
7876
|
-
const handleClick = (0,
|
|
7896
|
+
const handleClick = (0, import_react39.useCallback)(() => {
|
|
7877
7897
|
void cmd.run(chatContext);
|
|
7878
7898
|
}, [cmd, chatContext]);
|
|
7879
|
-
return /* @__PURE__ */
|
|
7899
|
+
return /* @__PURE__ */ import_react39.default.createElement("button", { className: "brokr-ai-chat-sidebar-button", onClick: handleClick, type: "button" }, cmd.text);
|
|
7880
7900
|
}
|
|
7881
7901
|
function MenuCommandItem({ cmd, chatContext, onClose }) {
|
|
7882
|
-
const handleClick = (0,
|
|
7902
|
+
const handleClick = (0, import_react39.useCallback)(() => {
|
|
7883
7903
|
onClose();
|
|
7884
7904
|
void cmd.run(chatContext);
|
|
7885
7905
|
}, [cmd, chatContext, onClose]);
|
|
7886
|
-
return /* @__PURE__ */
|
|
7906
|
+
return /* @__PURE__ */ import_react39.default.createElement("button", { className: "brokr-ai-chat-thread-dropdown-item", onClick: handleClick, type: "button" }, cmd.text);
|
|
7887
7907
|
}
|
|
7888
7908
|
function ChatHeader({
|
|
7889
7909
|
activeId,
|
|
@@ -7904,21 +7924,21 @@ function ChatHeader({
|
|
|
7904
7924
|
startRename,
|
|
7905
7925
|
deleteThread
|
|
7906
7926
|
}) {
|
|
7907
|
-
const handleOpenSidebar = (0,
|
|
7908
|
-
const handleToggleMenu = (0,
|
|
7927
|
+
const handleOpenSidebar = (0, import_react39.useCallback)(() => setSidebarOpen(true), [setSidebarOpen]);
|
|
7928
|
+
const handleToggleMenu = (0, import_react39.useCallback)(() => {
|
|
7909
7929
|
setThreadMenuOpenId(threadMenuOpenId ? null : activeId);
|
|
7910
7930
|
}, [setThreadMenuOpenId, threadMenuOpenId, activeId]);
|
|
7911
|
-
const closeMenu = (0,
|
|
7912
|
-
const handleRename = (0,
|
|
7931
|
+
const closeMenu = (0, import_react39.useCallback)(() => setThreadMenuOpenId(null), [setThreadMenuOpenId]);
|
|
7932
|
+
const handleRename = (0, import_react39.useCallback)(() => {
|
|
7913
7933
|
if (activeId) startRename(activeId);
|
|
7914
7934
|
}, [activeId, startRename]);
|
|
7915
|
-
const handleDelete = (0,
|
|
7935
|
+
const handleDelete = (0, import_react39.useCallback)(() => {
|
|
7916
7936
|
if (activeId) {
|
|
7917
7937
|
setThreadMenuOpenId(null);
|
|
7918
7938
|
void deleteThread(activeId);
|
|
7919
7939
|
}
|
|
7920
7940
|
}, [activeId, deleteThread, setThreadMenuOpenId]);
|
|
7921
|
-
return /* @__PURE__ */
|
|
7941
|
+
return /* @__PURE__ */ import_react39.default.createElement("header", { className: "brokr-ai-chat-topbar" }, /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-ai-chat-topbar-left" }, sidebarVisible ? /* @__PURE__ */ import_react39.default.createElement(
|
|
7922
7942
|
"button",
|
|
7923
7943
|
{
|
|
7924
7944
|
"aria-label": "Open sidebar",
|
|
@@ -7926,8 +7946,8 @@ function ChatHeader({
|
|
|
7926
7946
|
onClick: handleOpenSidebar,
|
|
7927
7947
|
type: "button"
|
|
7928
7948
|
},
|
|
7929
|
-
/* @__PURE__ */
|
|
7930
|
-
) : null), /* @__PURE__ */
|
|
7949
|
+
/* @__PURE__ */ import_react39.default.createElement(MenuIcon, { size: 18 })
|
|
7950
|
+
) : null), /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-ai-chat-topbar-actions" }, headerCommands.map((cmd) => /* @__PURE__ */ import_react39.default.createElement(CommandButton2, { key: cmd.id, cmd, chatContext })), modelSelectorVisible ? /* @__PURE__ */ import_react39.default.createElement(
|
|
7931
7951
|
ModelSelector,
|
|
7932
7952
|
{
|
|
7933
7953
|
activeModel,
|
|
@@ -7935,7 +7955,7 @@ function ChatHeader({
|
|
|
7935
7955
|
checkout,
|
|
7936
7956
|
setSelectedModel
|
|
7937
7957
|
}
|
|
7938
|
-
) : null, activeId && threadMenuVisible ? /* @__PURE__ */
|
|
7958
|
+
) : null, activeId && threadMenuVisible ? /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-ai-chat-thread-menu-wrap", ref: threadMenuRef }, /* @__PURE__ */ import_react39.default.createElement(
|
|
7939
7959
|
"button",
|
|
7940
7960
|
{
|
|
7941
7961
|
"aria-label": "Thread actions",
|
|
@@ -7943,17 +7963,17 @@ function ChatHeader({
|
|
|
7943
7963
|
onClick: handleToggleMenu,
|
|
7944
7964
|
type: "button"
|
|
7945
7965
|
},
|
|
7946
|
-
/* @__PURE__ */
|
|
7947
|
-
), threadMenuOpenId ? /* @__PURE__ */
|
|
7966
|
+
/* @__PURE__ */ import_react39.default.createElement(MoreHorizontalIcon, { size: 14 })
|
|
7967
|
+
), threadMenuOpenId ? /* @__PURE__ */ import_react39.default.createElement("div", { className: "brokr-ai-chat-thread-dropdown" }, /* @__PURE__ */ import_react39.default.createElement(
|
|
7948
7968
|
"button",
|
|
7949
7969
|
{
|
|
7950
7970
|
className: "brokr-ai-chat-thread-dropdown-item",
|
|
7951
7971
|
onClick: handleRename,
|
|
7952
7972
|
type: "button"
|
|
7953
7973
|
},
|
|
7954
|
-
/* @__PURE__ */
|
|
7974
|
+
/* @__PURE__ */ import_react39.default.createElement(PencilIcon, { size: 13 }),
|
|
7955
7975
|
"Rename"
|
|
7956
|
-
), threadMenuCommands.map((cmd) => /* @__PURE__ */
|
|
7976
|
+
), threadMenuCommands.map((cmd) => /* @__PURE__ */ import_react39.default.createElement(
|
|
7957
7977
|
MenuCommandItem,
|
|
7958
7978
|
{
|
|
7959
7979
|
key: cmd.id,
|
|
@@ -7961,7 +7981,7 @@ function ChatHeader({
|
|
|
7961
7981
|
chatContext,
|
|
7962
7982
|
onClose: closeMenu
|
|
7963
7983
|
}
|
|
7964
|
-
)), /* @__PURE__ */
|
|
7984
|
+
)), /* @__PURE__ */ import_react39.default.createElement(
|
|
7965
7985
|
"button",
|
|
7966
7986
|
{
|
|
7967
7987
|
className: "brokr-ai-chat-thread-dropdown-item",
|
|
@@ -7969,13 +7989,13 @@ function ChatHeader({
|
|
|
7969
7989
|
onClick: handleDelete,
|
|
7970
7990
|
type: "button"
|
|
7971
7991
|
},
|
|
7972
|
-
/* @__PURE__ */
|
|
7992
|
+
/* @__PURE__ */ import_react39.default.createElement(TrashIcon, { size: 13 }),
|
|
7973
7993
|
"Delete"
|
|
7974
7994
|
)) : null) : null));
|
|
7975
7995
|
}
|
|
7976
7996
|
|
|
7977
7997
|
// src/react/composites/FabAI.tsx
|
|
7978
|
-
var
|
|
7998
|
+
var import_react40 = __toESM(require("react"));
|
|
7979
7999
|
|
|
7980
8000
|
// src/react/composites/fab-context.ts
|
|
7981
8001
|
function buildFabSystemPrompt(appContext, brandName, existingPrompt) {
|
|
@@ -8026,34 +8046,34 @@ function FabAI({
|
|
|
8026
8046
|
}) {
|
|
8027
8047
|
const { can, user, theme } = useBrokr();
|
|
8028
8048
|
const brandName = theme?.brand?.name;
|
|
8029
|
-
const [isOpen, setIsOpen] = (0,
|
|
8030
|
-
const [input, setInput] = (0,
|
|
8031
|
-
const [error, setError] = (0,
|
|
8032
|
-
const [isSending, setIsSending] = (0,
|
|
8033
|
-
const [messages, setMessages] = (0,
|
|
8034
|
-
const conversationIdRef = (0,
|
|
8035
|
-
const launcherStyle = (0,
|
|
8049
|
+
const [isOpen, setIsOpen] = (0, import_react40.useState)(false);
|
|
8050
|
+
const [input, setInput] = (0, import_react40.useState)("");
|
|
8051
|
+
const [error, setError] = (0, import_react40.useState)(null);
|
|
8052
|
+
const [isSending, setIsSending] = (0, import_react40.useState)(false);
|
|
8053
|
+
const [messages, setMessages] = (0, import_react40.useState)([]);
|
|
8054
|
+
const conversationIdRef = (0, import_react40.useRef)(`fab_${crypto.randomUUID()}`);
|
|
8055
|
+
const launcherStyle = (0, import_react40.useMemo)(
|
|
8036
8056
|
() => ({
|
|
8037
8057
|
left: position === "bottom-left" ? "var(--brokr-space-6)" : void 0,
|
|
8038
8058
|
right: position === "bottom-right" ? "var(--brokr-space-6)" : void 0
|
|
8039
8059
|
}),
|
|
8040
8060
|
[position]
|
|
8041
8061
|
);
|
|
8042
|
-
const canChat = (0,
|
|
8043
|
-
const toggleOpen = (0,
|
|
8062
|
+
const canChat = (0, import_react40.useMemo)(() => can("ai.chat"), [can]);
|
|
8063
|
+
const toggleOpen = (0, import_react40.useCallback)(() => {
|
|
8044
8064
|
if (!canChat) {
|
|
8045
8065
|
redirectTo("/pricing");
|
|
8046
8066
|
return;
|
|
8047
8067
|
}
|
|
8048
8068
|
setIsOpen((current) => !current);
|
|
8049
8069
|
}, [canChat]);
|
|
8050
|
-
const handleInputChange = (0,
|
|
8070
|
+
const handleInputChange = (0, import_react40.useCallback)((event) => {
|
|
8051
8071
|
setInput(event.target.value);
|
|
8052
8072
|
}, []);
|
|
8053
|
-
const handleClose = (0,
|
|
8073
|
+
const handleClose = (0, import_react40.useCallback)(() => {
|
|
8054
8074
|
setIsOpen(false);
|
|
8055
8075
|
}, []);
|
|
8056
|
-
const sendPrompt = (0,
|
|
8076
|
+
const sendPrompt = (0, import_react40.useCallback)(async (prompt) => {
|
|
8057
8077
|
const nextPrompt = prompt.trim();
|
|
8058
8078
|
if (!nextPrompt || isSending) return;
|
|
8059
8079
|
const userMessage = { role: "user", content: nextPrompt };
|
|
@@ -8124,17 +8144,17 @@ function FabAI({
|
|
|
8124
8144
|
setIsSending(false);
|
|
8125
8145
|
}
|
|
8126
8146
|
}, [isSending, messages, model, onSendMessage, systemPrompt]);
|
|
8127
|
-
const handleSubmit = (0,
|
|
8147
|
+
const handleSubmit = (0, import_react40.useCallback)(async (event) => {
|
|
8128
8148
|
event.preventDefault();
|
|
8129
8149
|
await sendPrompt(input);
|
|
8130
8150
|
}, [input, sendPrompt]);
|
|
8131
|
-
const handleKeyDown = (0,
|
|
8151
|
+
const handleKeyDown = (0, import_react40.useCallback)((event) => {
|
|
8132
8152
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
8133
8153
|
event.preventDefault();
|
|
8134
8154
|
void sendPrompt(input);
|
|
8135
8155
|
}
|
|
8136
8156
|
}, [input, sendPrompt]);
|
|
8137
|
-
return /* @__PURE__ */
|
|
8157
|
+
return /* @__PURE__ */ import_react40.default.createElement(import_react40.default.Fragment, null, /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-chat-fab", style: launcherStyle }, /* @__PURE__ */ import_react40.default.createElement(
|
|
8138
8158
|
"button",
|
|
8139
8159
|
{
|
|
8140
8160
|
"aria-label": isOpen ? "Close AI chat" : "Open AI chat",
|
|
@@ -8144,21 +8164,21 @@ function FabAI({
|
|
|
8144
8164
|
onClick: toggleOpen,
|
|
8145
8165
|
type: "button"
|
|
8146
8166
|
},
|
|
8147
|
-
/* @__PURE__ */
|
|
8148
|
-
)), isOpen ? /* @__PURE__ */
|
|
8167
|
+
/* @__PURE__ */ import_react40.default.createElement(SparkIcon, { size: 18 })
|
|
8168
|
+
)), isOpen ? /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-panel brokr-chat-panel", role: "dialog" }, /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ import_react40.default.createElement("strong", null, "AI Chat"), user?.name ? /* @__PURE__ */ import_react40.default.createElement("span", { className: "brokr-copy" }, user.name) : null), /* @__PURE__ */ import_react40.default.createElement("button", { className: "brokr-button-ghost", onClick: handleClose, type: "button" }, /* @__PURE__ */ import_react40.default.createElement(CloseIcon, { size: 16 }))), /* @__PURE__ */ import_react40.default.createElement(
|
|
8149
8169
|
"div",
|
|
8150
8170
|
{
|
|
8151
8171
|
className: "brokr-chat-messages",
|
|
8152
8172
|
"data-empty": messages.length === 0
|
|
8153
8173
|
},
|
|
8154
|
-
messages.length === 0 ? /* @__PURE__ */
|
|
8174
|
+
messages.length === 0 ? /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-chat-empty" }, /* @__PURE__ */ import_react40.default.createElement(SparkIcon, { size: 18 }), /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ import_react40.default.createElement("strong", null, "Send a message to chat with the AI."), /* @__PURE__ */ import_react40.default.createElement("span", { className: "brokr-copy" }, "Ask a question or drop in a starter prompt below."))) : null,
|
|
8155
8175
|
messages.map((message, index) => {
|
|
8156
8176
|
const text = contentToText(message.content);
|
|
8157
8177
|
const isTyping = message.role === "assistant" && !text && isSending && index === messages.length - 1;
|
|
8158
|
-
return /* @__PURE__ */
|
|
8178
|
+
return /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-chat-bubble", "data-role": message.role, key: `${message.role}-${index}` }, isTyping ? /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-ai-chat-typing", "aria-label": "AI is typing" }, /* @__PURE__ */ import_react40.default.createElement("span", null), /* @__PURE__ */ import_react40.default.createElement("span", null), /* @__PURE__ */ import_react40.default.createElement("span", null)) : message.role === "assistant" ? /* @__PURE__ */ import_react40.default.createElement(MarkdownRenderer, { content: text }) : text);
|
|
8159
8179
|
}),
|
|
8160
|
-
error ? /* @__PURE__ */
|
|
8161
|
-
), /* @__PURE__ */
|
|
8180
|
+
error ? /* @__PURE__ */ import_react40.default.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null
|
|
8181
|
+
), /* @__PURE__ */ import_react40.default.createElement("form", { className: "brokr-section", onSubmit: handleSubmit, style: { gap: "var(--brokr-space-3)" } }, /* @__PURE__ */ import_react40.default.createElement(
|
|
8162
8182
|
"textarea",
|
|
8163
8183
|
{
|
|
8164
8184
|
className: "brokr-textarea brokr-chat-input",
|
|
@@ -8168,11 +8188,11 @@ function FabAI({
|
|
|
8168
8188
|
rows: 2,
|
|
8169
8189
|
value: input
|
|
8170
8190
|
}
|
|
8171
|
-
), /* @__PURE__ */
|
|
8191
|
+
), /* @__PURE__ */ import_react40.default.createElement("button", { className: "brokr-button", disabled: isSending || !input.trim(), type: "submit" }, isSending ? "Thinking" : "Send"))) : null);
|
|
8172
8192
|
}
|
|
8173
8193
|
|
|
8174
8194
|
// src/react/composites/SmartUpload.tsx
|
|
8175
|
-
var
|
|
8195
|
+
var import_react41 = __toESM(require("react"));
|
|
8176
8196
|
function SmartUpload({
|
|
8177
8197
|
accept,
|
|
8178
8198
|
maxSize = 500 * 1024 * 1024,
|
|
@@ -8180,17 +8200,17 @@ function SmartUpload({
|
|
|
8180
8200
|
purpose = "general"
|
|
8181
8201
|
}) {
|
|
8182
8202
|
const { paymentsMode } = useBrokr();
|
|
8183
|
-
const inputId = (0,
|
|
8184
|
-
const inputRef = (0,
|
|
8185
|
-
const [dragActive, setDragActive] = (0,
|
|
8186
|
-
const [error, setError] = (0,
|
|
8187
|
-
const [fileName, setFileName] = (0,
|
|
8188
|
-
const [progress, setProgress] = (0,
|
|
8189
|
-
const [isUploading, setIsUploading] = (0,
|
|
8190
|
-
const helperText = (0,
|
|
8203
|
+
const inputId = (0, import_react41.useId)();
|
|
8204
|
+
const inputRef = (0, import_react41.useRef)(null);
|
|
8205
|
+
const [dragActive, setDragActive] = (0, import_react41.useState)(false);
|
|
8206
|
+
const [error, setError] = (0, import_react41.useState)(null);
|
|
8207
|
+
const [fileName, setFileName] = (0, import_react41.useState)(null);
|
|
8208
|
+
const [progress, setProgress] = (0, import_react41.useState)(0);
|
|
8209
|
+
const [isUploading, setIsUploading] = (0, import_react41.useState)(false);
|
|
8210
|
+
const helperText = (0, import_react41.useMemo)(() => {
|
|
8191
8211
|
return `${Math.round(maxSize / (1024 * 1024))} MB max file size.`;
|
|
8192
8212
|
}, [maxSize]);
|
|
8193
|
-
const beginUpload = (0,
|
|
8213
|
+
const beginUpload = (0, import_react41.useCallback)((file) => {
|
|
8194
8214
|
if (file.size > maxSize) {
|
|
8195
8215
|
setError(`That file is larger than ${Math.round(maxSize / (1024 * 1024))} MB.`);
|
|
8196
8216
|
return;
|
|
@@ -8227,31 +8247,31 @@ function SmartUpload({
|
|
|
8227
8247
|
body.append("purpose", purpose);
|
|
8228
8248
|
request.send(body);
|
|
8229
8249
|
}, [maxSize, onUpload, purpose]);
|
|
8230
|
-
const handleInputChange = (0,
|
|
8250
|
+
const handleInputChange = (0, import_react41.useCallback)((event) => {
|
|
8231
8251
|
const file = event.target.files?.[0];
|
|
8232
8252
|
event.target.value = "";
|
|
8233
8253
|
if (!file) return;
|
|
8234
8254
|
beginUpload(file);
|
|
8235
8255
|
}, [beginUpload]);
|
|
8236
|
-
const handleDrop = (0,
|
|
8256
|
+
const handleDrop = (0, import_react41.useCallback)((event) => {
|
|
8237
8257
|
event.preventDefault();
|
|
8238
8258
|
setDragActive(false);
|
|
8239
8259
|
const file = event.dataTransfer.files?.[0];
|
|
8240
8260
|
if (!file) return;
|
|
8241
8261
|
beginUpload(file);
|
|
8242
8262
|
}, [beginUpload]);
|
|
8243
|
-
const handleDragEnter = (0,
|
|
8263
|
+
const handleDragEnter = (0, import_react41.useCallback)((event) => {
|
|
8244
8264
|
event.preventDefault();
|
|
8245
8265
|
setDragActive(true);
|
|
8246
8266
|
}, []);
|
|
8247
|
-
const handleDragLeave = (0,
|
|
8267
|
+
const handleDragLeave = (0, import_react41.useCallback)((event) => {
|
|
8248
8268
|
event.preventDefault();
|
|
8249
8269
|
setDragActive(false);
|
|
8250
8270
|
}, []);
|
|
8251
|
-
const handleBrowse = (0,
|
|
8271
|
+
const handleBrowse = (0, import_react41.useCallback)(() => {
|
|
8252
8272
|
inputRef.current?.click();
|
|
8253
8273
|
}, []);
|
|
8254
|
-
return /* @__PURE__ */
|
|
8274
|
+
return /* @__PURE__ */ import_react41.default.createElement("div", { className: "brokr-card brokr-upload-shell" }, /* @__PURE__ */ import_react41.default.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ import_react41.default.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ import_react41.default.createElement("strong", null, "Upload files"), /* @__PURE__ */ import_react41.default.createElement("span", { className: "brokr-copy" }, "Drop a file and let Brokr handle the boring part.")), paymentsMode === "sandbox" ? /* @__PURE__ */ import_react41.default.createElement("span", { className: "brokr-badge brokr-badge-sandbox" }, "Sandbox") : null), /* @__PURE__ */ import_react41.default.createElement(
|
|
8255
8275
|
"label",
|
|
8256
8276
|
{
|
|
8257
8277
|
className: "brokr-upload-dropzone",
|
|
@@ -8262,11 +8282,11 @@ function SmartUpload({
|
|
|
8262
8282
|
onDragOver: handleDragEnter,
|
|
8263
8283
|
onDrop: handleDrop
|
|
8264
8284
|
},
|
|
8265
|
-
/* @__PURE__ */
|
|
8266
|
-
/* @__PURE__ */
|
|
8267
|
-
/* @__PURE__ */
|
|
8268
|
-
/* @__PURE__ */
|
|
8269
|
-
), /* @__PURE__ */
|
|
8285
|
+
/* @__PURE__ */ import_react41.default.createElement(UploadIcon, { size: 24 }),
|
|
8286
|
+
/* @__PURE__ */ import_react41.default.createElement("strong", null, "Drag and drop a file here or choose one"),
|
|
8287
|
+
/* @__PURE__ */ import_react41.default.createElement("span", { className: "brokr-copy" }, helperText),
|
|
8288
|
+
/* @__PURE__ */ import_react41.default.createElement("button", { className: "brokr-button-secondary", onClick: handleBrowse, type: "button" }, "Choose file")
|
|
8289
|
+
), /* @__PURE__ */ import_react41.default.createElement(
|
|
8270
8290
|
"input",
|
|
8271
8291
|
{
|
|
8272
8292
|
accept,
|
|
@@ -8276,31 +8296,31 @@ function SmartUpload({
|
|
|
8276
8296
|
ref: inputRef,
|
|
8277
8297
|
type: "file"
|
|
8278
8298
|
}
|
|
8279
|
-
), fileName ? /* @__PURE__ */
|
|
8299
|
+
), fileName ? /* @__PURE__ */ import_react41.default.createElement("div", { className: "brokr-card brokr-upload-file" }, /* @__PURE__ */ import_react41.default.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ import_react41.default.createElement("strong", null, fileName), /* @__PURE__ */ import_react41.default.createElement("span", { className: "brokr-copy" }, isUploading ? `Uploading ${progress}%` : progress === 100 ? "Processed" : "Queued")), /* @__PURE__ */ import_react41.default.createElement("div", { className: "brokr-meter-bar" }, /* @__PURE__ */ import_react41.default.createElement("div", { className: "brokr-meter-fill", style: { width: `${progress}%` } }))) : null, error ? /* @__PURE__ */ import_react41.default.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null);
|
|
8280
8300
|
}
|
|
8281
8301
|
|
|
8282
8302
|
// src/react/composites/FeedbackWidget.tsx
|
|
8283
|
-
var
|
|
8303
|
+
var import_react42 = __toESM(require("react"));
|
|
8284
8304
|
function FeedbackWidget({
|
|
8285
8305
|
context,
|
|
8286
8306
|
onSubmit
|
|
8287
8307
|
}) {
|
|
8288
8308
|
const { user } = useBrokr();
|
|
8289
|
-
const [rating, setRating] = (0,
|
|
8290
|
-
const [text, setText] = (0,
|
|
8291
|
-
const [error, setError] = (0,
|
|
8292
|
-
const [message, setMessage] = (0,
|
|
8293
|
-
const [isPending, setIsPending] = (0,
|
|
8294
|
-
const handleTextChange = (0,
|
|
8309
|
+
const [rating, setRating] = (0, import_react42.useState)(null);
|
|
8310
|
+
const [text, setText] = (0, import_react42.useState)("");
|
|
8311
|
+
const [error, setError] = (0, import_react42.useState)(null);
|
|
8312
|
+
const [message, setMessage] = (0, import_react42.useState)(null);
|
|
8313
|
+
const [isPending, setIsPending] = (0, import_react42.useState)(false);
|
|
8314
|
+
const handleTextChange = (0, import_react42.useCallback)((event) => {
|
|
8295
8315
|
setText(event.target.value);
|
|
8296
8316
|
}, []);
|
|
8297
|
-
const handleRateUp = (0,
|
|
8317
|
+
const handleRateUp = (0, import_react42.useCallback)(() => {
|
|
8298
8318
|
setRating("up");
|
|
8299
8319
|
}, []);
|
|
8300
|
-
const handleRateDown = (0,
|
|
8320
|
+
const handleRateDown = (0, import_react42.useCallback)(() => {
|
|
8301
8321
|
setRating("down");
|
|
8302
8322
|
}, []);
|
|
8303
|
-
const handleSubmit = (0,
|
|
8323
|
+
const handleSubmit = (0, import_react42.useCallback)(async (event) => {
|
|
8304
8324
|
event.preventDefault();
|
|
8305
8325
|
if (!rating) {
|
|
8306
8326
|
setError("Choose a direction first.");
|
|
@@ -8330,7 +8350,7 @@ function FeedbackWidget({
|
|
|
8330
8350
|
setIsPending(false);
|
|
8331
8351
|
}
|
|
8332
8352
|
}, [context, onSubmit, rating, text, user?.id]);
|
|
8333
|
-
return /* @__PURE__ */
|
|
8353
|
+
return /* @__PURE__ */ import_react42.default.createElement("form", { className: "brokr-card brokr-feedback-shell", onSubmit: handleSubmit }, /* @__PURE__ */ import_react42.default.createElement("div", { className: "brokr-section", style: { gap: "0.5rem" } }, /* @__PURE__ */ import_react42.default.createElement("strong", null, "How did this feel?"), /* @__PURE__ */ import_react42.default.createElement("p", { className: "brokr-copy" }, "Tight signal only. Tell us what helped or what felt off.")), /* @__PURE__ */ import_react42.default.createElement("div", { className: "brokr-feedback-rating" }, /* @__PURE__ */ import_react42.default.createElement(
|
|
8334
8354
|
"button",
|
|
8335
8355
|
{
|
|
8336
8356
|
className: "brokr-rating-button",
|
|
@@ -8339,7 +8359,7 @@ function FeedbackWidget({
|
|
|
8339
8359
|
type: "button"
|
|
8340
8360
|
},
|
|
8341
8361
|
"This worked"
|
|
8342
|
-
), /* @__PURE__ */
|
|
8362
|
+
), /* @__PURE__ */ import_react42.default.createElement(
|
|
8343
8363
|
"button",
|
|
8344
8364
|
{
|
|
8345
8365
|
className: "brokr-rating-button",
|
|
@@ -8348,7 +8368,7 @@ function FeedbackWidget({
|
|
|
8348
8368
|
type: "button"
|
|
8349
8369
|
},
|
|
8350
8370
|
"Needs work"
|
|
8351
|
-
)), /* @__PURE__ */
|
|
8371
|
+
)), /* @__PURE__ */ import_react42.default.createElement(
|
|
8352
8372
|
"textarea",
|
|
8353
8373
|
{
|
|
8354
8374
|
className: "brokr-textarea",
|
|
@@ -8357,11 +8377,11 @@ function FeedbackWidget({
|
|
|
8357
8377
|
rows: 4,
|
|
8358
8378
|
value: text
|
|
8359
8379
|
}
|
|
8360
|
-
), error ? /* @__PURE__ */
|
|
8380
|
+
), error ? /* @__PURE__ */ import_react42.default.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null, message ? /* @__PURE__ */ import_react42.default.createElement("div", { className: "brokr-inline-message" }, message) : null, /* @__PURE__ */ import_react42.default.createElement("button", { className: "brokr-button", disabled: isPending, type: "submit" }, isPending ? "Sending" : "Send feedback"));
|
|
8361
8381
|
}
|
|
8362
8382
|
|
|
8363
8383
|
// src/react/BrokrErrorBoundary.tsx
|
|
8364
|
-
var
|
|
8384
|
+
var import_react43 = __toESM(require("react"));
|
|
8365
8385
|
|
|
8366
8386
|
// src/fix-registry.ts
|
|
8367
8387
|
var FIX_REGISTRY = {
|
|
@@ -8585,7 +8605,7 @@ var BrokrError = class extends Error {
|
|
|
8585
8605
|
};
|
|
8586
8606
|
|
|
8587
8607
|
// src/react/BrokrErrorBoundary.tsx
|
|
8588
|
-
var BrokrErrorBoundary = class extends
|
|
8608
|
+
var BrokrErrorBoundary = class extends import_react43.default.Component {
|
|
8589
8609
|
constructor() {
|
|
8590
8610
|
super(...arguments);
|
|
8591
8611
|
this.state = { hasError: false, error: null };
|
|
@@ -8597,11 +8617,11 @@ var BrokrErrorBoundary = class extends import_react42.default.Component {
|
|
|
8597
8617
|
if (this.state.hasError) {
|
|
8598
8618
|
if (this.props.fallback) return this.props.fallback;
|
|
8599
8619
|
const message = this.state.error instanceof BrokrError ? this.state.error.toUserMessage() : "Something went wrong.";
|
|
8600
|
-
return
|
|
8620
|
+
return import_react43.default.createElement(
|
|
8601
8621
|
"div",
|
|
8602
8622
|
{ style: { padding: 24, textAlign: "center" } },
|
|
8603
|
-
|
|
8604
|
-
|
|
8623
|
+
import_react43.default.createElement("p", null, message),
|
|
8624
|
+
import_react43.default.createElement(
|
|
8605
8625
|
"button",
|
|
8606
8626
|
{
|
|
8607
8627
|
onClick: () => this.setState({ hasError: false, error: null }),
|
|
@@ -8615,13 +8635,13 @@ var BrokrErrorBoundary = class extends import_react42.default.Component {
|
|
|
8615
8635
|
}
|
|
8616
8636
|
};
|
|
8617
8637
|
|
|
8618
|
-
// src/react/notifications/
|
|
8619
|
-
var
|
|
8638
|
+
// src/react/notifications/ActivityFeed.tsx
|
|
8639
|
+
var import_react45 = __toESM(require("react"));
|
|
8620
8640
|
|
|
8621
8641
|
// src/react/notifications/use-notifications.ts
|
|
8622
|
-
var
|
|
8642
|
+
var import_react44 = require("react");
|
|
8623
8643
|
function useNotifications() {
|
|
8624
|
-
const ctx = (0,
|
|
8644
|
+
const ctx = (0, import_react44.useContext)(NotificationsContext);
|
|
8625
8645
|
if (!ctx) {
|
|
8626
8646
|
throw new Error(
|
|
8627
8647
|
"useNotifications() requires <BrokrProvider notifications> or <BrokrProvider notifications={config}>. Pass the notifications prop to enable the notification system."
|
|
@@ -8630,7 +8650,7 @@ function useNotifications() {
|
|
|
8630
8650
|
return ctx;
|
|
8631
8651
|
}
|
|
8632
8652
|
|
|
8633
|
-
// src/react/notifications/
|
|
8653
|
+
// src/react/notifications/ActivityFeed.tsx
|
|
8634
8654
|
function timeAgo(iso) {
|
|
8635
8655
|
const diff = Date.now() - new Date(iso).getTime();
|
|
8636
8656
|
const mins = Math.floor(diff / 6e4);
|
|
@@ -8641,16 +8661,79 @@ function timeAgo(iso) {
|
|
|
8641
8661
|
const days = Math.floor(hours / 24);
|
|
8642
8662
|
return `${days}d ago`;
|
|
8643
8663
|
}
|
|
8664
|
+
function FeedItem({
|
|
8665
|
+
item,
|
|
8666
|
+
formatter
|
|
8667
|
+
}) {
|
|
8668
|
+
if (formatter) {
|
|
8669
|
+
return /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-feed-item" }, formatter(item));
|
|
8670
|
+
}
|
|
8671
|
+
return /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-feed-item" }, /* @__PURE__ */ import_react45.default.createElement("span", { className: `brokr-feed-dot brokr-feed-dot--${item.variant}` }), /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-feed-content" }, /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-feed-title" }, item.title), item.message ? /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-feed-message" }, item.message) : null), /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-feed-time" }, timeAgo(item.createdAt)));
|
|
8672
|
+
}
|
|
8673
|
+
function ActivityFeed({
|
|
8674
|
+
filter,
|
|
8675
|
+
maxItems = 20,
|
|
8676
|
+
formatters,
|
|
8677
|
+
emptyState
|
|
8678
|
+
}) {
|
|
8679
|
+
const { notifications, isLoading } = useNotifications();
|
|
8680
|
+
const items = (0, import_react45.useMemo)(() => {
|
|
8681
|
+
let filtered = notifications;
|
|
8682
|
+
if (filter && filter.length > 0) {
|
|
8683
|
+
const set = new Set(filter);
|
|
8684
|
+
filtered = notifications.filter(
|
|
8685
|
+
(n) => set.has(n.variant) || n.type && set.has(n.type)
|
|
8686
|
+
);
|
|
8687
|
+
}
|
|
8688
|
+
return [...filtered].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, maxItems);
|
|
8689
|
+
}, [notifications, filter, maxItems]);
|
|
8690
|
+
const getFormatter = (0, import_react45.useCallback)(
|
|
8691
|
+
(item) => {
|
|
8692
|
+
if (!formatters) return void 0;
|
|
8693
|
+
if (item.type && formatters[item.type]) return formatters[item.type];
|
|
8694
|
+
if (formatters[item.variant]) return formatters[item.variant];
|
|
8695
|
+
return void 0;
|
|
8696
|
+
},
|
|
8697
|
+
[formatters]
|
|
8698
|
+
);
|
|
8699
|
+
if (isLoading) {
|
|
8700
|
+
return /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-feed" }, [1, 2, 3].map((i) => /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-feed-item", key: i }, /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-feed-dot" }), /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-feed-content" }, /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-skeleton", style: { width: "60%", height: 12 } }), /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-skeleton", style: { width: "80%", height: 12 } })), /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-skeleton", style: { width: 40, height: 12 } }))));
|
|
8701
|
+
}
|
|
8702
|
+
if (items.length === 0) {
|
|
8703
|
+
return /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-feed" }, /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-feed-empty" }, emptyState ?? /* @__PURE__ */ import_react45.default.createElement("span", { className: "brokr-copy" }, "No activity yet.")));
|
|
8704
|
+
}
|
|
8705
|
+
return /* @__PURE__ */ import_react45.default.createElement("div", { className: "brokr-feed" }, items.map((item) => /* @__PURE__ */ import_react45.default.createElement(
|
|
8706
|
+
FeedItem,
|
|
8707
|
+
{
|
|
8708
|
+
key: item.id,
|
|
8709
|
+
item,
|
|
8710
|
+
formatter: getFormatter(item)
|
|
8711
|
+
}
|
|
8712
|
+
)));
|
|
8713
|
+
}
|
|
8714
|
+
|
|
8715
|
+
// src/react/notifications/NotificationBell.tsx
|
|
8716
|
+
var import_react46 = __toESM(require("react"));
|
|
8717
|
+
function timeAgo2(iso) {
|
|
8718
|
+
const diff = Date.now() - new Date(iso).getTime();
|
|
8719
|
+
const mins = Math.floor(diff / 6e4);
|
|
8720
|
+
if (mins < 1) return "just now";
|
|
8721
|
+
if (mins < 60) return `${mins}m ago`;
|
|
8722
|
+
const hours = Math.floor(mins / 60);
|
|
8723
|
+
if (hours < 24) return `${hours}h ago`;
|
|
8724
|
+
const days = Math.floor(hours / 24);
|
|
8725
|
+
return `${days}d ago`;
|
|
8726
|
+
}
|
|
8644
8727
|
function NotifDropdownItem({
|
|
8645
8728
|
notif,
|
|
8646
8729
|
registry,
|
|
8647
8730
|
onClick
|
|
8648
8731
|
}) {
|
|
8649
|
-
const handleClick = (0,
|
|
8732
|
+
const handleClick = (0, import_react46.useCallback)(() => onClick(notif), [notif, onClick]);
|
|
8650
8733
|
const notifData = notif.data ?? {};
|
|
8651
8734
|
const notifType = notifData.type ?? "default";
|
|
8652
8735
|
const resolved = resolveNotificationType(registry, notifType, notifData);
|
|
8653
|
-
return /* @__PURE__ */
|
|
8736
|
+
return /* @__PURE__ */ import_react46.default.createElement(
|
|
8654
8737
|
"button",
|
|
8655
8738
|
{
|
|
8656
8739
|
type: "button",
|
|
@@ -8658,18 +8741,18 @@ function NotifDropdownItem({
|
|
|
8658
8741
|
onClick: handleClick,
|
|
8659
8742
|
role: "menuitem"
|
|
8660
8743
|
},
|
|
8661
|
-
resolved.image ? /* @__PURE__ */
|
|
8662
|
-
/* @__PURE__ */
|
|
8663
|
-
/* @__PURE__ */
|
|
8744
|
+
resolved.image ? /* @__PURE__ */ import_react46.default.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-notif-item-logo" }) : /* @__PURE__ */ import_react46.default.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
|
|
8745
|
+
/* @__PURE__ */ import_react46.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react46.default.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ import_react46.default.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
|
|
8746
|
+
/* @__PURE__ */ import_react46.default.createElement("span", { className: "brokr-notif-item-time" }, timeAgo2(notif.createdAt))
|
|
8664
8747
|
);
|
|
8665
8748
|
}
|
|
8666
8749
|
function NotificationBell() {
|
|
8667
8750
|
const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
|
|
8668
|
-
const [open, setOpen] = (0,
|
|
8669
|
-
const containerRef = (0,
|
|
8670
|
-
const markReadTimerRef = (0,
|
|
8671
|
-
const toggle = (0,
|
|
8672
|
-
(0,
|
|
8751
|
+
const [open, setOpen] = (0, import_react46.useState)(false);
|
|
8752
|
+
const containerRef = (0, import_react46.useRef)(null);
|
|
8753
|
+
const markReadTimerRef = (0, import_react46.useRef)(null);
|
|
8754
|
+
const toggle = (0, import_react46.useCallback)(() => setOpen((o) => !o), []);
|
|
8755
|
+
(0, import_react46.useEffect)(() => {
|
|
8673
8756
|
if (markReadTimerRef.current) {
|
|
8674
8757
|
clearTimeout(markReadTimerRef.current);
|
|
8675
8758
|
markReadTimerRef.current = null;
|
|
@@ -8686,7 +8769,7 @@ function NotificationBell() {
|
|
|
8686
8769
|
}
|
|
8687
8770
|
};
|
|
8688
8771
|
}, [open, unreadCount, markAllRead]);
|
|
8689
|
-
(0,
|
|
8772
|
+
(0, import_react46.useEffect)(() => {
|
|
8690
8773
|
if (!open) return;
|
|
8691
8774
|
function handleClick(e) {
|
|
8692
8775
|
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
@@ -8696,7 +8779,7 @@ function NotificationBell() {
|
|
|
8696
8779
|
document.addEventListener("mousedown", handleClick);
|
|
8697
8780
|
return () => document.removeEventListener("mousedown", handleClick);
|
|
8698
8781
|
}, [open]);
|
|
8699
|
-
(0,
|
|
8782
|
+
(0, import_react46.useEffect)(() => {
|
|
8700
8783
|
if (!open) return;
|
|
8701
8784
|
function handleKey(e) {
|
|
8702
8785
|
if (e.key === "Escape") setOpen(false);
|
|
@@ -8704,13 +8787,13 @@ function NotificationBell() {
|
|
|
8704
8787
|
document.addEventListener("keydown", handleKey);
|
|
8705
8788
|
return () => document.removeEventListener("keydown", handleKey);
|
|
8706
8789
|
}, [open]);
|
|
8707
|
-
const sorted = (0,
|
|
8790
|
+
const sorted = (0, import_react46.useMemo)(
|
|
8708
8791
|
() => [...notifications].sort(
|
|
8709
8792
|
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
8710
8793
|
),
|
|
8711
8794
|
[notifications]
|
|
8712
8795
|
);
|
|
8713
|
-
const handleItemClick = (0,
|
|
8796
|
+
const handleItemClick = (0, import_react46.useCallback)((notif) => {
|
|
8714
8797
|
if (!notif.read) markRead(notif.id);
|
|
8715
8798
|
const notifData = notif.data ?? {};
|
|
8716
8799
|
const notifType = notif.type ?? notifData.type ?? "default";
|
|
@@ -8720,7 +8803,7 @@ function NotificationBell() {
|
|
|
8720
8803
|
window.location.assign(href);
|
|
8721
8804
|
}
|
|
8722
8805
|
}, [markRead, registry]);
|
|
8723
|
-
return /* @__PURE__ */
|
|
8806
|
+
return /* @__PURE__ */ import_react46.default.createElement("div", { className: "brokr-notif-bell-wrap", ref: containerRef }, /* @__PURE__ */ import_react46.default.createElement(
|
|
8724
8807
|
"button",
|
|
8725
8808
|
{
|
|
8726
8809
|
type: "button",
|
|
@@ -8730,9 +8813,9 @@ function NotificationBell() {
|
|
|
8730
8813
|
"aria-expanded": open,
|
|
8731
8814
|
"aria-haspopup": "menu"
|
|
8732
8815
|
},
|
|
8733
|
-
/* @__PURE__ */
|
|
8734
|
-
unreadCount > 0 && /* @__PURE__ */
|
|
8735
|
-
), open && /* @__PURE__ */
|
|
8816
|
+
/* @__PURE__ */ import_react46.default.createElement("svg", { "aria-hidden": "true", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react46.default.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ import_react46.default.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })),
|
|
8817
|
+
unreadCount > 0 && /* @__PURE__ */ import_react46.default.createElement("span", { className: "brokr-notif-badge" }, unreadCount > 99 ? "99+" : unreadCount)
|
|
8818
|
+
), open && /* @__PURE__ */ import_react46.default.createElement("div", { className: "brokr-notif-dropdown", role: "menu" }, /* @__PURE__ */ import_react46.default.createElement("div", { className: "brokr-notif-dropdown-header" }, /* @__PURE__ */ import_react46.default.createElement("span", { className: "brokr-notif-dropdown-title" }, "Notifications"), unreadCount > 0 && /* @__PURE__ */ import_react46.default.createElement(
|
|
8736
8819
|
"button",
|
|
8737
8820
|
{
|
|
8738
8821
|
type: "button",
|
|
@@ -8740,7 +8823,7 @@ function NotificationBell() {
|
|
|
8740
8823
|
onClick: markAllRead
|
|
8741
8824
|
},
|
|
8742
8825
|
"Mark all read"
|
|
8743
|
-
)), /* @__PURE__ */
|
|
8826
|
+
)), /* @__PURE__ */ import_react46.default.createElement("div", { className: "brokr-notif-dropdown-list" }, isLoading ? /* @__PURE__ */ import_react46.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react46.default.createElement("span", { className: "brokr-notif-empty-text" }, "Loading\u2026")) : sorted.length === 0 ? /* @__PURE__ */ import_react46.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react46.default.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : sorted.map((notif) => /* @__PURE__ */ import_react46.default.createElement(
|
|
8744
8827
|
NotifDropdownItem,
|
|
8745
8828
|
{
|
|
8746
8829
|
key: notif.id,
|
|
@@ -8752,7 +8835,7 @@ function NotificationBell() {
|
|
|
8752
8835
|
}
|
|
8753
8836
|
|
|
8754
8837
|
// src/react/notifications/NotificationList.tsx
|
|
8755
|
-
var
|
|
8838
|
+
var import_react47 = __toESM(require("react"));
|
|
8756
8839
|
function formatTimestamp(iso) {
|
|
8757
8840
|
const date = new Date(iso);
|
|
8758
8841
|
return new Intl.DateTimeFormat("en-US", {
|
|
@@ -8763,38 +8846,38 @@ function formatTimestamp(iso) {
|
|
|
8763
8846
|
}).format(date);
|
|
8764
8847
|
}
|
|
8765
8848
|
function NotificationListSkeleton() {
|
|
8766
|
-
return /* @__PURE__ */
|
|
8849
|
+
return /* @__PURE__ */ import_react47.default.createElement("div", { className: "brokr-notif-list-items" }, [1, 2, 3].map((i) => /* @__PURE__ */ import_react47.default.createElement("div", { key: i, className: "brokr-notif-list-row brokr-notif-list-row--skeleton" }, /* @__PURE__ */ import_react47.default.createElement("span", { className: "brokr-notif-item-dot brokr-notif-item-dot--skeleton" }), /* @__PURE__ */ import_react47.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react47.default.createElement("span", { className: "brokr-notif-item-title brokr-skeleton-line", style: { width: "60%" } }), /* @__PURE__ */ import_react47.default.createElement("span", { className: "brokr-notif-item-message brokr-skeleton-line", style: { width: "80%" } })), /* @__PURE__ */ import_react47.default.createElement("span", { className: "brokr-notif-item-time brokr-skeleton-line", style: { width: 48 } }))));
|
|
8767
8850
|
}
|
|
8768
8851
|
function NotifListItem({
|
|
8769
8852
|
notif,
|
|
8770
8853
|
registry,
|
|
8771
8854
|
onClick
|
|
8772
8855
|
}) {
|
|
8773
|
-
const handleClick = (0,
|
|
8856
|
+
const handleClick = (0, import_react47.useCallback)(() => onClick(notif), [notif, onClick]);
|
|
8774
8857
|
const notifData = notif.data ?? {};
|
|
8775
8858
|
const notifType = notif.type ?? notifData.type ?? "default";
|
|
8776
8859
|
const resolved = resolveNotificationType(registry, notifType, notifData);
|
|
8777
|
-
return /* @__PURE__ */
|
|
8860
|
+
return /* @__PURE__ */ import_react47.default.createElement(
|
|
8778
8861
|
"button",
|
|
8779
8862
|
{
|
|
8780
8863
|
type: "button",
|
|
8781
8864
|
className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
|
|
8782
8865
|
onClick: handleClick
|
|
8783
8866
|
},
|
|
8784
|
-
resolved.image ? /* @__PURE__ */
|
|
8785
|
-
/* @__PURE__ */
|
|
8786
|
-
/* @__PURE__ */
|
|
8867
|
+
resolved.image ? /* @__PURE__ */ import_react47.default.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-notif-item-logo" }) : /* @__PURE__ */ import_react47.default.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
|
|
8868
|
+
/* @__PURE__ */ import_react47.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react47.default.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ import_react47.default.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
|
|
8869
|
+
/* @__PURE__ */ import_react47.default.createElement("span", { className: "brokr-notif-item-time" }, formatTimestamp(notif.createdAt))
|
|
8787
8870
|
);
|
|
8788
8871
|
}
|
|
8789
8872
|
function NotificationList() {
|
|
8790
8873
|
const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
|
|
8791
|
-
const sorted = (0,
|
|
8874
|
+
const sorted = (0, import_react47.useMemo)(
|
|
8792
8875
|
() => [...notifications].sort(
|
|
8793
8876
|
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
8794
8877
|
),
|
|
8795
8878
|
[notifications]
|
|
8796
8879
|
);
|
|
8797
|
-
const handleClick = (0,
|
|
8880
|
+
const handleClick = (0, import_react47.useCallback)((notif) => {
|
|
8798
8881
|
if (!notif.read) markRead(notif.id);
|
|
8799
8882
|
const notifData = notif.data ?? {};
|
|
8800
8883
|
const notifType = notif.type ?? notifData.type ?? "default";
|
|
@@ -8804,7 +8887,7 @@ function NotificationList() {
|
|
|
8804
8887
|
window.location.assign(href);
|
|
8805
8888
|
}
|
|
8806
8889
|
}, [markRead, registry]);
|
|
8807
|
-
return /* @__PURE__ */
|
|
8890
|
+
return /* @__PURE__ */ import_react47.default.createElement("div", { className: "brokr-notif-list" }, /* @__PURE__ */ import_react47.default.createElement("div", { className: "brokr-notif-list-header" }, /* @__PURE__ */ import_react47.default.createElement("span", { className: "brokr-notif-list-title" }, "Notifications"), unreadCount > 0 && /* @__PURE__ */ import_react47.default.createElement(
|
|
8808
8891
|
"button",
|
|
8809
8892
|
{
|
|
8810
8893
|
type: "button",
|
|
@@ -8812,7 +8895,7 @@ function NotificationList() {
|
|
|
8812
8895
|
onClick: markAllRead
|
|
8813
8896
|
},
|
|
8814
8897
|
"Mark all read"
|
|
8815
|
-
)), isLoading ? /* @__PURE__ */
|
|
8898
|
+
)), isLoading ? /* @__PURE__ */ import_react47.default.createElement(NotificationListSkeleton, null) : sorted.length === 0 ? /* @__PURE__ */ import_react47.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react47.default.createElement("svg", { "aria-hidden": "true", width: "40", height: "40", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", style: { opacity: 0.3 } }, /* @__PURE__ */ import_react47.default.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ import_react47.default.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })), /* @__PURE__ */ import_react47.default.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : /* @__PURE__ */ import_react47.default.createElement("div", { className: "brokr-notif-list-items" }, sorted.map((notif) => /* @__PURE__ */ import_react47.default.createElement(
|
|
8816
8899
|
NotifListItem,
|
|
8817
8900
|
{
|
|
8818
8901
|
key: notif.id,
|
|
@@ -8853,6 +8936,7 @@ function defineAccount(config) {
|
|
|
8853
8936
|
0 && (module.exports = {
|
|
8854
8937
|
AIChat,
|
|
8855
8938
|
AccountPanel,
|
|
8939
|
+
ActivityFeed,
|
|
8856
8940
|
AuthPageShell,
|
|
8857
8941
|
AuthWall,
|
|
8858
8942
|
AutoReloadToggle,
|
|
@@ -8882,8 +8966,10 @@ function defineAccount(config) {
|
|
|
8882
8966
|
UpdateBilling,
|
|
8883
8967
|
UpgradePrompt,
|
|
8884
8968
|
UsageGate,
|
|
8969
|
+
UsageGrid,
|
|
8885
8970
|
UserButton,
|
|
8886
8971
|
defineAccount,
|
|
8972
|
+
defineBrokrTheme,
|
|
8887
8973
|
defineChat,
|
|
8888
8974
|
useBrokr,
|
|
8889
8975
|
useBrokrTheme,
|