@iota-uz/sdk 0.4.33 → 0.4.35

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.
@@ -3647,16 +3647,26 @@ function hashString(str) {
3647
3647
  return Math.abs(hash);
3648
3648
  }
3649
3649
  var colorPalette = [
3650
- { bg: "bg-blue-500", text: "text-white" },
3651
- { bg: "bg-green-500", text: "text-white" },
3652
- { bg: "bg-purple-500", text: "text-white" },
3653
- { bg: "bg-pink-500", text: "text-white" },
3654
- { bg: "bg-indigo-500", text: "text-white" },
3655
- { bg: "bg-teal-500", text: "text-white" },
3656
- { bg: "bg-orange-500", text: "text-white" },
3657
- { bg: "bg-cyan-500", text: "text-white" },
3658
- { bg: "bg-amber-500", text: "text-white" },
3659
- { bg: "bg-lime-500", text: "text-white" }
3650
+ { bg: "#3b82f6", text: "#ffffff" },
3651
+ // blue-500
3652
+ { bg: "#22c55e", text: "#111827" },
3653
+ // green-500 (light bg)
3654
+ { bg: "#a855f7", text: "#ffffff" },
3655
+ // purple-500
3656
+ { bg: "#ec4899", text: "#ffffff" },
3657
+ // pink-500
3658
+ { bg: "#6366f1", text: "#ffffff" },
3659
+ // indigo-500
3660
+ { bg: "#14b8a6", text: "#111827" },
3661
+ // teal-500 (light bg)
3662
+ { bg: "#f97316", text: "#ffffff" },
3663
+ // orange-500
3664
+ { bg: "#06b6d4", text: "#111827" },
3665
+ // cyan-500 (light bg)
3666
+ { bg: "#f59e0b", text: "#111827" },
3667
+ // amber-500 (light bg)
3668
+ { bg: "#84cc16", text: "#111827" }
3669
+ // lime-500 (light bg)
3660
3670
  ];
