@brokr/sdk 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/feature.js +22 -2
  2. package/dist/feature.mjs +22 -2
  3. package/dist/index.js +22 -2
  4. package/dist/index.mjs +22 -2
  5. package/dist/next.js +22 -2
  6. package/dist/next.mjs +22 -2
  7. package/dist/react-notifications.js +63 -50
  8. package/dist/react-notifications.mjs +63 -50
  9. package/dist/react-styles.js +386 -169
  10. package/dist/react-styles.mjs +386 -169
  11. package/dist/react-theme.js +6 -4
  12. package/dist/react-theme.mjs +6 -4
  13. package/dist/react.js +644 -317
  14. package/dist/react.mjs +682 -355
  15. package/dist/runtime.js +22 -2
  16. package/dist/runtime.mjs +22 -2
  17. package/dist/src/chat/config.d.ts +2 -0
  18. package/dist/src/chat/config.d.ts.map +1 -1
  19. package/dist/src/gateway.d.ts.map +1 -1
  20. package/dist/src/models.d.ts +2 -0
  21. package/dist/src/models.d.ts.map +1 -1
  22. package/dist/src/react/account/AccountPanel.d.ts.map +1 -1
  23. package/dist/src/react/account/UserButton.d.ts.map +1 -1
  24. package/dist/src/react/chat/AIChat.d.ts.map +1 -1
  25. package/dist/src/react/chat/ChatContext.d.ts +3 -6
  26. package/dist/src/react/chat/ChatContext.d.ts.map +1 -1
  27. package/dist/src/react/chat/ChatInput.d.ts.map +1 -1
  28. package/dist/src/react/chat/MarkdownRenderer.d.ts.map +1 -1
  29. package/dist/src/react/chat/MessagePane.d.ts.map +1 -1
  30. package/dist/src/react/chat/ModelSelector.d.ts +1 -1
  31. package/dist/src/react/chat/ModelSelector.d.ts.map +1 -1
  32. package/dist/src/react/chat/ThreadSidebar.d.ts.map +1 -1
  33. package/dist/src/react/chat/memory.d.ts +12 -0
  34. package/dist/src/react/chat/memory.d.ts.map +1 -0
  35. package/dist/src/react/chat/types.d.ts +6 -0
  36. package/dist/src/react/chat/types.d.ts.map +1 -1
  37. package/dist/src/react/chat/useChat.d.ts +8 -6
  38. package/dist/src/react/chat/useChat.d.ts.map +1 -1
  39. package/dist/src/react/composites/FabAI.d.ts +6 -1
  40. package/dist/src/react/composites/FabAI.d.ts.map +1 -1
  41. package/dist/src/react/composites/fab-context.d.ts +26 -0
  42. package/dist/src/react/composites/fab-context.d.ts.map +1 -0
  43. package/dist/src/react/css/account.d.ts +1 -1
  44. package/dist/src/react/css/account.d.ts.map +1 -1
  45. package/dist/src/react/css/auth.d.ts +1 -1
  46. package/dist/src/react/css/auth.d.ts.map +1 -1
  47. package/dist/src/react/css/chat-extras.d.ts +1 -1
  48. package/dist/src/react/css/chat-extras.d.ts.map +1 -1
  49. package/dist/src/react/css/chat.d.ts +1 -1
  50. package/dist/src/react/css/chat.d.ts.map +1 -1
  51. package/dist/src/react/css/composites.d.ts +1 -1
  52. package/dist/src/react/css/composites.d.ts.map +1 -1
  53. package/dist/src/react/css/gates.d.ts +1 -1
  54. package/dist/src/react/css/gates.d.ts.map +1 -1
  55. package/dist/src/react/css/markdown.d.ts +1 -1
  56. package/dist/src/react/css/markdown.d.ts.map +1 -1
  57. package/dist/src/react/css/notifications.d.ts +1 -1
  58. package/dist/src/react/css/notifications.d.ts.map +1 -1
  59. package/dist/src/react/css/primitives.d.ts +1 -1
  60. package/dist/src/react/css/primitives.d.ts.map +1 -1
  61. package/dist/src/react/css/reset.d.ts +1 -1
  62. package/dist/src/react/css/reset.d.ts.map +1 -1
  63. package/dist/src/react/css/responsive.d.ts +1 -1
  64. package/dist/src/react/css/responsive.d.ts.map +1 -1
  65. package/dist/src/react/css/skeleton.d.ts +1 -1
  66. package/dist/src/react/css/skeleton.d.ts.map +1 -1
  67. package/dist/src/react/css/tokens.d.ts +1 -1
  68. package/dist/src/react/css/tokens.d.ts.map +1 -1
  69. package/dist/src/react/notifications/NotificationBell.d.ts.map +1 -1
  70. package/dist/src/react/notifications/NotificationList.d.ts.map +1 -1
  71. package/dist/src/react/notifications/Toast.d.ts.map +1 -1
  72. package/dist/src/react/payments/Plans.d.ts.map +1 -1
  73. package/dist/src/react/theme.d.ts.map +1 -1
  74. package/dist/src/react/types.d.ts +4 -4
  75. package/dist/src/react/types.d.ts.map +1 -1
  76. package/dist/tsconfig.tsbuildinfo +1 -1
  77. package/package.json +1 -1
