@tangle-network/sandbox-ui 0.3.7 → 0.3.11

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 (39) hide show
  1. package/dist/active-sessions-store-CeOmXgv5.d.ts +85 -0
  2. package/dist/artifact-pane-Bh45Ssco.d.ts +24 -0
  3. package/dist/{chat-container-B34uj-J1.d.ts → chat-container-Dn1jWtWo.d.ts} +15 -3
  4. package/dist/chat.d.ts +16 -4
  5. package/dist/chat.js +2 -2
  6. package/dist/{chunk-PXRPYAMM.js → chunk-6H3EFUUC.js} +96 -74
  7. package/dist/{chunk-JF6E2DS5.js → chunk-6NYG2R7V.js} +171 -155
  8. package/dist/{chunk-WUR652Y3.js → chunk-72UEKFZ2.js} +113 -89
  9. package/dist/chunk-BOBXH6CH.js +10981 -0
  10. package/dist/{chunk-ZSNOGOUX.js → chunk-CREVWUCA.js} +541 -76
  11. package/dist/{chunk-5LV6DZZF.js → chunk-FOQTE67I.js} +278 -21
  12. package/dist/chunk-OEX7NZE3.js +321 -0
  13. package/dist/chunk-Q56BYXQF.js +61 -0
  14. package/dist/{chunk-PDV7W4NY.js → chunk-SULQQJPB.js} +1 -56
  15. package/dist/chunk-W4LM3QYZ.js +54 -0
  16. package/dist/document-editor-pane-AFBP2KFT.js +11 -0
  17. package/dist/document-editor-pane-Xnl8SmA7.d.ts +124 -0
  18. package/dist/editor.d.ts +7 -84
  19. package/dist/editor.js +18 -699
  20. package/dist/{expanded-tool-detail-BDi_h_dZ.d.ts → expanded-tool-detail-DM5M_T9h.d.ts} +10 -2
  21. package/dist/{file-tabs-CmaoDVBI.d.ts → file-tabs-BLfxfmAH.d.ts} +1 -22
  22. package/dist/files.d.ts +25 -3
  23. package/dist/files.js +2 -1
  24. package/dist/hooks.d.ts +3 -1
  25. package/dist/hooks.js +6 -1
  26. package/dist/index.d.ts +12 -6
  27. package/dist/index.js +21 -8
  28. package/dist/pages.js +4 -2
  29. package/dist/primitives.js +4 -2
  30. package/dist/run.d.ts +1 -1
  31. package/dist/run.js +1 -1
  32. package/dist/sdk-hooks.d.ts +32 -1
  33. package/dist/sdk-hooks.js +6 -1
  34. package/dist/stores.d.ts +1 -0
  35. package/dist/stores.js +60 -1
  36. package/dist/types.d.ts +2 -0
  37. package/dist/workspace.d.ts +84 -6
  38. package/dist/workspace.js +10 -4
  39. package/package.json +17 -6
@@ -1,6 +1,17 @@
1
1
  import {
2
2
  parseToolEvent
3
3
  } from "./chunk-CCKNIAS7.js";
4
+ import {
5
+ bumpActiveSessionActivity,
6
+ registerActiveSession,
7
+ setActiveSessionAttention,
8
+ setActiveSessionConnection,
9
+ setActiveSessionError,
10
+ setActiveSessionRunning,
11
+ setForegroundActiveSession,
12
+ unregisterActiveSession,
13
+ updateActiveSessionMeta
14
+ } from "./chunk-OEX7NZE3.js";
4
15
 
5
16
  // src/hooks/use-tool-call-stream.ts
6
17
  import { useState, useCallback, useRef } from "react";
@@ -744,8 +755,252 @@ function useSdkSession({
744
755
  };
745
756
  }
746
757
 
