@ottocode/web-sdk 0.1.269 → 0.1.271

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 (85) hide show
  1. package/dist/components/chat/ChatInput.d.ts.map +1 -1
  2. package/dist/components/chat/InputQueueBar.d.ts +6 -0
  3. package/dist/components/chat/InputQueueBar.d.ts.map +1 -0
  4. package/dist/components/chat/InputTodosBar.d.ts +6 -0
  5. package/dist/components/chat/InputTodosBar.d.ts.map +1 -0
  6. package/dist/components/chat/ShortcutsModal.d.ts.map +1 -1
  7. package/dist/components/file-browser/FileBrowserSidebar.d.ts.map +1 -1
  8. package/dist/components/file-browser/FileBrowserSidebarToggle.d.ts.map +1 -1
  9. package/dist/components/file-browser/FileViewerPanel.d.ts +3 -0
  10. package/dist/components/file-browser/FileViewerPanel.d.ts.map +1 -1
  11. package/dist/components/file-browser/QuickFilePicker.d.ts.map +1 -1
  12. package/dist/components/git/GitDiffPanel.d.ts +4 -0
  13. package/dist/components/git/GitDiffPanel.d.ts.map +1 -1
  14. package/dist/components/git/GitDiffViewer.d.ts.map +1 -1
  15. package/dist/components/git/GitFileItem.d.ts.map +1 -1
  16. package/dist/components/git/GitSidebarToggle.d.ts.map +1 -1
  17. package/dist/components/index.d.ts +3 -0
  18. package/dist/components/index.d.ts.map +1 -1
  19. package/dist/components/index.js +5092 -3747
  20. package/dist/components/index.js.map +61 -55
  21. package/dist/components/mcp/MCPSidebarToggle.d.ts.map +1 -1
  22. package/dist/components/messages/ActionToolBox.d.ts +2 -1
  23. package/dist/components/messages/ActionToolBox.d.ts.map +1 -1
  24. package/dist/components/messages/AssistantMessageGroup.d.ts.map +1 -1
  25. package/dist/components/messages/CompactActivityGroup.d.ts +2 -1
  26. package/dist/components/messages/CompactActivityGroup.d.ts.map +1 -1
  27. package/dist/components/messages/MessagePartItem.d.ts.map +1 -1
  28. package/dist/components/messages/MessageThread.d.ts.map +1 -1
  29. package/dist/components/messages/UserMessageGroup.d.ts.map +1 -1
  30. package/dist/components/messages/renderers/ProgressUpdateRenderer.d.ts.map +1 -1
  31. package/dist/components/messages/renderers/index.d.ts.map +1 -1
  32. package/dist/components/onboarding/OnboardingModal.d.ts.map +1 -1
  33. package/dist/components/onboarding/steps/ProviderSetupStep.d.ts.map +1 -1
  34. package/dist/components/session-files/SessionFilesDiffPanel.d.ts +7 -0
  35. package/dist/components/session-files/SessionFilesDiffPanel.d.ts.map +1 -1
  36. package/dist/components/session-files/SessionFilesSidebar.d.ts.map +1 -1
  37. package/dist/components/session-files/SessionFilesSidebarToggle.d.ts.map +1 -1
  38. package/dist/components/sessions/SessionItem.d.ts.map +1 -1
  39. package/dist/components/sessions/SessionListContainer.d.ts.map +1 -1
  40. package/dist/components/sessions/session-time.d.ts.map +1 -1
  41. package/dist/components/settings/SettingsSidebarToggle.d.ts.map +1 -1
  42. package/dist/components/sidebar/SidebarShortcutBadge.d.ts +6 -0
  43. package/dist/components/sidebar/SidebarShortcutBadge.d.ts.map +1 -0
  44. package/dist/components/skills/SkillViewerPanel.d.ts +4 -0
  45. package/dist/components/skills/SkillViewerPanel.d.ts.map +1 -1
  46. package/dist/components/skills/SkillsSidebarToggle.d.ts.map +1 -1
  47. package/dist/components/tunnel/TunnelSidebarToggle.d.ts.map +1 -1
  48. package/dist/components/ui/Modal.d.ts.map +1 -1
  49. package/dist/components/workspace/ViewerTabs.d.ts +2 -0
  50. package/dist/components/workspace/ViewerTabs.d.ts.map +1 -0
  51. package/dist/hooks/index.js +365 -74
  52. package/dist/hooks/index.js.map +17 -15
  53. package/dist/hooks/useClientEvents.d.ts.map +1 -1
  54. package/dist/hooks/useKeyboardShortcuts.d.ts.map +1 -1
  55. package/dist/hooks/useMessages.d.ts.map +1 -1
  56. package/dist/hooks/useQueueState.d.ts.map +1 -1
  57. package/dist/hooks/useSessions.d.ts +1 -0
  58. package/dist/hooks/useSessions.d.ts.map +1 -1
  59. package/dist/hooks/useShortcutHintsVisible.d.ts +5 -0
  60. package/dist/hooks/useShortcutHintsVisible.d.ts.map +1 -0
  61. package/dist/index.js +5287 -3843
  62. package/dist/index.js.map +63 -57
  63. package/dist/lib/api-client/index.d.ts +1 -0
  64. package/dist/lib/api-client/index.d.ts.map +1 -1
  65. package/dist/lib/api-client/sessions.d.ts +1 -0
  66. package/dist/lib/api-client/sessions.d.ts.map +1 -1
  67. package/dist/lib/api-client/utils.d.ts.map +1 -1
  68. package/dist/lib/index.js +11 -2
  69. package/dist/lib/index.js.map +5 -5
  70. package/dist/stores/fileBrowserStore.d.ts +1 -0
  71. package/dist/stores/fileBrowserStore.d.ts.map +1 -1
  72. package/dist/stores/gitStore.d.ts.map +1 -1
  73. package/dist/stores/index.d.ts +2 -0
  74. package/dist/stores/index.d.ts.map +1 -1
  75. package/dist/stores/index.js +201 -60
  76. package/dist/stores/index.js.map +9 -7
  77. package/dist/stores/sessionFilesStore.d.ts.map +1 -1
  78. package/dist/stores/skillsStore.d.ts.map +1 -1
  79. package/dist/stores/todoStore.d.ts +18 -0
  80. package/dist/stores/todoStore.d.ts.map +1 -0
  81. package/dist/stores/viewerTabsStore.d.ts +41 -0
  82. package/dist/stores/viewerTabsStore.d.ts.map +1 -0
  83. package/dist/types/api.d.ts +7 -0
  84. package/dist/types/api.d.ts.map +1 -1
  85. package/package.json +3 -3
