@brokr/sdk 2.1.1 → 2.1.2

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