3661
3671
  var sizeClasses = {
3662
3672
  xs: "w-6 h-6 text-[10px]",
@@ -3686,8 +3696,6 @@ function UserAvatar({
3686
3696
  {
3687
3697
  className: `
3688
3698
  ${sizeClasses[size]}
3689
- ${colors.bg}
3690
- ${colors.text}
3691
3699
  ${className}
3692
3700
  rounded-full
3693
3701
  flex
@@ -3697,6 +3705,7 @@ function UserAvatar({
3697
3705
  flex-shrink-0
3698
3706
  select-none
3699
3707
  `,
3708
+ style: { backgroundColor: colors.bg, color: colors.text },
3700
3709
  "aria-label": `${firstName} ${lastName}`,
3701
3710
  title: `${firstName} ${lastName}`,
3702
3711
  children: initials
@@ -12668,7 +12677,9 @@ function ModelSelector() {
12668
12677
  }, [currentModel, models, setModel]);
12669
12678
  useEffect(() => {
12670
12679
  const handler = (e) => {
12671
- if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === "m") {
12680
+ const isModifierPressed = e.metaKey || e.ctrlKey;
12681
+ const isShortcutKey = e.code === "KeyM" || e.key.toLowerCase() === "m";
12682
+ if (isModifierPressed && e.shiftKey && !e.altKey && isShortcutKey) {
12672
12683
  e.preventDefault();
12673
12684
  rotateModel();
12674
12685
  }
@@ -14373,6 +14384,62 @@ function AllChatsList({ dataSource, onSessionSelect, activeSessionId }) {
14373
14384
  });
14374
14385
  return Array.from(userMap.values());
14375
14386
  }, [chats, users, dataSource.listUsers]);
14387
+ const [expandedGroups, setExpandedGroups] = useState(/* @__PURE__ */ new Set());
14388
+ const toggleGroup = useCallback((ownerId) => {
14389
+ setExpandedGroups((prev) => {
14390
+ const next = new Set(prev);
14391
+ if (next.has(ownerId)) {
14392
+ next.delete(ownerId);
14393
+ } else {
14394
+ next.add(ownerId);
14395
+ }
14396
+ return next;
14397
+ });
14398
+ }, []);
14399
+ useEffect(() => {
14400
+ if (!activeSessionId || selectedUser) {
14401
+ return;
14402
+ }
14403
+ const chat = chats.find((c) => c.id === activeSessionId);
14404
+ if (chat?.owner?.id) {
14405
+ setExpandedGroups((prev) => {
14406
+ if (prev.has(chat.owner.id)) {
14407
+ return prev;
14408
+ }
14409
+ return /* @__PURE__ */ new Set([...prev, chat.owner.id]);
14410
+ });
14411
+ }
14412
+ }, [activeSessionId, chats, selectedUser]);
14413
+ const groupedChats = useMemo(() => {
14414
+ if (selectedUser) {
14415
+ return null;
14416
+ }
14417
+ const groupMap = /* @__PURE__ */ new Map();
14418
+ chats.forEach((chat) => {
14419
+ const owner = chat.owner ?? {
14420
+ id: "__unknown__",
14421
+ firstName: t("BiChat.Common.Untitled"),
14422
+ lastName: "",
14423
+ initials: "?"
14424
+ };
14425
+ const ownerId = owner.id;
14426
+ if (!groupMap.has(ownerId)) {
14427
+ groupMap.set(ownerId, {
14428
+ owner,
14429
+ chats: [],
14430
+ latestUpdatedAt: chat.updatedAt
14431
+ });
14432
+ }
14433
+ const group = groupMap.get(ownerId);
14434
+ group.chats.push(chat);
14435
+ if (chat.updatedAt > group.latestUpdatedAt) {
14436
+ group.latestUpdatedAt = chat.updatedAt;
14437
+ }
14438
+ });
14439
+ return Array.from(groupMap.values()).sort(
14440
+ (a, b) => b.latestUpdatedAt.localeCompare(a.latestUpdatedAt)
14441
+ );
14442
+ }, [chats, selectedUser, t]);
14376
14443
  return /* @__PURE__ */ jsxs(
14377
14444
  "div",
14378
14445
  {
@@ -14418,61 +14485,159 @@ function AllChatsList({ dataSource, onSessionSelect, activeSessionId }) {
14418
14485
  role: "list",
14419
14486
  "aria-label": t("BiChat.AllChats.OrganizationChatSessions"),
14420
14487
  children: [
14421
- chats.map((chat) => {
14422
- const owner = chat.owner ?? {
14423
- firstName: "",
14424
- lastName: "",
14425
- initials: "U"
14426
- };
14427
- const ownerName = [owner.firstName, owner.lastName].filter(Boolean).join(" ");
14428
- return /* @__PURE__ */ jsx(
14429
- motion.div,
14430
- {
14431
- initial: { opacity: 0, y: -10 },
14432
- animate: { opacity: 1, y: 0 },
14433
- exit: { opacity: 0, y: -10 },
14434
- children: /* @__PURE__ */ jsx(
14488
+ groupedChats ? (
14489
+ /* ── Grouped view (no user selected) ── */
14490
+ groupedChats.map((group) => {
14491
+ const ownerId = group.owner.id;
14492
+ const ownerName = [group.owner.firstName, group.owner.lastName].filter(Boolean).join(" ");
14493
+ const isCollapsed = !expandedGroups.has(ownerId);
14494
+ return /* @__PURE__ */ jsxs("div", { className: "mb-1", children: [
14495
+ /* @__PURE__ */ jsxs(
14435
14496
  "div",
14436
14497
  {
14437
- role: "link",
14498
+ role: "button",
14438
14499
  tabIndex: 0,
14439
- onClick: () => onSessionSelect(chat.id),
14500
+ onClick: () => toggleGroup(ownerId),
14440
14501
  onKeyDown: (e) => {
14441
14502
  if (e.key === "Enter" || e.key === " ") {
14442
14503
  e.preventDefault();
14443
- onSessionSelect(chat.id);
14504
+ toggleGroup(ownerId);
14444
14505
  }
14445
14506
  },
14446
- className: `
14447
- block px-3 py-2 rounded-lg transition-smooth group cursor-pointer
14448
- ${chat.id === activeSessionId ? "bg-primary-50/50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-400 border-l-4 border-primary-400 dark:border-primary-600" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 border-l-4 border-transparent"}
14449
- `,
14450
- "aria-current": chat.id === activeSessionId ? "page" : void 0,
14451
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
14507
+ className: "flex items-center gap-2 px-3 py-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-lg transition-smooth select-none",
14508
+ "aria-expanded": !isCollapsed,
14509
+ children: [
14510
+ /* @__PURE__ */ jsx(
14511
+ CaretRight,
14512
+ {
14513
+ size: 14,
14514
+ weight: "bold",
14515
+ className: `shrink-0 text-gray-500 dark:text-gray-400 transition-transform duration-150 ${isCollapsed ? "" : "rotate-90"}`
14516
+ }
14517
+ ),
14452
14518
  /* @__PURE__ */ jsx(
14453
14519
  MemoizedUserAvatar,
14454
14520
  {
14455
- firstName: owner.firstName,
14456
- lastName: owner.lastName,
14457
- initials: owner.initials,
14521
+ firstName: group.owner.firstName,
14522
+ lastName: group.owner.lastName,
14523
+ initials: group.owner.initials,
14458
14524
  size: "sm"
14459
14525
  }
14460
14526
  ),
14461
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
14462
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: chat.title || t("BiChat.Common.Untitled") }),
14463
- ownerName && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 truncate", children: ownerName }),
14464
- chat.status === "archived" && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 mt-1 px-2 py-0.5 bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 rounded-full text-xs", children: [
14465
- /* @__PURE__ */ jsx(Archive, { size: 12, className: "w-3 h-3" }),
14466
- t("BiChat.Chat.Archived")
14527
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300 truncate flex-1 min-w-0", children: ownerName || t("BiChat.Common.Untitled") }),
14528
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded-full flex-shrink-0", children: group.chats.length })
14529
+ ]
14530
+ }
14531
+ ),
14532
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: !isCollapsed && /* @__PURE__ */ jsx(
14533
+ motion.div,
14534
+ {
14535
+ initial: { height: 0, opacity: 0 },
14536
+ animate: { height: "auto", opacity: 1 },
14537
+ exit: { height: 0, opacity: 0 },
14538
+ transition: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
14539
+ className: "overflow-hidden",
14540
+ children: /* @__PURE__ */ jsx("div", { className: "space-y-0.5 pl-6", children: group.chats.map((chat) => /* @__PURE__ */ jsx(
14541
+ motion.div,
14542
+ {
14543
+ initial: { opacity: 0, y: -10 },
14544
+ animate: { opacity: 1, y: 0 },
14545
+ exit: { opacity: 0, y: -10 },
14546
+ children: /* @__PURE__ */ jsx(
14547
+ "div",
14548
+ {
14549
+ role: "link",
14550
+ tabIndex: 0,
14551
+ onClick: () => onSessionSelect(chat.id),
14552
+ onKeyDown: (e) => {
14553
+ if (e.key === "Enter" || e.key === " ") {
14554
+ e.preventDefault();
14555
+ onSessionSelect(chat.id);
14556
+ }
14557
+ },
14558
+ className: `
14559
+ block px-3 py-2 rounded-lg transition-smooth group cursor-pointer
14560
+ ${chat.id === activeSessionId ? "bg-primary-50/50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-400 border-l-4 border-primary-400 dark:border-primary-600" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 border-l-4 border-transparent"}
14561
+ `,
14562
+ "aria-current": chat.id === activeSessionId ? "page" : void 0,
14563
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
14564
+ /* @__PURE__ */ jsx("p", { className: "text-sm truncate flex-1 min-w-0", children: chat.title || t("BiChat.Common.Untitled") }),
14565
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 flex-shrink-0", children: [
14566
+ chat.isGroup && chat.memberCount && chat.memberCount > 1 && /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: chat.memberCount }),
14567
+ chat.status === "archived" && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 rounded-full text-xs", children: [
14568
+ /* @__PURE__ */ jsx(Archive, { size: 12, className: "w-3 h-3" }),
14569
+ t("BiChat.Chat.Archived")
14570
+ ] })
14571
+ ] })
14572
+ ] })
14573
+ }
14574
+ )
14575
+ },
14576
+ chat.id
14577
+ )) })
14578
+ },
14579
+ `group-${ownerId}`
14580
+ ) })
14581
+ ] }, ownerId);
14582
+ })
14583
+ ) : (
14584
+ /* ── Flat view (user selected) ── */
14585
+ chats.map((chat) => {
14586
+ const owner = chat.owner ?? {
14587
+ firstName: "",
14588
+ lastName: "",
14589
+ initials: "U"
14590
+ };
14591
+ const ownerName = [owner.firstName, owner.lastName].filter(Boolean).join(" ");
14592
+ return /* @__PURE__ */ jsx(
14593
+ motion.div,
14594
+ {
14595
+ initial: { opacity: 0, y: -10 },
14596
+ animate: { opacity: 1, y: 0 },
14597
+ exit: { opacity: 0, y: -10 },
14598
+ children: /* @__PURE__ */ jsx(
14599
+ "div",
14600
+ {
14601
+ role: "link",
14602
+ tabIndex: 0,
14603
+ onClick: () => onSessionSelect(chat.id),
14604
+ onKeyDown: (e) => {
14605
+ if (e.key === "Enter" || e.key === " ") {
14606
+ e.preventDefault();
14607
+ onSessionSelect(chat.id);
14608
+ }
14609
+ },
14610
+ className: `
14611
+ block px-3 py-2 rounded-lg transition-smooth group cursor-pointer
14612
+ ${chat.id === activeSessionId ? "bg-primary-50/50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-400 border-l-4 border-primary-400 dark:border-primary-600" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 border-l-4 border-transparent"}
14613
+ `,
14614
+ "aria-current": chat.id === activeSessionId ? "page" : void 0,
14615
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
14616
+ /* @__PURE__ */ jsx(
14617
+ MemoizedUserAvatar,
14618
+ {
14619
+ firstName: owner.firstName,
14620
+ lastName: owner.lastName,
14621
+ initials: owner.initials,
14622
+ size: "sm"
14623
+ }
14624
+ ),
14625
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
14626
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: chat.title || t("BiChat.Common.Untitled") }),
14627
+ ownerName && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 truncate", children: ownerName }),
14628
+ chat.status === "archived" && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 mt-1 px-2 py-0.5 bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 rounded-full text-xs", children: [
14629
+ /* @__PURE__ */ jsx(Archive, { size: 12, className: "w-3 h-3" }),
14630
+ t("BiChat.Chat.Archived")
14631
+ ] })
14467
14632
  ] })