@@ -9,6 +9,7 @@ import {
9
9
  createMessage as apiCreateMessage,
10
10
  abortSession as apiAbortSession,
11
11
  deleteSession as apiDeleteSession,
12
+ markSessionViewed as apiMarkSessionViewed,
12
13
  updateSession as apiUpdateSession,
13
14
  getSessionQueue as apiGetSessionQueue,
14
15
  removeFromQueue as apiRemoveFromQueue,
@@ -95,7 +96,8 @@ function convertSession(apiSession) {
95
96
  ...apiSession,
96
97
  title: apiSession.title ?? null,
97
98
  createdAt: typeof apiSession.createdAt === "string" ? new Date(apiSession.createdAt).getTime() : apiSession.createdAt,
98
- lastActiveAt: typeof apiSession.lastActiveAt === "string" ? new Date(apiSession.lastActiveAt).getTime() : apiSession.lastActiveAt
99
+ lastActiveAt: typeof apiSession.lastActiveAt === "string" ? new Date(apiSession.lastActiveAt).getTime() : apiSession.lastActiveAt,
100
+ lastViewedAt: typeof apiSession.lastViewedAt === "string" ? new Date(apiSession.lastViewedAt).getTime() : apiSession.lastViewedAt
99
101
  };
100
102
  }