758
+ // src/hooks/use-realtime-session.ts
759
+ import { createElement, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
760
+ function parseEvent(message) {
761
+ try {
762
+ const parsed = JSON.parse(message);
763
+ return typeof parsed?.type === "string" ? parsed : null;
764
+ } catch {
765
+ return null;
766
+ }
767
+ }
768
+ function eventTimestamp(event) {
769
+ const rootTimestamp = event.timestamp;
770
+ if (typeof rootTimestamp === "number" && Number.isFinite(rootTimestamp)) {
771
+ return rootTimestamp;
772
+ }
773
+ const raw = event.data?.timestamp ?? event.data?.ts ?? event.data?.time ?? event.data?.eventAt;
774
+ if (typeof raw === "number" && Number.isFinite(raw)) return raw;
775
+ return null;
776
+ }
777
+ function resolveErrorMessage(event) {
778
+ const message = event.data?.message;
779
+ return typeof message === "string" && message.length > 0 ? message : null;
780
+ }
781
+ function updateStoreFromEvent(sessionId, event) {
782
+ const lastEventAt = eventTimestamp(event) ?? Date.now();
783
+ bumpActiveSessionActivity(sessionId, { lastEventAt });
784
+ if (event.type === "session.run.started") {
785
+ setActiveSessionRunning(sessionId, true, { lastEventAt });
786
+ return;
787
+ }
788
+ if (event.type === "session.run.completed" || event.type === "done" || event.type === "result") {
789
+ setActiveSessionRunning(sessionId, false, { lastEventAt });
790
+ return;
791
+ }
792
+ if (event.type === "session.attention") {
793
+ setActiveSessionAttention(sessionId, true, { lastEventAt });
794
+ return;
795
+ }
796
+ if (event.type === "error" || event.type === "session.run.failed") {
797
+ setActiveSessionError(sessionId, resolveErrorMessage(event) ?? "Session error");
798
+ return;
799
+ }
800
+ }
801
+ function useRealtimeSession({
802
+ sessionId,
803
+ projectId = null,
804
+ projectLabel,
805
+ title,
806
+ href,
807
+ metadata,
808
+ connectUrl,
809
+ enabled = true,
810
+ foreground = true,
811
+ keepRegistered = true,
812
+ reconnect = true,
813
+ reconnectIntervalMs = 1500,
814
+ maxReconnectAttempts = Infinity,
815
+ transportMode = "websocket",
816
+ onEvent,
817
+ onOpen,
818
+ onClose,
819
+ onError
820
+ }) {
821
+ const [connectionState, setConnectionState] = useState4("disconnected");
822
+ const [lastError, setLastError] = useState4(null);
823
+ const [reconnectAttempts, setReconnectAttempts] = useState4(0);
824
+ const socketRef = useRef4(null);
825
+ const timerRef = useRef4(null);
826
+ const reconnectAttemptsRef = useRef4(0);
827
+ const shouldReconnectRef = useRef4(true);
828
+ const registration = useMemo2(
829
+ () => ({
830
+ sessionId,
831
+ projectId,
832
+ projectLabel,
833
+ title,
834
+ href,
835
+ metadata
836
+ }),
837
+ [href, metadata, projectId, projectLabel, sessionId, title]
838
+ );
839
+ useEffect2(() => {
840
+ if (!sessionId || !keepRegistered) return void 0;
841
+ registerActiveSession(registration);
842
+ return () => {
843
+ unregisterActiveSession(sessionId);
844
+ };
845
+ }, [keepRegistered, registration, sessionId]);
846
+ useEffect2(() => {
847
+ if (!sessionId || !keepRegistered) return;
848
+ updateActiveSessionMeta(sessionId, {
849
+ projectId,
850
+ projectLabel,
851
+ title,
852
+ href,
853
+ metadata
854
+ });
855
+ }, [href, keepRegistered, metadata, projectId, projectLabel, sessionId, title]);
856
+ useEffect2(() => {
857
+ if (!sessionId || !foreground) return void 0;
858
+ setForegroundActiveSession(sessionId);
859
+ return () => {
860
+ setForegroundActiveSession(null);
861
+ };
862
+ }, [foreground, sessionId]);
863
+ useEffect2(() => {
864
+ if (!enabled || !sessionId || !connectUrl || typeof window === "undefined") {
865
+ setConnectionState("disconnected");
866
+ if (sessionId) {
867
+ setActiveSessionConnection(sessionId, {
868
+ connectionState: "disconnected",
869
+ reconnectState: "idle",
870
+ transportMode
871
+ });
872
+ }
873
+ return void 0;
874
+ }
875
+ shouldReconnectRef.current = true;
876
+ const clearReconnectTimer = () => {
877
+ if (timerRef.current != null) {
878
+ window.clearTimeout(timerRef.current);
879
+ timerRef.current = null;
880
+ }
881
+ };
882
+ const connect = () => {
883
+ clearReconnectTimer();
884
+ setConnectionState(reconnectAttemptsRef.current > 0 ? "reconnecting" : "connecting");
885
+ setActiveSessionConnection(sessionId, {
886
+ connectionState: reconnectAttemptsRef.current > 0 ? "reconnecting" : "connecting",
887
+ reconnectState: reconnectAttemptsRef.current > 0 ? "reconnecting" : "idle",
888
+ transportMode,
889
+ lastError: null
890
+ });
891
+ const socket = new window.WebSocket(connectUrl);
892
+ socketRef.current = socket;
893
+ socket.onopen = () => {
894
+ reconnectAttemptsRef.current = 0;
895
+ setReconnectAttempts(0);
896
+ setLastError(null);
897
+ setConnectionState("connected");
898
+ registerActiveSession(registration);
899
+ setActiveSessionConnection(sessionId, {
900
+ connectionState: "connected",
901
+ reconnectState: "idle",
902
+ transportMode,
903
+ lastError: null,
904
+ lastEventAt: Date.now()
905
+ });
906
+ onOpen?.();
907
+ };
908
+ socket.onmessage = (message) => {
909
+ const event = parseEvent(message.data);
910
+ if (!event) return;
911
+ registerActiveSession(registration);
912
+ updateStoreFromEvent(sessionId, event);
913
+ onEvent?.(event);
914
+ };
915
+ socket.onerror = () => {
916
+ const nextError = "Realtime session connection error";
917
+ setLastError(nextError);
918
+ setConnectionState("error");
919
+ setActiveSessionConnection(sessionId, {
920
+ connectionState: "error",
921
+ reconnectState: reconnect ? "reconnecting" : "failed",
922
+ transportMode,
923
+ lastError: nextError
924
+ });
925
+ onError?.(new Error(nextError));
926
+ };
927
+ socket.onclose = () => {
928
+ socketRef.current = null;
929
+ onClose?.();
930
+ if (!shouldReconnectRef.current || !reconnect) {
931
+ setConnectionState("disconnected");
932
+ setActiveSessionConnection(sessionId, {
933
+ connectionState: "disconnected",
934
+ reconnectState: "idle",
935
+ transportMode
936
+ });
937
+ return;
938
+ }
939
+ reconnectAttemptsRef.current += 1;
940
+ setReconnectAttempts(reconnectAttemptsRef.current);
941
+ if (reconnectAttemptsRef.current > maxReconnectAttempts) {
942
+ setConnectionState("error");
943
+ setActiveSessionConnection(sessionId, {
944
+ connectionState: "error",
945
+ reconnectState: "failed",
946
+ transportMode,
947
+ lastError: "Unable to reconnect to realtime session"
948
+ });
949
+ return;
950
+ }
951
+ setConnectionState("reconnecting");
952
+ timerRef.current = window.setTimeout(connect, reconnectIntervalMs);
953
+ };
954
+ };
955
+ connect();
956
+ return () => {
957
+ shouldReconnectRef.current = false;
958
+ clearReconnectTimer();
959
+ const socket = socketRef.current;
960
+ socketRef.current = null;
961
+ if (socket && socket.readyState < WebSocket.CLOSING) {
962
+ socket.close();
963
+ }
964
+ };
965
+ }, [
966
+ connectUrl,
967
+ enabled,
968
+ maxReconnectAttempts,
969
+ onClose,
970
+ onError,
971
+ onEvent,
972
+ onOpen,
973
+ reconnect,
974
+ reconnectIntervalMs,
975
+ registration,
976
+ sessionId,
977
+ transportMode
978
+ ]);
979
+ return {
980
+ connectionState,
981
+ lastError,
982
+ reconnectAttempts,
983
+ isConnected: connectionState === "connected"
984
+ };
985
+ }
986
+ function RealtimeSessionRegistryItem(props) {
987
+ useRealtimeSession({
988
+ ...props,
989
+ foreground: props.foreground ?? false
990
+ });
991
+ return null;
992
+ }
993
+ function RealtimeSessionRegistry({ sessions }) {
994
+ return sessions.map(
995
+ (session) => createElement(RealtimeSessionRegistryItem, {
996
+ key: session.key ?? session.sessionId,
997
+ ...session
998
+ })
999
+ );
1000
+ }
1001
+
747
1002
  // src/hooks/use-session-stream.ts
748
- import { useCallback as useCallback4, useEffect as useEffect2, useRef as useRef4, useState as useState4 } from "react";
1003
+ import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef5, useState as useState5 } from "react";
749
1004
  var _insertionCounter = 0;