package/dist/feature.js CHANGED
@@ -700,9 +700,29 @@ async function gatewayStream(gatewayUrl, token, path, body, capability) {
700
700
  if (res.status === 401) {
701
701
  throw new BrokrAuthError("Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
702
702
  }
703
- if (!res.ok || !res.body) {
703
+ if (!res.ok) {
704
+ const body2 = await res.json().catch(() => ({}));
705
+ const errObj = typeof body2.error === "object" && body2.error !== null ? body2.error : void 0;
706
+ const errorData = body2.data ?? errObj?.data ?? body2;
707
+ const errorCode = errorData.errorCode ?? body2.code;
708
+ const hint = errorData.hint;
709
+ const requestId = errorData.requestId ?? res.headers.get("x-request-id");
710
+ const retryable = body2.retryable ?? errorData.retryable;
711
+ const errorStr = typeof body2.error === "string" ? body2.error : void 0;
712
+ const message = body2.message ?? errObj?.message ?? errorStr ?? `${capability} stream failed (HTTP ${res.status})`;
713
+ throw new BrokrError(
714
+ message,
715
+ errorCode ?? `${capability.toUpperCase()}_STREAM_FAILED`,
716
+ capability,
717
+ retryable ?? false,
718
+ errorCode ?? void 0,
719
+ requestId ?? void 0,
720
+ hint ?? void 0
721
+ );
722
+ }
723
+ if (!res.body) {
704
724
  throw new BrokrError(
705
- `${capability} stream failed (HTTP ${res.status})`,
725
+ `${capability} stream failed (empty body)`,
706
726
  `${capability.toUpperCase()}_STREAM_FAILED`,
707
727
  capability
708
728
  );
package/dist/feature.mjs CHANGED
@@ -675,9 +675,29 @@ async function gatewayStream(gatewayUrl, token, path, body, capability) {
675
675
  if (res.status === 401) {
676
676
  throw new BrokrAuthError("Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
677
677
  }
678
- if (!res.ok || !res.body) {
678
+ if (!res.ok) {
679
+ const body2 = await res.json().catch(() => ({}));
680
+ const errObj = typeof body2.error === "object" && body2.error !== null ? body2.error : void 0;
681
+ const errorData = body2.data ?? errObj?.data ?? body2;
682
+ const errorCode = errorData.errorCode ?? body2.code;
683
+ const hint = errorData.hint;
684
+ const requestId = errorData.requestId ?? res.headers.get("x-request-id");
685
+ const retryable = body2.retryable ?? errorData.retryable;
686
+ const errorStr = typeof body2.error === "string" ? body2.error : void 0;
687
+ const message = body2.message ?? errObj?.message ?? errorStr ?? `${capability} stream failed (HTTP ${res.status})`;
688
+ throw new BrokrError(
689
+ message,
690
+ errorCode ?? `${capability.toUpperCase()}_STREAM_FAILED`,
691
+ capability,
692
+ retryable ?? false,
693
+ errorCode ?? void 0,
694
+ requestId ?? void 0,
695
+ hint ?? void 0
696
+ );
697
+ }
698
+ if (!res.body) {
679
699
  throw new BrokrError(
680
- `${capability} stream failed (HTTP ${res.status})`,
700
+ `${capability} stream failed (empty body)`,
681
701
  `${capability.toUpperCase()}_STREAM_FAILED`,
682
702
  capability
683
703
  );
package/dist/index.js CHANGED
@@ -1533,9 +1533,29 @@ async function gatewayStream(gatewayUrl, token, path, body, capability) {
1533
1533
  if (res.status === 401) {
1534
1534
  throw new BrokrAuthError("Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
1535
1535
  }
1536
- if (!res.ok || !res.body) {
1536
+ if (!res.ok) {
1537
+ const body2 = await res.json().catch(() => ({}));
1538
+ const errObj = typeof body2.error === "object" && body2.error !== null ? body2.error : void 0;
1539
+ const errorData = body2.data ?? errObj?.data ?? body2;
1540
+ const errorCode = errorData.errorCode ?? body2.code;
1541
+ const hint = errorData.hint;
1542
+ const requestId = errorData.requestId ?? res.headers.get("x-request-id");
1543
+ const retryable = body2.retryable ?? errorData.retryable;
1544
+ const errorStr = typeof body2.error === "string" ? body2.error : void 0;
1545
+ const message = body2.message ?? errObj?.message ?? errorStr ?? `${capability} stream failed (HTTP ${res.status})`;
1546
+ throw new BrokrError(
1547
+ message,
1548
+ errorCode ?? `${capability.toUpperCase()}_STREAM_FAILED`,
1549
+ capability,
1550
+ retryable ?? false,
1551
+ errorCode ?? void 0,
1552
+ requestId ?? void 0,
1553
+ hint ?? void 0
1554
+ );
1555
+ }
1556
+ if (!res.body) {
1537
1557
  throw new BrokrError(
1538
- `${capability} stream failed (HTTP ${res.status})`,
1558
+ `${capability} stream failed (empty body)`,
1539
1559
  `${capability.toUpperCase()}_STREAM_FAILED`,
1540
1560
  capability
1541
1561
  );
package/dist/index.mjs CHANGED
@@ -1501,9 +1501,29 @@ async function gatewayStream(gatewayUrl, token, path, body, capability) {
1501
1501
  if (res.status === 401) {
1502
1502
  throw new BrokrAuthError("Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
1503
1503
  }
1504
- if (!res.ok || !res.body) {
1504
+ if (!res.ok) {
1505
+ const body2 = await res.json().catch(() => ({}));
1506
+ const errObj = typeof body2.error === "object" && body2.error !== null ? body2.error : void 0;
1507
+ const errorData = body2.data ?? errObj?.data ?? body2;
1508
+ const errorCode = errorData.errorCode ?? body2.code;
1509
+ const hint = errorData.hint;
1510
+ const requestId = errorData.requestId ?? res.headers.get("x-request-id");
1511
+ const retryable = body2.retryable ?? errorData.retryable;
1512
+ const errorStr = typeof body2.error === "string" ? body2.error : void 0;
1513
+ const message = body2.message ?? errObj?.message ?? errorStr ?? `${capability} stream failed (HTTP ${res.status})`;
1514
+ throw new BrokrError(
1515
+ message,
1516
+ errorCode ?? `${capability.toUpperCase()}_STREAM_FAILED`,
1517
+ capability,
1518
+ retryable ?? false,
1519
+ errorCode ?? void 0,
1520
+ requestId ?? void 0,
1521
+ hint ?? void 0
1522
+ );
1523
+ }
1524
+ if (!res.body) {
1505
1525
  throw new BrokrError(
1506
- `${capability} stream failed (HTTP ${res.status})`,
1526
+ `${capability} stream failed (empty body)`,
1507
1527
  `${capability.toUpperCase()}_STREAM_FAILED`,
1508
1528
  capability
1509
1529
  );
package/dist/next.js CHANGED
@@ -1569,9 +1569,29 @@ async function gatewayStream(gatewayUrl, token, path, body, capability) {
1569
1569
  if (res.status === 401) {
1570
1570
  throw new BrokrAuthError("Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
1571
1571
  }
1572
- if (!res.ok || !res.body) {
1572
+ if (!res.ok) {
1573
+ const body2 = await res.json().catch(() => ({}));
1574
+ const errObj = typeof body2.error === "object" && body2.error !== null ? body2.error : void 0;
1575
+ const errorData = body2.data ?? errObj?.data ?? body2;
1576
+ const errorCode = errorData.errorCode ?? body2.code;
1577
+ const hint = errorData.hint;
1578
+ const requestId = errorData.requestId ?? res.headers.get("x-request-id");
1579
+ const retryable = body2.retryable ?? errorData.retryable;
1580
+ const errorStr = typeof body2.error === "string" ? body2.error : void 0;
1581
+ const message = body2.message ?? errObj?.message ?? errorStr ?? `${capability} stream failed (HTTP ${res.status})`;
1582
+ throw new BrokrError(
1583
+ message,
1584
+ errorCode ?? `${capability.toUpperCase()}_STREAM_FAILED`,
1585
+ capability,
1586
+ retryable ?? false,
1587
+ errorCode ?? void 0,
1588
+ requestId ?? void 0,
1589
+ hint ?? void 0
1590
+ );
1591
+ }
1592
+ if (!res.body) {
1573
1593
  throw new BrokrError(
1574
- `${capability} stream failed (HTTP ${res.status})`,
1594
+ `${capability} stream failed (empty body)`,
1575
1595
  `${capability.toUpperCase()}_STREAM_FAILED`,
1576
1596
  capability
1577
1597
  );
package/dist/next.mjs CHANGED
@@ -1555,9 +1555,29 @@ async function gatewayStream(gatewayUrl, token, path, body, capability) {
1555
1555
  if (res.status === 401) {
1556
1556
  throw new BrokrAuthError("Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
1557
1557
  }
1558
- if (!res.ok || !res.body) {
1558
+ if (!res.ok) {
1559
+ const body2 = await res.json().catch(() => ({}));
1560
+ const errObj = typeof body2.error === "object" && body2.error !== null ? body2.error : void 0;
1561
+ const errorData = body2.data ?? errObj?.data ?? body2;
1562
+ const errorCode = errorData.errorCode ?? body2.code;
1563
+ const hint = errorData.hint;
1564
+ const requestId = errorData.requestId ?? res.headers.get("x-request-id");
1565
+ const retryable = body2.retryable ?? errorData.retryable;
1566
+ const errorStr = typeof body2.error === "string" ? body2.error : void 0;
1567
+ const message = body2.message ?? errObj?.message ?? errorStr ?? `${capability} stream failed (HTTP ${res.status})`;
1568
+ throw new BrokrError(
1569
+ message,
1570
+ errorCode ?? `${capability.toUpperCase()}_STREAM_FAILED`,
1571
+ capability,
1572
+ retryable ?? false,
1573
+ errorCode ?? void 0,
1574
+ requestId ?? void 0,
1575
+ hint ?? void 0
1576
+ );
1577
+ }
1578
+ if (!res.body) {
1559
1579
  throw new BrokrError(
1560
- `${capability} stream failed (HTTP ${res.status})`,
1580
+ `${capability} stream failed (empty body)`,
1561
1581
  `${capability.toUpperCase()}_STREAM_FAILED`,
1562
1582
  capability
1563
1583
  );
@@ -109,6 +109,28 @@ function timeAgo(iso) {
109
109
  const days = Math.floor(hours / 24);
110
110
  return `${days}d ago`;
111
111
  }
112
+ function NotifDropdownItem({
113
+ notif,
114
+ registry,
115
+ onClick
116
+ }) {
117
+ const handleClick = (0, import_react4.useCallback)(() => onClick(notif), [notif, onClick]);
118
+ const notifData = notif.data ?? {};
119
+ const notifType = notifData.type ?? "default";
120
+ const resolved = resolveNotificationType(registry, notifType, notifData);
121
+ return /* @__PURE__ */ import_react4.default.createElement(
122
+ "button",
123
+ {
124
+ type: "button",
125
+ className: `brokr-notif-item${notif.read ? "" : " brokr-notif-item--unread"}`,
126
+ onClick: handleClick,
127
+ role: "menuitem"
128
+ },
129
+ resolved.image ? /* @__PURE__ */ import_react4.default.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-notif-item-logo" }) : /* @__PURE__ */ import_react4.default.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
130
+ /* @__PURE__ */ import_react4.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
131
+ /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-item-time" }, timeAgo(notif.createdAt))
132
+ );
133
+ }
112
134
  function NotificationBell() {
113
135
  const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
114
136
  const [open, setOpen] = (0, import_react4.useState)(false);
@@ -173,7 +195,8 @@ function NotificationBell() {
173
195
  className: "brokr-notif-bell",
174
196
  onClick: toggle,
175
197
  "aria-label": `Notifications${unreadCount > 0 ? ` (${unreadCount} unread)` : ""}`,
176
- "aria-expanded": open
198
+ "aria-expanded": open,
199
+ "aria-haspopup": "menu"
177
200
  },
178
201
  /* @__PURE__ */ import_react4.default.createElement("svg", { "aria-hidden": "true", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react4.default.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ import_react4.default.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })),
179
202
  unreadCount > 0 && /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-badge" }, unreadCount > 99 ? "99+" : unreadCount)
@@ -185,31 +208,15 @@ function NotificationBell() {
185
208
  onClick: markAllRead
186
209
  },
187
210
  "Mark all read"
188
- )), /* @__PURE__ */ import_react4.default.createElement("div", { className: "brokr-notif-dropdown-list" }, isLoading ? /* @__PURE__ */ import_react4.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-empty-text" }, "Loading\u2026")) : sorted.length === 0 ? /* @__PURE__ */ import_react4.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : sorted.map((notif) => {
189
- const notifData = notif.data ?? {};
190
- const notifType = notifData.type ?? "default";
191
- const resolved = resolveNotificationType(registry, notifType, notifData);
192
- return /* @__PURE__ */ import_react4.default.createElement(
193
- "button",
194
- {
195
- key: notif.id,
196
- type: "button",
197
- className: `brokr-notif-item${notif.read ? "" : " brokr-notif-item--unread"}`,
198
- onClick: () => handleItemClick(notif),
199
- role: "menuitem"
200
- },
201
- resolved.image ? /* @__PURE__ */ import_react4.default.createElement(
202
- "img",
203
- {
204
- src: resolved.image.url,
205
- alt: resolved.image.alt,
206
- className: "brokr-notif-item-logo"
207
- }
208
- ) : /* @__PURE__ */ import_react4.default.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
209
- /* @__PURE__ */ import_react4.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
210
- /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-item-time" }, timeAgo(notif.createdAt))
211
- );
212
- }))));
211
+ )), /* @__PURE__ */ import_react4.default.createElement("div", { className: "brokr-notif-dropdown-list" }, isLoading ? /* @__PURE__ */ import_react4.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-empty-text" }, "Loading\u2026")) : sorted.length === 0 ? /* @__PURE__ */ import_react4.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : sorted.map((notif) => /* @__PURE__ */ import_react4.default.createElement(
212
+ NotifDropdownItem,
213
+ {
214
+ key: notif.id,
215
+ notif,
216
+ registry,
217
+ onClick: handleItemClick
218
+ }
219
+ )))));
213
220
  }
214
221
 
215
222
  // src/react/notifications/NotificationList.tsx
@@ -226,6 +233,27 @@ function formatTimestamp(iso) {
226
233
  function NotificationListSkeleton() {
227
234
  return /* @__PURE__ */ import_react5.default.createElement("div", { className: "brokr-notif-list-items" }, [1, 2, 3].map((i) => /* @__PURE__ */ import_react5.default.createElement("div", { key: i, className: "brokr-notif-list-row brokr-notif-list-row--skeleton" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-item-dot brokr-notif-item-dot--skeleton" }), /* @__PURE__ */ import_react5.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-item-title brokr-skeleton-line", style: { width: "60%" } }), /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-item-message brokr-skeleton-line", style: { width: "80%" } })), /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-item-time brokr-skeleton-line", style: { width: 48 } }))));
228
235
  }
236
+ function NotifListItem({
237
+ notif,
238
+ registry,
239
+ onClick
240
+ }) {
241
+ const handleClick = (0, import_react5.useCallback)(() => onClick(notif), [notif, onClick]);
242
+ const notifData = notif.data ?? {};
243
+ const notifType = notif.type ?? notifData.type ?? "default";
244
+ const resolved = resolveNotificationType(registry, notifType, notifData);
245
+ return /* @__PURE__ */ import_react5.default.createElement(
246
+ "button",
247
+ {
248
+ type: "button",
249
+ className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
250
+ onClick: handleClick
251
+ },
252
+ resolved.image ? /* @__PURE__ */ import_react5.default.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-notif-item-logo" }) : /* @__PURE__ */ import_react5.default.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
253
+ /* @__PURE__ */ import_react5.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
254
+ /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-item-time" }, formatTimestamp(notif.createdAt))
255
+ );
256
+ }
229
257
  function NotificationList() {
230
258
  const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
231
259
  const sorted = (0, import_react5.useMemo)(
@@ -252,30 +280,15 @@ function NotificationList() {
252
280
  onClick: markAllRead
253
281
  },
254
282
  "Mark all read"
255
- )), isLoading ? /* @__PURE__ */ import_react5.default.createElement(NotificationListSkeleton, null) : sorted.length === 0 ? /* @__PURE__ */ import_react5.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react5.default.createElement("svg", { "aria-hidden": "true", width: "40", height: "40", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", style: { opacity: 0.3 } }, /* @__PURE__ */ import_react5.default.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ import_react5.default.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })), /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : /* @__PURE__ */ import_react5.default.createElement("div", { className: "brokr-notif-list-items" }, sorted.map((notif) => {
256
- const notifData = notif.data ?? {};
257
- const notifType = notif.type ?? notifData.type ?? "default";
258
- const resolved = resolveNotificationType(registry, notifType, notifData);
259
- return /* @__PURE__ */ import_react5.default.createElement(
260
- "button",
261
- {
262
- key: notif.id,
263
- type: "button",
264
- className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
265
- onClick: () => handleClick(notif)
266
- },
267
- resolved.image ? /* @__PURE__ */ import_react5.default.createElement(
268
- "img",
269
- {
270
- src: resolved.image.url,
271
- alt: resolved.image.alt,
272
- className: "brokr-notif-item-logo"
273
- }
274
- ) : /* @__PURE__ */ import_react5.default.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
275
- /* @__PURE__ */ import_react5.default.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
276
- /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-item-time" }, formatTimestamp(notif.createdAt))
277
- );
278
- })));
283
+ )), isLoading ? /* @__PURE__ */ import_react5.default.createElement(NotificationListSkeleton, null) : sorted.length === 0 ? /* @__PURE__ */ import_react5.default.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ import_react5.default.createElement("svg", { "aria-hidden": "true", width: "40", height: "40", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1", strokeLinecap: "round", strokeLinejoin: "round", style: { opacity: 0.3 } }, /* @__PURE__ */ import_react5.default.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ import_react5.default.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })), /* @__PURE__ */ import_react5.default.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : /* @__PURE__ */ import_react5.default.createElement("div", { className: "brokr-notif-list-items" }, sorted.map((notif) => /* @__PURE__ */ import_react5.default.createElement(
284
+ NotifListItem,
285
+ {
286
+ key: notif.id,
287
+ notif,
288
+ registry,
289
+ onClick: handleClick
290
+ }
291
+ ))));
279
292
  }
280
293
  // Annotate the CommonJS export names for ESM import in node:
281
294
  0 && (module.exports = {
@@ -78,6 +78,28 @@ function timeAgo(iso) {
78
78
  const days = Math.floor(hours / 24);
79
79
  return `${days}d ago`;
80
80
  }
81
+ function NotifDropdownItem({
82
+ notif,
83
+ registry,
84
+ onClick
85
+ }) {
86
+ const handleClick = useCallback3(() => onClick(notif), [notif, onClick]);
87
+ const notifData = notif.data ?? {};
88
+ const notifType = notifData.type ?? "default";
89
+ const resolved = resolveNotificationType(registry, notifType, notifData);
90
+ return /* @__PURE__ */ React3.createElement(
91
+ "button",
92
+ {
93
+ type: "button",
94
+ className: `brokr-notif-item${notif.read ? "" : " brokr-notif-item--unread"}`,
95
+ onClick: handleClick,
96
+ role: "menuitem"
97
+ },
98
+ resolved.image ? /* @__PURE__ */ React3.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-notif-item-logo" }) : /* @__PURE__ */ React3.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
99
+ /* @__PURE__ */ React3.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
100
+ /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-item-time" }, timeAgo(notif.createdAt))
101
+ );
102
+ }
81
103
  function NotificationBell() {
82
104
  const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
83
105
  const [open, setOpen] = useState3(false);
@@ -142,7 +164,8 @@ function NotificationBell() {
142
164
  className: "brokr-notif-bell",
143
165
  onClick: toggle,
144
166
  "aria-label": `Notifications${unreadCount > 0 ? ` (${unreadCount} unread)` : ""}`,
145
- "aria-expanded": open
167
+ "aria-expanded": open,
168
+ "aria-haspopup": "menu"
146
169
  },
147
170
  /* @__PURE__ */ React3.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__ */ React3.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ React3.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })),
148
171
  unreadCount > 0 && /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-badge" }, unreadCount > 99 ? "99+" : unreadCount)
@@ -154,31 +177,15 @@ function NotificationBell() {
154
177
  onClick: markAllRead
155
178
  },
156
179
  "Mark all read"
157
- )), /* @__PURE__ */ React3.createElement("div", { className: "brokr-notif-dropdown-list" }, isLoading ? /* @__PURE__ */ React3.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-empty-text" }, "Loading\u2026")) : sorted.length === 0 ? /* @__PURE__ */ React3.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : sorted.map((notif) => {
158
- const notifData = notif.data ?? {};
159
- const notifType = notifData.type ?? "default";
160
- const resolved = resolveNotificationType(registry, notifType, notifData);
161
- return /* @__PURE__ */ React3.createElement(
162
- "button",
163
- {
164
- key: notif.id,
165
- type: "button",
166
- className: `brokr-notif-item${notif.read ? "" : " brokr-notif-item--unread"}`,
167
- onClick: () => handleItemClick(notif),
168
- role: "menuitem"
169
- },
170
- resolved.image ? /* @__PURE__ */ React3.createElement(
171
- "img",
172
- {
173
- src: resolved.image.url,
174
- alt: resolved.image.alt,
175
- className: "brokr-notif-item-logo"
176
- }
177
- ) : /* @__PURE__ */ React3.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
178
- /* @__PURE__ */ React3.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
179
- /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-item-time" }, timeAgo(notif.createdAt))
180
- );
181
- }))));
180
+ )), /* @__PURE__ */ React3.createElement("div", { className: "brokr-notif-dropdown-list" }, isLoading ? /* @__PURE__ */ React3.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-empty-text" }, "Loading\u2026")) : sorted.length === 0 ? /* @__PURE__ */ React3.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ React3.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : sorted.map((notif) => /* @__PURE__ */ React3.createElement(
181
+ NotifDropdownItem,
182
+ {
183
+ key: notif.id,
184
+ notif,
185
+ registry,
186
+ onClick: handleItemClick
187
+ }
188
+ )))));
182
189
  }