101
103
  function convertMessage(apiMessage) {
@@ -145,6 +147,12 @@ var sessionsMixin = {
145
147
  throw new Error(extractErrorMessage(response.error));
146
148
  return convertSession(response.data);
147
149
  },
150
+ async markSessionViewed(sessionId) {
151
+ const response = await apiMarkSessionViewed({ path: { sessionId } });
152
+ if (response.error)
153
+ throw new Error(extractErrorMessage(response.error));
154
+ return convertSession(response.data);
155
+ },
148
156
  async deleteSession(sessionId) {
149
157
  const response = await apiDeleteSession({ path: { sessionId } });
150
158
  if (response.error)
@@ -872,6 +880,7 @@ class ApiClient {
872
880
  getSessionsPage = sessionsMixin.getSessionsPage;
873
881
  createSession = sessionsMixin.createSession;
874
882
  updateSession = sessionsMixin.updateSession;
883
+ markSessionViewed = sessionsMixin.markSessionViewed;
875
884
  deleteSession = sessionsMixin.deleteSession;
876
885
  abortSession = sessionsMixin.abortSession;
877
886
  abortMessage = sessionsMixin.abortMessage;
@@ -1184,29 +1193,117 @@ function useFiles(options = {}) {
1184
1193
  import { useQuery as useQuery3, useMutation as useMutation2, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
1185
1194
 
1186
1195
  // src/stores/gitStore.ts
1187
- import { create as create8 } from "zustand";
1196
+ import { create as create9 } from "zustand";
1188
1197
 
1189
1198
  // src/stores/sessionFilesStore.ts
1190
- import { create as create7 } from "zustand";
1199
+ import { create as create8 } from "zustand";
1191
1200
 
1192
1201
  // src/stores/researchStore.ts
1193
- import { create as create6 } from "zustand";
1202
+ import { create as create7 } from "zustand";
1194
1203
 
1195
1204
  // src/stores/settingsStore.ts
1196
- import { create as create5 } from "zustand";
1205
+ import { create as create6 } from "zustand";
1197
1206
 
1198
1207
  // src/stores/tunnelStore.ts
1199
- import { create as create4 } from "zustand";
1208
+ import { create as create5 } from "zustand";
1200
1209
 
1201
1210
  // src/stores/fileBrowserStore.ts
1202
- import { create as create3 } from "zustand";
1211
+ import { create as create4 } from "zustand";
1203
1212
 
1204
1213
  // src/stores/mcpStore.ts
1205
- import { create as create2 } from "zustand";
1214
+ import { create as create3 } from "zustand";
1206
1215
 
1207
1216
  // src/stores/skillsStore.ts
1217
+ import { create as create2 } from "zustand";
1218
+
1219
+ // src/stores/viewerTabsStore.ts
1208
1220
  import { create } from "zustand";
1209
- var useSkillsStore = create((set) => ({
1221
+ function titleFromPath(path) {
1222
+ return path.split("/").pop() || path;
1223
+ }
1224
+ function upsertTab(tabs, tab) {
1225
+ const existingIndex = tabs.findIndex((item) => item.id === tab.id);
1226
+ if (existingIndex === -1) {
1227
+ return [...tabs, tab];
1228
+ }
1229
+ const next = [...tabs];
1230
+ next[existingIndex] = tab;
1231
+ return next;
1232
+ }
1233
+ var useViewerTabsStore = create((set) => ({
1234
+ tabs: [],
1235
+ activeTabId: null,
1236
+ openGitDiffTab: (path, staged) => {
1237
+ const id = `git-diff:${staged ? "staged" : "unstaged"}:${path}`;
1238
+ set((state) => ({
1239
+ tabs: upsertTab(state.tabs, {
1240
+ id,
1241
+ type: "git-diff",
1242
+ title: titleFromPath(path),
1243
+ path,
1244
+ staged
1245
+ }),
1246
+ activeTabId: id
1247
+ }));
1248
+ },
1249
+ openSessionFileDiffTab: (path, operations) => {
1250
+ const id = `session-file-diff:${path}`;
1251
+ set((state) => ({
1252
+ tabs: upsertTab(state.tabs, {
1253
+ id,
1254
+ type: "session-file-diff",
1255
+ title: titleFromPath(path),
1256
+ path,
1257
+ operations,
1258
+ selectedOperationIndex: Math.max(0, operations.length - 1)
1259
+ }),
1260
+ activeTabId: id
1261
+ }));
1262
+ },
1263
+ openFileTab: (path) => {
1264
+ const id = `file:${path}`;
1265
+ set((state) => ({
1266
+ tabs: upsertTab(state.tabs, {
1267
+ id,
1268
+ type: "file",
1269
+ title: titleFromPath(path),
1270
+ path
1271
+ }),
1272
+ activeTabId: id
1273
+ }));
1274
+ },
1275
+ openSkillFileTab: (skill, file) => {
1276
+ const displayFile = file ?? "SKILL.md";
1277
+ const id = `skill-file:${skill}:${displayFile}`;
1278
+ set((state) => ({
1279
+ tabs: upsertTab(state.tabs, {
1280
+ id,
1281
+ type: "skill-file",
1282
+ title: titleFromPath(displayFile),
1283
+ skill,
1284
+ file
1285
+ }),
1286
+ activeTabId: id
1287
+ }));
1288
+ },
1289
+ setActiveTab: (id) => set({ activeTabId: id }),
1290
+ closeTab: (id) => set((state) => {
1291
+ const closingIndex = state.tabs.findIndex((tab) => tab.id === id);
1292
+ const tabs = state.tabs.filter((tab) => tab.id !== id);
1293
+ let activeTabId = state.activeTabId;
1294
+ if (state.activeTabId === id) {
1295
+ activeTabId = tabs[closingIndex]?.id ?? tabs[closingIndex - 1]?.id ?? null;
1296
+ }
1297
+ return { tabs, activeTabId };
1298
+ }),
1299
+ updateSessionFileOperationIndex: (id, index) => set((state) => ({
1300
+ tabs: state.tabs.map((tab) => tab.id === id && tab.type === "session-file-diff" ? { ...tab, selectedOperationIndex: index } : tab)
1301
+ })),
1302
+ closeAllTabs: () => set({ tabs: [], activeTabId: null })
1303
+ }));
1304
+
1305
+ // src/stores/skillsStore.ts
1306
+ var useSkillsStore = create2((set, get) => ({
1210
1307
  isExpanded: false,
1211
1308
  skills: [],
1212
1309
  globalEnabled: true,
@@ -1244,12 +1341,18 @@ var useSkillsStore = create((set) => ({
1244
1341
  setSkills: (skills) => set({ skills }),
1245
1342
  setSkillsConfig: ({ skills, globalEnabled, totalCount, enabledCount }) => set({ skills, globalEnabled, totalCount, enabledCount }),
1246
1343
  selectSkill: (name) => set({ selectedSkill: name, isViewerOpen: false, viewingFile: null }),
1247
- openViewer: (file) => set({ isViewerOpen: true, viewingFile: file }),
1344
+ openViewer: (file) => {
1345
+ const selectedSkill = get().selectedSkill;
1346
+ if (selectedSkill) {
1347
+ useViewerTabsStore.getState().openSkillFileTab(selectedSkill, file);
1348
+ }
1349
+ set({ isViewerOpen: true, viewingFile: file });
1350
+ },
1248
1351
  closeViewer: () => set({ isViewerOpen: false, viewingFile: null })
1249
1352
  }));
1250
1353
 
1251
1354
  // src/stores/mcpStore.ts
1252
- var useMCPStore = create2((set) => ({
1355
+ var useMCPStore = create3((set) => ({
1253
1356
  isExpanded: false,
1254
1357
  servers: [],
1255
1358
  loading: new Set,
@@ -1305,7 +1408,22 @@ var useMCPStore = create2((set) => ({
1305
1408
  }));
1306
1409
 
1307
1410
  // src/stores/fileBrowserStore.ts
1308
- var useFileBrowserStore = create3((set) => ({
1411
+ function getAncestorDirs(path) {
1412
+ const normalizedPath = path.replace(/\\/g, "/").replace(/^\.\//, "");
1413
+ const parts = normalizedPath.split("/").filter(Boolean);
1414
+ return parts.slice(0, -1).map((_, index) => parts.slice(0, index + 1).join("/"));
1415
+ }
1416
+ function revealFileState(state, path) {
1417
+ const expandedDirs = new Set(state.expandedDirs);
1418
+ for (const dir of getAncestorDirs(path)) {
1419
+ expandedDirs.add(dir);
1420
+ }
1421
+ return {
1422
+ selectedFile: path,
1423
+ expandedDirs
1424
+ };
1425
+ }
1426
+ var useFileBrowserStore = create4((set) => ({
1309
1427
  isExpanded: false,
1310
1428
  selectedFile: null,
1311
1429
  isViewerOpen: false,
@@ -1340,10 +1458,14 @@ var useFileBrowserStore = create3((set) => ({
1340
1458
  isViewerOpen: false,
1341
1459
  selectedFile: null
1342
1460
  }),
1343
- openFile: (path) => set({
1344
- selectedFile: path,
1345
- isViewerOpen: true
1346
- }),
1461
+ openFile: (path) => {
1462
+ useViewerTabsStore.getState().openFileTab(path);
1463
+ set((state) => ({
1464
+ ...revealFileState(state, path),
1465
+ isViewerOpen: true
1466
+ }));
1467
+ },
1468
+ revealFile: (path) => set((state) => revealFileState(state, path)),
1347
1469
  closeViewer: () => set({
1348
1470
  isViewerOpen: false,
1349
1471
  selectedFile: null
@@ -1360,7 +1482,7 @@ var useFileBrowserStore = create3((set) => ({
1360
1482
  }));
1361
1483
 
1362
1484
  // src/stores/tunnelStore.ts
1363
- var useTunnelStore = create4((set) => ({
1485
+ var useTunnelStore = create5((set) => ({
1364
1486
  isExpanded: false,
1365
1487
  status: "idle",
1366
1488
  url: null,
@@ -1408,7 +1530,7 @@ var useTunnelStore = create4((set) => ({
1408
1530
  }));
1409
1531
 
1410
1532
  // src/stores/settingsStore.ts
1411
- var useSettingsStore = create5((set) => ({
1533
+ var useSettingsStore = create6((set) => ({
1412
1534
  isExpanded: false,
1413
1535
  toggleSidebar: () => {
1414
1536
  set((state) => {
@@ -1430,7 +1552,7 @@ var useSettingsStore = create5((set) => ({
1430
1552
  }));
1431
1553
 
1432
1554
  // src/stores/researchStore.ts
1433
- var useResearchStore = create6((set, get) => ({
1555
+ var useResearchStore = create7((set, get) => ({
1434
1556
  isExpanded: false,
1435
1557
  activeResearchSessionId: null,
1436
1558
  parentSessionId: null,
@@ -1477,7 +1599,7 @@ var useResearchStore = create6((set, get) => ({
1477
1599
  }));
1478
1600
 
1479
1601
  // src/stores/sessionFilesStore.ts
1480
- var useSessionFilesStore = create7((set) => ({
1602
+ var useSessionFilesStore = create8((set) => ({
1481
1603
  isExpanded: false,
1482
1604
  selectedFile: null,
1483
1605
  allOperations: [],
@@ -1515,13 +1637,16 @@ var useSessionFilesStore = create7((set) => ({
1515
1637
  allOperations: [],
1516
1638
  selectedOperationIndex: 0
1517
1639
  }),
1518
- openDiff: (file, operations) => set({
1519
- selectedFile: file,
1520
- allOperations: operations,
1521
- selectedOperationIndex: operations.length - 1,
1522
- isDiffOpen: true,
1523
- isExpanded: true
1524
- }),
1640
+ openDiff: (file, operations) => {
1641
+ useViewerTabsStore.getState().openSessionFileDiffTab(file, operations);
1642
+ set({
1643
+ selectedFile: file,
1644
+ allOperations: operations,
1645
+ selectedOperationIndex: operations.length - 1,
1646
+ isDiffOpen: true,
1647
+ isExpanded: true
1648
+ });
1649
+ },
1525
1650
  selectOperation: (index) => set({ selectedOperationIndex: index }),
1526
1651
  closeDiff: () => set({
1527
1652
  isDiffOpen: false,
@@ -1532,7 +1657,7 @@ var useSessionFilesStore = create7((set) => ({
1532
1657
  }));
1533
1658
 
1534
1659
  // src/stores/gitStore.ts
1535
- var useGitStore = create8((set) => ({
1660
+ var useGitStore = create9((set) => ({
1536
1661
  isExpanded: false,
1537
1662
  activeSessionId: null,
1538
1663
  selectedFile: null,
@@ -1558,12 +1683,15 @@ var useGitStore = create8((set) => ({
1558
1683
  },
1559
1684
  expandSidebar: () => set({ isExpanded: true }),
1560
1685
  collapseSidebar: () => set({ isExpanded: false, isDiffOpen: false, selectedFile: null }),
1561
- openDiff: (file, staged) => set({
1562
- selectedFile: file,
1563
- selectedFileStaged: staged,
1564
- isDiffOpen: true,
1565
- isExpanded: true
1566
- }),
1686
+ openDiff: (file, staged) => {
1687
+ useViewerTabsStore.getState().openGitDiffTab(file, staged);
1688
+ set({
1689
+ selectedFile: file,
1690
+ selectedFileStaged: staged,
1691
+ isDiffOpen: true,
1692
+ isExpanded: true
1693
+ });
1694
+ },
1567
1695
  closeDiff: () => set({
1568
1696
  isDiffOpen: false,
1569
1697
  selectedFile: null
@@ -1803,6 +1931,25 @@ function useUpdateSession(sessionId) {
1803
1931
  }
1804
1932
  });
1805
1933
  }
1934
+ function useMarkSessionViewed() {
1935
+ const queryClient = useQueryClient3();
1936
+ return useMutation3({
1937
+ mutationFn: (sessionId) => apiClient.markSessionViewed(sessionId),
1938
+ onSuccess: (updatedSession) => {
1939
+ queryClient.setQueryData(sessionsQueryKey, (old) => {
1940
+ if (!old)
1941
+ return old;
1942
+ return {
1943
+ ...old,
1944
+ pages: old.pages.map((page) => ({
1945
+ ...page,
1946
+ items: page.items.map((session) => session.id === updatedSession.id ? { ...session, ...updatedSession } : session)
1947
+ }))
1948
+ };
1949
+ });
1950
+ }
1951
+ });
1952
+ }
1806
1953
  function useDeleteSession() {
1807
1954
  const queryClient = useQueryClient3();
1808
1955
  return useMutation3({
@@ -1832,7 +1979,12 @@ function useMessages(sessionId, options = {}) {
1832
1979
  function useSendMessage(sessionId) {
1833
1980
  const queryClient = useQueryClient4();
1834
1981
  return useMutation4({
1835
- mutationFn: (data) => apiClient.sendMessage(sessionId, data),
1982
+ mutationFn: async (data) => {
1983
+ await apiClient.markSessionViewed(sessionId).catch(() => {
1984
+ return;
1985
+ });
1986
+ return apiClient.sendMessage(sessionId, data);
1987
+ },
1836
1988
  onSuccess: () => {
1837
1989
  queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
1838
1990
  queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
@@ -1956,8 +2108,8 @@ class SSEClient {
1956
2108
  }
1957
2109
 
1958
2110
  // src/stores/toolApprovalStore.ts
1959
- import { create as create9 } from "zustand";
1960
- var useToolApprovalStore = create9((set) => ({
2111
+ import { create as create10 } from "zustand";
2112
+ var useToolApprovalStore = create10((set) => ({
1961
2113
  pendingApprovals: [],
1962
2114
  addPendingApproval: (approval) => set((state) => ({
1963
2115
  pendingApprovals: [...state.pendingApprovals, approval]
@@ -2717,9 +2869,9 @@ import {
2717
2869
  } from "@ottocode/api";
2718
2870
 
2719
2871
  // src/stores/toastStore.ts
2720
- import { create as create10 } from "zustand";
2872
+ import { create as create11 } from "zustand";
2721
2873
  var toastId = 0;
2722
- var useToastStore = create10((set) => ({
2874
+ var useToastStore = create11((set) => ({
2723
2875
  toasts: [],
2724
2876
  addToast: (toast) => {
2725
2877
  const id = `toast-${++toastId}`;
@@ -2899,6 +3051,56 @@ async function requestLocalhostAccess(baseUrl) {
2899
3051
  throw new Error(`Local API returned ${response.status}`);
2900
3052
  }
2901
3053
  }
3054
+ var localAccessToastIds = new Map;
3055
+ var localAccessChecksInFlight = new Set;
3056
+ function localAccessStorageKey(baseUrl) {
3057
+ return `otto-local-access-confirmed:${baseUrl}`;
3058
+ }
3059
+ function hasConfirmedLocalAccess(baseUrl) {
3060
+ try {
3061
+ return window.localStorage.getItem(localAccessStorageKey(baseUrl)) === "1";
3062
+ } catch {
3063
+ return false;
3064
+ }
3065
+ }
3066
+ function markLocalAccessConfirmed(baseUrl) {
3067
+ try {
3068
+ window.localStorage.setItem(localAccessStorageKey(baseUrl), "1");
3069
+ } catch {}
3070
+ }
3071
+ async function maybeShowLocalAccessToast(baseUrl) {
3072
+ if (hasPlatformOpenUrl())
3073
+ return;
3074
+ if (!isLocalApiUrl(baseUrl) || hasConfirmedLocalAccess(baseUrl))
3075
+ return;
3076
+ if (localAccessToastIds.has(baseUrl) || localAccessChecksInFlight.has(baseUrl)) {
3077
+ return;
3078
+ }
3079
+ localAccessChecksInFlight.add(baseUrl);
3080
+ try {
3081
+ await requestLocalhostAccess(baseUrl);
3082
+ markLocalAccessConfirmed(baseUrl);
3083
+ return;
3084
+ } catch {} finally {
3085
+ localAccessChecksInFlight.delete(baseUrl);
3086
+ }
3087
+ if (localAccessToastIds.has(baseUrl))
3088
+ return;
3089
+ const id = toast("Safari may need permission to access the local otto server.", "default", 0);
3090
+ localAccessToastIds.set(baseUrl, id);
3091
+ useToastStore.getState().updateToast(id, {
3092
+ action: {
3093
+ label: "Allow access",
3094
+ onClick: async () => {
3095
+ await requestLocalhostAccess(baseUrl);
3096
+ markLocalAccessConfirmed(baseUrl);
3097
+ useToastStore.getState().removeToast(id);
3098
+ localAccessToastIds.delete(baseUrl);
3099
+ toast.success("Local otto server access confirmed.");
3100
+ }
3101
+ }
3102
+ });
3103
+ }
2902
3104
  function useClientEvents(activeSessionId) {
2903
3105
  const queryClient = useQueryClient6();
2904
3106
  const activeSessionIdRef = useRef2(activeSessionId);
@@ -2934,7 +3136,6 @@ function useClientEvents(activeSessionId) {
2934
3136
  }, []);
2935
3137
  useEffect2(() => {
2936
3138
  const controller = new AbortController;
2937
- let hasShownLocalAccessToast = false;
2938
3139
  const baseUrl = getBaseUrl();
2939
3140
  createClientEventsStream({
2940
3141
  baseUrl,
@@ -2974,19 +3175,7 @@ function useClientEvents(activeSessionId) {
2974
3175
  onError: (error) => {
2975
3176
  if (!controller.signal.aborted) {
2976
3177
  console.error("[client-events] Stream error:", error);
2977
- if (!hasShownLocalAccessToast && isLocalApiUrl(baseUrl)) {
2978
- hasShownLocalAccessToast = true;
2979
- const id = toast("Safari may need permission to access the local otto server.", "default", 0);
2980
- useToastStore.getState().updateToast(id, {
2981
- action: {
2982
- label: "Allow access",
2983
- onClick: async () => {
2984
- await requestLocalhostAccess(baseUrl);
2985
- toast.success("Local otto server access confirmed.");
2986
- }
2987
- }
2988
- });
2989
- }
3178
+ maybeShowLocalAccessToast(baseUrl);
2990
3179
  }
2991
3180
  }
2992
3181
  }, controller.signal);
@@ -3078,8 +3267,8 @@ function useWorkingDirectory() {
3078
3267
  import { useEffect as useEffect5, useCallback as useCallback3 } from "react";
3079
3268
 
3080
3269
  // src/stores/focusStore.ts
3081
- import { create as create11 } from "zustand";
3082
- var useFocusStore = create11((set) => ({
3270
+ import { create as create12 } from "zustand";
3271
+ var useFocusStore = create12((set) => ({
3083
3272
  currentFocus: null,
3084
3273
  sessionIndex: 0,
3085
3274
  gitFileIndex: 0,
@@ -3091,9 +3280,9 @@ var useFocusStore = create11((set) => ({
3091
3280
  }));
3092
3281
 
3093
3282
  // src/stores/sidebarStore.ts
3094
- import { create as create12 } from "zustand";
3283
+ import { create as create13 } from "zustand";
3095
3284
  import { persist } from "zustand/middleware";
3096
- var useSidebarStore = create12()(persist((set) => ({
3285
+ var useSidebarStore = create13()(persist((set) => ({
3097
3286
  isCollapsed: false,
3098
3287
  toggleCollapse: () => set((state) => ({ isCollapsed: !state.isCollapsed })),
3099
3288
  setCollapsed: (collapsed) => set({ isCollapsed: collapsed })
@@ -3102,14 +3291,47 @@ var useSidebarStore = create12()(persist((set) => ({
3102
3291
  }));
3103
3292
 
3104
3293
  // src/stores/filePickerStore.ts
3105
- import { create as create13 } from "zustand";
3106
- var useFilePickerStore = create13((set) => ({
3294
+ import { create as create14 } from "zustand";
3295
+ var useFilePickerStore = create14((set) => ({
3107
3296
  isOpen: false,
3108
3297
  open: () => set({ isOpen: true }),
3109
3298
  close: () => set({ isOpen: false }),
3110
3299
  toggle: () => set((state) => ({ isOpen: !state.isOpen }))
3111
3300
  }));
3112
3301
 
3302
+ // src/stores/terminalStore.ts
3303
+ import { create as create15 } from "zustand";
3304
+ var DEFAULT_HEIGHT = 300;
3305
+ var MIN_HEIGHT = 150;
3306
+ var useTerminalStore = create15((set) => ({
3307
+ isOpen: false,
3308
+ panelHeight: DEFAULT_HEIGHT,
3309
+ activeTabId: null,
3310
+ isMaximized: false,
3311
+ get isExpanded() {
3312
+ return this.isOpen;
3313
+ },
3314
+ get selectedTerminalId() {
3315
+ return this.activeTabId;
3316
+ },
3317
+ openPanel: () => set({ isOpen: true }),
3318
+ closePanel: () => set({ isOpen: false, isMaximized: false }),
3319
+ togglePanel: () => set((s) => ({
3320
+ isOpen: !s.isOpen,
3321
+ isMaximized: !s.isOpen ? s.isMaximized : false
3322
+ })),
3323
+ setPanelHeight: (height) => set({ panelHeight: Math.max(MIN_HEIGHT, height) }),
3324
+ selectTab: (id) => set({ activeTabId: id, isOpen: true }),
3325
+ toggleMaximize: () => set((s) => ({ isMaximized: !s.isMaximized })),
3326
+ expandSidebar: () => set({ isOpen: true }),
3327
+ collapseSidebar: () => set({ isOpen: false, activeTabId: null, isMaximized: false }),
3328
+ toggleSidebar: () => set((s) => ({
3329
+ isOpen: !s.isOpen,
3330
+ isMaximized: !s.isOpen ? s.isMaximized : false
3331
+ })),
3332
+ selectTerminal: (id) => set({ activeTabId: id, isOpen: true })
3333
+ }));
3334
+
3113
3335
  // src/hooks/useKeyboardShortcuts.ts
3114
3336
  function useKeyboardShortcuts({
3115
3337
  sessionIds,
@@ -3142,12 +3364,59 @@ function useKeyboardShortcuts({
3142
3364
  } = useSidebarStore();
3143
3365
  const { isExpanded: isGitExpanded, toggleSidebar: toggleGit } = useGitStore();
3144
3366
  const closeDiff = useGitStore((state) => state.closeDiff);
3367
+ const toggleSessionFiles = useSessionFilesStore((state) => state.toggleSidebar);
3368
+ const toggleFileBrowser = useFileBrowserStore((state) => state.toggleSidebar);
3369
+ const toggleTunnel = useTunnelStore((state) => state.toggleSidebar);
3370
+ const toggleMCP = useMCPStore((state) => state.toggleSidebar);
3371
+ const toggleSkills = useSkillsStore((state) => state.toggleSidebar);
3372
+ const toggleSettings = useSettingsStore((state) => state.toggleSidebar);
3145
3373
  const toggleResearch = useResearchStore((state) => state.toggleSidebar);
3374
+ const toggleTerminalPanel = useTerminalStore((state) => state.togglePanel);
3146
3375
  const currentSessionIndex = sessionIds.indexOf(activeSessionId || "");
3147
3376
  const handleKeyDown = useCallback3((e) => {
3148
3377
  const target = e.target;
3149
3378
  const isInInput = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
3150
3379
  const isInTerminal = !!target.closest("[data-terminal-viewer]");
3380
+ const isShortcutModifierPressed = e.ctrlKey || e.metaKey;
3381
+ if (isShortcutModifierPressed && !e.shiftKey && !e.altKey && e.key >= "1" && e.key <= "7") {
3382
+ e.preventDefault();
3383
+ switch (e.key) {
3384
+ case "1":
3385
+ toggleGit();
3386
+ if (isGitExpanded && currentFocus === "git") {
3387
+ setFocus("input");
3388
+ } else {
3389
+ setFocus("git");
3390
+ resetGitFileIndex();
3391
+ }
3392
+ break;
3393
+ case "2":
3394
+ toggleSessionFiles();
3395
+ setFocus("input");
3396
+ break;
3397
+ case "3":
3398
+ toggleFileBrowser();
3399
+ setFocus("input");
3400
+ break;
3401
+ case "4":
3402
+ toggleTunnel();
3403
+ setFocus("input");
3404
+ break;
3405
+ case "5":
3406
+ toggleMCP();
3407
+ setFocus("input");
3408
+ break;
3409
+ case "6":
3410
+ toggleSkills();
3411
+ setFocus("input");
3412
+ break;
3413
+ case "7":
3414
+ toggleSettings();
3415
+ setFocus("input");
3416
+ break;
3417
+ }
3418
+ return;
3419
+ }
3151
3420
  if ((e.ctrlKey || e.metaKey) && e.key === "h") {
3152
3421
  e.preventDefault();
3153
3422
  if (currentFocus === "sessions") {
@@ -3204,6 +3473,11 @@ function useKeyboardShortcuts({
3204
3473
  toggleGit();
3205
3474
  return;
3206
3475
  }
3476
+ if ((e.ctrlKey || e.metaKey) && e.key === "j") {
3477
+ e.preventDefault();
3478
+ toggleTerminalPanel();
3479
+ return;
3480
+ }
3207
3481
  if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === "r") {
3208
3482
  e.preventDefault();
3209
3483
  toggleResearch();
@@ -3327,6 +3601,13 @@ function useKeyboardShortcuts({
3327
3601
  resetGitFileIndex,
3328
3602
  setSessionListCollapsed,
3329
3603
  toggleGit,
3604
+ toggleSessionFiles,
3605
+ toggleFileBrowser,
3606
+ toggleTunnel,
3607
+ toggleMCP,
3608
+ toggleSkills,
3609
+ toggleSettings,
3610
+ toggleTerminalPanel,
3330
3611
  toggleResearch,
3331
3612
  toggleSessionList,
3332
3613
  onSelectSession,
@@ -3343,8 +3624,8 @@ function useKeyboardShortcuts({
3343
3624
  closeDiff
3344
3625
  ]);
3345
3626
  useEffect5(() => {
3346
- window.addEventListener("keydown", handleKeyDown);
3347
- return () => window.removeEventListener("keydown", handleKeyDown);
3627
+ window.addEventListener("keydown", handleKeyDown, true);
3628
+ return () => window.removeEventListener("keydown", handleKeyDown, true);
3348
3629
  }, [handleKeyDown]);
3349
3630
  return {
3350
3631
  currentFocus,
@@ -3897,9 +4178,18 @@ var defaultQueueState = {
3897
4178
  function useQueueState(sessionId) {
3898
4179
  const { data } = useQuery6({
3899
4180
  queryKey: ["queueState", sessionId],
3900
- queryFn: () => defaultQueueState,
4181
+ queryFn: async () => {
4182
+ if (!sessionId)
4183
+ return defaultQueueState;
4184
+ const queueState = await apiClient.getQueueState(sessionId);
4185
+ return {
4186
+ currentMessageId: queueState.currentMessageId,
4187
+ queuedMessages: queueState.queuedMessages,
4188
+ queueLength: queueState.queuedMessages.length
4189
+ };
4190
+ },
3901
4191
  enabled: !!sessionId,
3902
- initialData: defaultQueueState,
4192
+ placeholderData: defaultQueueState,
3903
4193
  staleTime: Infinity
3904
4194
  });
3905
4195
  return data ?? defaultQueueState;
@@ -3971,8 +4261,8 @@ import {
3971
4261
  } from "@ottocode/api";
3972
4262
 
3973
4263
  // src/stores/pendingResearchStore.ts
3974
- import { create as create14 } from "zustand";
3975
- var usePendingResearchStore = create14((set, get) => ({
4264
+ import { create as create16 } from "zustand";
4265
+ var usePendingResearchStore = create16((set, get) => ({
3976
4266
  pendingContexts: new Map,
3977
4267
  addContext: (parentSessionId, context) => {
3978
4268
  set((state) => {
@@ -4121,8 +4411,8 @@ function useExportToSession() {
4121
4411
  import { useEffect as useEffect8, useRef as useRef3 } from "react";
4122
4412
 
4123
4413
  // src/stores/ottorouterStore.ts
4124
- import { create as create15 } from "zustand";
4125
- var useOttoRouterStore = create15((set) => ({
4414
+ import { create as create17 } from "zustand";
4415
+ var useOttoRouterStore = create17((set) => ({
4126
4416
  balance: null,
4127
4417
  usdcBalance: null,
4128
4418
  network: "mainnet",
@@ -4151,8 +4441,8 @@ var useOttoRouterStore = create15((set) => ({
4151
4441
  }));
4152
4442
 
4153
4443
  // src/stores/topupApprovalStore.ts
4154
- import { create as create16 } from "zustand";
4155
- var useTopupApprovalStore = create16((set) => ({
4444
+ import { create as create18 } from "zustand";
4445
+ var useTopupApprovalStore = create18((set) => ({
4156
4446
  pendingTopup: null,
4157
4447
  isProcessing: false,
4158
4448
  selectedMethod: null,
@@ -4305,8 +4595,8 @@ function useSetuPayments(sessionId) {
4305
4595
  import { useEffect as useEffect9, useCallback as useCallback6 } from "react";
4306
4596
 
4307
4597
  // src/stores/usageStore.ts
4308
- import { create as create17 } from "zustand";
4309
- var useUsageStore = create17((set) => ({
4598
+ import { create as create19 } from "zustand";
4599
+ var useUsageStore = create19((set) => ({
4310
4600
  usage: {},
4311
4601
  isLoading: {},
4312
4602
  lastFetched: {},
@@ -4555,9 +4845,9 @@ import { useEffect as useEffect12, useCallback as useCallback8, useState as useS
4555
4845
  import { useQueryClient as useQueryClient9 } from "@tanstack/react-query";
4556
4846
 
4557
4847
  // src/stores/onboardingStore.ts
4558
- import { create as create18 } from "zustand";
4848
+ import { create as create20 } from "zustand";
4559
4849
  var STEPS = ["wallet", "defaults"];
4560
- var useOnboardingStore = create18((set, get) => ({
4850
+ var useOnboardingStore = create20((set, get) => ({
4561
4851
  isOpen: false,
4562
4852
  currentStep: "wallet",
4563
4853
  manageMode: false,
@@ -5463,6 +5753,7 @@ export {
5463
5753
  useModels,
5464
5754
  useMessages,
5465
5755
  useMessageQueuePosition,
5756
+ useMarkSessionViewed,
5466
5757
  useMCPServers,
5467
5758
  useMCPAuthStatus,
5468
5759
  useKeyboardShortcuts,
@@ -5500,4 +5791,4 @@ export {
5500
5791
  sessionsQueryKey
5501
5792
  };
5502
5793
 
5503
- //# debugId=5F8DA6B0D961E4EF64756E2164756E21
5794
+ //# debugId=26C8EA5B27C5271C64756E2164756E21