750
1005
  function mapApiMessage(msg) {
751
1006
  const created = msg.info.timestamp ? new Date(msg.info.timestamp).getTime() : Date.now();
@@ -795,13 +1050,13 @@ function useSessionStream({
795
1050
  sessionId,
796
1051
  enabled = true
797
1052
  }) {
798
- const [messages, setMessages] = useState4([]);
799
- const [partMap, setPartMap] = useState4({});
800
- const [isStreaming, setIsStreaming] = useState4(false);
801
- const [error, setError] = useState4(null);
802
- const [connected, setConnected] = useState4(false);
803
- const abortRef = useRef4(null);
804
- const streamingMsgIdRef = useRef4(null);
1053
+ const [messages, setMessages] = useState5([]);
1054
+ const [partMap, setPartMap] = useState5({});
1055
+ const [isStreaming, setIsStreaming] = useState5(false);
1056
+ const [error, setError] = useState5(null);
1057
+ const [connected, setConnected] = useState5(false);
1058
+ const abortRef = useRef5(null);
1059
+ const streamingMsgIdRef = useRef5(null);
805
1060
  const refetch = useCallback4(async () => {
806
1061
  if (!token || !sessionId || !apiUrl) return;
807
1062
  try {
@@ -988,7 +1243,7 @@ function useSessionStream({
988
1243
  setError(msg);
989
1244
  }
990
1245
  }, [apiUrl, token, sessionId]);
991
- useEffect2(() => {
1246
+ useEffect3(() => {
992
1247
  if (!enabled || !token || !sessionId) return;
993
1248
  refetch();
994
1249
  connectSSE();
@@ -1001,12 +1256,12 @@ function useSessionStream({
1001
1256
  }
1002
1257
 
1003
1258
  // src/hooks/use-dropdown-menu.ts
1004
- import { useEffect as useEffect3, useRef as useRef5, useState as useState5 } from "react";
1259
+ import { useEffect as useEffect4, useRef as useRef6, useState as useState6 } from "react";
1005
1260
  function useDropdownMenu(options) {
1006
1261
  const closeOnEsc = options?.closeOnEsc ?? true;
1007
- const [open, setOpen] = useState5(false);
1008
- const ref = useRef5(null);
1009
- useEffect3(() => {
1262
+ const [open, setOpen] = useState6(false);
1263
+ const ref = useRef6(null);
1264
+ useEffect4(() => {
1010
1265
  function handleClick(e) {
1011
1266
  if (ref.current && !ref.current.contains(e.target)) {
1012
1267
  setOpen(false);
@@ -1017,7 +1272,7 @@ function useDropdownMenu(options) {
1017
1272
  }
1018
1273
  return () => document.removeEventListener("mousedown", handleClick);
1019
1274
  }, [open]);
1020
- useEffect3(() => {
1275
+ useEffect4(() => {
1021
1276
  if (!open || !closeOnEsc) return;
1022
1277
  function handleKey(e) {
1023
1278
  if (e.key === "Escape") setOpen(false);
@@ -1035,7 +1290,7 @@ function useDropdownMenu(options) {
1035
1290
  }
1036
1291
 
1037
1292
  // src/hooks/use-sidecar-auth.ts
1038
- import { useState as useState6, useCallback as useCallback5, useEffect as useEffect4, useRef as useRef6 } from "react";
1293
+ import { useState as useState7, useCallback as useCallback5, useEffect as useEffect5, useRef as useRef7 } from "react";
1039
1294
  function storageKey(resourceId, apiUrl) {
1040
1295
  return `sidecar_session_${resourceId}__${apiUrl}`;
1041
1296
  }
@@ -1067,11 +1322,11 @@ function clearSession(resourceId, apiUrl) {
1067
1322
  }
1068
1323
  function useSidecarAuth({ resourceId, apiUrl, signMessage }) {
1069
1324
  const cached = loadSession(resourceId, apiUrl);
1070
- const [token, setToken] = useState6(cached?.token ?? null);
1071
- const [expiresAt, setExpiresAt] = useState6(cached?.expiresAt ?? 0);
1072
- const [isAuthenticating, setIsAuthenticating] = useState6(false);
1073
- const [error, setError] = useState6(null);
1074
- const refreshTimerRef = useRef6(void 0);
1325
+ const [token, setToken] = useState7(cached?.token ?? null);
1326
+ const [expiresAt, setExpiresAt] = useState7(cached?.expiresAt ?? 0);
1327
+ const [isAuthenticating, setIsAuthenticating] = useState7(false);
1328
+ const [error, setError] = useState7(null);
1329
+ const refreshTimerRef = useRef7(void 0);
1075
1330
  const clearCachedToken = useCallback5(() => {
1076
1331
  setToken(null);
1077
1332
  setExpiresAt(0);
@@ -1112,7 +1367,7 @@ function useSidecarAuth({ resourceId, apiUrl, signMessage }) {
1112
1367
  setIsAuthenticating(false);
1113
1368
  }
1114
1369
  }, [resourceId, apiUrl, signMessage, clearCachedToken]);
1115
- useEffect4(() => {
1370
+ useEffect5(() => {
1116
1371
  if (refreshTimerRef.current) {
1117
1372
  clearTimeout(refreshTimerRef.current);
1118
1373
  }
@@ -1147,6 +1402,8 @@ export {
1147
1402
  useToolCallStream,
1148
1403
  useSSEStream,
1149
1404
  useSdkSession,
1405
+ useRealtimeSession,
1406
+ RealtimeSessionRegistry,
1150
1407
  useSessionStream,
1151
1408
  useDropdownMenu,
1152
1409
  useSidecarAuth
@@ -0,0 +1,321 @@
1
+ // src/stores/active-sessions-store.ts
2
+ import { useStore } from "@nanostores/react";
3
+ import { atom } from "nanostores";
4
+ import { useMemo } from "react";
5
+ var INITIAL_STATE = {
6
+ sessions: {},
7
+ lastUpdatedAt: 0
8
+ };
9
+ var STATUS_PRIORITY = {
10
+ running: 0,
11
+ error: 1,
12
+ "attention-needed": 2,
13
+ idle: 3
14
+ };
15
+ var DEFAULT_RECORD = {
16
+ status: "idle",
17
+ isRunning: false,
18
+ isForeground: false,
19
+ needsAttention: false,
20
+ connectionState: "disconnected",
21
+ reconnectState: "idle",
22
+ transportMode: null,
23
+ lastError: null,
24
+ lastEventAt: null
25
+ };
26
+ var activeSessionsAtom = atom(INITIAL_STATE);
27
+ var foregroundSessionId = null;
28
+ function createRecord(options, now) {
29
+ return {
30
+ sessionId: options.sessionId,
31
+ projectId: options.projectId ?? null,
32
+ projectLabel: options.projectLabel,
33
+ title: options.title,
34
+ href: options.href,
35
+ registeredAt: now,
36
+ lastActivityAt: now,
37
+ metadata: options.metadata,
38
+ ...DEFAULT_RECORD,
39
+ isForeground: options.sessionId === foregroundSessionId
40
+ };
41
+ }
42
+ function resolveStatus(session) {
43
+ if (session.isRunning) return "running";
44
+ if (session.lastError || session.reconnectState === "failed") return "error";
45
+ if (session.needsAttention) return "attention-needed";
46
+ return "idle";
47
+ }
48
+ function normalizeSession(session) {
49
+ return {
50
+ ...session,
51
+ status: resolveStatus(session)
52
+ };
53
+ }
54
+ function updateSession(sessionId, updater) {
55
+ const current = activeSessionsAtom.get();
56
+ const existing = current.sessions[sessionId];
57
+ if (!existing) return;
58
+ const now = Date.now();
59
+ activeSessionsAtom.set({
60
+ sessions: {
61
+ ...current.sessions,
62
+ [sessionId]: normalizeSession(updater(existing, now))
63
+ },
64
+ lastUpdatedAt: now
65
+ });
66
+ }
67
+ function registerActiveSession(options) {
68
+ const now = Date.now();
69
+ const current = activeSessionsAtom.get();
70
+ const existing = current.sessions[options.sessionId];
71
+ activeSessionsAtom.set({
72
+ sessions: {
73
+ ...current.sessions,
74
+ [options.sessionId]: normalizeSession(
75
+ existing ? {
76
+ ...existing,
77
+ projectId: existing.projectId ?? options.projectId ?? null,
78
+ projectLabel: options.projectLabel ?? existing.projectLabel,
79
+ title: options.title ?? existing.title,
80
+ href: options.href ?? existing.href,
81
+ metadata: options.metadata ?? existing.metadata,
82
+ lastActivityAt: now,
83
+ isForeground: options.sessionId === foregroundSessionId || existing.isForeground
84
+ } : createRecord(options, now)
85
+ )
86
+ },
87
+ lastUpdatedAt: now
88
+ });
89
+ }
90
+ function unregisterActiveSession(sessionId) {
91
+ const current = activeSessionsAtom.get();
92
+ if (!current.sessions[sessionId]) return;
93
+ const { [sessionId]: _removed, ...remaining } = current.sessions;
94
+ if (foregroundSessionId === sessionId) {
95
+ foregroundSessionId = null;
96
+ }
97
+ activeSessionsAtom.set({
98
+ sessions: remaining,
99
+ lastUpdatedAt: Date.now()
100
+ });
101
+ }
102
+ function setForegroundActiveSession(sessionId) {
103
+ foregroundSessionId = sessionId;
104
+ const current = activeSessionsAtom.get();
105
+ const now = Date.now();
106
+ let changed = false;
107
+ const sessions = Object.fromEntries(
108
+ Object.entries(current.sessions).map(([id, session]) => {
109
+ const isForeground = id === sessionId;
110
+ if (session.isForeground !== isForeground) {
111
+ changed = true;
112
+ }
113
+ return [
114
+ id,
115
+ {
116
+ ...session,
117
+ isForeground,
118
+ lastActivityAt: isForeground ? now : session.lastActivityAt
119
+ }
120
+ ];
121
+ })
122
+ );
123
+ if (!changed) return;
124
+ activeSessionsAtom.set({
125
+ sessions,
126
+ lastUpdatedAt: now
127
+ });
128
+ }
129
+ function updateActiveSessionMeta(sessionId, meta) {
130
+ updateSession(sessionId, (session, now) => ({
131
+ ...session,
132
+ ...meta,
133
+ lastActivityAt: now
134
+ }));
135
+ }
136
+ function setActiveSessionConnection(sessionId, options) {
137
+ updateSession(sessionId, (session, now) => ({
138
+ ...session,
139
+ connectionState: options.connectionState,
140
+ reconnectState: options.reconnectState ?? session.reconnectState,
141
+ transportMode: options.transportMode ?? session.transportMode,
142
+ lastError: options.lastError === void 0 ? session.lastError : options.lastError,
143
+ lastEventAt: options.lastEventAt ?? session.lastEventAt,
144
+ lastActivityAt: now
145
+ }));
146
+ }
147
+ function setActiveSessionRunning(sessionId, isRunning, options) {
148
+ updateSession(sessionId, (session, now) => ({
149
+ ...session,
150
+ isRunning,
151
+ needsAttention: isRunning ? false : session.needsAttention,
152
+ lastError: isRunning ? null : session.lastError,
153
+ lastEventAt: options?.lastEventAt ?? session.lastEventAt,
154
+ lastActivityAt: now
155
+ }));
156
+ }
157
+ function setActiveSessionAttention(sessionId, needsAttention, options) {
158
+ updateSession(sessionId, (session, now) => ({
159
+ ...session,
160
+ needsAttention,
161
+ isRunning: needsAttention ? false : session.isRunning,
162
+ lastEventAt: options?.lastEventAt ?? session.lastEventAt,
163
+ lastActivityAt: now
164
+ }));
165
+ }
166
+ function setActiveSessionError(sessionId, error) {
167
+ updateSession(sessionId, (session, now) => ({
168
+ ...session,
169
+ isRunning: false,
170
+ needsAttention: false,
171
+ lastError: error,
172
+ reconnectState: error ? "failed" : "idle",
173
+ connectionState: error ? "error" : session.connectionState,
174
+ lastEventAt: now,
175
+ lastActivityAt: now
176
+ }));
177
+ }
178
+ function bumpActiveSessionActivity(sessionId, options) {
179
+ updateSession(sessionId, (session, now) => ({
180
+ ...session,
181
+ lastEventAt: options?.lastEventAt ?? session.lastEventAt ?? now,
182
+ lastActivityAt: now
183
+ }));
184
+ }
185
+ function resetActiveSessions() {
186
+ foregroundSessionId = null;
187
+ activeSessionsAtom.set(INITIAL_STATE);
188
+ }
189
+ function getAllActiveSessions(state) {
190
+ return Object.values(state.sessions);
191
+ }
192
+ function getActiveSession(state, sessionId) {
193
+ return state.sessions[sessionId] ?? null;
194
+ }
195
+ function getSessionsForProject(state, projectId) {
196
+ return Object.values(state.sessions).filter((session) => session.projectId === projectId);
197
+ }
198
+ function getSessionsForNavbar(state, projectId) {
199
+ const sessions = projectId == null ? Object.values(state.sessions) : getSessionsForProject(state, projectId);
200
+ return [...sessions].sort((left, right) => {
201
+ if (left.isForeground !== right.isForeground) {
202
+ return Number(right.isForeground) - Number(left.isForeground);
203
+ }
204
+ const statusDiff = STATUS_PRIORITY[left.status] - STATUS_PRIORITY[right.status];
205
+ if (statusDiff !== 0) {
206
+ return statusDiff;
207
+ }
208
+ return right.lastActivityAt - left.lastActivityAt;
209
+ });
210
+ }
211
+ function getSessionsByActivity(state) {
212
+ return [...Object.values(state.sessions)].sort(
213
+ (left, right) => right.lastActivityAt - left.lastActivityAt
214
+ );
215
+ }
216
+ function getTotalRunningSessionCount(state) {
217
+ return Object.values(state.sessions).filter((session) => session.isRunning).length;
218
+ }
219
+ function hasBackgroundRunningSessions(state) {
220
+ return Object.values(state.sessions).some(
221
+ (session) => session.isRunning && !session.isForeground
222
+ );
223
+ }
224
+ function getAllProjectActivity(state) {
225
+ const grouped = /* @__PURE__ */ new Map();
226
+ for (const session of Object.values(state.sessions)) {
227
+ if (session.projectId == null) continue;
228
+ const existing = grouped.get(session.projectId);
229
+ if (existing) {
230
+ existing.activeSessionCount += 1;
231
+ existing.lastActivityAt = Math.max(existing.lastActivityAt, session.lastActivityAt);
232
+ if (session.isRunning) {
233
+ existing.runningSessionIds.push(session.sessionId);
234
+ }
235
+ if (!existing.projectLabel && session.projectLabel) {
236
+ existing.projectLabel = session.projectLabel;
237
+ }
238
+ continue;
239
+ }
240
+ grouped.set(session.projectId, {
241
+ projectId: session.projectId,
242
+ projectLabel: session.projectLabel,
243
+ activeSessionCount: 1,
244
+ runningSessionIds: session.isRunning ? [session.sessionId] : [],
245
+ lastActivityAt: session.lastActivityAt
246
+ });
247
+ }
248
+ return [...grouped.values()].sort((left, right) => right.lastActivityAt - left.lastActivityAt);
249
+ }
250
+ function useActiveSessionsState() {
251
+ return useStore(activeSessionsAtom);
252
+ }
253
+ function useActiveSessions() {
254
+ const state = useStore(activeSessionsAtom);
255
+ return useMemo(() => getAllActiveSessions(state), [state]);
256
+ }
257
+ function useActiveSession(sessionId) {
258
+ const state = useStore(activeSessionsAtom);
259
+ return useMemo(
260
+ () => sessionId ? getActiveSession(state, sessionId) : null,
261
+ [sessionId, state]
262
+ );
263
+ }
264
+ function useProjectSessions(projectId) {
265
+ const state = useStore(activeSessionsAtom);
266
+ return useMemo(
267
+ () => projectId == null ? [] : getSessionsForProject(state, projectId),
268
+ [projectId, state]
269
+ );
270
+ }
271
+ function useNavbarSessions(projectId) {
272
+ const state = useStore(activeSessionsAtom);
273
+ return useMemo(() => getSessionsForNavbar(state, projectId), [projectId, state]);
274
+ }
275
+ function useSessionsByActivity() {
276
+ const state = useStore(activeSessionsAtom);
277
+ return useMemo(() => getSessionsByActivity(state), [state]);
278
+ }
279
+ function useProjectActivity() {
280
+ const state = useStore(activeSessionsAtom);
281
+ return useMemo(() => getAllProjectActivity(state), [state]);
282
+ }
283
+ function useTotalRunningSessions() {
284
+ const state = useStore(activeSessionsAtom);
285
+ return useMemo(() => getTotalRunningSessionCount(state), [state]);
286
+ }
287
+ function useHasBackgroundRunningSessions() {
288
+ const state = useStore(activeSessionsAtom);
289
+ return useMemo(() => hasBackgroundRunningSessions(state), [state]);
290
+ }
291
+
292
+ export {
293
+ activeSessionsAtom,
294
+ registerActiveSession,
295
+ unregisterActiveSession,
296
+ setForegroundActiveSession,
297
+ updateActiveSessionMeta,
298
+ setActiveSessionConnection,
299
+ setActiveSessionRunning,
300
+ setActiveSessionAttention,
301
+ setActiveSessionError,
302
+ bumpActiveSessionActivity,
303
+ resetActiveSessions,
304
+ getAllActiveSessions,
305
+ getActiveSession,
306
+ getSessionsForProject,
307
+ getSessionsForNavbar,
308
+ getSessionsByActivity,
309
+ getTotalRunningSessionCount,
310
+ hasBackgroundRunningSessions,
311
+ getAllProjectActivity,
312
+ useActiveSessionsState,
313
+ useActiveSessions,
314
+ useActiveSession,
315
+ useProjectSessions,
316
+ useNavbarSessions,
317
+ useSessionsByActivity,
318
+ useProjectActivity,
319
+ useTotalRunningSessions,
320
+ useHasBackgroundRunningSessions
321
+ };