@brokr/sdk 2.0.0 → 2.1.0

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.
Files changed (41) hide show
  1. package/dist/react-notifications.js +63 -50
  2. package/dist/react-notifications.mjs +63 -50
  3. package/dist/react-styles.js +113 -75
  4. package/dist/react-styles.mjs +113 -75
  5. package/dist/react-theme.js +6 -4
  6. package/dist/react-theme.mjs +6 -4
  7. package/dist/react.js +325 -246
  8. package/dist/react.mjs +360 -281
  9. package/dist/src/react/account/AccountPanel.d.ts.map +1 -1
  10. package/dist/src/react/account/UserButton.d.ts.map +1 -1
  11. package/dist/src/react/chat/AIChat.d.ts.map +1 -1
  12. package/dist/src/react/chat/ChatInput.d.ts.map +1 -1
  13. package/dist/src/react/chat/MessagePane.d.ts.map +1 -1
  14. package/dist/src/react/chat/ModelSelector.d.ts.map +1 -1
  15. package/dist/src/react/chat/ThreadSidebar.d.ts.map +1 -1
  16. package/dist/src/react/composites/FabAI.d.ts.map +1 -1
  17. package/dist/src/react/css/account.d.ts +1 -1
  18. package/dist/src/react/css/account.d.ts.map +1 -1
  19. package/dist/src/react/css/auth.d.ts +1 -1
  20. package/dist/src/react/css/auth.d.ts.map +1 -1
  21. package/dist/src/react/css/chat-extras.d.ts +1 -1
  22. package/dist/src/react/css/chat-extras.d.ts.map +1 -1
  23. package/dist/src/react/css/chat.d.ts +1 -1
  24. package/dist/src/react/css/chat.d.ts.map +1 -1
  25. package/dist/src/react/css/composites.d.ts +1 -1
  26. package/dist/src/react/css/composites.d.ts.map +1 -1
  27. package/dist/src/react/css/markdown.d.ts +1 -1
  28. package/dist/src/react/css/markdown.d.ts.map +1 -1
  29. package/dist/src/react/css/notifications.d.ts +1 -1
  30. package/dist/src/react/css/notifications.d.ts.map +1 -1
  31. package/dist/src/react/css/tokens.d.ts +1 -1
  32. package/dist/src/react/css/tokens.d.ts.map +1 -1
  33. package/dist/src/react/notifications/NotificationBell.d.ts.map +1 -1
  34. package/dist/src/react/notifications/NotificationList.d.ts.map +1 -1
  35. package/dist/src/react/notifications/Toast.d.ts.map +1 -1
  36. package/dist/src/react/payments/Plans.d.ts.map +1 -1
  37. package/dist/src/react/theme.d.ts.map +1 -1
  38. package/dist/src/react/types.d.ts +4 -4
  39. package/dist/src/react/types.d.ts.map +1 -1
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/package.json +1 -1
package/dist/react.mjs CHANGED
@@ -4173,10 +4173,12 @@ function getBrokrRootStyle(theme) {
4173
4173
  ...theme.colors.error ? { ["--brokr-error"]: theme.colors.error } : {},
4174
4174
  ...theme.colors.success ? { ["--brokr-success"]: theme.colors.success } : {},
4175
4175
  ...theme.colors.warning ? { ["--brokr-warning"]: theme.colors.warning } : {},
4176
- ...theme.colors.toastBg ? { ["--brokr-toast-bg"]: theme.colors.toastBg } : {},
4177
- ...theme.colors.toastText ? { ["--brokr-toast-text"]: theme.colors.toastText } : {},
4178
- ...theme.colors.toastTextSecondary ? { ["--brokr-toast-text-secondary"]: theme.colors.toastTextSecondary } : {},
4179
- ...theme.colors.toastBorder ? { ["--brokr-toast-border"]: theme.colors.toastBorder } : {},
4176
+ // Toast vars always emitted with smart defaults — toasts stay light in dark mode.
4177
+ // Users can override via theme.colors.toastBg etc.
4178
+ ["--brokr-toast-bg"]: theme.colors.toastBg ?? "#ffffff",
4179
+ ["--brokr-toast-text"]: theme.colors.toastText ?? "#0a0a0a",
4180
+ ["--brokr-toast-text-secondary"]: theme.colors.toastTextSecondary ?? "#52525b",
4181
+ ["--brokr-toast-border"]: theme.colors.toastBorder ?? "#e4e4e7",
4180
4182
  ["--brokr-radius-card"]: `${theme.radii.card}px`,
4181
4183
  ["--brokr-radius-input"]: `${theme.radii.input}px`,
4182
4184
  ["--brokr-radius-button"]: `${theme.radii.button}px`
@@ -4298,6 +4300,37 @@ function resolveNotificationType(registry, type, data) {
4298
4300
 
4299
4301
  // src/react/notifications/Toast.tsx
4300
4302
  import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
4303
+ function ToastItem({
4304
+ notification,
4305
+ exiting,
4306
+ registry,
4307
+ onDismiss
4308
+ }) {
4309
+ const handleDismiss = useCallback(() => onDismiss(notification.id), [notification.id, onDismiss]);
4310
+ const notifData = notification.data ?? {};
4311
+ const notifType = notifData.type ?? "default";
4312
+ const resolved = resolveNotificationType(registry, notifType, notifData);
4313
+ return /* @__PURE__ */ React.createElement(
4314
+ "div",
4315
+ {
4316
+ className: `brokr-toast brokr-toast--${notification.variant}${exiting ? " brokr-toast--exit" : ""}`,
4317
+ role: "alert",
4318
+ "aria-live": "polite"
4319
+ },
4320
+ resolved.image ? /* @__PURE__ */ React.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-toast-provider-logo" }) : /* @__PURE__ */ React.createElement("span", { className: `brokr-toast-icon brokr-toast-icon--${notification.variant}` }),
4321
+ /* @__PURE__ */ React.createElement("div", { className: "brokr-toast-content" }, /* @__PURE__ */ React.createElement("span", { className: "brokr-toast-title" }, notification.title), /* @__PURE__ */ React.createElement("span", { className: "brokr-toast-message" }, notification.message)),
4322
+ /* @__PURE__ */ React.createElement(
4323
+ "button",
4324
+ {
4325
+ type: "button",
4326
+ className: "brokr-toast-dismiss",
4327
+ onClick: handleDismiss,
4328
+ "aria-label": "Dismiss notification"
4329
+ },
4330
+ /* @__PURE__ */ React.createElement("svg", { "aria-hidden": "true", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M18 6 6 18M6 6l12 12" }))
4331
+ )
4332
+ );
4333
+ }
4301
4334
  var DEFAULT_DURATION = 4e3;
4302
4335
  var DEFAULT_MAX_VISIBLE = 3;
4303
4336
  var EXIT_ANIMATION_MS = 300;
@@ -4308,6 +4341,26 @@ function ToastLayer({ toasts, config, registry, onDismiss }) {
4308
4341
  const duration = config.toast?.duration ?? DEFAULT_DURATION;
4309
4342
  const maxVisible = config.toast?.maxVisible ?? DEFAULT_MAX_VISIBLE;
4310
4343
  const position = config.toast?.position ?? "top-right";
4344
+ const onDismissRef = useRef(onDismiss);
4345
+ onDismissRef.current = onDismiss;
4346
+ const exitingRef = useRef(/* @__PURE__ */ new Set());
4347
+ const dismissToast = useCallback((id) => {
4348
+ if (exitingRef.current.has(id)) return;
4349
+ exitingRef.current.add(id);
4350
+ setEntries(
4351
+ (prev) => prev.map((e) => e.notification.id === id ? { ...e, exiting: true } : e)
4352
+ );
4353
+ setTimeout(() => {
4354
+ setEntries((prev) => prev.filter((e) => e.notification.id !== id));
4355
+ onDismissRef.current(id);
4356
+ exitingRef.current.delete(id);
4357
+ }, EXIT_ANIMATION_MS);
4358
+ const timer = timersRef.current.get(id);
4359
+ if (timer) {
4360
+ clearTimeout(timer);
4361
+ timersRef.current.delete(id);
4362
+ }
4363
+ }, []);
4311
4364
  useEffect(() => {
4312
4365
  for (const toast of toasts) {
4313
4366
  if (processedRef.current.has(toast.id)) continue;
@@ -4326,21 +4379,7 @@ function ToastLayer({ toasts, config, registry, onDismiss }) {
4326
4379
  timersRef.current.set(toast.id, timer);
4327
4380
  }
4328
4381
  }
4329
- }, [toasts, duration, maxVisible]);
4330
- const dismissToast = useCallback((id) => {
4331
- setEntries(
4332
- (prev) => prev.map((e) => e.notification.id === id ? { ...e, exiting: true } : e)
4333
- );
4334
- setTimeout(() => {
4335
- setEntries((prev) => prev.filter((e) => e.notification.id !== id));
4336
- onDismiss(id);
4337
- }, EXIT_ANIMATION_MS);
4338
- const timer = timersRef.current.get(id);
4339
- if (timer) {
4340
- clearTimeout(timer);
4341
- timersRef.current.delete(id);
4342
- }
4343
- }, [onDismiss]);
4382
+ }, [toasts, duration, maxVisible, dismissToast]);
4344
4383
  useEffect(() => {
4345
4384
  return () => {
4346
4385
  for (const timer of timersRef.current.values()) {
@@ -4360,39 +4399,16 @@ function ToastLayer({ toasts, config, registry, onDismiss }) {
4360
4399
  return map[position] ?? "brokr-toast-layer--bottom-right";
4361
4400
  }, [position]);
4362
4401
  if (entries.length === 0) return null;
4363
- return /* @__PURE__ */ React.createElement("div", { className: `brokr-toast-layer ${positionClass}` }, entries.map(({ notification, exiting }) => {
4364
- const notifData = notification.data ?? {};
4365
- const notifType = notifData.type ?? "default";
4366
- const resolved = resolveNotificationType(registry, notifType, notifData);
4367
- return /* @__PURE__ */ React.createElement(
4368
- "div",
4369
- {
4370
- key: notification.id,
4371
- className: `brokr-toast brokr-toast--${notification.variant}${exiting ? " brokr-toast--exit" : ""}`,
4372
- role: "alert",
4373
- "aria-live": "polite"
4374
- },
4375
- resolved.image ? /* @__PURE__ */ React.createElement(
4376
- "img",
4377
- {
4378
- src: resolved.image.url,
4379
- alt: resolved.image.alt,
4380
- className: "brokr-toast-provider-logo"
4381
- }
4382
- ) : /* @__PURE__ */ React.createElement("span", { className: `brokr-toast-icon brokr-toast-icon--${notification.variant}` }),
4383
- /* @__PURE__ */ React.createElement("div", { className: "brokr-toast-content" }, /* @__PURE__ */ React.createElement("span", { className: "brokr-toast-title" }, notification.title), /* @__PURE__ */ React.createElement("span", { className: "brokr-toast-message" }, notification.message)),
4384
- /* @__PURE__ */ React.createElement(
4385
- "button",
4386
- {
4387
- type: "button",
4388
- className: "brokr-toast-dismiss",
4389
- onClick: () => dismissToast(notification.id),
4390
- "aria-label": "Dismiss notification"
4391
- },
4392
- /* @__PURE__ */ React.createElement("svg", { "aria-hidden": "true", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M18 6 6 18M6 6l12 12" }))
4393
- )
4394
- );
4395
- }));
4402
+ return /* @__PURE__ */ React.createElement("div", { className: `brokr-toast-layer ${positionClass}` }, entries.map(({ notification, exiting }) => /* @__PURE__ */ React.createElement(
4403
+ ToastItem,
4404
+ {
4405
+ key: notification.id,
4406
+ notification,
4407
+ exiting,
4408
+ registry,
4409
+ onDismiss: dismissToast
4410
+ }
4411
+ )));
4396
4412
  }
4397
4413
 
4398
4414
  // src/react/notifications/provider.tsx
@@ -5515,6 +5531,26 @@ function filterDefined(defaults, overrides) {
5515
5531
  }
5516
5532
 
5517
5533
  // src/react/account/AccountPanel.tsx
5534
+ function AccountTabButton({
5535
+ tab,
5536
+ active,
5537
+ icon,
5538
+ label,
5539
+ onSelect
5540
+ }) {
5541
+ const handleClick = useCallback8(() => onSelect(tab), [tab, onSelect]);
5542
+ return /* @__PURE__ */ React10.createElement(
5543
+ "button",
5544
+ {
5545
+ className: "brokr-account-tab",
5546
+ "data-active": active,
5547
+ onClick: handleClick,
5548
+ type: "button"
5549
+ },
5550
+ /* @__PURE__ */ React10.createElement("span", { className: "brokr-account-tab-icon" }, icon),
5551
+ /* @__PURE__ */ React10.createElement("span", null, label)
5552
+ );
5553
+ }
5518
5554
  var TAB_META = {
5519
5555
  general: {
5520
5556
  label: "General"
@@ -5646,16 +5682,15 @@ function AccountPanel(inlineProps) {
5646
5682
  }
5647
5683
  }, []);
5648
5684
  return /* @__PURE__ */ React10.createElement("section", { className: "brokr-account-panel", "data-density": density }, /* @__PURE__ */ React10.createElement("aside", { className: "brokr-account-sidebar" }, groupedTabs.map((group) => /* @__PURE__ */ React10.createElement("div", { className: "brokr-account-sidebar-group", key: group.label }, /* @__PURE__ */ React10.createElement("span", { className: "brokr-account-sidebar-kicker" }, group.label), /* @__PURE__ */ React10.createElement("nav", { className: "brokr-account-tab-list", "aria-label": `${group.label} settings` }, group.tabs.map((tab) => /* @__PURE__ */ React10.createElement(
5649
- "button",
5685
+ AccountTabButton,
5650
5686
  {
5651
- className: "brokr-account-tab",
5652
- "data-active": activeTab === tab,
5653
5687
  key: tab,
5654
- onClick: () => setActiveTab(tab),
5655
- type: "button"
5656
- },
5657
- /* @__PURE__ */ React10.createElement("span", { className: "brokr-account-tab-icon" }, getTabIcon(tab)),
5658
- /* @__PURE__ */ React10.createElement("span", null, TAB_META[tab].label)
5688
+ tab,
5689
+ active: activeTab === tab,
5690
+ icon: getTabIcon(tab),
5691
+ label: TAB_META[tab].label,
5692
+ onSelect: setActiveTab
5693
+ }
5659
5694
  )))))), /* @__PURE__ */ React10.createElement("div", { className: "brokr-account-surface" }, /* @__PURE__ */ React10.createElement("header", { className: "brokr-account-surface-header" }, /* @__PURE__ */ React10.createElement("h2", { className: "brokr-title" }, activeTabMeta.label)), activeTab === "general" ? /* @__PURE__ */ React10.createElement("form", { className: "brokr-account-form", onSubmit: handleSaveProfile }, /* @__PURE__ */ React10.createElement("section", { className: "brokr-account-card" }, /* @__PURE__ */ React10.createElement("div", { className: "brokr-account-photo-row" }, /* @__PURE__ */ React10.createElement(ProfilePhotoButton, { size: 88 }), /* @__PURE__ */ React10.createElement("div", { className: "brokr-account-photo-copy" }, /* @__PURE__ */ React10.createElement("strong", null, "Profile photo"), /* @__PURE__ */ React10.createElement("span", { className: "brokr-copy" }, "PNG, JPEG, and GIF under 10MB"), /* @__PURE__ */ React10.createElement("span", { className: "brokr-account-photo-hint" }, /* @__PURE__ */ React10.createElement(UploadIcon, { size: 14 }), "Upload new picture")))), /* @__PURE__ */ React10.createElement("section", { className: "brokr-account-card brokr-account-field-list" }, /* @__PURE__ */ React10.createElement("div", { className: "brokr-account-field-row" }, /* @__PURE__ */ React10.createElement("div", { className: "brokr-account-field-head" }, /* @__PURE__ */ React10.createElement("label", { className: "brokr-label", htmlFor: "brokr-account-name" }, "Name")), /* @__PURE__ */ React10.createElement("div", { className: "brokr-account-field-body" }, /* @__PURE__ */ React10.createElement(
5660
5695
  "input",
5661
5696
  {
@@ -5763,6 +5798,9 @@ function UserButton({
5763
5798
  setOpen(false);
5764
5799
  await signOut();
5765
5800
  }, [signOut]);
5801
+ const stopPropagation = useCallback9((event) => {
5802
+ event.stopPropagation();
5803
+ }, []);
5766
5804
  useEffect7(() => {
5767
5805
  if (!open) return void 0;
5768
5806
  const handlePointerDown = (event) => {
@@ -5796,13 +5834,13 @@ function UserButton({
5796
5834
  "data-align": align,
5797
5835
  role: "dialog"
5798
5836
  },
5799
- /* @__PURE__ */ React12.createElement("div", { className: "brokr-account-menu" }, /* @__PURE__ */ React12.createElement("div", { className: "brokr-account-menu-header" }, /* @__PURE__ */ React12.createElement(Avatar, { email: summary.email, name: summary.name, src: user.image, size: 44 }), /* @__PURE__ */ React12.createElement("div", { className: "brokr-account-menu-copy" }, /* @__PURE__ */ React12.createElement("strong", null, summary.name), /* @__PURE__ */ React12.createElement("span", null, summary.email))), /* @__PURE__ */ React12.createElement("div", { className: "brokr-account-menu-actions" }, /* @__PURE__ */ React12.createElement("button", { className: "brokr-account-menu-action", onClick: openSettings, type: "button" }, /* @__PURE__ */ React12.createElement("span", { className: "brokr-account-menu-action-icon" }, /* @__PURE__ */ React12.createElement(SettingsIcon, { size: 15 })), /* @__PURE__ */ React12.createElement("span", null, "Settings")), /* @__PURE__ */ React12.createElement("button", { className: "brokr-account-menu-action", onClick: () => void handleSignOut(), type: "button" }, /* @__PURE__ */ React12.createElement("span", { className: "brokr-account-menu-action-icon" }, /* @__PURE__ */ React12.createElement(LogoutIcon, { size: 15 })), /* @__PURE__ */ React12.createElement("span", null, "Sign out"))))
5837
+ /* @__PURE__ */ React12.createElement("div", { className: "brokr-account-menu" }, /* @__PURE__ */ React12.createElement("div", { className: "brokr-account-menu-header" }, /* @__PURE__ */ React12.createElement(Avatar, { email: summary.email, name: summary.name, src: user.image, size: 44 }), /* @__PURE__ */ React12.createElement("div", { className: "brokr-account-menu-copy" }, /* @__PURE__ */ React12.createElement("strong", null, summary.name), /* @__PURE__ */ React12.createElement("span", null, summary.email))), /* @__PURE__ */ React12.createElement("div", { className: "brokr-account-menu-actions" }, /* @__PURE__ */ React12.createElement("button", { className: "brokr-account-menu-action", onClick: openSettings, type: "button" }, /* @__PURE__ */ React12.createElement("span", { className: "brokr-account-menu-action-icon" }, /* @__PURE__ */ React12.createElement(SettingsIcon, { size: 15 })), /* @__PURE__ */ React12.createElement("span", null, "Settings")), /* @__PURE__ */ React12.createElement("button", { className: "brokr-account-menu-action", onClick: handleSignOut, type: "button" }, /* @__PURE__ */ React12.createElement("span", { className: "brokr-account-menu-action-icon" }, /* @__PURE__ */ React12.createElement(LogoutIcon, { size: 15 })), /* @__PURE__ */ React12.createElement("span", null, "Sign out"))))
5800
5838
  ) : null, settingsOpen ? /* @__PURE__ */ React12.createElement("div", { className: "brokr-modal-backdrop", role: "presentation", onClick: closeSettings }, /* @__PURE__ */ React12.createElement(
5801
5839
  "div",
5802
5840
  {
5803
5841
  "aria-modal": "true",
5804
5842
  className: "brokr-panel brokr-modal-dialog brokr-account-settings-dialog",
5805
- onClick: (event) => event.stopPropagation(),
5843
+ onClick: stopPropagation,
5806
5844
  role: "dialog"
5807
5845
  },
5808
5846
  /* @__PURE__ */ React12.createElement("div", { className: "brokr-modal-toolbar" }, /* @__PURE__ */ React12.createElement("button", { className: "brokr-button-ghost", onClick: closeSettings, type: "button" }, /* @__PURE__ */ React12.createElement(CloseIcon, { size: 16 }))),
@@ -5998,6 +6036,28 @@ function AuthWall({ children }) {
5998
6036
 
5999
6037
  // src/react/payments/Plans.tsx
6000
6038
  import React21, { useCallback as useCallback12, useMemo as useMemo13, useState as useState12 } from "react";
6039
+ function PlanCard({
6040
+ plan,
6041
+ highlight,
6042
+ isPending,
6043
+ onSelect
6044
+ }) {
6045
+ const isHighlighted = highlight === plan.slug;
6046
+ const features = Object.entries(plan.features);
6047
+ const handleClick = useCallback12(() => {
6048
+ void onSelect(plan.slug);
6049
+ }, [plan.slug, onSelect]);
6050
+ return /* @__PURE__ */ React21.createElement("article", { className: "brokr-card brokr-plan-card", "data-highlight": isHighlighted, key: plan.slug }, /* @__PURE__ */ React21.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-3)" } }, /* @__PURE__ */ React21.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ React21.createElement("strong", null, plan.name), plan.isCurrent ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-badge" }, "Current") : null, isHighlighted && !plan.isCurrent ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-badge" }, "Recommended") : null), /* @__PURE__ */ React21.createElement("div", { className: "brokr-plan-price" }, /* @__PURE__ */ React21.createElement("span", { className: "brokr-plan-value" }, plan.price === 0 ? "$0" : `$${(plan.price / 100).toFixed(0)}`), /* @__PURE__ */ React21.createElement("span", { className: "brokr-copy" }, "/", plan.interval)), plan.trialDays ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-copy" }, plan.trialDays, " day trial included.") : null), /* @__PURE__ */ React21.createElement(
6051
+ "button",
6052
+ {
6053
+ className: plan.isCurrent ? "brokr-button-secondary" : "brokr-button",
6054
+ disabled: plan.isCurrent || isPending,
6055
+ onClick: handleClick,
6056
+ type: "button"
6057
+ },
6058
+ plan.isCurrent ? "Current plan" : isPending ? "Redirecting" : "Choose plan"
6059
+ ), /* @__PURE__ */ React21.createElement("ul", { className: "brokr-feature-list" }, features.map(([key, value]) => /* @__PURE__ */ React21.createElement("li", { className: "brokr-feature-item", key }, /* @__PURE__ */ React21.createElement(CheckIcon, { size: 14 }), /* @__PURE__ */ React21.createElement("span", null, featureLabel(key), " \xB7 ", featureValueLabel(value))))));
6060
+ }
6001
6061
  function Plans({
6002
6062
  columns,
6003
6063
  highlight,
@@ -6028,34 +6088,16 @@ function Plans({
6028
6088
  setPendingPlan(null);
6029
6089
  }
6030
6090
  }, [checkout, onSelect]);
6031
- return /* @__PURE__ */ React21.createElement("div", { className: "brokr-section" }, paymentsMode === "sandbox" ? /* @__PURE__ */ React21.createElement("div", { className: "brokr-inline-message" }, "Sandbox mode \u2014 test cards only.") : null, error ? /* @__PURE__ */ React21.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null, /* @__PURE__ */ React21.createElement("div", { className: "brokr-grid-auto", style: layoutStyle }, plans.map((plan) => {
6032
- const features = Object.entries(plan.features);
6033
- const isHighlighted = highlight === plan.slug;
6034
- const isPending = pendingPlan === plan.slug;
6035
- const handleClick = () => {
6036
- void handleSelect(plan.slug);
6037
- };
6038
- return /* @__PURE__ */ React21.createElement(
6039
- "article",
6040
- {
6041
- className: "brokr-card brokr-plan-card",
6042
- "data-highlight": isHighlighted,
6043
- key: plan.slug
6044
- },
6045
- /* @__PURE__ */ React21.createElement("div", { className: "brokr-section", style: { gap: "0.75rem" } }, /* @__PURE__ */ React21.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ React21.createElement("strong", null, plan.name), plan.isCurrent ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-badge" }, "Current") : null, isHighlighted && !plan.isCurrent ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-badge" }, "Recommended") : null), /* @__PURE__ */ React21.createElement("div", { className: "brokr-plan-price" }, /* @__PURE__ */ React21.createElement("span", { className: "brokr-plan-value" }, plan.price === 0 ? "$0" : `$${(plan.price / 100).toFixed(0)}`), /* @__PURE__ */ React21.createElement("span", { className: "brokr-copy" }, "/", plan.interval)), plan.trialDays ? /* @__PURE__ */ React21.createElement("span", { className: "brokr-copy" }, plan.trialDays, " day trial included.") : null),
6046
- /* @__PURE__ */ React21.createElement(
6047
- "button",
6048
- {
6049
- className: plan.isCurrent ? "brokr-button-secondary" : "brokr-button",
6050
- disabled: plan.isCurrent || isPending,
6051
- onClick: handleClick,
6052
- type: "button"
6053
- },
6054
- plan.isCurrent ? "Current plan" : isPending ? "Redirecting" : "Choose plan"
6055
- ),
6056
- /* @__PURE__ */ React21.createElement("ul", { className: "brokr-feature-list" }, features.map(([key, value]) => /* @__PURE__ */ React21.createElement("li", { className: "brokr-feature-item", key }, /* @__PURE__ */ React21.createElement(CheckIcon, { size: 14 }), /* @__PURE__ */ React21.createElement("span", null, featureLabel(key), " \xB7 ", featureValueLabel(value)))))
6057
- );
6058
- })));
6091
+ return /* @__PURE__ */ React21.createElement("div", { className: "brokr-section" }, paymentsMode === "sandbox" ? /* @__PURE__ */ React21.createElement("div", { className: "brokr-inline-message" }, "Sandbox mode \u2014 test cards only.") : null, error ? /* @__PURE__ */ React21.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null, /* @__PURE__ */ React21.createElement("div", { className: "brokr-grid-auto", style: layoutStyle }, plans.map((plan) => /* @__PURE__ */ React21.createElement(
6092
+ PlanCard,
6093
+ {
6094
+ key: plan.slug,
6095
+ plan,
6096
+ highlight,
6097
+ isPending: pendingPlan === plan.slug,
6098
+ onSelect: handleSelect
6099
+ }
6100
+ ))));
6059
6101
  }
6060
6102
 
6061
6103
  // src/react/payments/FeatureMeter.tsx
@@ -6193,7 +6235,7 @@ function AutoReloadToggle({ onChange }) {
6193
6235
  setIsPending(false);
6194
6236
  }
6195
6237
  }, [enabled, onChange]);
6196
- return /* @__PURE__ */ React26.createElement("div", { className: "brokr-card brokr-gate-card" }, /* @__PURE__ */ React26.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ React26.createElement("div", { className: "brokr-section", style: { gap: "0.35rem" } }, /* @__PURE__ */ React26.createElement("strong", null, "Auto reload"), /* @__PURE__ */ React26.createElement("span", { className: "brokr-copy" }, helperText)), /* @__PURE__ */ React26.createElement(
6238
+ return /* @__PURE__ */ React26.createElement("div", { className: "brokr-card brokr-gate-card" }, /* @__PURE__ */ React26.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ React26.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ React26.createElement("strong", null, "Auto reload"), /* @__PURE__ */ React26.createElement("span", { className: "brokr-copy" }, helperText)), /* @__PURE__ */ React26.createElement(
6197
6239
  "button",
6198
6240
  {
6199
6241
  className: "brokr-button-secondary",
@@ -6243,7 +6285,7 @@ function CancelSubscription({ onCancel }) {
6243
6285
 
6244
6286
  // src/react/chat/AIChat.tsx
6245
6287
  import React35, {
6246
- useCallback as useCallback22,
6288
+ useCallback as useCallback23,
6247
6289
  useEffect as useEffect12,
6248
6290
  useMemo as useMemo23,
6249
6291
  useRef as useRef7,
@@ -6889,6 +6931,37 @@ function useChat(config) {
6889
6931
 
6890
6932
  // src/react/chat/ModelSelector.tsx
6891
6933
  import React29, { useCallback as useCallback17, useEffect as useEffect10, useMemo as useMemo19, useRef as useRef6, useState as useState17 } from "react";
6934
+ function ModelOption({
6935
+ provider,
6936
+ isActive,
6937
+ isLocked,
6938
+ onSelect,
6939
+ onCheckout
6940
+ }) {
6941
+ const handleClick = useCallback17(() => {
6942
+ if (isLocked) {
6943
+ void onCheckout({ plan: "pro" });
6944
+ } else {
6945
+ onSelect(provider.model);
6946
+ }
6947
+ }, [isLocked, onCheckout, onSelect, provider.model]);
6948
+ return /* @__PURE__ */ React29.createElement(
6949
+ "button",
6950
+ {
6951
+ "aria-selected": isActive,
6952
+ className: "brokr-model-option",
6953
+ "data-active": isActive,
6954
+ "data-locked": isLocked,
6955
+ disabled: isLocked,
6956
+ onClick: handleClick,
6957
+ title: isLocked ? "Add credits to unlock" : void 0,
6958
+ type: "button"
6959
+ },
6960
+ /* @__PURE__ */ React29.createElement("img", { alt: "", className: "brokr-model-logo", src: provider.logo }),
6961
+ /* @__PURE__ */ React29.createElement("span", { className: "brokr-model-option-label" }, provider.label),
6962
+ isLocked ? /* @__PURE__ */ React29.createElement("span", { className: "brokr-model-lock", "aria-hidden": "true" }, /* @__PURE__ */ React29.createElement(LockIcon, { size: 13 })) : null
6963
+ );
6964
+ }
6892
6965
  function ModelSelector({
6893
6966
  activeModel,
6894
6967
  setSelectedModel,
@@ -6901,6 +6974,10 @@ function ModelSelector({
6901
6974
  () => providers.find((p) => p.model === activeModel) ?? providers[0],
6902
6975
  [activeModel]
6903
6976
  );
6977
+ const handleModelSelect = useCallback17((model) => {
6978
+ setSelectedModel(model);
6979
+ setSelectorOpen(false);
6980
+ }, [setSelectedModel]);
6904
6981
  useEffect10(() => {
6905
6982
  if (!selectorOpen) return;
6906
6983
  const onMouseDown = (e) => {
@@ -6931,37 +7008,34 @@ function ModelSelector({
6931
7008
  activeProvider ? /* @__PURE__ */ React29.createElement("img", { alt: "", className: "brokr-model-logo", src: activeProvider.logo }) : /* @__PURE__ */ React29.createElement("span", { className: "brokr-model-dot", style: { background: "currentColor" } }),
6932
7009
  activeProvider?.label ?? "Model",
6933
7010
  /* @__PURE__ */ React29.createElement(ChevronDownIcon, { size: 13 })
6934
- ), selectorOpen ? /* @__PURE__ */ React29.createElement("div", { className: "brokr-model-dropdown", role: "listbox" }, providers.map((p) => {
6935
- const locked = !availableProviders.some((a) => a.id === p.id);
6936
- return /* @__PURE__ */ React29.createElement(
6937
- "button",
6938
- {
6939
- "aria-selected": p.model === activeModel,
6940
- className: "brokr-model-option",
6941
- "data-active": p.model === activeModel,
6942
- "data-locked": locked,
6943
- disabled: locked,
6944
- key: p.id,
6945
- onClick: () => {
6946
- if (locked) {
6947
- void checkout({ plan: "pro" });
6948
- } else {
6949
- setSelectedModel(p.model);
6950
- setSelectorOpen(false);
6951
- }
6952
- },
6953
- title: locked ? "Add credits to unlock" : void 0,
6954
- type: "button"
6955
- },
6956
- /* @__PURE__ */ React29.createElement("img", { alt: "", className: "brokr-model-logo", src: p.logo }),
6957
- /* @__PURE__ */ React29.createElement("span", { className: "brokr-model-option-label" }, p.label),
6958
- locked ? /* @__PURE__ */ React29.createElement("span", { className: "brokr-model-lock", "aria-hidden": "true" }, /* @__PURE__ */ React29.createElement(LockIcon, { size: 13 })) : null
6959
- );
6960
- })) : null);
7011
+ ), selectorOpen ? /* @__PURE__ */ React29.createElement("div", { className: "brokr-model-dropdown", role: "listbox" }, providers.map((p) => /* @__PURE__ */ React29.createElement(
7012
+ ModelOption,
7013
+ {
7014
+ key: p.id,
7015
+ provider: p,
7016
+ isActive: p.model === activeModel,
7017
+ isLocked: !availableProviders.some((a) => a.id === p.id),
7018
+ onSelect: handleModelSelect,
7019
+ onCheckout: checkout
7020
+ }
7021
+ ))) : null);
6961
7022
  }
6962
7023
 
6963
7024
  // src/react/chat/ThreadSidebar.tsx
6964
7025
  import React30, { useCallback as useCallback18, useMemo as useMemo20 } from "react";
7026
+ function ThreadItemButton({ id, title, isActive, onSelect }) {
7027
+ const handleClick = useCallback18(() => onSelect(id), [id, onSelect]);
7028
+ return /* @__PURE__ */ React30.createElement(
7029
+ "button",
7030
+ {
7031
+ className: "brokr-ai-chat-conversation",
7032
+ "data-active": isActive,
7033
+ onClick: handleClick,
7034
+ type: "button"
7035
+ },
7036
+ title
7037
+ );
7038
+ }
6965
7039
  function ThreadSidebar() {
6966
7040
  const {
6967
7041
  startNewChat,
@@ -7012,15 +7086,14 @@ function ThreadSidebar() {
7012
7086
  value: renameValue
7013
7087
  }
7014
7088
  ) : /* @__PURE__ */ React30.createElement(
7015
- "button",
7089
+ ThreadItemButton,
7016
7090
  {
7017
- className: "brokr-ai-chat-conversation",
7018
- "data-active": item.id === activeId,
7019
7091
  key: item.id,
7020
- onClick: () => selectThreadAndCloseSidebar(item.id),
7021
- type: "button"
7022
- },
7023
- item.title
7092
+ id: item.id,
7093
+ title: item.title,
7094
+ isActive: item.id === activeId,
7095
+ onSelect: selectThreadAndCloseSidebar
7096
+ }
7024
7097
  )));
7025
7098
  }, [
7026
7099
  threadsLoading,
@@ -7037,7 +7110,7 @@ function ThreadSidebar() {
7037
7110
  }
7038
7111
 
7039
7112
  // src/react/chat/MessagePane.tsx
7040
- import React33, { useMemo as useMemo22 } from "react";
7113
+ import React33, { useCallback as useCallback21, useMemo as useMemo22 } from "react";
7041
7114
 
7042
7115
  // src/react/chat/MessageBubble.tsx
7043
7116
  import React32, { useCallback as useCallback20 } from "react";
@@ -7220,6 +7293,12 @@ function MessageBubble({ message, isTyping, user }) {
7220
7293
  }
7221
7294
 
7222
7295
  // src/react/chat/MessagePane.tsx
7296
+ function StarterPromptButton({ prompt, onSend }) {
7297
+ const handleClick = useCallback21(() => {
7298
+ void onSend(prompt);
7299
+ }, [prompt, onSend]);
7300
+ return /* @__PURE__ */ React33.createElement("button", { className: "brokr-ai-chat-starter", onClick: handleClick, type: "button" }, prompt);
7301
+ }
7223
7302
  function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
7224
7303
  const {
7225
7304
  displayMessages,
@@ -7250,20 +7329,17 @@ function MessagePane({ starterPrompts, emptyTitle, emptyCopy, subtitle }) {
7250
7329
  );
7251
7330
  });
7252
7331
  }, [visibleMessages, isSubmitting, user]);
7253
- 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(
7254
- "button",
7255
- {
7256
- className: "brokr-ai-chat-starter",
7257
- key: sp,
7258
- onClick: () => void sendMessage(sp),
7259
- type: "button"
7260
- },
7261
- sp
7262
- ))) : 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 })));
7332
+ 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 })));
7263
7333
  }
7264
7334
 
7265
7335
  // src/react/chat/ChatInput.tsx
7266
- import React34, { useCallback as useCallback21, useEffect as useEffect11 } from "react";
7336
+ import React34, { useCallback as useCallback22, useEffect as useEffect11 } from "react";
7337
+ function CommandButton({ cmd, chatContext }) {
7338
+ const handleClick = useCallback22(() => {
7339
+ void cmd.run(chatContext);
7340
+ }, [cmd, chatContext]);
7341
+ return /* @__PURE__ */ React34.createElement("button", { className: "brokr-ai-chat-sidebar-button", key: cmd.id, onClick: handleClick, type: "button" }, cmd.text);
7342
+ }
7267
7343
  function ChatInput() {
7268
7344
  const {
7269
7345
  input,
@@ -7281,29 +7357,20 @@ function ChatInput() {
7281
7357
  el.style.height = "auto";
7282
7358
  el.style.height = `${Math.min(el.scrollHeight, 168)}px`;
7283
7359
  }, [input, textareaRef]);
7284
- const handleSubmit = useCallback21((e) => {
7360
+ const handleSubmit = useCallback22((e) => {
7285
7361
  e.preventDefault();
7286
7362
  void sendMessage();
7287
7363
  }, [sendMessage]);
7288
- const handleKeyDown = useCallback21((e) => {
7364
+ const handleKeyDown = useCallback22((e) => {
7289
7365
  if (e.key === "Enter" && !e.shiftKey) {
7290
7366
  e.preventDefault();
7291
7367
  void sendMessage();
7292
7368
  }
7293
7369
  }, [sendMessage]);
7294
- const handleChange = useCallback21((e) => {
7370
+ const handleChange = useCallback22((e) => {
7295
7371
  setInput(e.target.value);
7296
7372
  }, [setInput]);
7297
- 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(
7298
- "button",
7299
- {
7300
- className: "brokr-ai-chat-sidebar-button",
7301
- key: cmd.id,
7302
- onClick: () => void cmd.run(chatContext),
7303
- type: "button"
7304
- },
7305
- cmd.text
7306
- ))) : null, /* @__PURE__ */ React34.createElement("div", { className: "brokr-ai-chat-input-row" }, /* @__PURE__ */ React34.createElement(
7373
+ 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(
7307
7374
  "textarea",
7308
7375
  {
7309
7376
  className: "brokr-ai-chat-textarea",
@@ -7437,11 +7504,19 @@ function AIChat(inlineProps) {
7437
7504
  const [sidebarOpen, setSidebarOpen] = useState18(false);
7438
7505
  const [threadMenuOpenId, setThreadMenuOpenId] = useState18(null);
7439
7506
  const threadMenuRef = useRef7(null);
7440
- const closeSidebar = useCallback22(() => setSidebarOpen(false), []);
7441
- const selectThreadAndCloseSidebar = useCallback22((id) => {
7507
+ const closeSidebar = useCallback23(() => setSidebarOpen(false), []);
7508
+ const selectThreadAndCloseSidebar = useCallback23((id) => {
7442
7509
  chat.selectThread(id);
7443
7510
  setSidebarOpen(false);
7444
7511
  }, [chat.selectThread]);
7512
+ const handleCopy = useCallback23((content) => {
7513
+ navigator.clipboard.writeText(content).catch(() => {
7514
+ });
7515
+ }, []);
7516
+ const handleStartRename = useCallback23((threadId) => {
7517
+ setThreadMenuOpenId(null);
7518
+ chat.startRename(threadId);
7519
+ }, [chat.startRename]);
7445
7520
  useEffect12(() => {
7446
7521
  if (!threadMenuOpenId) return;
7447
7522
  const onMouseDown = (e) => {
@@ -7499,17 +7574,11 @@ function AIChat(inlineProps) {
7499
7574
  startNewChat: chat.startNewChat,
7500
7575
  selectThread: chat.selectThread,
7501
7576
  deleteThread: chat.deleteThread,
7502
- handleCopy: (content) => {
7503
- navigator.clipboard.writeText(content).catch(() => {
7504
- });
7505
- },
7577
+ handleCopy,
7506
7578
  renamingId: chat.renamingId,
7507
7579
  renameValue: chat.renameValue,
7508
7580
  setRenameValue: chat.setRenameValue,
7509
- startRename: (threadId) => {
7510
- setThreadMenuOpenId(null);
7511
- chat.startRename(threadId);
7512
- },
7581
+ startRename: handleStartRename,
7513
7582
  submitRename: chat.submitRename,
7514
7583
  displaySidebarItems: chat.displaySidebarItems,
7515
7584
  threadsLoading: chat.threadsLoading,
@@ -7595,6 +7664,19 @@ function AIChat(inlineProps) {
7595
7664
  ), /* @__PURE__ */ React35.createElement(ChatInput, null))
7596
7665
  ));
7597
7666
  }
7667
+ function CommandButton2({ cmd, chatContext }) {
7668
+ const handleClick = useCallback23(() => {
7669
+ void cmd.run(chatContext);
7670
+ }, [cmd, chatContext]);
7671
+ return /* @__PURE__ */ React35.createElement("button", { className: "brokr-ai-chat-sidebar-button", onClick: handleClick, type: "button" }, cmd.text);
7672
+ }
7673
+ function MenuCommandItem({ cmd, chatContext, onClose }) {
7674
+ const handleClick = useCallback23(() => {
7675
+ onClose();
7676
+ void cmd.run(chatContext);
7677
+ }, [cmd, chatContext, onClose]);
7678
+ return /* @__PURE__ */ React35.createElement("button", { className: "brokr-ai-chat-thread-dropdown-item", onClick: handleClick, type: "button" }, cmd.text);
7679
+ }
7598
7680
  function ChatHeader({
7599
7681
  activeId,
7600
7682
  sidebarVisible,
@@ -7614,10 +7696,20 @@ function ChatHeader({
7614
7696
  startRename,
7615
7697
  deleteThread
7616
7698
  }) {
7617
- const handleOpenSidebar = useCallback22(() => setSidebarOpen(true), [setSidebarOpen]);
7618
- const handleToggleMenu = useCallback22(() => {
7699
+ const handleOpenSidebar = useCallback23(() => setSidebarOpen(true), [setSidebarOpen]);
7700
+ const handleToggleMenu = useCallback23(() => {
7619
7701
  setThreadMenuOpenId(threadMenuOpenId ? null : activeId);
7620
7702
  }, [setThreadMenuOpenId, threadMenuOpenId, activeId]);
7703
+ const closeMenu = useCallback23(() => setThreadMenuOpenId(null), [setThreadMenuOpenId]);
7704
+ const handleRename = useCallback23(() => {
7705
+ if (activeId) startRename(activeId);
7706
+ }, [activeId, startRename]);
7707
+ const handleDelete = useCallback23(() => {
7708
+ if (activeId) {
7709
+ setThreadMenuOpenId(null);
7710
+ void deleteThread(activeId);
7711
+ }
7712
+ }, [activeId, deleteThread, setThreadMenuOpenId]);
7621
7713
  return /* @__PURE__ */ React35.createElement("header", { className: "brokr-ai-chat-topbar" }, /* @__PURE__ */ React35.createElement("div", { className: "brokr-ai-chat-topbar-left" }, sidebarVisible ? /* @__PURE__ */ React35.createElement(
7622
7714
  "button",
7623
7715
  {
@@ -7627,16 +7719,7 @@ function ChatHeader({
7627
7719
  type: "button"
7628
7720
  },
7629
7721
  /* @__PURE__ */ React35.createElement(MenuIcon, { size: 18 })
7630
- ) : null), /* @__PURE__ */ React35.createElement("div", { className: "brokr-ai-chat-topbar-actions" }, headerCommands.map((cmd) => /* @__PURE__ */ React35.createElement(
7631
- "button",
7632
- {
7633
- className: "brokr-ai-chat-sidebar-button",
7634
- key: cmd.id,
7635
- onClick: () => void cmd.run(chatContext),
7636
- type: "button"
7637
- },
7638
- cmd.text
7639
- )), modelSelectorVisible ? /* @__PURE__ */ React35.createElement(
7722
+ ) : 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(
7640
7723
  ModelSelector,
7641
7724
  {
7642
7725
  activeModel,
@@ -7657,32 +7740,25 @@ function ChatHeader({
7657
7740
  "button",
7658
7741
  {
7659
7742
  className: "brokr-ai-chat-thread-dropdown-item",
7660
- onClick: () => startRename(activeId),
7743
+ onClick: handleRename,
7661
7744
  type: "button"
7662
7745
  },
7663
7746
  /* @__PURE__ */ React35.createElement(PencilIcon, { size: 13 }),
7664
7747
  "Rename"
7665
7748
  ), threadMenuCommands.map((cmd) => /* @__PURE__ */ React35.createElement(
7666
- "button",
7749
+ MenuCommandItem,
7667
7750
  {
7668
- className: "brokr-ai-chat-thread-dropdown-item",
7669
7751
  key: cmd.id,
7670
- onClick: () => {
7671
- setThreadMenuOpenId(null);
7672
- void cmd.run(chatContext);
7673
- },
7674
- type: "button"
7675
- },
7676
- cmd.text
7752
+ cmd,
7753
+ chatContext,
7754
+ onClose: closeMenu
7755
+ }
7677
7756
  )), /* @__PURE__ */ React35.createElement(
7678
7757
  "button",
7679
7758
  {
7680
7759
  className: "brokr-ai-chat-thread-dropdown-item",
7681
7760
  "data-tone": "danger",
7682
- onClick: () => {
7683
- setThreadMenuOpenId(null);
7684
- void deleteThread(activeId);
7685
- },
7761
+ onClick: handleDelete,
7686
7762
  type: "button"
7687
7763
  },
7688
7764
  /* @__PURE__ */ React35.createElement(TrashIcon, { size: 13 }),
@@ -7691,7 +7767,11 @@ function ChatHeader({
7691
7767
  }
7692
7768
 
7693
7769
  // src/react/composites/FabAI.tsx
7694
- import React36, { useCallback as useCallback23, useMemo as useMemo24, useState as useState19 } from "react";
7770
+ import React36, { useCallback as useCallback24, useMemo as useMemo24, useState as useState19 } from "react";
7771
+ function StarterButton({ prompt, onSelect }) {
7772
+ const handleClick = useCallback24(() => onSelect(prompt), [prompt, onSelect]);
7773
+ return /* @__PURE__ */ React36.createElement("button", { className: "brokr-chat-starter", onClick: handleClick, type: "button" }, prompt);
7774
+ }
7695
7775
  function FabAI({
7696
7776
  model,
7697
7777
  onSendMessage,
@@ -7713,20 +7793,20 @@ function FabAI({
7713
7793
  [position]
7714
7794
  );
7715
7795
  const canChat = useMemo24(() => can("ai.chat"), [can]);
7716
- const toggleOpen = useCallback23(() => {
7796
+ const toggleOpen = useCallback24(() => {
7717
7797
  if (!canChat) {
7718
7798
  redirectTo("/pricing");
7719
7799
  return;
7720
7800
  }
7721
7801
  setIsOpen((current) => !current);
7722
7802
  }, [canChat]);
7723
- const handleInputChange = useCallback23((event) => {
7803
+ const handleInputChange = useCallback24((event) => {
7724
7804
  setInput(event.target.value);
7725
7805
  }, []);
7726
- const handleClose = useCallback23(() => {
7806
+ const handleClose = useCallback24(() => {
7727
7807
  setIsOpen(false);
7728
7808
  }, []);
7729
- const sendPrompt = useCallback23(async (prompt) => {
7809
+ const sendPrompt = useCallback24(async (prompt) => {
7730
7810
  const nextPrompt = prompt.trim();
7731
7811
  if (!nextPrompt) return;
7732
7812
  const nextMessages = [...messages, { role: "user", content: nextPrompt }];
@@ -7759,17 +7839,17 @@ function FabAI({
7759
7839
  setIsSending(false);
7760
7840
  }
7761
7841
  }, [messages, model, onSendMessage, systemPrompt]);
7762
- const handleSubmit = useCallback23(async (event) => {
7842
+ const handleSubmit = useCallback24(async (event) => {
7763
7843
  event.preventDefault();
7764
7844
  await sendPrompt(input);
7765
7845
  }, [input, sendPrompt]);
7766
- const handleKeyDown = useCallback23((event) => {
7846
+ const handleKeyDown = useCallback24((event) => {
7767
7847
  if (event.key === "Enter" && !event.shiftKey) {
7768
7848
  event.preventDefault();
7769
7849
  void sendPrompt(input);
7770
7850
  }
7771
7851
  }, [input, sendPrompt]);
7772
- const handleStarterPrompt = useCallback23((prompt) => {
7852
+ const handleStarterPrompt = useCallback24((prompt) => {
7773
7853
  void sendPrompt(prompt);
7774
7854
  }, [sendPrompt]);
7775
7855
  return /* @__PURE__ */ React36.createElement(React36.Fragment, null, /* @__PURE__ */ React36.createElement("div", { className: "brokr-chat-fab", style: launcherStyle }, /* @__PURE__ */ React36.createElement(
@@ -7783,30 +7863,16 @@ function FabAI({
7783
7863
  },
7784
7864
  /* @__PURE__ */ React36.createElement(MessageIcon, { size: 16 }),
7785
7865
  "Ask AI"
7786
- )), 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: "0.25rem" } }, /* @__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(
7866
+ )), isOpen ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-panel brokr-chat-panel", role: "dialog" }, /* @__PURE__ */ React36.createElement("div", { className: "brokr-brand-row", style: { justifyContent: "space-between" } }, /* @__PURE__ */ React36.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ React36.createElement("strong", null, "AI Chat"), user?.name ? /* @__PURE__ */ React36.createElement("span", { className: "brokr-copy" }, user.name) : null), /* @__PURE__ */ React36.createElement("button", { className: "brokr-button-ghost", onClick: handleClose, type: "button" }, /* @__PURE__ */ React36.createElement(CloseIcon, { size: 16 }))), /* @__PURE__ */ React36.createElement(
7787
7867
  "div",
7788
7868
  {
7789
7869
  className: "brokr-chat-messages",
7790
7870
  "data-empty": messages.length === 0
7791
7871
  },
7792
- 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: "0.35rem" } }, /* @__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.")), starterPrompts.length > 0 ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-chat-starters" }, starterPrompts.map((prompt) => {
7793
- const handleClick = () => {
7794
- handleStarterPrompt(prompt);
7795
- };
7796
- return /* @__PURE__ */ React36.createElement(
7797
- "button",
7798
- {
7799
- className: "brokr-chat-starter",
7800
- key: prompt,
7801
- onClick: handleClick,
7802
- type: "button"
7803
- },
7804
- prompt
7805
- );
7806
- })) : null) : null,
7872
+ messages.length === 0 ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-chat-empty" }, /* @__PURE__ */ React36.createElement(SparkIcon, { size: 18 }), /* @__PURE__ */ React36.createElement("div", { className: "brokr-section", style: { gap: "var(--brokr-space-1)" } }, /* @__PURE__ */ React36.createElement("strong", null, "Send a message to chat with the AI."), /* @__PURE__ */ React36.createElement("span", { className: "brokr-copy" }, "Ask a question or drop in a starter prompt below.")), starterPrompts.length > 0 ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-chat-starters" }, starterPrompts.map((prompt) => /* @__PURE__ */ React36.createElement(StarterButton, { key: prompt, prompt, onSelect: handleStarterPrompt }))) : null) : null,
7807
7873
  messages.map((message, index) => /* @__PURE__ */ React36.createElement("div", { className: "brokr-chat-bubble", "data-role": message.role, key: `${message.role}-${index}` }, contentToText(message.content))),
7808
7874
  error ? /* @__PURE__ */ React36.createElement("div", { className: "brokr-inline-message", "data-tone": "error" }, error) : null
7809
- ), /* @__PURE__ */ React36.createElement("form", { className: "brokr-section", onSubmit: handleSubmit, style: { gap: "0.75rem" } }, /* @__PURE__ */ React36.createElement(
7875
+ ), /* @__PURE__ */ React36.createElement("form", { className: "brokr-section", onSubmit: handleSubmit, style: { gap: "var(--brokr-space-3)" } }, /* @__PURE__ */ React36.createElement(
7810
7876
  "textarea",
7811
7877
  {
7812
7878
  className: "brokr-textarea brokr-chat-input",
@@ -7821,7 +7887,7 @@ function FabAI({
7821
7887
 
7822
7888
  // src/react/composites/SmartUpload.tsx
7823
7889
  import React37, {
7824
- useCallback as useCallback24,
7890
+ useCallback as useCallback25,
7825
7891
  useId as useId2,
7826
7892
  useMemo as useMemo25,
7827
7893
  useRef as useRef8,
@@ -7844,7 +7910,7 @@ function SmartUpload({
7844
7910
  const helperText = useMemo25(() => {
7845
7911
  return `${Math.round(maxSize / (1024 * 1024))} MB max file size.`;
7846
7912
  }, [maxSize]);
7847
- const beginUpload = useCallback24((file) => {
7913
+ const beginUpload = useCallback25((file) => {
7848
7914
  if (file.size > maxSize) {
7849
7915
  setError(`That file is larger than ${Math.round(maxSize / (1024 * 1024))} MB.`);
7850
7916
  return;
@@ -7881,31 +7947,31 @@ function SmartUpload({
7881
7947
  body.append("purpose", purpose);
7882
7948
  request.send(body);
7883
7949
  }, [maxSize, onUpload, purpose]);
7884
- const handleInputChange = useCallback24((event) => {
7950
+ const handleInputChange = useCallback25((event) => {
7885
7951
  const file = event.target.files?.[0];
7886
7952
  event.target.value = "";
7887
7953
  if (!file) return;
7888
7954
  beginUpload(file);
7889
7955
  }, [beginUpload]);
7890
- const handleDrop = useCallback24((event) => {
7956
+ const handleDrop = useCallback25((event) => {
7891
7957
  event.preventDefault();
7892
7958
  setDragActive(false);
7893
7959
  const file = event.dataTransfer.files?.[0];
7894
7960
  if (!file) return;
7895
7961
  beginUpload(file);
7896
7962
  }, [beginUpload]);
7897
- const handleDragEnter = useCallback24((event) => {
7963
+ const handleDragEnter = useCallback25((event) => {
7898
7964
  event.preventDefault();
7899
7965
  setDragActive(true);
7900
7966
  }, []);
7901
- const handleDragLeave = useCallback24((event) => {
7967
+ const handleDragLeave = useCallback25((event) => {
7902
7968
  event.preventDefault();
7903
7969
  setDragActive(false);
7904
7970
  }, []);
7905
- const handleBrowse = useCallback24(() => {
7971
+ const handleBrowse = useCallback25(() => {
7906
7972
  inputRef.current?.click();
7907
7973
  }, []);
7908
- 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: "0.35rem" } }, /* @__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(
7974
+ 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(
7909
7975
  "label",
7910
7976
  {
7911
7977
  className: "brokr-upload-dropzone",
@@ -7930,11 +7996,11 @@ function SmartUpload({
7930
7996
  ref: inputRef,
7931
7997
  type: "file"
7932
7998
  }
7933
- ), fileName ? /* @__PURE__ */ React37.createElement("div", { className: "brokr-card brokr-upload-file" }, /* @__PURE__ */ React37.createElement("div", { className: "brokr-section", style: { gap: "0.35rem" } }, /* @__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", style: { width: "7rem" } }, /* @__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);
7999
+ ), 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);
7934
8000
  }
7935
8001
 
7936
8002
  // src/react/composites/FeedbackWidget.tsx
7937
- import React38, { useCallback as useCallback25, useState as useState21 } from "react";
8003
+ import React38, { useCallback as useCallback26, useState as useState21 } from "react";
7938
8004
  function FeedbackWidget({
7939
8005
  context,
7940
8006
  onSubmit
@@ -7945,16 +8011,16 @@ function FeedbackWidget({
7945
8011
  const [error, setError] = useState21(null);
7946
8012
  const [message, setMessage] = useState21(null);
7947
8013
  const [isPending, setIsPending] = useState21(false);
7948
- const handleTextChange = useCallback25((event) => {
8014
+ const handleTextChange = useCallback26((event) => {
7949
8015
  setText(event.target.value);
7950
8016
  }, []);
7951
- const handleRateUp = useCallback25(() => {
8017
+ const handleRateUp = useCallback26(() => {
7952
8018
  setRating("up");
7953
8019
  }, []);
7954
- const handleRateDown = useCallback25(() => {
8020
+ const handleRateDown = useCallback26(() => {
7955
8021
  setRating("down");
7956
8022
  }, []);
7957
- const handleSubmit = useCallback25(async (event) => {
8023
+ const handleSubmit = useCallback26(async (event) => {
7958
8024
  event.preventDefault();
7959
8025
  if (!rating) {
7960
8026
  setError("Choose a direction first.");
@@ -8270,7 +8336,7 @@ var BrokrErrorBoundary = class extends React39.Component {
8270
8336
  };
8271
8337
 
8272
8338
  // src/react/notifications/NotificationBell.tsx
8273
- import React40, { useCallback as useCallback26, useEffect as useEffect13, useMemo as useMemo26, useRef as useRef9, useState as useState22 } from "react";
8339
+ import React40, { useCallback as useCallback27, useEffect as useEffect13, useMemo as useMemo26, useRef as useRef9, useState as useState22 } from "react";
8274
8340
 
8275
8341
  // src/react/notifications/use-notifications.ts
8276
8342
  import { useContext as useContext4 } from "react";
@@ -8295,12 +8361,34 @@ function timeAgo(iso) {
8295
8361
  const days = Math.floor(hours / 24);
8296
8362
  return `${days}d ago`;
8297
8363
  }
8364
+ function NotifDropdownItem({
8365
+ notif,
8366
+ registry,
8367
+ onClick
8368
+ }) {
8369
+ const handleClick = useCallback27(() => onClick(notif), [notif, onClick]);
8370
+ const notifData = notif.data ?? {};
8371
+ const notifType = notifData.type ?? "default";
8372
+ const resolved = resolveNotificationType(registry, notifType, notifData);
8373
+ return /* @__PURE__ */ React40.createElement(
8374
+ "button",
8375
+ {
8376
+ type: "button",
8377
+ className: `brokr-notif-item${notif.read ? "" : " brokr-notif-item--unread"}`,
8378
+ onClick: handleClick,
8379
+ role: "menuitem"
8380
+ },
8381
+ 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}` }),
8382
+ /* @__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)),
8383
+ /* @__PURE__ */ React40.createElement("span", { className: "brokr-notif-item-time" }, timeAgo(notif.createdAt))
8384
+ );
8385
+ }
8298
8386
  function NotificationBell() {
8299
8387
  const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
8300
8388
  const [open, setOpen] = useState22(false);
8301
8389
  const containerRef = useRef9(null);
8302
8390
  const markReadTimerRef = useRef9(null);
8303
- const toggle = useCallback26(() => setOpen((o) => !o), []);
8391
+ const toggle = useCallback27(() => setOpen((o) => !o), []);
8304
8392
  useEffect13(() => {
8305
8393
  if (markReadTimerRef.current) {
8306
8394
  clearTimeout(markReadTimerRef.current);
@@ -8342,7 +8430,7 @@ function NotificationBell() {
8342
8430
  ),
8343
8431
  [notifications]
8344
8432
  );
8345
- const handleItemClick = useCallback26((notif) => {
8433
+ const handleItemClick = useCallback27((notif) => {
8346
8434
  if (!notif.read) markRead(notif.id);
8347
8435
  const notifData = notif.data ?? {};
8348
8436
  const notifType = notif.type ?? notifData.type ?? "default";
@@ -8359,7 +8447,8 @@ function NotificationBell() {
8359
8447
  className: "brokr-notif-bell",
8360
8448
  onClick: toggle,
8361
8449
  "aria-label": `Notifications${unreadCount > 0 ? ` (${unreadCount} unread)` : ""}`,
8362
- "aria-expanded": open
8450
+ "aria-expanded": open,
8451
+ "aria-haspopup": "menu"
8363
8452
  },
8364
8453
  /* @__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" })),
8365
8454
  unreadCount > 0 && /* @__PURE__ */ React40.createElement("span", { className: "brokr-notif-badge" }, unreadCount > 99 ? "99+" : unreadCount)
@@ -8371,35 +8460,19 @@ function NotificationBell() {
8371
8460
  onClick: markAllRead
8372
8461
  },
8373
8462
  "Mark all read"
8374
- )), /* @__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) => {
8375
- const notifData = notif.data ?? {};
8376
- const notifType = notifData.type ?? "default";
8377
- const resolved = resolveNotificationType(registry, notifType, notifData);
8378
- return /* @__PURE__ */ React40.createElement(
8379
- "button",
8380
- {
8381
- key: notif.id,
8382
- type: "button",
8383
- className: `brokr-notif-item${notif.read ? "" : " brokr-notif-item--unread"}`,
8384
- onClick: () => handleItemClick(notif),
8385
- role: "menuitem"
8386
- },
8387
- resolved.image ? /* @__PURE__ */ React40.createElement(
8388
- "img",
8389
- {
8390
- src: resolved.image.url,
8391
- alt: resolved.image.alt,
8392
- className: "brokr-notif-item-logo"
8393
- }
8394
- ) : /* @__PURE__ */ React40.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
8395
- /* @__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)),
8396
- /* @__PURE__ */ React40.createElement("span", { className: "brokr-notif-item-time" }, timeAgo(notif.createdAt))
8397
- );
8398
- }))));
8463
+ )), /* @__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(
8464
+ NotifDropdownItem,
8465
+ {
8466
+ key: notif.id,
8467
+ notif,
8468
+ registry,
8469
+ onClick: handleItemClick
8470
+ }
8471
+ )))));
8399
8472
  }
8400
8473
 
8401
8474
  // src/react/notifications/NotificationList.tsx
8402
- import React41, { useCallback as useCallback27, useMemo as useMemo27 } from "react";
8475
+ import React41, { useCallback as useCallback28, useMemo as useMemo27 } from "react";
8403
8476
  function formatTimestamp(iso) {
8404
8477
  const date = new Date(iso);
8405
8478
  return new Intl.DateTimeFormat("en-US", {
@@ -8412,6 +8485,27 @@ function formatTimestamp(iso) {
8412
8485
  function NotificationListSkeleton() {
8413
8486
  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 } }))));
8414
8487
  }
8488
+ function NotifListItem({
8489
+ notif,
8490
+ registry,
8491
+ onClick
8492
+ }) {
8493
+ const handleClick = useCallback28(() => onClick(notif), [notif, onClick]);
8494
+ const notifData = notif.data ?? {};
8495
+ const notifType = notif.type ?? notifData.type ?? "default";
8496
+ const resolved = resolveNotificationType(registry, notifType, notifData);
8497
+ return /* @__PURE__ */ React41.createElement(
8498
+ "button",
8499
+ {
8500
+ type: "button",
8501
+ className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
8502
+ onClick: handleClick
8503
+ },
8504
+ 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}` }),
8505
+ /* @__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)),
8506
+ /* @__PURE__ */ React41.createElement("span", { className: "brokr-notif-item-time" }, formatTimestamp(notif.createdAt))
8507
+ );
8508
+ }
8415
8509
  function NotificationList() {
8416
8510
  const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
8417
8511
  const sorted = useMemo27(
@@ -8420,7 +8514,7 @@ function NotificationList() {
8420
8514
  ),
8421
8515
  [notifications]
8422
8516
  );
8423
- const handleClick = useCallback27((notif) => {
8517
+ const handleClick = useCallback28((notif) => {
8424
8518
  if (!notif.read) markRead(notif.id);
8425
8519
  const notifData = notif.data ?? {};
8426
8520
  const notifType = notif.type ?? notifData.type ?? "default";
@@ -8438,30 +8532,15 @@ function NotificationList() {
8438
8532
  onClick: markAllRead
8439
8533
  },
8440
8534
  "Mark all read"
8441
- )), 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) => {
8442
- const notifData = notif.data ?? {};
8443
- const notifType = notif.type ?? notifData.type ?? "default";
8444
- const resolved = resolveNotificationType(registry, notifType, notifData);
8445
- return /* @__PURE__ */ React41.createElement(
8446
- "button",
8447
- {
8448
- key: notif.id,
8449
- type: "button",
8450
- className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
8451
- onClick: () => handleClick(notif)
8452
- },
8453
- resolved.image ? /* @__PURE__ */ React41.createElement(
8454
- "img",
8455
- {
8456
- src: resolved.image.url,
8457
- alt: resolved.image.alt,
8458
- className: "brokr-notif-item-logo"
8459
- }
8460
- ) : /* @__PURE__ */ React41.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
8461
- /* @__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)),
8462
- /* @__PURE__ */ React41.createElement("span", { className: "brokr-notif-item-time" }, formatTimestamp(notif.createdAt))
8463
- );
8464
- })));
8535
+ )), 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(
8536
+ NotifListItem,
8537
+ {
8538
+ key: notif.id,
8539
+ notif,
8540
+ registry,
8541
+ onClick: handleClick
8542
+ }
8543
+ ))));
8465
8544
  }
8466
8545
 
8467
8546
  // src/react/hooks/use-user.ts