183
190
 
184
191
  // src/react/notifications/NotificationList.tsx
@@ -195,6 +202,27 @@ function formatTimestamp(iso) {
195
202
  function NotificationListSkeleton() {
196
203
  return /* @__PURE__ */ React4.createElement("div", { className: "brokr-notif-list-items" }, [1, 2, 3].map((i) => /* @__PURE__ */ React4.createElement("div", { key: i, className: "brokr-notif-list-row brokr-notif-list-row--skeleton" }, /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-item-dot brokr-notif-item-dot--skeleton" }), /* @__PURE__ */ React4.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-item-title brokr-skeleton-line", style: { width: "60%" } }), /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-item-message brokr-skeleton-line", style: { width: "80%" } })), /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-item-time brokr-skeleton-line", style: { width: 48 } }))));
197
204
  }
205
+ function NotifListItem({
206
+ notif,
207
+ registry,
208
+ onClick
209
+ }) {
210
+ const handleClick = useCallback4(() => onClick(notif), [notif, onClick]);
211
+ const notifData = notif.data ?? {};
212
+ const notifType = notif.type ?? notifData.type ?? "default";
213
+ const resolved = resolveNotificationType(registry, notifType, notifData);
214
+ return /* @__PURE__ */ React4.createElement(
215
+ "button",
216
+ {
217
+ type: "button",
218
+ className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
219
+ onClick: handleClick
220
+ },
221
+ resolved.image ? /* @__PURE__ */ React4.createElement("img", { src: resolved.image.url, alt: resolved.image.alt, className: "brokr-notif-item-logo" }) : /* @__PURE__ */ React4.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
222
+ /* @__PURE__ */ React4.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
223
+ /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-item-time" }, formatTimestamp(notif.createdAt))
224
+ );
225
+ }
198
226
  function NotificationList() {
199
227
  const { notifications, unreadCount, markRead, markAllRead, isLoading, registry } = useNotifications();
200
228
  const sorted = useMemo4(
@@ -221,30 +249,15 @@ function NotificationList() {
221
249
  onClick: markAllRead
222
250
  },
223
251
  "Mark all read"
224
- )), isLoading ? /* @__PURE__ */ React4.createElement(NotificationListSkeleton, null) : sorted.length === 0 ? /* @__PURE__ */ React4.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ React4.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__ */ React4.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ React4.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })), /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : /* @__PURE__ */ React4.createElement("div", { className: "brokr-notif-list-items" }, sorted.map((notif) => {
225
- const notifData = notif.data ?? {};
226
- const notifType = notif.type ?? notifData.type ?? "default";
227
- const resolved = resolveNotificationType(registry, notifType, notifData);
228
- return /* @__PURE__ */ React4.createElement(
229
- "button",
230
- {
231
- key: notif.id,
232
- type: "button",
233
- className: `brokr-notif-list-row${notif.read ? "" : " brokr-notif-list-row--unread"}`,
234
- onClick: () => handleClick(notif)
235
- },
236
- resolved.image ? /* @__PURE__ */ React4.createElement(
237
- "img",
238
- {
239
- src: resolved.image.url,
240
- alt: resolved.image.alt,
241
- className: "brokr-notif-item-logo"
242
- }
243
- ) : /* @__PURE__ */ React4.createElement("span", { className: `brokr-notif-item-dot brokr-notif-item-dot--${notif.variant}` }),
244
- /* @__PURE__ */ React4.createElement("div", { className: "brokr-notif-item-body" }, /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-item-title" }, notif.title), /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-item-message" }, notif.message)),
245
- /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-item-time" }, formatTimestamp(notif.createdAt))
246
- );
247
- })));
252
+ )), isLoading ? /* @__PURE__ */ React4.createElement(NotificationListSkeleton, null) : sorted.length === 0 ? /* @__PURE__ */ React4.createElement("div", { className: "brokr-notif-empty" }, /* @__PURE__ */ React4.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__ */ React4.createElement("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }), /* @__PURE__ */ React4.createElement("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })), /* @__PURE__ */ React4.createElement("span", { className: "brokr-notif-empty-text" }, "No notifications yet")) : /* @__PURE__ */ React4.createElement("div", { className: "brokr-notif-list-items" }, sorted.map((notif) => /* @__PURE__ */ React4.createElement(
253
+ NotifListItem,
254
+ {
255
+ key: notif.id,
256
+ notif,
257
+ registry,
258
+ onClick: handleClick
259
+ }
260
+ ))));
248
261
  }
249
262
  export {
250
263
  NotificationBell,