14468
14633
  ] })
14469
- ] })
14470
- }
14471
- )
14472
- },
14473
- chat.id
14474
- );
14475
- }),
14634
+ }
14635
+ )
14636
+ },
14637
+ chat.id
14638
+ );
14639
+ })
14640
+ ),
14476
14641
  hasMore && /* @__PURE__ */ jsx("div", { ref: loadMoreRef, className: "py-4 text-center", children: fetching ? /* @__PURE__ */ jsx(SessionSkeleton, { count: 2 }) : /* @__PURE__ */ jsx(
14477
14642
  "button",
14478
14643
  {
@@ -14695,7 +14860,9 @@ function Sidebar2({
14695
14860
  onClose,
14696
14861
  headerSlot,
14697
14862
  footerSlot,
14698
- className = ""
14863
+ className = "",
14864
+ activeTab: controlledActiveTab,
14865
+ onTabChange
14699
14866
  }) {
14700
14867
  const { t } = useTranslation();
14701
14868
  const toast = useToast();
@@ -14755,7 +14922,14 @@ function Sidebar2({
14755
14922
  const timer = setTimeout(() => setCollapsedOverflowVisible(true), 300);
14756
14923
  return () => clearTimeout(timer);
14757
14924
  }, [showCollapsed]);
14758
- const [activeTab, setActiveTab] = useState("my-chats");
14925
+ const [internalActiveTab, setInternalActiveTab] = useState("my-chats");
14926
+ const activeTab = controlledActiveTab ?? internalActiveTab;
14927
+ const handleTabChange = useCallback((tab) => {
14928
+ if (controlledActiveTab === void 0) {
14929
+ setInternalActiveTab(tab);
14930
+ }
14931
+ onTabChange?.(tab);
14932
+ }, [controlledActiveTab, onTabChange]);
14759
14933
  const [searchQuery, setSearchQuery] = useState("");
14760
14934
  const [sessions, setSessions] = useState([]);
14761
14935
  const [loading, setLoading] = useState(true);
@@ -15369,7 +15543,7 @@ function Sidebar2({
15369
15543
  onClick: (e) => {
15370
15544
  e.preventDefault();
15371
15545
  e.stopPropagation();
15372
- setActiveTab("all-chats");
15546
+ handleTabChange("all-chats");
15373
15547
  close();
15374
15548
  },
15375
15549
  className: `cursor-pointer flex w-full items-center gap-2.5 rounded-lg px-2.5 py-1.5 text-[13px] text-gray-600 dark:text-gray-300 transition-colors ${focus ? "bg-gray-100 dark:bg-gray-800/70" : ""}`,
@@ -15386,7 +15560,7 @@ function Sidebar2({
15386
15560
  onClick: (e) => {
15387
15561
  e.preventDefault();
15388
15562
  e.stopPropagation();
15389
- setActiveTab("my-chats");
15563
+ handleTabChange("my-chats");
15390
15564
  close();
15391
15565
  },
15392
15566
  className: `cursor-pointer flex w-full items-center gap-2.5 rounded-lg px-2.5 py-1.5 text-[13px] text-gray-600 dark:text-gray-300 transition-colors ${focus ? "bg-gray-100 dark:bg-gray-800/70" : ""}`,
@@ -16881,6 +17055,140 @@ function useStreaming2(options = {}) {
16881
17055
  reset
16882
17056
  };
16883
17057
  }
17058
+ var TERMINAL = /* @__PURE__ */ new Set([
17059
+ "completed",
17060
+ "cancelled",
17061
+ "failed"
17062
+ ]);
17063
+ function useActiveRuns(dataSource, options = {}) {
17064
+ const [runs, setRuns] = useState({});
17065
+ const [ready, setReady] = useState(false);
17066
+ const onErrorRef = useRef(options.onError);
17067
+ onErrorRef.current = options.onError;
17068
+ const enabled = options.enabled ?? true;
17069
+ const retainTerminalMs = options.retainTerminalMs ?? 0;
17070
+ const emptyStateTimeoutMs = options.emptyStateTimeoutMs ?? 250;
17071
+ useEffect(() => {
17072
+ if (!enabled) {
17073
+ return;
17074
+ }
17075
+ if (!dataSource.subscribeActiveRuns) {
17076
+ return;
17077
+ }
17078
+ const controller = new AbortController();
17079
+ let stagingTimer;
17080
+ const staging = {};
17081
+ let sawSnapshotRow = false;
17082
+ const pendingTerminalTimers = /* @__PURE__ */ new Map();
17083
+ const flushSnapshot = () => {
17084
+ setRuns((prev) => ({ ...prev, ...staging }));
17085
+ setReady(true);
17086
+ stagingTimer = void 0;
17087
+ };
17088
+ const subscription = dataSource.subscribeActiveRuns({
17089
+ signal: controller.signal,
17090
+ onError: (evt) => onErrorRef.current?.(evt),
17091
+ onEvent: (evt) => {
17092
+ if (evt.event === "snapshot") {
17093
+ sawSnapshotRow = true;
17094
+ const pending2 = pendingTerminalTimers.get(evt.sessionId);
17095
+ if (pending2 !== void 0) {
17096
+ clearTimeout(pending2);
17097
+ pendingTerminalTimers.delete(evt.sessionId);
17098
+ }
17099
+ staging[evt.sessionId] = {
17100
+ runId: evt.runId,
17101
+ status: evt.status,
17102
+ updatedAt: evt.updatedAt
17103
+ };
17104
+ if (stagingTimer === void 0) {
17105
+ stagingTimer = setTimeout(flushSnapshot, 16);
17106
+ }
17107
+ return;
17108
+ }
17109
+ if (TERMINAL.has(evt.status)) {
17110
+ if (retainTerminalMs <= 0) {
17111
+ setRuns((prev) => {
17112
+ if (!(evt.sessionId in prev)) {
17113
+ return prev;
17114
+ }
17115
+ const next = { ...prev };
17116
+ delete next[evt.sessionId];
17117
+ return next;
17118
+ });
17119
+ return;
17120
+ }
17121
+ setRuns((prev) => ({
17122
+ ...prev,
17123
+ [evt.sessionId]: {
17124
+ runId: evt.runId,
17125
+ status: evt.status,
17126
+ updatedAt: evt.updatedAt
17127
+ }
17128
+ }));
17129
+ const existing = pendingTerminalTimers.get(evt.sessionId);
17130
+ if (existing !== void 0) {
17131
+ clearTimeout(existing);
17132
+ }
17133
+ const handle = setTimeout(() => {
17134
+ pendingTerminalTimers.delete(evt.sessionId);
17135
+ setRuns((prev) => {
17136
+ if (!(evt.sessionId in prev)) {
17137
+ return prev;
17138
+ }
17139
+ const next = { ...prev };
17140
+ delete next[evt.sessionId];
17141
+ return next;
17142
+ });
17143
+ }, retainTerminalMs);
17144
+ pendingTerminalTimers.set(evt.sessionId, handle);
17145
+ return;
17146
+ }
17147
+ const pending = pendingTerminalTimers.get(evt.sessionId);
17148
+ if (pending !== void 0) {
17149
+ clearTimeout(pending);
17150
+ pendingTerminalTimers.delete(evt.sessionId);
17151
+ }
17152
+ setRuns((prev) => ({
17153
+ ...prev,
17154
+ [evt.sessionId]: {
17155
+ runId: evt.runId,
17156
+ status: evt.status,
17157
+ updatedAt: evt.updatedAt
17158
+ }
17159
+ }));
17160
+ }
17161
+ });
17162
+ subscription?.catch((err) => {
17163
+ if (controller.signal.aborted) {
17164
+ return;
17165
+ }
17166
+ const surface = err instanceof Event ? err : new Event("error");
17167
+ onErrorRef.current?.(surface);
17168
+ });
17169
+ const readyTimeout = setTimeout(() => {
17170
+ if (!sawSnapshotRow) {
17171
+ setReady(true);
17172
+ }
17173
+ }, emptyStateTimeoutMs);
17174
+ return () => {
17175
+ controller.abort();
17176
+ if (stagingTimer !== void 0) {
17177
+ clearTimeout(stagingTimer);
17178
+ }
17179
+ clearTimeout(readyTimeout);
17180
+ for (const handle of pendingTerminalTimers.values()) {
17181
+ clearTimeout(handle);
17182
+ }
17183
+ pendingTerminalTimers.clear();
17184
+ };
17185
+ }, [dataSource, enabled, retainTerminalMs, emptyStateTimeoutMs]);
17186
+ const status = useCallback(
17187
+ (sessionId) => runs[sessionId]?.status,
17188
+ [runs]
17189
+ );
17190
+ return { runs, ready, status };
17191
+ }
16884
17192
 
16885
17193
  // ui/src/bichat/index.ts
16886
17194
  init_useTranslation();
@@ -17493,42 +17801,66 @@ function useBichatRouter({
17493
17801
  pathname,
17494
17802
  onNavigate
17495
17803
  }) {
17804
+ const isAllChats = pathname.startsWith("/all-chats");
17496
17805
  const activeSessionId = useMemo(
17497
17806
  () => pathname.match(SESSION_PATH_REGEX)?.[1],
17498
17807
  [pathname]
17499
17808
  );
17809
+ const sidebarTab = useMemo(
17810
+ () => isAllChats ? "all-chats" : "my-chats",
17811
+ [isAllChats]
17812
+ );
17500
17813
  const maybeClose = useCallback(() => {
17501
17814
  onNavigate?.();
17502
17815
  }, [onNavigate]);
17503
17816
  const onSessionSelect = useCallback(
17504
17817
  (sessionId) => {
17505
17818
  if (sessionId) {
17506
- navigate(`/session/${sessionId}`);
17819
+ const prefix = isAllChats ? "/all-chats" : "";
17820
+ navigate(`${prefix}/session/${sessionId}`);
17507
17821
  } else {
17508
- navigate("/");
17822
+ navigate(isAllChats ? "/all-chats" : "/");
17509
17823
  }
17510
17824
  maybeClose();
17511
17825
  },
17512
- [navigate, maybeClose]
17826
+ [navigate, maybeClose, isAllChats]
17513
17827
  );
17514
17828
  const onNewChat = useCallback(() => {
17515
- navigate("/");
17829
+ navigate(isAllChats ? "/all-chats" : "/");
17516
17830
  maybeClose();
17517
- }, [navigate, maybeClose]);
17831
+ }, [navigate, maybeClose, isAllChats]);
17518
17832
  const onArchivedView = useCallback(() => {
17519
17833
  navigate("/archived");
17520
17834
  maybeClose();
17521
17835
  }, [navigate, maybeClose]);
17836
+ const onAllChatsView = useCallback(() => {
17837
+ navigate("/all-chats");
17838
+ maybeClose();
17839
+ }, [navigate, maybeClose]);
17522
17840
  const onBack = useCallback(() => {
17523
17841
  navigate("/");
17524
17842
  maybeClose();
17525
17843
  }, [navigate, maybeClose]);
17844
+ const onSidebarTabChange = useCallback(
17845
+ (tab) => {
17846
+ if (tab === "all-chats") {
17847
+ navigate("/all-chats");
17848
+ } else {
17849
+ navigate("/");
17850
+ }
17851
+ maybeClose();
17852
+ },
17853
+ [navigate, maybeClose]
17854
+ );
17526
17855
  return {
17527
17856
  activeSessionId,
17528
17857
  onSessionSelect,
17529
17858
  onNewChat,
17530
17859
  onArchivedView,
17531
- onBack
17860
+ onBack,
17861
+ onAllChatsView,
17862
+ sidebarTab,
17863
+ onSidebarTabChange
17532
17864
  };
17533
17865
  }
17534
17866
 
@@ -18926,6 +19258,8 @@ function toStreamEvent(chunk) {
18926
19258
  return chunk.sessionId ? { type: "user_message", sessionId: chunk.sessionId } : null;
18927
19259
  case "interrupt":
18928
19260
  return chunk.interrupt ? { type: "interrupt", interrupt: chunk.interrupt, sessionId: chunk.sessionId } : null;
19261
+ case "text_block_end":
19262
+ return { type: "text_block_end", seq: chunk.textBlockSeq ?? 0 };
18929
19263
  case "done":
18930
19264
  return { type: "done", sessionId: chunk.sessionId, generationMs: chunk.generationMs };
18931
19265
  case "error":
@@ -18935,6 +19269,90 @@ function toStreamEvent(chunk) {
18935
19269
  }
18936
19270
  }
18937
19271
 
19272
+ // ui/src/bichat/utils/eventNames.ts
19273
+ var STREAM_EVENT_TYPES = [
19274
+ "chunk",
19275
+ "content",
19276
+ "thinking",
19277
+ "tool_start",
19278
+ "tool_end",
19279
+ "text_block_end",
19280
+ "snapshot",
19281
+ "interrupt",
19282
+ "citation",
19283
+ "usage",
19284
+ "ping",
19285
+ "stream_started",
19286
+ "done",
19287
+ "cancelled",
19288
+ "error",
19289
+ "failed"
19290
+ ];
19291
+ var TERMINAL_STREAM_EVENT_TYPES = [
19292
+ "done",
19293
+ "cancelled",
19294
+ "error",
19295
+ "failed"
19296
+ ];
19297
+ function isTerminalEvent(name) {
19298
+ return TERMINAL_STREAM_EVENT_TYPES.includes(name);
19299
+ }
19300
+
19301
+ // ui/src/bichat/data/openManagedEventSource.ts
19302
+ function openManagedEventSource(opts) {
19303
+ const graceMs = opts.connectGraceMs ?? 500;
19304
+ return new Promise((resolve, reject) => {
19305
+ const startedAt = Date.now();
19306
+ const es = new EventSource(opts.url, {
19307
+ withCredentials: opts.withCredentials ?? true
19308
+ });
19309
+ let settled = false;
19310
+ let sawEvent = false;
19311
+ const settle = (err) => {
19312
+ if (settled) {
19313
+ return;
19314
+ }
19315
+ settled = true;
19316
+ es.close();
19317
+ if (err) {
19318
+ reject(err);
19319
+ } else {
19320
+ resolve();
19321
+ }
19322
+ };
19323
+ if (opts.signal) {
19324
+ if (opts.signal.aborted) {
19325
+ settle();
19326
+ return;
19327
+ }
19328
+ opts.signal.addEventListener("abort", () => settle(), { once: true });
19329
+ }
19330
+ const forward = (name) => (evt) => {
19331
+ sawEvent = true;
19332
+ let parsed;
19333
+ try {
19334
+ parsed = JSON.parse(evt.data);
19335
+ } catch {
19336
+ parsed = { __unparseable: true, raw: String(evt.data) };
19337
+ }
19338
+ try {
19339
+ opts.onMessage(name, parsed);
19340
+ } catch {
19341
+ }
19342
+ };
19343
+ for (const name of opts.events) {
19344
+ es.addEventListener(name, forward(name));
19345
+ }
19346
+ es.onerror = (evt) => {
19347
+ opts.onError?.(evt);
19348
+ if (graceMs > 0 && !sawEvent && Date.now() - startedAt < graceMs) {
19349
+ const err = opts.onConnectError ? opts.onConnectError(evt) : new Error("EventSource failed to connect before first event");
19350
+ settle(err);
19351
+ }
19352
+ };
19353
+ });
19354
+ }
19355
+
18938
19356
  // ui/src/bichat/data/AttachmentUploader.ts
18939
19357
  init_chartSpec();
18940
19358
  var MIME_TO_EXTENSION = {
@@ -19189,6 +19607,26 @@ async function ensureAttachmentUpload(attachment, context, uploadFileFn) {
19189
19607
  }
19190
19608
 
19191
19609
  // ui/src/bichat/data/MessageTransport.ts
19610
+ var RunEventsConnectError = class extends Error {
19611
+ constructor(message, cause) {
19612
+ super(message);
19613
+ this.name = "RunEventsConnectError";
19614
+ this.cause = cause;
19615
+ }
19616
+ };
19617
+ function generateRequestId() {
19618
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
19619
+ return crypto.randomUUID();
19620
+ }
19621
+ const bytes = new Uint8Array(16);
19622
+ for (let i = 0; i < bytes.length; i++) {
19623
+ bytes[i] = Math.floor(Math.random() * 256);
19624
+ }
19625
+ bytes[6] = bytes[6] & 15 | 64;
19626
+ bytes[8] = bytes[8] & 63 | 128;
19627
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
19628
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
19629
+ }
19192
19630
  async function* sendMessage(deps, sessionId, content, attachments = [], signal, options) {
19193
19631
  const abortController = new AbortController();
19194
19632
  let onExternalAbort;
@@ -19216,12 +19654,14 @@ async function* sendMessage(deps, sessionId, content, attachments = [], signal,
19216
19654
  sessionId,
19217
19655
  attachmentCount: streamAttachments.length
19218
19656
  });
19657
+ const requestId = options?.requestId ?? generateRequestId();
19219
19658
  const payload = {
19220
19659
  sessionId,
19221
19660
  content,
19222
19661
  debugMode: options?.debugMode ?? false,
19223
19662
  replaceFromMessageId: options?.replaceFromMessageID,
19224
- attachments: streamAttachments
19663
+ attachments: streamAttachments,
19664
+ requestId
19225
19665
  };
19226
19666
  if (options?.reasoningEffort) {
19227
19667
  payload.reasoningEffort = options.reasoningEffort;
@@ -19389,6 +19829,67 @@ async function resumeStream(deps, sessionId, runId, onChunk, signal) {
19389
19829
  }
19390
19830
  }
19391
19831
  }
19832
+ function subscribeRunEvents(deps, sessionId, runId, options) {
19833
+ const base = buildStreamUrl(deps, "/events");
19834
+ const qs = new URLSearchParams({ sessionId, runId });
19835
+ const url = `${base}?${qs.toString()}`;
19836
+ const withCursor = options.lastEventId ? `${url}&${new URLSearchParams({ lastEventId: options.lastEventId }).toString()}` : url;
19837
+ const settleController = new AbortController();
19838
+ if (options.signal) {
19839
+ if (options.signal.aborted) {
19840
+ settleController.abort();
19841
+ } else {
19842
+ options.signal.addEventListener("abort", () => settleController.abort(), {
19843
+ once: true
19844
+ });
19845
+ }
19846
+ }
19847
+ return openManagedEventSource({
19848
+ url: withCursor,
19849
+ events: STREAM_EVENT_TYPES,
19850
+ withCredentials: true,
19851
+ signal: settleController.signal,
19852
+ onError: options.onError,
19853
+ onConnectError: (evt) => new RunEventsConnectError(
19854
+ "EventSource failed to connect before first event",
19855
+ evt
19856
+ ),
19857
+ onMessage: (name, data) => {
19858
+ if (typeof data === "object" && data !== null && data.__unparseable) {
19859
+ options.onChunk({
19860
+ type: "error",
19861
+ error: `Failed to parse event: ${data.raw}`
19862
+ });
19863
+ return;
19864
+ }
19865
+ const parsed = data;
19866
+ if (!parsed.type) {
19867
+ parsed.type = name;
19868
+ }
19869
+ options.onChunk(parsed);
19870
+ if (isTerminalEvent(name) || isTerminalEvent(String(parsed.type))) {
19871
+ settleController.abort();
19872
+ }
19873
+ }
19874
+ });
19875
+ }
19876
+ function subscribeActiveRuns(deps, options) {
19877
+ const url = buildStreamUrl(deps, "/active-runs");
19878
+ return openManagedEventSource({
19879
+ url,
19880
+ events: ["snapshot", "update"],
19881
+ withCredentials: true,
19882
+ signal: options.signal,
19883
+ onError: options.onError,
19884
+ onMessage: (name, data) => {
19885
+ if (typeof data !== "object" || data === null || data.__unparseable) {
19886
+ return;
19887
+ }
19888
+ const body = data;
19889
+ options.onEvent({ event: name, ...body });
19890
+ }
19891
+ });
19892
+ }
19392
19893
  async function submitQuestionAnswers(callRPC, sessionId, questionId, answers) {
19393
19894
  try {
19394
19895
  const flatAnswers = {};
@@ -19657,6 +20158,39 @@ var HttpDataSource = class {
19657
20158
  signal
19658
20159
  );
19659
20160
  }
20161
+ /**
20162
+ * Open a native EventSource against GET /stream/events for the given
20163
+ * run. Used by components that want browser-native auto-reconnect
20164
+ * with Last-Event-ID (tab close, wifi drop, device switch). Prefer
20165
+ * over resumeStream when tailing an already-running generation that
20166
+ * another tab started.
20167
+ */
20168
+ subscribeRunEvents(sessionId, runId, options) {
20169
+ return subscribeRunEvents(
20170
+ {
20171
+ baseUrl: this.config.baseUrl,
20172
+ streamEndpoint: this.config.streamEndpoint
20173
+ },
20174
+ sessionId,
20175
+ runId,
20176
+ options
20177
+ );
20178
+ }
20179
+ /**
20180
+ * Subscribe to the per-tenant active-run fan-out
20181
+ * (GET /stream/active-runs). Never resolves until the caller aborts
20182
+ * via the signal; use from a top-level component (sidebar container)
20183
+ * that mounts for the lifetime of the chat app.
20184
+ */
20185
+ subscribeActiveRuns(options) {
20186
+ return subscribeActiveRuns(
20187
+ {
20188
+ baseUrl: this.config.baseUrl,
20189
+ streamEndpoint: this.config.streamEndpoint
20190
+ },
20191
+ options
20192
+ );
20193
+ }
19660
20194
  async *sendMessage(sessionId, content, attachments = [], signal, options) {
19661
20195
  this.abortController = new AbortController();
19662
20196
  let onExternalAbort;
@@ -19730,6 +20264,50 @@ function createHttpDataSource(config) {
19730
20264
  return new HttpDataSource(config);
19731
20265
  }
19732
20266
 
19733
- export { ATTACHMENT_ACCEPT_ATTRIBUTE, ActionButton, ActivityTrace, Alert_default as Alert, AllChatsList, ArchiveBanner_default as ArchiveBanner, ArchivedChatList, AssistantMessage, AssistantTurnView, MemoizedAttachmentGrid as AttachmentGrid, AttachmentPreview_default as AttachmentPreview, AttachmentUpload_default as AttachmentUpload, Avatar, AvatarStack, BiChatLayout, Bubble, CHART_VISUAL, ChartCard, ChatHeader, ChatMachine, ChatSession, ChatSessionProvider, MemoizedCodeBlock as CodeBlock, CodeOutputsPanel, CompactionDoodle, ConfigProvider, ConfirmModal, ConfirmationStep, DateGroupHeader, DebugPanel, DefaultErrorContent, DownloadCard, MemoizedEditableText as EditableText, MemoizedEmptyState as EmptyState, ErrorBoundary, HttpDataSource, ImageModal, InlineQuestionForm, InteractiveTableCard, IotaContextProvider, ListItemSkeleton, MemoizedLoadingSpinner as LoadingSpinner, MemoizedMarkdownRenderer as MarkdownRenderer, MessageActions, MessageInput, MessageList, MessageRole, ModelSelector, PermissionGuard, QuestionForm, QuestionStep, RateLimiter, RetryActionArea, ScreenReaderAnnouncer, ScrollToBottomButton, MemoizedSearchInput as SearchInput, SessionArtifactList, SessionArtifactPreview, SessionArtifactsPanel, SessionItem_default as SessionItem, SessionMembersModal, SessionSkeleton, Sidebar2 as Sidebar, MemoizedSkeleton as Skeleton, SkeletonAvatar, SkeletonCard, SkeletonGroup, SkeletonText, SkipLink, Slot, SourcesPanel, StreamError, StreamingCursor, SystemMessage, TabbedChartGroup, TabbedTableGroup, TableExportButton, TableWithExport, ThemeProvider, Toast, ToastContainer, TouchContextMenu, Turn, TurnBubble, MemoizedTypingIndicator as TypingIndicator, MemoizedUserAvatar as UserAvatar, MemoizedUserFilter as UserFilter, UserMessage, UserTurnView, WelcomeContent, addCSRFHeader, backdropVariants, buttonVariants, convertToBase64, createDataUrl, createHeadersWithCSRF, createHttpDataSource, darkTheme, dropdownVariants, errorMessageVariants, fadeInUpVariants, fadeInVariants, floatingButtonVariants, formatFileSize, getCSRFToken, getFileVisual, getToolLabel, getValidChildren, groupSessionsByDate, groupSteps, hasPermission, isImageMimeType, isPermissionDeniedError, lightTheme, listItemVariants, messageContainerVariants, messageVariants, parseBichatStream, parseBichatStreamEvents, parseSSEStream, scaleFadeVariants, sessionItemVariants, staggerContainerVariants, toErrorDisplay, typingDotVariants, useActionButtonContext, useAttachments, useAutoScroll, useAvatarContext, useBichatRouter, useBubbleContext, useChatInput, useChatMessaging, useChatSession, useConfig, useDataTable, useFocusTrap, useHttpDataSourceConfigFromApplet, useImageGallery, useIotaContext, useKeyboardShortcuts, useLongPress, useMarkdownCopy, useMessageActions, useModalLock, useOptionalChatMessaging, useRequiredConfig, useScrollToBottom, useSidebarState, useStreaming2 as useStreaming, useTheme, useToast, useTranslation, useTurnContext, validateAttachmentFile, validateFileCount, validateImageFile, verbTransitionVariants };
20267
+ // ui/src/bichat/utils/textBlocks.ts
20268
+ function splitIntoTextBlocks(content, offsets) {
20269
+ if (!content) {
20270
+ return [];
20271
+ }
20272
+ if (!offsets || offsets.length === 0) {
20273
+ return [{ seq: 0, content }];
20274
+ }
20275
+ const sanitized = [...offsets].map((n) => Math.max(0, Math.min(Math.floor(n), content.length))).sort((a, b) => a - b);
20276
+ const blocks = [];
20277
+ let cursor = 0;
20278
+ for (let i = 0; i < sanitized.length; i++) {
20279
+ const end = sanitized[i];
20280
+ if (end <= cursor) {
20281
+ continue;
20282
+ }
20283
+ const slice = content.slice(cursor, end);
20284
+ if (slice) {
20285
+ blocks.push({ seq: blocks.length, content: slice });
20286
+ }
20287
+ cursor = end;
20288
+ }
20289
+ if (cursor < content.length) {
20290
+ blocks.push({ seq: blocks.length, content: content.slice(cursor) });
20291
+ }
20292
+ return blocks;
20293
+ }
20294
+ function readTextBlockOffsets(partialMetadata) {
20295
+ if (!partialMetadata) {
20296
+ return [];
20297
+ }
20298
+ const raw = partialMetadata["text_block_offsets"];
20299
+ if (!Array.isArray(raw)) {
20300
+ return [];
20301
+ }
20302
+ const out = [];
20303
+ for (const entry of raw) {
20304
+ if (typeof entry === "number" && Number.isFinite(entry) && entry >= 0) {
20305
+ out.push(Math.floor(entry));
20306
+ }
20307
+ }
20308
+ return out;
20309
+ }
20310
+
20311
+ export { ATTACHMENT_ACCEPT_ATTRIBUTE, ActionButton, ActivityTrace, Alert_default as Alert, AllChatsList, ArchiveBanner_default as ArchiveBanner, ArchivedChatList, AssistantMessage, AssistantTurnView, MemoizedAttachmentGrid as AttachmentGrid, AttachmentPreview_default as AttachmentPreview, AttachmentUpload_default as AttachmentUpload, Avatar, AvatarStack, BiChatLayout, Bubble, CHART_VISUAL, ChartCard, ChatHeader, ChatMachine, ChatSession, ChatSessionProvider, MemoizedCodeBlock as CodeBlock, CodeOutputsPanel, CompactionDoodle, ConfigProvider, ConfirmModal, ConfirmationStep, DateGroupHeader, DebugPanel, DefaultErrorContent, DownloadCard, MemoizedEditableText as EditableText, MemoizedEmptyState as EmptyState, ErrorBoundary, HttpDataSource, ImageModal, InlineQuestionForm, InteractiveTableCard, IotaContextProvider, ListItemSkeleton, MemoizedLoadingSpinner as LoadingSpinner, MemoizedMarkdownRenderer as MarkdownRenderer, MessageActions, MessageInput, MessageList, MessageRole, ModelSelector, PermissionGuard, QuestionForm, QuestionStep, RateLimiter, RetryActionArea, RunEventsConnectError, STREAM_EVENT_TYPES, ScreenReaderAnnouncer, ScrollToBottomButton, MemoizedSearchInput as SearchInput, SessionArtifactList, SessionArtifactPreview, SessionArtifactsPanel, SessionItem_default as SessionItem, SessionMembersModal, SessionSkeleton, Sidebar2 as Sidebar, MemoizedSkeleton as Skeleton, SkeletonAvatar, SkeletonCard, SkeletonGroup, SkeletonText, SkipLink, Slot, SourcesPanel, StreamError, StreamingCursor, SystemMessage, TERMINAL_STREAM_EVENT_TYPES, TabbedChartGroup, TabbedTableGroup, TableExportButton, TableWithExport, ThemeProvider, Toast, ToastContainer, TouchContextMenu, Turn, TurnBubble, MemoizedTypingIndicator as TypingIndicator, MemoizedUserAvatar as UserAvatar, MemoizedUserFilter as UserFilter, UserMessage, UserTurnView, WelcomeContent, addCSRFHeader, backdropVariants, buttonVariants, convertToBase64, createDataUrl, createHeadersWithCSRF, createHttpDataSource, darkTheme, dropdownVariants, errorMessageVariants, fadeInUpVariants, fadeInVariants, floatingButtonVariants, formatFileSize, getCSRFToken, getFileVisual, getToolLabel, getValidChildren, groupSessionsByDate, groupSteps, hasPermission, isImageMimeType, isPermissionDeniedError, isTerminalEvent, lightTheme, listItemVariants, messageContainerVariants, messageVariants, parseBichatStream, parseBichatStreamEvents, parseSSEStream, readTextBlockOffsets, scaleFadeVariants, sessionItemVariants, splitIntoTextBlocks, staggerContainerVariants, toErrorDisplay, typingDotVariants, useActionButtonContext, useActiveRuns, useAttachments, useAutoScroll, useAvatarContext, useBichatRouter, useBubbleContext, useChatInput, useChatMessaging, useChatSession, useConfig, useDataTable, useFocusTrap, useHttpDataSourceConfigFromApplet, useImageGallery, useIotaContext, useKeyboardShortcuts, useLongPress, useMarkdownCopy, useMessageActions, useModalLock, useOptionalChatMessaging, useRequiredConfig, useScrollToBottom, useSidebarState, useStreaming2 as useStreaming, useTheme, useToast, useTranslation, useTurnContext, validateAttachmentFile, validateFileCount, validateImageFile, verbTransitionVariants };
19734
20312
  //# sourceMappingURL=index.mjs.map
19735
20313
  //# sourceMappingURL=index.mjs.map