@ottocode/web-sdk 0.1.275 → 0.1.277

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 (40) hide show
  1. package/dist/components/chat/ChatInputContainer.d.ts.map +1 -1
  2. package/dist/components/chat/InputQueueBar.d.ts.map +1 -1
  3. package/dist/components/file-browser/FileViewerPanel.d.ts +2 -1
  4. package/dist/components/file-browser/FileViewerPanel.d.ts.map +1 -1
  5. package/dist/components/git/GitDiffViewer.d.ts.map +1 -1
  6. package/dist/components/index.js +1078 -433
  7. package/dist/components/index.js.map +23 -23
  8. package/dist/components/messages/MessagePartItem.d.ts.map +1 -1
  9. package/dist/components/messages/MessageThread.d.ts.map +1 -1
  10. package/dist/components/messages/MessageThreadContainer.d.ts.map +1 -1
  11. package/dist/components/messages/renderers/DiffView.d.ts.map +1 -1
  12. package/dist/components/session-files/SessionFilesDiffPanel.d.ts.map +1 -1
  13. package/dist/components/ui/CodeMirrorViewer.d.ts +2 -1
  14. package/dist/components/ui/CodeMirrorViewer.d.ts.map +1 -1
  15. package/dist/components/ui/Toaster.d.ts.map +1 -1
  16. package/dist/components/workspace/ToolPreviewPanel.d.ts.map +1 -1
  17. package/dist/components/workspace/ViewerTabs.d.ts.map +1 -1
  18. package/dist/hooks/index.js +309 -60
  19. package/dist/hooks/index.js.map +8 -8
  20. package/dist/hooks/useProviderUsage.d.ts +1 -1
  21. package/dist/hooks/useProviderUsage.d.ts.map +1 -1
  22. package/dist/hooks/useQueueState.d.ts +9 -0
  23. package/dist/hooks/useQueueState.d.ts.map +1 -1
  24. package/dist/hooks/useSessionStream.d.ts.map +1 -1
  25. package/dist/index.js +1096 -450
  26. package/dist/index.js.map +23 -23
  27. package/dist/lib/api-client/index.d.ts +13 -0
  28. package/dist/lib/api-client/index.d.ts.map +1 -1
  29. package/dist/lib/api-client/sessions.d.ts +13 -0
  30. package/dist/lib/api-client/sessions.d.ts.map +1 -1
  31. package/dist/lib/commands.d.ts.map +1 -1
  32. package/dist/lib/index.js +25 -1
  33. package/dist/lib/index.js.map +4 -4
  34. package/dist/stores/index.js +77 -16
  35. package/dist/stores/index.js.map +3 -3
  36. package/dist/stores/viewerTabsStore.d.ts +9 -0
  37. package/dist/stores/viewerTabsStore.d.ts.map +1 -1
  38. package/dist/types/api.d.ts +1 -1
  39. package/dist/types/api.d.ts.map +1 -1
  40. package/package.json +5 -3
package/dist/index.js CHANGED
@@ -480,6 +480,8 @@ function ToastItem({ toast: toast2 }) {
480
480
  };
481
481
  const style = typeStyles[toast2.type];
482
482
  const transitionClass = phase === "enter" ? "opacity-0 translate-y-2 scale-95" : phase === "exit" ? "opacity-0 translate-y-1 scale-95" : "opacity-100 translate-y-0 scale-100";
483
+ const rowAlignmentClass = toast2.type === "loading" ? "items-center" : "items-start";
484
+ const iconOffsetClass = toast2.type === "loading" ? "" : "mt-px";
483
485
  return /* @__PURE__ */ jsxs3("div", {
484
486
  className: `
485
487
  relative overflow-hidden
@@ -494,10 +496,10 @@ function ToastItem({ toast: toast2 }) {
494
496
  role: "alert",
495
497
  children: [
496
498
  /* @__PURE__ */ jsxs3("div", {
497
- className: "flex items-start gap-2.5 px-3 py-2.5",
499
+ className: `flex ${rowAlignmentClass} gap-2.5 px-3 py-2.5`,
498
500
  children: [
499
501
  /* @__PURE__ */ jsx7("span", {
500
- className: "mt-px shrink-0",
502
+ className: `${iconOffsetClass} shrink-0`,
501
503
  children: style.icon
502
504
  }),
503
505
  /* @__PURE__ */ jsx7("div", {
@@ -1191,6 +1193,21 @@ var sessionsMixin = {
1191
1193
  throw new Error(extractErrorMessage(response.error));
1192
1194
  return response.data;
1193
1195
  },
1196
+ async createHandoff(sessionId) {
1197
+ const response = await fetch(`${getBaseUrl()}/v1/sessions/${encodeURIComponent(sessionId)}/handoff`, { method: "POST" });
1198
+ const data = await response.json().catch(() => null);
1199
+ if (!response.ok)
1200
+ throw new Error(extractErrorMessage(data));
1201
+ if (!data?.session || !data?.sessionId) {
1202
+ throw new Error("No data returned from handoff");
1203
+ }
1204
+ return {
1205
+ session: convertSession(data.session),
1206
+ sessionId: String(data.sessionId),
1207
+ sourceSessionId: String(data.sourceSessionId),
1208
+ message: String(data.message ?? "")
1209
+ };
1210
+ },
1194
1211
  async abortSession(sessionId) {
1195
1212
  const response = await apiAbortSession({ path: { sessionId } });
1196
1213
  if (response.error)
@@ -1219,6 +1236,13 @@ var sessionsMixin = {
1219
1236
  throw new Error("Failed to remove from queue");
1220
1237
  return response.data;
1221
1238
  },
1239
+ async sendQueuedMessageNow(sessionId, messageId) {
1240
+ const response = await fetch(`${getBaseUrl()}/v1/sessions/${encodeURIComponent(sessionId)}/queue/${encodeURIComponent(messageId)}/send-now`, { method: "POST" });
1241
+ const data = await response.json().catch(() => null);
1242
+ if (!response.ok)
1243
+ throw new Error(extractErrorMessage(data));
1244
+ return data;
1245
+ },
1222
1246
  async getMessages(sessionId) {
1223
1247
  const response = await apiListMessages({ path: { id: sessionId } });
1224
1248
  if (response.error)
@@ -1925,10 +1949,12 @@ class ApiClient {
1925
1949
  updateSession = sessionsMixin.updateSession;
1926
1950
  markSessionViewed = sessionsMixin.markSessionViewed;
1927
1951
  deleteSession = sessionsMixin.deleteSession;
1952
+ createHandoff = sessionsMixin.createHandoff;
1928
1953
  abortSession = sessionsMixin.abortSession;
1929
1954
  abortMessage = sessionsMixin.abortMessage;
1930
1955
  getQueueState = sessionsMixin.getQueueState;
1931
1956
  removeFromQueue = sessionsMixin.removeFromQueue;
1957
+ sendQueuedMessageNow = sessionsMixin.sendQueuedMessageNow;
1932
1958
  getMessages = sessionsMixin.getMessages;
1933
1959
  sendMessage = sessionsMixin.sendMessage;
1934
1960
  getStreamUrl = sessionsMixin.getStreamUrl;
@@ -2199,7 +2225,8 @@ import {
2199
2225
  Trash2,
2200
2226
  Share2,
2201
2227
  RefreshCw,
2202
- FileText as FileText2
2228
+ FileText as FileText2,
2229
+ ArrowRightLeft
2203
2230
  } from "lucide-react";
2204
2231
  var COMMANDS = [
2205
2232
  {
@@ -2268,6 +2295,12 @@ var COMMANDS = [
2268
2295
  description: "Generate AGENTS.md and .agents docs from the real repo structure",
2269
2296
  icon: FileText2
2270
2297
  },
2298
+ {
2299
+ id: "handoff",
2300
+ label: "/handoff",
2301
+ description: "Create a new session with current context",
2302
+ icon: ArrowRightLeft
2303
+ },
2271
2304
  {
2272
2305
  id: "branch",
2273
2306
  label: "/branch",
@@ -3828,6 +3861,7 @@ import {
3828
3861
  Clock,
3829
3862
  ListOrdered,
3830
3863
  RotateCcw,
3864
+ Send,
3831
3865
  Trash2 as Trash22
3832
3866
  } from "lucide-react";
3833
3867
  import { useQueryClient as useQueryClient4 } from "@tanstack/react-query";
@@ -3840,14 +3874,26 @@ import { useQuery as useQuery4 } from "@tanstack/react-query";
3840
3874
  var defaultQueueState = {
3841
3875
  currentMessageId: null,
3842
3876
  queuedMessages: [],
3843
- queueLength: 0
3877
+ queueLength: 0,
3878
+ isRunning: false
3844
3879
  };
3880
+ function normalizeQueueState(state) {
3881
+ const isRunning = state.isRunning ?? Boolean(state.currentMessageId);
3882
+ const currentMessageId = isRunning ? state.currentMessageId : null;
3883
+ const hasActiveTurn = Boolean(currentMessageId);
3884
+ const queuedMessages = hasActiveTurn ? state.queuedMessages : [];
3885
+ return {
3886
+ currentMessageId,
3887
+ queuedMessages,
3888
+ queueLength: queuedMessages.length,
3889
+ isRunning: hasActiveTurn
3890
+ };
3891
+ }
3845
3892
  function optimisticallyQueueMessage(queryClient, sessionId, messageId) {
3846
3893
  queryClient.setQueryData(["queueState", sessionId], (current) => {
3847
3894
  if (!current)
3848
3895
  return current;
3849
- const isBusy = Boolean(current.currentMessageId) || current.queuedMessages.length > 0 || current.queueLength > 0;
3850
- if (!isBusy)
3896
+ if (!current.isRunning || !current.currentMessageId)
3851
3897
  return current;
3852
3898
  if (current.currentMessageId === messageId)
3853
3899
  return current;
@@ -3872,11 +3918,7 @@ function useQueueState(sessionId) {
3872
3918
  if (!sessionId)
3873
3919
  return defaultQueueState;
3874
3920
  const queueState = await apiClient.getQueueState(sessionId);
3875
- return {
3876
- currentMessageId: queueState.currentMessageId,
3877
- queuedMessages: queueState.queuedMessages,
3878
- queueLength: queueState.queuedMessages.length
3879
- };
3921
+ return normalizeQueueState(queueState);
3880
3922
  },
3881
3923
  enabled: !!sessionId,
3882
3924
  placeholderData: defaultQueueState,
@@ -4082,6 +4124,7 @@ function getQueuedPreviews(messages, queuedMessages) {
4082
4124
  }
4083
4125
  function QueueRow({
4084
4126
  item,
4127
+ onSendNow,
4085
4128
  onCancel,
4086
4129
  onDelete
4087
4130
  }) {
@@ -4099,6 +4142,15 @@ function QueueRow({
4099
4142
  /* @__PURE__ */ jsxs11("div", {
4100
4143
  className: "flex items-center gap-1 flex-shrink-0",
4101
4144
  children: [
4145
+ /* @__PURE__ */ jsx17("button", {
4146
+ type: "button",
4147
+ onClick: () => onSendNow(item),
4148
+ className: "flex h-7 w-7 items-center justify-center rounded bg-transparent text-muted-foreground transition-colors hover:bg-primary/10 hover:text-primary",
4149
+ title: "Send now",
4150
+ children: /* @__PURE__ */ jsx17(Send, {
4151
+ className: "h-3.5 w-3.5"
4152
+ })
4153
+ }),
4102
4154
  /* @__PURE__ */ jsx17("button", {
4103
4155
  type: "button",
4104
4156
  onClick: () => onCancel(item),
@@ -4148,6 +4200,15 @@ var InputQueueBar = memo4(function InputQueueBar2({
4148
4200
  console.error("Failed to remove queued message:", error);
4149
4201
  }
4150
4202
  };
4203
+ const sendQueuedItemNow = async (item) => {
4204
+ try {
4205
+ await apiClient.sendQueuedMessageNow(sessionId, item.assistantMessageId);
4206
+ queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
4207
+ queryClient.invalidateQueries({ queryKey: ["queueState", sessionId] });
4208
+ } catch (error) {
4209
+ console.error("Failed to send queued message now:", error);
4210
+ }
4211
+ };
4151
4212
  return /* @__PURE__ */ jsx17("div", {
4152
4213
  className: "grid transition-[grid-template-rows,opacity] duration-200 ease-out",
4153
4214
  style: {
@@ -4246,6 +4307,7 @@ var InputQueueBar = memo4(function InputQueueBar2({
4246
4307
  className: "divide-y divide-border",
4247
4308
  children: queuedItems.map((item) => /* @__PURE__ */ jsx17(QueueRow, {
4248
4309
  item,
4310
+ onSendNow: sendQueuedItemNow,
4249
4311
  onCancel: (queuedItem) => removeQueuedItem(queuedItem, true),
4250
4312
  onDelete: (queuedItem) => removeQueuedItem(queuedItem, false)
4251
4313
  }, item.assistantMessageId))
@@ -5219,6 +5281,53 @@ function mergeChangedLines(existing, incoming) {
5219
5281
  return existing;
5220
5282
  return [...new Set([...existing, ...incoming])].sort((a, b) => a - b);
5221
5283
  }
5284
+ function countContentLines(content) {
5285
+ return content.length === 0 ? 1 : content.split(`
5286
+ `).length;
5287
+ }
5288
+ function annotationId(preview, targetPath) {
5289
+ return `${preview.toolName}:${preview.callId ?? `${normalizeViewerPath(targetPath)}:${preview.patch ?? preview.content ?? ""}`}`;
5290
+ }
5291
+ function buildAnnotation(preview, targetPath, existing) {
5292
+ if (preview.status === "error")
5293
+ return existing;
5294
+ const id = annotationId(preview, targetPath);
5295
+ if (preview.toolName === "write") {
5296
+ const content = preview.content;
5297
+ if (content === undefined)
5298
+ return existing;
5299
+ return {
5300
+ id,
5301
+ reason: "write",
5302
+ callId: preview.callId,
5303
+ status: preview.status,
5304
+ lineTones: Array.from({ length: countContentLines(content) }, (_, index) => [index + 1, "add"]),
5305
+ createdAt: existing?.createdAt ?? Date.now()
5306
+ };
5307
+ }
5308
+ const lineTones = preview.previewLineTones?.length ? preview.previewLineTones : preview.changedLines?.length ? preview.changedLines.map((line) => [line, "add"]) : existing?.lineTones;
5309
+ if (!lineTones?.length)
5310
+ return existing;
5311
+ return {
5312
+ id,
5313
+ reason: "apply_patch",
5314
+ callId: preview.callId,
5315
+ status: preview.status,
5316
+ lineTones,
5317
+ createdAt: existing?.createdAt ?? Date.now()
5318
+ };
5319
+ }
5320
+ function upsertAnnotation(annotations, annotation) {
5321
+ if (!annotation)
5322
+ return annotations;
5323
+ const existing = annotations ?? [];
5324
+ const index = existing.findIndex((item) => item.id === annotation.id);
5325
+ if (index === -1)
5326
+ return [...existing, annotation];
5327
+ const next = [...existing];
5328
+ next[index] = annotation;
5329
+ return next;
5330
+ }
5222
5331
  var useViewerTabsStore = create8((set) => ({
5223
5332
  tabs: [],
5224
5333
  activeTabId: null,
@@ -5265,7 +5374,11 @@ var useViewerTabsStore = create8((set) => ({
5265
5374
  id: targetId,
5266
5375
  type: "file",
5267
5376
  title: existingFile?.title ?? titleFromPath(targetPath),
5268
- path: targetPath
5377
+ path: targetPath,
5378
+ highlight: existingFile?.highlight,
5379
+ annotations: existingFile?.annotations,
5380
+ patchPreview: existingFile?.patchPreview,
5381
+ writePreview: existingFile?.writePreview
5269
5382
  }),
5270
5383
  activeTabId: targetId
5271
5384
  };
@@ -5286,6 +5399,7 @@ var useViewerTabsStore = create8((set) => ({
5286
5399
  title: existingFile?.title ?? titleFromPath(targetPath),
5287
5400
  path: targetPath,
5288
5401
  highlight,
5402
+ annotations: existingFile?.annotations,
5289
5403
  patchPreview: undefined,
5290
5404
  writePreview: undefined
5291
5405
  }),
@@ -5307,6 +5421,12 @@ var useViewerTabsStore = create8((set) => ({
5307
5421
  const samePatchCall = isSamePatchCall(existingPatchPreview, preview);
5308
5422
  const baseContent = preview.baseContent ?? (samePatchCall ? existingPatchPreview?.baseContent : existingPatchPreview?.resultContent ?? existingPatchPreview?.baseContent);
5309
5423
  const changedLines = samePatchCall ? preview.changedLines ?? existingPatchPreview?.changedLines : mergeChangedLines(existingPatchPreview?.changedLines, preview.changedLines);
5424
+ const annotationPreview = {
5425
+ ...preview,
5426
+ changedLines: preview.changedLines
5427
+ };
5428
+ const existingAnnotation = existingFile?.annotations?.find((annotation2) => annotation2.id === annotationId(annotationPreview, targetPath));
5429
+ const annotations = upsertAnnotation(existingFile?.annotations, buildAnnotation(annotationPreview, targetPath, existingAnnotation));
5310
5430
  return {
5311
5431
  tabs: upsertTab(tabs, {
5312
5432
  id: targetId,
@@ -5314,6 +5434,7 @@ var useViewerTabsStore = create8((set) => ({
5314
5434
  title: existingFile?.title ?? titleFromPath(targetPath),
5315
5435
  path: targetPath,
5316
5436
  highlight: undefined,
5437
+ annotations,
5317
5438
  writePreview: undefined,
5318
5439
  patchPreview: {
5319
5440
  path: targetPath,
@@ -5336,10 +5457,13 @@ var useViewerTabsStore = create8((set) => ({
5336
5457
  }
5337
5458
  if (existingFile) {
5338
5459
  const existingWritePreview = existingFile.writePreview;
5460
+ const existingAnnotation = existingFile.annotations?.find((annotation2) => annotation2.id === annotationId(preview, targetPath));
5461
+ const annotations = upsertAnnotation(existingFile.annotations, buildAnnotation(preview, targetPath, existingAnnotation));
5339
5462
  return {
5340
5463
  tabs: upsertTab(tabs, {
5341
5464
  ...existingFile,
5342
5465
  highlight: undefined,
5466
+ annotations,
5343
5467
  patchPreview: undefined,
5344
5468
  writePreview: {
5345
5469
  path: targetPath,
@@ -5354,25 +5478,24 @@ var useViewerTabsStore = create8((set) => ({
5354
5478
  };
5355
5479
  }
5356
5480
  const existingWrite = existing?.toolName === "write" ? existing : undefined;
5481
+ const annotation = buildAnnotation(preview, preview.path);
5357
5482
  return {
5358
5483
  tabs: upsertTab(tabs, {
5359
5484
  id,
5360
- type: "tool-preview",
5485
+ type: "file",
5361
5486
  title: titleFromPath(preview.path),
5362
5487
  path: preview.path,
5363
- toolName: preview.toolName,
5364
- callId: preview.callId,
5365
- content: preview.content ?? existingWrite?.content,
5366
- baseContent: undefined,
5367
- patch: undefined,
5368
- changedLines: undefined,
5369
- previewContent: undefined,
5370
- resultContent: undefined,
5371
- previewLineTones: undefined,
5372
- previewFirstLine: undefined,
5373
- previewLatestLine: undefined,
5374
- status: preview.status,
5375
- error: preview.error ?? existingWrite?.error
5488
+ highlight: undefined,
5489
+ annotations: annotation ? [annotation] : undefined,
5490
+ patchPreview: undefined,
5491
+ writePreview: {
5492
+ path: preview.path,
5493
+ toolName: "write",
5494
+ callId: preview.callId,
5495
+ content: preview.content ?? existingWrite?.content,
5496
+ status: preview.status,
5497
+ error: preview.error ?? existingWrite?.error
5498
+ }
5376
5499
  }),
5377
5500
  activeTabId: id
5378
5501
  };
@@ -7280,6 +7403,24 @@ ${content}` : content;
7280
7403
  handleSendMessage("/compact");
7281
7404
  } else if (commandId === "init") {
7282
7405
  handleSendMessage("/init");
7406
+ } else if (commandId === "handoff") {
7407
+ const toastId2 = toast.loading("Creating handoff...");
7408
+ try {
7409
+ const result = await apiClient.createHandoff(sessionId);
7410
+ queryClient.invalidateQueries({ queryKey: ["sessions"] });
7411
+ queryClient.invalidateQueries({
7412
+ queryKey: ["messages", sessionId]
7413
+ });
7414
+ queryClient.invalidateQueries({
7415
+ queryKey: ["messages", result.sessionId]
7416
+ });
7417
+ openPlatformSession(result.sessionId);
7418
+ toast.success("Handoff created");
7419
+ } catch (error) {
7420
+ toast.error(error instanceof Error ? error.message : "Failed to create handoff");
7421
+ } finally {
7422
+ useToastStore.getState().removeToast(toastId2);
7423
+ }
7283
7424
  } else if (commandId === "delete") {
7284
7425
  deleteSession.mutate(sessionId, {
7285
7426
  onSuccess: () => {
@@ -7913,7 +8054,9 @@ function getLanguageFromPath(path) {
7913
8054
  html: "html",
7914
8055
  css: "css",
7915
8056
  scss: "scss",
7916
- md: "markdown",
8057
+ md: "text",
8058
+ markdown: "text",
8059
+ mdx: "text",
7917
8060
  txt: "text",
7918
8061
  svelte: "svelte"
7919
8062
  };
@@ -7928,6 +8071,11 @@ function parseDiff(patch) {
7928
8071
  let inHunk = false;
7929
8072
  let filePath = "";
7930
8073
  for (const line of lines) {
8074
+ const lineModePath = line.match(/^\*\*\* (?:Delete Lines in|Replace Lines in|Insert Before in|Insert After in): (.+)$/);
8075
+ if (lineModePath?.[1]) {
8076
+ filePath = lineModePath[1];
8077
+ inHunk = false;
8078
+ }
7931
8079
  if (line.startsWith("*** Update File:") || line.startsWith("*** Add File:")) {
7932
8080
  const match = line.match(/\*\*\* (?:Update|Add) File: (.+)/);
7933
8081
  if (match)
@@ -12913,101 +13061,83 @@ var MessagePartItem = memo10(function MessagePartItem2({
12913
13061
  } else if (data) {
12914
13062
  content = JSON.stringify(data, null, 2);
12915
13063
  }
12916
- return /* @__PURE__ */ jsxs50("div", {
13064
+ return /* @__PURE__ */ jsx59("div", {
12917
13065
  className: "relative group",
12918
- children: [
12919
- /* @__PURE__ */ jsx59("div", {
12920
- className: "absolute -top-1 right-0 opacity-0 group-hover:opacity-100 transition-opacity z-10",
12921
- children: /* @__PURE__ */ jsx59(CopyButton, {
12922
- text: content,
12923
- className: "bg-background/80 backdrop-blur-sm border border-border/50 shadow-sm",
12924
- size: "md"
12925
- })
12926
- }),
12927
- /* @__PURE__ */ jsx59("div", {
12928
- className: `${isCompactThread ? "text-[16.5px]" : "text-[17px]"} text-foreground leading-relaxed markdown-content max-w-full overflow-x-auto`,
12929
- children: /* @__PURE__ */ jsx59(ReactMarkdown, {
12930
- remarkPlugins: [remarkGfm],
12931
- components: {
12932
- a: ({
12933
- href,
12934
- children,
12935
- ...props
12936
- }) => /* @__PURE__ */ jsx59("a", {
12937
- href,
12938
- target: "_blank",
12939
- rel: "noopener noreferrer",
12940
- className: "text-primary underline decoration-primary/35 underline-offset-2 transition-colors hover:text-primary/90 hover:decoration-primary",
12941
- onClick: (e) => {
12942
- if (window.self !== window.top && href) {
12943
- e.preventDefault();
12944
- window.parent.postMessage({
12945
- type: "otto-open-url",
12946
- url: href
12947
- }, "*");
13066
+ children: /* @__PURE__ */ jsx59("div", {
13067
+ className: `${isCompactThread ? "text-[16.5px]" : "text-[17px]"} text-foreground leading-relaxed markdown-content max-w-full overflow-x-auto`,
13068
+ children: /* @__PURE__ */ jsx59(ReactMarkdown, {
13069
+ remarkPlugins: [remarkGfm],
13070
+ components: {
13071
+ a: ({
13072
+ href,
13073
+ children,
13074
+ ...props
13075
+ }) => /* @__PURE__ */ jsx59("a", {
13076
+ href,
13077
+ target: "_blank",
13078
+ rel: "noopener noreferrer",
13079
+ className: "text-primary underline decoration-primary/35 underline-offset-2 transition-colors hover:text-primary/90 hover:decoration-primary",
13080
+ onClick: (e) => {
13081
+ if (window.self !== window.top && href) {
13082
+ e.preventDefault();
13083
+ window.parent.postMessage({
13084
+ type: "otto-open-url",
13085
+ url: href
13086
+ }, "*");
13087
+ }
13088
+ },
13089
+ ...props,
13090
+ children
13091
+ }),
13092
+ pre: ({
13093
+ children,
13094
+ ...props
13095
+ }) => {
13096
+ const codeContent = (() => {
13097
+ if (!children)
13098
+ return "";
13099
+ const child = Array.isArray(children) ? children[0] : children;
13100
+ if (child && typeof child === "object" && "props" in child) {
13101
+ const codeProps = child.props;
13102
+ if (typeof codeProps.children === "string") {
13103
+ return codeProps.children;
12948
13104
  }
12949
- },
13105
+ }
13106
+ return "";
13107
+ })();
13108
+ return /* @__PURE__ */ jsxs50("div", {
13109
+ className: "relative group/code my-3",
13110
+ children: [
13111
+ /* @__PURE__ */ jsx59("div", {
13112
+ className: "absolute top-2 right-2 opacity-0 group-hover/code:opacity-100 transition-opacity z-10",
13113
+ children: /* @__PURE__ */ jsx59(CopyButton, {
13114
+ text: codeContent,
13115
+ className: "bg-background/80 backdrop-blur-sm border border-border/50 shadow-sm",
13116
+ size: "sm"
13117
+ })
13118
+ }),
13119
+ /* @__PURE__ */ jsx59("pre", {
13120
+ ...props,
13121
+ className: "overflow-x-auto",
13122
+ children
13123
+ })
13124
+ ]
13125
+ });
13126
+ },
13127
+ table: ({
13128
+ children,
13129
+ ...props
13130
+ }) => /* @__PURE__ */ jsx59("div", {
13131
+ className: "overflow-x-auto max-w-full min-w-0 my-3",
13132
+ children: /* @__PURE__ */ jsx59("table", {
12950
13133
  ...props,
12951
13134
  children
12952
- }),
12953
- pre: ({
12954
- children,
12955
- ...props
12956
- }) => {
12957
- const codeContent = (() => {
12958
- if (!children)
12959
- return "";
12960
- const child = Array.isArray(children) ? children[0] : children;
12961
- if (child && typeof child === "object" && "props" in child) {
12962
- const codeProps = child.props;
12963
- if (typeof codeProps.children === "string") {
12964
- return codeProps.children;
12965
- }
12966
- }
12967
- return "";
12968
- })();
12969
- return /* @__PURE__ */ jsxs50("div", {
12970
- className: "relative group/code my-3",
12971
- children: [
12972
- /* @__PURE__ */ jsx59("div", {
12973
- className: "absolute top-2 right-2 opacity-0 group-hover/code:opacity-100 transition-opacity z-10",
12974
- children: /* @__PURE__ */ jsx59(CopyButton, {
12975
- text: codeContent,
12976
- className: "bg-background/80 backdrop-blur-sm border border-border/50 shadow-sm",
12977
- size: "sm"
12978
- })
12979
- }),
12980
- /* @__PURE__ */ jsx59("pre", {
12981
- ...props,
12982
- className: "overflow-x-auto",
12983
- children
12984
- })
12985
- ]
12986
- });
12987
- },
12988
- table: ({
12989
- children,
12990
- ...props
12991
- }) => /* @__PURE__ */ jsx59("div", {
12992
- className: "overflow-x-auto max-w-full min-w-0 my-3",
12993
- children: /* @__PURE__ */ jsx59("table", {
12994
- ...props,
12995
- children
12996
- })
12997
13135
  })
12998
- },
12999
- children: content
13000
- })
13001
- }),
13002
- content.length > 500 && /* @__PURE__ */ jsx59("div", {
13003
- className: "absolute -bottom-1 right-0 opacity-0 group-hover:opacity-100 transition-opacity z-10",
13004
- children: /* @__PURE__ */ jsx59(CopyButton, {
13005
- text: content,
13006
- className: "bg-background/80 backdrop-blur-sm border border-border/50 shadow-sm",
13007
- size: "md"
13008
- })
13136
+ })
13137
+ },
13138
+ children: content
13009
13139
  })
13010
- ]
13140
+ })
13011
13141
  });
13012
13142
  }
13013
13143
  if (part.type === "error") {
@@ -14059,7 +14189,7 @@ function ActionToolBox({ part, showLine, compact }) {
14059
14189
  },
14060
14190
  children: /* @__PURE__ */ jsx61("pre", {
14061
14191
  ref: contentMeasureRef,
14062
- className: "px-1 pt-2.5 pb-1 text-[12px] leading-relaxed text-foreground/60 font-mono whitespace-pre-wrap break-all",
14192
+ className: "px-1 pt-2.5 pb-1 text-[12px] leading-relaxed text-foreground/60 font-mono whitespace-pre-wrap break-words",
14063
14193
  children: displayContent
14064
14194
  })
14065
14195
  })
@@ -14123,20 +14253,10 @@ function extractJsonStringField(raw, field) {
14123
14253
  let result = "";
14124
14254
  let i = start;
14125
14255
  while (i < raw.length) {
14126
- if (raw[i] === "\\" && i + 1 < raw.length) {
14127
- const next = raw[i + 1];
14128
- if (next === "n")
14129
- result += `
14130
- `;
14131
- else if (next === "t")
14132
- result += "\t";
14133
- else if (next === '"')
14134
- result += '"';
14135
- else if (next === "\\")
14136
- result += "\\";
14137
- else
14138
- result += next;
14139
- i += 2;
14256
+ const decoded = decodeJsonStringChar(raw, i);
14257
+ if (decoded) {
14258
+ result += decoded.value;
14259
+ i = decoded.nextIndex;
14140
14260
  } else if (raw[i] === '"') {
14141
14261
  break;
14142
14262
  } else {
@@ -14146,6 +14266,38 @@ function extractJsonStringField(raw, field) {
14146
14266
  }
14147
14267
  return result;
14148
14268
  }
14269
+ function decodeJsonStringChar(raw, index) {
14270
+ if (raw[index] !== "\\" || index + 1 >= raw.length)
14271
+ return null;
14272
+ const next = raw[index + 1];
14273
+ if (next === "n")
14274
+ return { value: `
14275
+ `, nextIndex: index + 2 };
14276
+ if (next === "t")
14277
+ return { value: "\t", nextIndex: index + 2 };
14278
+ if (next === "r")
14279
+ return { value: "\r", nextIndex: index + 2 };
14280
+ if (next === "b")
14281
+ return { value: "\b", nextIndex: index + 2 };
14282
+ if (next === "f")
14283
+ return { value: "\f", nextIndex: index + 2 };
14284
+ if (next === '"')
14285
+ return { value: '"', nextIndex: index + 2 };
14286
+ if (next === "\\")
14287
+ return { value: "\\", nextIndex: index + 2 };
14288
+ if (next === "/")
14289
+ return { value: "/", nextIndex: index + 2 };
14290
+ if (next === "u" && index + 5 < raw.length) {
14291
+ const hex = raw.slice(index + 2, index + 6);
14292
+ if (/^[0-9a-fA-F]{4}$/.test(hex)) {
14293
+ return {
14294
+ value: String.fromCharCode(Number.parseInt(hex, 16)),
14295
+ nextIndex: index + 6
14296
+ };
14297
+ }
14298
+ }
14299
+ return { value: next, nextIndex: index + 2 };
14300
+ }
14149
14301
  function extractJsonStringFieldPreview(raw, field) {
14150
14302
  const pattern = new RegExp(`"${field}"\\s*:\\s*"`);
14151
14303
  const m = pattern.exec(raw);
@@ -14155,8 +14307,25 @@ function extractJsonStringFieldPreview(raw, field) {
14155
14307
  if (raw.length - start <= LIVE_TOOL_CONTENT_PREVIEW_CHARS) {
14156
14308
  return extractJsonStringField(raw, field);
14157
14309
  }
14310
+ let result = "";
14311
+ let i = start;
14312
+ while (i < raw.length) {
14313
+ const decoded = decodeJsonStringChar(raw, i);
14314
+ if (decoded) {
14315
+ result += decoded.value;
14316
+ i = decoded.nextIndex;
14317
+ } else if (raw[i] === '"') {
14318
+ break;
14319
+ } else {
14320
+ result += raw[i];
14321
+ i += 1;
14322
+ }
14323
+ if (result.length > LIVE_TOOL_CONTENT_PREVIEW_CHARS) {
14324
+ result = result.slice(-LIVE_TOOL_CONTENT_PREVIEW_CHARS);
14325
+ }
14326
+ }
14158
14327
  return `… showing latest streamed content only …
14159
- ${raw.slice(-LIVE_TOOL_CONTENT_PREVIEW_CHARS)}`;
14328
+ ${result}`;
14160
14329
  }
14161
14330
  function getLiveToolContentPreview(toolName, content) {
14162
14331
  if (toolName !== "write" && toolName !== "apply_patch" && content.length <= LIVE_TOOL_CONTENT_PREVIEW_CHARS) {
@@ -15837,21 +16006,23 @@ var UsageModal = memo14(function UsageModal2() {
15837
16006
  // src/hooks/useProviderUsage.ts
15838
16007
  import { useEffect as useEffect23, useCallback as useCallback15, useRef as useRef15 } from "react";
15839
16008
  var POLL_INTERVAL = 60000;
15840
- var STALE_THRESHOLD = 30000;
16009
+ var STALE_THRESHOLD = 60000;
15841
16010
  var inflight = new Set;
15842
16011
  function useProviderUsage(provider, authType) {
15843
16012
  const setUsage = useUsageStore((s) => s.setUsage);
15844
16013
  const setLoading = useUsageStore((s) => s.setLoading);
15845
16014
  const setLastFetched = useUsageStore((s) => s.setLastFetched);
16015
+ const isModalOpen = useUsageStore((s) => s.isModalOpen);
16016
+ const modalProvider = useUsageStore((s) => s.modalProvider);
15846
16017
  const usage = useUsageStore((s) => provider ? s.usage[provider] : undefined);
15847
16018
  const isOAuthProvider = authType === "oauth" && (provider === "anthropic" || provider === "openai");
15848
- const fetchUsage = useCallback15(async () => {
16019
+ const fetchUsage = useCallback15(async (force = false) => {
15849
16020
  if (!provider || !isOAuthProvider)
15850
16021
  return;
15851
16022
  if (inflight.has(provider))
15852
16023
  return;
15853
16024
  const last = useUsageStore.getState().lastFetched[provider] ?? 0;
15854
- if (last && Date.now() - last < STALE_THRESHOLD)
16025
+ if (!force && last && Date.now() - last < STALE_THRESHOLD)
15855
16026
  return;
15856
16027
  inflight.add(provider);
15857
16028
  setLoading(provider, true);
@@ -15870,9 +16041,15 @@ function useProviderUsage(provider, authType) {
15870
16041
  if (!provider || !isOAuthProvider)
15871
16042
  return;
15872
16043
  fetchRef.current();
16044
+ }, [isOAuthProvider, provider]);
16045
+ useEffect23(() => {
16046
+ if (!provider || !isOAuthProvider || !isModalOpen || modalProvider !== provider) {
16047
+ return;
16048
+ }
16049
+ fetchRef.current(true);
15873
16050
  const interval = setInterval(() => fetchRef.current(), POLL_INTERVAL);
15874
16051
  return () => clearInterval(interval);
15875
- }, [isOAuthProvider, provider]);
16052
+ }, [isModalOpen, isOAuthProvider, modalProvider, provider]);
15876
16053
  return {
15877
16054
  usage,
15878
16055
  fetchUsage,
@@ -16585,7 +16762,7 @@ var MessageThread = memo16(function MessageThread2({
16585
16762
  }
16586
16763
  };
16587
16764
  const filteredMessages = useMemo17(() => {
16588
- const visibleMessages = messages.filter((message) => message.role !== "system");
16765
+ const visibleMessages = messages.filter((message) => message.role !== "system" && !(message.role === "assistant" && message.status === "complete" && (message.parts?.length ?? 0) === 0));
16589
16766
  const queueBusy = Boolean(queueState.currentMessageId) || queueState.queueLength > 0;
16590
16767
  if (!queueBusy)
16591
16768
  return visibleMessages;
@@ -16756,7 +16933,8 @@ var MessageThread = memo16(function MessageThread2({
16756
16933
  });
16757
16934
  });
16758
16935
  // src/components/messages/MessageThreadContainer.tsx
16759
- import { memo as memo19, useMemo as useMemo18 } from "react";
16936
+ import { memo as memo19, useEffect as useEffect29, useMemo as useMemo18 } from "react";
16937
+ import { useQueryClient as useQueryClient12 } from "@tanstack/react-query";
16760
16938
 
16761
16939
  // src/hooks/useSessionStream.ts
16762
16940
  import { useEffect as useEffect26, useRef as useRef18 } from "react";
@@ -17163,22 +17341,30 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17163
17341
  return;
17164
17342
  let changeLines = 0;
17165
17343
  let stableChangeLength = 0;
17344
+ let lineDirectiveCount = 0;
17166
17345
  for (const line of stablePatch.split(`
17167
17346
  `)) {
17168
17347
  if (line.startsWith("+") && !line.startsWith("+++") || line.startsWith("-") && !line.startsWith("---")) {
17169
17348
  changeLines += 1;
17170
17349
  stableChangeLength += line.length;
17350
+ } else if (/^\*\*\* (?:Delete Lines in|Replace Lines in|Insert Before in|Insert After in): /.test(line) || line.startsWith("*** Lines:") || line.startsWith("*** Line:") || line.startsWith("*** With:")) {
17351
+ lineDirectiveCount += 1;
17171
17352
  }
17172
17353
  }
17173
- return changeLines > 0 ? `${changeLines}:${stableChangeLength}` : undefined;
17354
+ if (changeLines > 0)
17355
+ return `${changeLines}:${stableChangeLength}`;
17356
+ return lineDirectiveCount > 0 ? `lines:${lineDirectiveCount}:${stablePatch.length}` : undefined;
17174
17357
  };
17175
17358
  const extractPathsFromPatch = (patch) => {
17176
17359
  const paths = new Set;
17177
17360
  for (const line of patch.split(`
17178
17361
  `)) {
17179
17362
  const directive = line.match(/^\*\*\* (?:Update|Add|Delete) File: (.+)$/);
17180
- if (directive?.[1]) {
17181
- paths.add(directive[1].trim());
17363
+ const replaceDirective = line.match(/^\*\*\* Replace in: (.+)$/);
17364
+ const lineDirective = line.match(/^\*\*\* (?:Delete Lines in|Replace Lines in|Insert Before in|Insert After in): (.+)$/);
17365
+ const path = directive?.[1] ?? replaceDirective?.[1] ?? lineDirective?.[1];
17366
+ if (path) {
17367
+ paths.add(path.trim());
17182
17368
  continue;
17183
17369
  }
17184
17370
  const unified = line.match(/^\+\+\+ (?:b\/)?(.+)$/);
@@ -17188,6 +17374,47 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17188
17374
  }
17189
17375
  return [...paths];
17190
17376
  };
17377
+ const getExtension = (path) => path.split(".").pop()?.toLowerCase() ?? "";
17378
+ const updateFileContentCache = (path, content) => {
17379
+ queryClient.setQueryData(["files", "read", path], {
17380
+ content,
17381
+ path,
17382
+ extension: getExtension(path),
17383
+ lineCount: content.split(`
17384
+ `).length
17385
+ });
17386
+ };
17387
+ const mergeReadResultIntoFileCache = (path, result, startLine, endLine) => {
17388
+ if (typeof result?.content !== "string")
17389
+ return;
17390
+ const readContent = result.content;
17391
+ if (!startLine || !endLine) {
17392
+ updateFileContentCache(path, readContent);
17393
+ return;
17394
+ }
17395
+ queryClient.setQueryData(["files", "read", path], (current) => {
17396
+ if (!current?.content)
17397
+ return current;
17398
+ const lines = current.content.split(`
17399
+ `);
17400
+ if (lines.at(-1) === "")
17401
+ lines.pop();
17402
+ const readLines = readContent.split(`
17403
+ `);
17404
+ lines.splice(startLine - 1, endLine - startLine + 1, ...readLines);
17405
+ const content = `${lines.join(`
17406
+ `)}
17407
+ `;
17408
+ return {
17409
+ ...current,
17410
+ content,
17411
+ lineCount: typeof result.totalLines === "number" ? result.totalLines : lines.length
17412
+ };
17413
+ });
17414
+ };
17415
+ const invalidateFileContentCache = (path) => {
17416
+ queryClient.invalidateQueries({ queryKey: ["files", "read", path] });
17417
+ };
17191
17418
  const getChangedLinesForPath = (result, path) => {
17192
17419
  const changes = Array.isArray(result?.changes) ? result.changes : [];
17193
17420
  const lines = new Set;
@@ -17217,9 +17444,6 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17217
17444
  return lines.size > 0 ? [...lines] : undefined;
17218
17445
  };
17219
17446
  const handleReadToolActivity = (eventType, payload, delta) => {
17220
- const viewerStore = useViewerTabsStore.getState();
17221
- if (!viewerStore.followToolActivity)
17222
- return;
17223
17447
  const name = getToolEventName(payload);
17224
17448
  if (name !== "read")
17225
17449
  return;
@@ -17232,6 +17456,12 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17232
17456
  const startLine = normalizeLineNumber(args.startLine) ?? normalizeLineNumber(args.start_line) ?? rangeFromResult.startLine;
17233
17457
  const endLine = normalizeLineNumber(args.endLine) ?? normalizeLineNumber(args.end_line) ?? rangeFromResult.endLine ?? startLine;
17234
17458
  const failed = result?.ok === false || eventType === "error";
17459
+ if (eventType === "tool.result" && !failed) {
17460
+ mergeReadResultIntoFileCache(path, result, startLine, endLine);
17461
+ }
17462
+ const viewerStore = useViewerTabsStore.getState();
17463
+ if (!viewerStore.followToolActivity)
17464
+ return;
17235
17465
  viewerStore.openToolReadTab(path, {
17236
17466
  startLine,
17237
17467
  endLine,
@@ -17241,9 +17471,6 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17241
17471
  });
17242
17472
  };
17243
17473
  const handleWriteToolActivity = (eventType, payload, delta) => {
17244
- const viewerStore = useViewerTabsStore.getState();
17245
- if (!viewerStore.followToolActivity)
17246
- return;
17247
17474
  const name = getToolEventName(payload);
17248
17475
  if (name !== "write")
17249
17476
  return;
@@ -17257,6 +17484,15 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17257
17484
  const callId = getToolEventCallId(payload) ?? undefined;
17258
17485
  const status = failed ? "error" : eventType === "tool.result" ? "success" : "streaming";
17259
17486
  const content = status === "streaming" ? getStreamingWritePreviewContent(args, buffer) : getStringArg(args, buffer, "content");
17487
+ if (status === "success") {
17488
+ if (content !== undefined)
17489
+ updateFileContentCache(path, content);
17490
+ else
17491
+ invalidateFileContentCache(path);
17492
+ }
17493
+ const viewerStore = useViewerTabsStore.getState();
17494
+ if (!viewerStore.followToolActivity)
17495
+ return;
17260
17496
  if (status === "streaming" && content !== undefined && content.length >= TOOL_PREVIEW_THROTTLE_MIN_CHARS) {
17261
17497
  const previewKey = callId ?? path;
17262
17498
  const now = Date.now();
@@ -17280,9 +17516,6 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17280
17516
  });
17281
17517
  };
17282
17518
  const handleApplyPatchToolActivity = (eventType, payload, delta) => {
17283
- const viewerStore = useViewerTabsStore.getState();
17284
- if (!viewerStore.followToolActivity)
17285
- return;
17286
17519
  const name = getToolEventName(payload);
17287
17520
  if (name !== "apply_patch")
17288
17521
  return;
@@ -17306,6 +17539,13 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17306
17539
  const patchPaths = extractPathsFromPatch(patch);
17307
17540
  if (patchPaths.length === 0)
17308
17541
  return;
17542
+ if (status === "success") {
17543
+ for (const path of patchPaths)
17544
+ invalidateFileContentCache(path);
17545
+ }
17546
+ const viewerStore = useViewerTabsStore.getState();
17547
+ if (!viewerStore.followToolActivity)
17548
+ return;
17309
17549
  const matchingFileTabs = viewerStore.tabs.filter((tab) => tab.type === "file" && patchPaths.some((path) => patchPathMayReferToTarget(path, tab.path)));
17310
17550
  const activeMatchingFileTab = matchingFileTabs.find((tab) => tab.id === viewerStore.activeTabId);
17311
17551
  const fallbackPath = patchPaths.find(isLikelyCompletePatchPath);
@@ -17411,6 +17651,11 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17411
17651
  const applyMessageDelta = (payload) => {
17412
17652
  const messageId = typeof payload?.messageId === "string" ? payload.messageId : null;
17413
17653
  const partId = typeof payload?.partId === "string" ? payload.partId : null;
17654
+ const payloadType = typeof payload?.type === "string" ? payload.type : undefined;
17655
+ if (payloadType === "error") {
17656
+ upsertErrorPart(payload);
17657
+ return;
17658
+ }
17414
17659
  const delta = typeof payload?.delta === "string" ? payload.delta : null;
17415
17660
  if (!messageId || !partId || delta === null)
17416
17661
  return;
@@ -17461,6 +17706,90 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17461
17706
  return nextMessages;
17462
17707
  });
17463
17708
  };
17709
+ const toRecord = (value) => {
17710
+ if (value && typeof value === "object" && !Array.isArray(value)) {
17711
+ return value;
17712
+ }
17713
+ return null;
17714
+ };
17715
+ const parseErrorContent = (payload) => {
17716
+ const contentRecord = toRecord(payload.content);
17717
+ if (contentRecord)
17718
+ return contentRecord;
17719
+ if (typeof payload.content === "string") {
17720
+ try {
17721
+ const parsed = JSON.parse(payload.content);
17722
+ const parsedRecord = toRecord(parsed);
17723
+ if (parsedRecord)
17724
+ return parsedRecord;
17725
+ } catch {}
17726
+ }
17727
+ const message = typeof payload.error === "string" ? payload.error : typeof payload.message === "string" ? payload.message : "Assistant run failed";
17728
+ return {
17729
+ message,
17730
+ type: typeof payload.errorType === "string" ? payload.errorType : "error",
17731
+ details: toRecord(payload.details) ?? undefined,
17732
+ isAborted: payload.isAborted === true,
17733
+ autoCompacted: payload.autoCompacted === true
17734
+ };
17735
+ };
17736
+ const upsertErrorPart = (payload) => {
17737
+ const messageId = typeof payload?.messageId === "string" ? payload.messageId : null;
17738
+ if (!payload || !messageId)
17739
+ return;
17740
+ const contentJson = parseErrorContent(payload);
17741
+ const content = JSON.stringify(contentJson);
17742
+ const errorMessage = typeof contentJson.message === "string" ? contentJson.message : typeof payload.error === "string" ? payload.error : "Assistant run failed";
17743
+ const stepIndex = typeof payload.stepIndex === "number" ? payload.stepIndex : null;
17744
+ const partId = typeof payload.partId === "string" ? payload.partId : `error-${messageId}`;
17745
+ queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
17746
+ if (!oldMessages)
17747
+ return oldMessages;
17748
+ const nextMessages = [...oldMessages];
17749
+ const messageIndex = nextMessages.findIndex((message) => message.id === messageId);
17750
+ if (messageIndex === -1)
17751
+ return oldMessages;
17752
+ const targetMessage = nextMessages[messageIndex];
17753
+ const parts = targetMessage.parts ? [...targetMessage.parts] : [];
17754
+ const partIndex = parts.findIndex((part) => part.id === partId);
17755
+ if (partIndex === -1) {
17756
+ const newPart = {
17757
+ id: partId,
17758
+ messageId,
17759
+ index: getOptimisticPartIndex(parts, stepIndex),
17760
+ stepIndex,
17761
+ type: "error",
17762
+ content,
17763
+ contentJson,
17764
+ agent: targetMessage.agent,
17765
+ provider: targetMessage.provider,
17766
+ model: targetMessage.model,
17767
+ startedAt: Date.now(),
17768
+ completedAt: Date.now(),
17769
+ toolName: null,
17770
+ toolCallId: null,
17771
+ toolDurationMs: null
17772
+ };
17773
+ parts.push(newPart);
17774
+ } else {
17775
+ parts[partIndex] = {
17776
+ ...parts[partIndex],
17777
+ content,
17778
+ contentJson,
17779
+ stepIndex: stepIndex ?? parts[partIndex].stepIndex ?? null,
17780
+ completedAt: Date.now()
17781
+ };
17782
+ }
17783
+ nextMessages[messageIndex] = {
17784
+ ...targetMessage,
17785
+ status: "error",
17786
+ completedAt: targetMessage.completedAt ?? Date.now(),
17787
+ error: errorMessage,
17788
+ parts
17789
+ };
17790
+ return nextMessages;
17791
+ });
17792
+ };
17464
17793
  const upsertEphemeralToolCall = (payload) => {
17465
17794
  if (!payload)
17466
17795
  return;
@@ -17890,6 +18219,17 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17890
18219
  }
17891
18220
  markMessageCompleted(payload);
17892
18221
  clearEphemeralForMessage(id);
18222
+ if (id) {
18223
+ queryClient.setQueryData(["queueState", sessionId], (current) => {
18224
+ if (!current || current.currentMessageId !== id)
18225
+ return current;
18226
+ return normalizeQueueState({
18227
+ currentMessageId: null,
18228
+ queuedMessages: [],
18229
+ isRunning: false
18230
+ });
18231
+ });
18232
+ }
17893
18233
  queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
17894
18234
  queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
17895
18235
  break;
@@ -17963,22 +18303,7 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
17963
18303
  assistantMessageIdRef.current = null;
17964
18304
  }
17965
18305
  clearEphemeralForMessage(messageId);
17966
- const errorMessage = typeof payload?.error === "string" ? payload.error : typeof payload?.message === "string" ? payload.message : "Assistant run failed";
17967
- queryClient.setQueryData(["messages", sessionId], (oldMessages) => {
17968
- if (!oldMessages)
17969
- return oldMessages;
17970
- const idx = oldMessages.findIndex((m) => m.id === messageId);
17971
- if (idx === -1)
17972
- return oldMessages;
17973
- const next = [...oldMessages];
17974
- next[idx] = {
17975
- ...next[idx],
17976
- status: "error",
17977
- completedAt: next[idx].completedAt ?? Date.now(),
17978
- error: errorMessage
17979
- };
17980
- return next;
17981
- });
18306
+ upsertErrorPart(payload);
17982
18307
  }
17983
18308
  queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
17984
18309
  break;
@@ -18013,11 +18338,11 @@ ${bestEffortUnescapeJsonString(rawTail)}`;
18013
18338
  break;
18014
18339
  }
18015
18340
  case "queue.updated": {
18016
- const queueState = {
18341
+ const queueState = normalizeQueueState({
18017
18342
  currentMessageId: payload?.currentMessageId,
18018
18343
  queuedMessages: payload?.queuedMessages ?? [],
18019
- queueLength: payload?.queueLength ?? 0
18020
- };
18344
+ isRunning: typeof payload?.isRunning === "boolean" ? payload.isRunning : undefined
18345
+ });
18021
18346
  queryClient.setQueryData(["queueState", sessionId], queueState);
18022
18347
  break;
18023
18348
  }
@@ -18962,10 +19287,16 @@ var MessageThreadContainer = memo19(function MessageThreadContainer2({
18962
19287
  sessionId,
18963
19288
  onSelectSession
18964
19289
  }) {
19290
+ const queryClient = useQueryClient12();
18965
19291
  const { data: messages = [], isLoading } = useMessages(sessionId);
18966
19292
  const { data: sessions = [] } = useSessions();
18967
19293
  const { preferences: preferences2 } = usePreferences();
18968
19294
  useSessionStream(sessionId);
19295
+ useEffect29(() => {
19296
+ queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
19297
+ queryClient.invalidateQueries({ queryKey: ["queueState", sessionId] });
19298
+ queryClient.invalidateQueries({ queryKey: sessionsQueryKey });
19299
+ }, [queryClient, sessionId]);
18969
19300
  useToolApprovalShortcuts(sessionId);
18970
19301
  const session = useMemo18(() => sessions.find((s) => s.id === sessionId), [sessions, sessionId]);
18971
19302
  const isGenerating = useMemo18(() => messages.some((m) => m.role === "assistant" && m.status === "pending"), [messages]);
@@ -19110,7 +19441,7 @@ var SessionItem = memo20(function SessionItem2({
19110
19441
  });
19111
19442
  });
19112
19443
  // src/components/sessions/SessionListContainer.tsx
19113
- import { memo as memo21, useMemo as useMemo19, useCallback as useCallback20, useEffect as useEffect29, useRef as useRef20 } from "react";
19444
+ import { memo as memo21, useMemo as useMemo19, useCallback as useCallback20, useEffect as useEffect30, useRef as useRef20 } from "react";
19114
19445
 
19115
19446
  // src/stores/focusStore.ts
19116
19447
  import { create as create20 } from "zustand";
@@ -19206,7 +19537,7 @@ var SessionListContainer = memo21(function SessionListContainer2({
19206
19537
  sessions: groupedSessions
19207
19538
  }));
19208
19539
  }, [sessionSnapshot, statusSessionIds]);
19209
- useEffect29(() => {
19540
+ useEffect30(() => {
19210
19541
  if (currentFocus === "sessions") {
19211
19542
  const session = sessionSnapshot[sessionIndex];
19212
19543
  if (session) {
@@ -19229,14 +19560,14 @@ var SessionListContainer = memo21(function SessionListContainer2({
19229
19560
  onError: () => markedViewedRef.current.delete(session.id)
19230
19561
  });
19231
19562
  }, [markSessionViewed, sessionSnapshot]);
19232
- useEffect29(() => {
19563
+ useEffect30(() => {
19233
19564
  const previousId = previousActiveSessionId.current;
19234
19565
  if (previousId && previousId !== activeSessionId) {
19235
19566
  markViewedIfReady(previousId);
19236
19567
  }
19237
19568
  previousActiveSessionId.current = activeSessionId;
19238
19569
  }, [activeSessionId, markViewedIfReady]);
19239
- useEffect29(() => {
19570
+ useEffect30(() => {
19240
19571
  if (!activeSessionId || lastScrolledSessionId.current === activeSessionId || sessions.length === 0)
19241
19572
  return;
19242
19573
  const activeIndex = sessions.findIndex((s) => s.id === activeSessionId);
@@ -19252,7 +19583,7 @@ var SessionListContainer = memo21(function SessionListContainer2({
19252
19583
  });
19253
19584
  }
19254
19585
  }, [activeSessionId, sessions, hasNextPage, fetchNextPage]);
19255
- useEffect29(() => {
19586
+ useEffect30(() => {
19256
19587
  const container = scrollContainerRef.current;
19257
19588
  if (!container)
19258
19589
  return;
@@ -19273,7 +19604,7 @@ var SessionListContainer = memo21(function SessionListContainer2({
19273
19604
  container.addEventListener("scroll", handleScroll, { passive: true });
19274
19605
  return () => container.removeEventListener("scroll", handleScroll);
19275
19606
  }, [hasNextPage, isFetchingNextPage, fetchNextPage]);
19276
- useEffect29(() => {
19607
+ useEffect30(() => {
19277
19608
  const container = scrollContainerRef.current;
19278
19609
  const sentinel = paginationSentinelRef.current;
19279
19610
  if (!container || !sentinel || typeof IntersectionObserver === "undefined") {
@@ -19422,7 +19753,7 @@ import {
19422
19753
  lineNumbers
19423
19754
  } from "@codemirror/view";
19424
19755
  import { tags } from "@lezer/highlight";
19425
- import { useCallback as useCallback21, useEffect as useEffect30, useMemo as useMemo20, useRef as useRef21 } from "react";
19756
+ import { useCallback as useCallback21, useEffect as useEffect31, useMemo as useMemo20, useRef as useRef21 } from "react";
19426
19757
  import { jsx as jsx78 } from "react/jsx-runtime";
19427
19758
  var viewerTheme = EditorView.theme({
19428
19759
  "&": {
@@ -19579,7 +19910,7 @@ var syntaxTheme = HighlightStyle.define([
19579
19910
  { tag: tags.invalid, color: "var(--otto-cm-invalid)" }
19580
19911
  ]);
19581
19912
  function lineDecorationsExtension(highlightedLines, highlightTone = "primary", lineTones) {
19582
- return EditorView.decorations.compute([], (state) => {
19913
+ return EditorView.decorations.compute(["doc"], (state) => {
19583
19914
  const decorations = [];
19584
19915
  for (const [line, tone] of lineTones ?? []) {
19585
19916
  if (line > 0)
@@ -19600,7 +19931,7 @@ function lineDecorationsExtension(highlightedLines, highlightTone = "primary", l
19600
19931
  return builder.finish();
19601
19932
  });
19602
19933
  }
19603
- function getLanguageExtension(path) {
19934
+ function getLanguageExtension(path, disableMarkdownSyntax = false) {
19604
19935
  const ext = path?.split(".").pop()?.toLowerCase() ?? "";
19605
19936
  switch (ext) {
19606
19937
  case "js":
@@ -19632,6 +19963,8 @@ function getLanguageExtension(path) {
19632
19963
  case "md":
19633
19964
  case "markdown":
19634
19965
  case "mdx":
19966
+ if (disableMarkdownSyntax)
19967
+ return [];
19635
19968
  return markdown();
19636
19969
  case "sql":
19637
19970
  return sql();
@@ -19658,7 +19991,8 @@ function CodeMirrorViewer({
19658
19991
  highlightTone = "primary",
19659
19992
  lineTones,
19660
19993
  scrollToLine,
19661
- scrollToEndSignal
19994
+ scrollToEndSignal,
19995
+ disableMarkdownSyntax = false
19662
19996
  }) {
19663
19997
  const hostRef = useRef21(null);
19664
19998
  const viewRef = useRef21(null);
@@ -19667,7 +20001,7 @@ function CodeMirrorViewer({
19667
20001
  const decorationsCompartmentRef = useRef21(new Compartment);
19668
20002
  const languageExtensionRef = useRef21([]);
19669
20003
  const decorationsExtensionRef = useRef21([]);
19670
- const languageExtension = useMemo20(() => getLanguageExtension(path), [path]);
20004
+ const languageExtension = useMemo20(() => getLanguageExtension(path, disableMarkdownSyntax), [path, disableMarkdownSyntax]);
19671
20005
  const decorationsExtension = useMemo20(() => lineDecorationsExtension(highlightedLines, highlightTone, lineTones), [highlightedLines, highlightTone, lineTones]);
19672
20006
  languageExtensionRef.current = languageExtension;
19673
20007
  decorationsExtensionRef.current = decorationsExtension;
@@ -19683,7 +20017,7 @@ function CodeMirrorViewer({
19683
20017
  decorationsCompartmentRef.current.of(decorationsExtension)
19684
20018
  ]
19685
20019
  }), [languageExtension, decorationsExtension]);
19686
- useEffect30(() => {
20020
+ useEffect31(() => {
19687
20021
  const host = hostRef.current;
19688
20022
  if (!host)
19689
20023
  return;
@@ -19710,7 +20044,7 @@ function CodeMirrorViewer({
19710
20044
  viewRef.current = null;
19711
20045
  };
19712
20046
  }, []);
19713
- useEffect30(() => {
20047
+ useEffect31(() => {
19714
20048
  const view = viewRef.current;
19715
20049
  if (!view)
19716
20050
  return;
@@ -19722,7 +20056,7 @@ function CodeMirrorViewer({
19722
20056
  view.setState(createEditorState(contentRef.current));
19723
20057
  }
19724
20058
  }, [languageExtension, createEditorState]);
19725
- useEffect30(() => {
20059
+ useEffect31(() => {
19726
20060
  const view = viewRef.current;
19727
20061
  if (!view)
19728
20062
  return;
@@ -19734,7 +20068,7 @@ function CodeMirrorViewer({
19734
20068
  view.setState(createEditorState(contentRef.current));
19735
20069
  }
19736
20070
  }, [decorationsExtension, createEditorState]);
19737
- useEffect30(() => {
20071
+ useEffect31(() => {
19738
20072
  const view = viewRef.current;
19739
20073
  if (!view)
19740
20074
  return;
@@ -19750,7 +20084,7 @@ function CodeMirrorViewer({
19750
20084
  }
19751
20085
  contentRef.current = content;
19752
20086
  }, [content, createEditorState]);
19753
- useEffect30(() => {
20087
+ useEffect31(() => {
19754
20088
  const view = viewRef.current;
19755
20089
  if (!view || !scrollToLine || scrollToLine < 1)
19756
20090
  return;
@@ -19763,7 +20097,7 @@ function CodeMirrorViewer({
19763
20097
  view.setState(createEditorState(contentRef.current));
19764
20098
  }
19765
20099
  }, [scrollToLine, createEditorState]);
19766
- useEffect30(() => {
20100
+ useEffect31(() => {
19767
20101
  const view = viewRef.current;
19768
20102
  if (!view || scrollToEndSignal === undefined)
19769
20103
  return;
@@ -19839,7 +20173,8 @@ function GitDiffViewer({ diff: diff2 }) {
19839
20173
  className: "flex-1 min-h-0",
19840
20174
  children: /* @__PURE__ */ jsx79(CodeMirrorViewer, {
19841
20175
  content: diff2.content,
19842
- path: diff2.file
20176
+ path: diff2.file,
20177
+ disableMarkdownSyntax: true
19843
20178
  })
19844
20179
  })
19845
20180
  ]
@@ -19882,8 +20217,9 @@ function GitDiffViewer({ diff: diff2 }) {
19882
20217
  children: "No changes to display"
19883
20218
  }) : /* @__PURE__ */ jsx79(CodeMirrorViewer, {
19884
20219
  content: diff2.diff,
19885
- path: "diff.patch",
19886
- lineTones: getDiffLineTones(diff2.diff)
20220
+ path: diff2.file,
20221
+ lineTones: getDiffLineTones(diff2.diff),
20222
+ disableMarkdownSyntax: true
19887
20223
  })
19888
20224
  })
19889
20225
  });
@@ -20143,7 +20479,7 @@ ${file.absPath}`,
20143
20479
  }
20144
20480
 
20145
20481
  // src/components/git/GitFileList.tsx
20146
- import { useEffect as useEffect31, useRef as useRef22, useMemo as useMemo21 } from "react";
20482
+ import { useEffect as useEffect32, useRef as useRef22, useMemo as useMemo21 } from "react";
20147
20483
  import { jsx as jsx81, jsxs as jsxs70 } from "react/jsx-runtime";
20148
20484
  function GitFileList({ status }) {
20149
20485
  const { openCommitModal, openDiff } = useGitStore();
@@ -20177,7 +20513,7 @@ function GitFileList({ status }) {
20177
20513
  }
20178
20514
  };
20179
20515
  const conflictedLength = status.conflicted?.length ?? 0;
20180
- useEffect31(() => {
20516
+ useEffect32(() => {
20181
20517
  if (currentFocus === "git" && gitFileIndex >= 0) {
20182
20518
  const element = itemRefs.current.get(gitFileIndex);
20183
20519
  element?.scrollIntoView({ block: "nearest", behavior: "smooth" });
@@ -20381,7 +20717,7 @@ function GitFileList({ status }) {
20381
20717
  });
20382
20718
  }
20383
20719
  // src/components/git/GitSidebar.tsx
20384
- import { memo as memo22, useCallback as useCallback22, useEffect as useEffect32, useState as useState36 } from "react";
20720
+ import { memo as memo22, useCallback as useCallback22, useEffect as useEffect33, useState as useState36 } from "react";
20385
20721
  import {
20386
20722
  FolderGit2,
20387
20723
  ChevronRight as ChevronRight11,
@@ -20396,7 +20732,7 @@ import {
20396
20732
  Upload,
20397
20733
  X as X12
20398
20734
  } from "lucide-react";
20399
- import { useQueryClient as useQueryClient12 } from "@tanstack/react-query";
20735
+ import { useQueryClient as useQueryClient13 } from "@tanstack/react-query";
20400
20736
  import { jsx as jsx82, jsxs as jsxs71, Fragment as Fragment33 } from "react/jsx-runtime";
20401
20737
  var PANEL_KEY = "git";
20402
20738
  var DEFAULT_WIDTH = 320;
@@ -20410,7 +20746,7 @@ var GitSidebar = memo22(function GitSidebar2({
20410
20746
  const panelWidth = usePanelWidthStore((s) => s.widths[PANEL_KEY] ?? DEFAULT_WIDTH);
20411
20747
  const { data: status, isLoading, error, refetch } = useGitStatus();
20412
20748
  const { data: remotes } = useGitRemotes();
20413
- const queryClient = useQueryClient12();
20749
+ const queryClient = useQueryClient13();
20414
20750
  const pushMutation = usePushCommits();
20415
20751
  const pullMutation = usePullChanges();
20416
20752
  const initMutation = useGitInit();
@@ -20423,7 +20759,7 @@ var GitSidebar = memo22(function GitSidebar2({
20423
20759
  const [remoteName, setRemoteName] = useState36("origin");
20424
20760
  const [remoteUrl, setRemoteUrl] = useState36("");
20425
20761
  const [confirmRemoveRemote, setConfirmRemoveRemote] = useState36(null);
20426
- useEffect32(() => {
20762
+ useEffect33(() => {
20427
20763
  if (isExpanded) {
20428
20764
  queryClient.invalidateQueries({ queryKey: ["git", "status"] });
20429
20765
  }
@@ -20938,11 +21274,11 @@ import { GitBranch as GitBranch9 } from "lucide-react";
20938
21274
  import { memo as memo23 } from "react";
20939
21275
 
20940
21276
  // src/hooks/useShortcutHintsVisible.ts
20941
- import { useEffect as useEffect33, useState as useState37 } from "react";
21277
+ import { useEffect as useEffect34, useState as useState37 } from "react";
20942
21278
  var SHORTCUT_HINT_MODIFIERS = new Set(["Control", "Meta"]);
20943
21279
  function useShortcutHintsVisible() {
20944
21280
  const [isVisible, setIsVisible] = useState37(false);
20945
- useEffect33(() => {
21281
+ useEffect34(() => {
20946
21282
  const handleKeyDown = (event) => {
20947
21283
  if (SHORTCUT_HINT_MODIFIERS.has(event.key) || event.ctrlKey || event.metaKey) {
20948
21284
  setIsVisible(true);
@@ -21008,7 +21344,7 @@ var GitSidebarToggle = memo24(function GitSidebarToggle2() {
21008
21344
  });
21009
21345
  });
21010
21346
  // src/components/git/GitDiffPanel.tsx
21011
- import { useEffect as useEffect34, memo as memo25, useState as useState38 } from "react";
21347
+ import { useEffect as useEffect35, memo as memo25, useState as useState38 } from "react";
21012
21348
  import { X as X13, Maximize2, Minimize2 as Minimize22 } from "lucide-react";
21013
21349
 
21014
21350
  // src/hooks/useFileBrowser.ts
@@ -21063,11 +21399,11 @@ var GitDiffPanel = memo25(function GitDiffPanel2({
21063
21399
  const { data: fullFileDiff, isLoading: fullFileLoading } = useGitDiffFullFile(selectedFile, selectedFileStaged, showFullFile);
21064
21400
  const activeDiff = showFullFile && fullFileDiff ? fullFileDiff : diff2;
21065
21401
  const activeLoading = showFullFile ? fullFileLoading : isLoading;
21066
- useEffect34(() => {
21402
+ useEffect35(() => {
21067
21403
  if (!isDiffOpen)
21068
21404
  setShowFullFile(false);
21069
21405
  }, [isDiffOpen]);
21070
- useEffect34(() => {
21406
+ useEffect35(() => {
21071
21407
  const handleEscape = (e) => {
21072
21408
  const target = e.target;
21073
21409
  const isInInput = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
@@ -21162,7 +21498,7 @@ ${activeDiff?.absPath || ""}`,
21162
21498
  });
21163
21499
  });
21164
21500
  // src/components/git/GitCommitModal.tsx
21165
- import { useState as useState39, useId as useId2, useEffect as useEffect35, useCallback as useCallback23 } from "react";
21501
+ import { useState as useState39, useId as useId2, useEffect as useEffect36, useCallback as useCallback23 } from "react";
21166
21502
  import { GitCommit as GitCommit4, Sparkles as Sparkles5 } from "lucide-react";
21167
21503
  import { jsx as jsx86, jsxs as jsxs74, Fragment as Fragment34 } from "react/jsx-runtime";
21168
21504
  function GitCommitModal() {
@@ -21195,7 +21531,7 @@ function GitCommitModal() {
21195
21531
  console.error("Failed to generate commit message:", error);
21196
21532
  }
21197
21533
  }, [generateMessage]);
21198
- useEffect35(() => {
21534
+ useEffect36(() => {
21199
21535
  if (!isCommitModalOpen)
21200
21536
  return;
21201
21537
  const handleKeyDown = (e) => {
@@ -21334,7 +21670,7 @@ function GitCommitModal() {
21334
21670
  });
21335
21671
  }
21336
21672
  // src/components/terminals/TerminalsPanel.tsx
21337
- import { memo as memo27, useCallback as useCallback25, useRef as useRef24, useEffect as useEffect37 } from "react";
21673
+ import { memo as memo27, useCallback as useCallback25, useRef as useRef24, useEffect as useEffect38 } from "react";
21338
21674
  import {
21339
21675
  Terminal as TerminalIcon,
21340
21676
  Maximize2 as Maximize22,
@@ -21376,7 +21712,7 @@ var useTerminalStore = create21((set) => ({
21376
21712
  }));
21377
21713
 
21378
21714
  // src/hooks/useTerminals.ts
21379
- import { useQuery as useQuery9, useMutation as useMutation6, useQueryClient as useQueryClient13 } from "@tanstack/react-query";
21715
+ import { useQuery as useQuery9, useMutation as useMutation6, useQueryClient as useQueryClient14 } from "@tanstack/react-query";
21380
21716
  import {
21381
21717
  getTerminals,
21382
21718
  postTerminals,
@@ -21397,7 +21733,7 @@ function useTerminals() {
21397
21733
  });
21398
21734
  }
21399
21735
  function useCreateTerminal() {
21400
- const queryClient = useQueryClient13();
21736
+ const queryClient = useQueryClient14();
21401
21737
  return useMutation6({
21402
21738
  mutationFn: async (params) => {
21403
21739
  const response = await postTerminals({
@@ -21414,7 +21750,7 @@ function useCreateTerminal() {
21414
21750
  });
21415
21751
  }
21416
21752
  function useKillTerminal() {
21417
- const queryClient = useQueryClient13();
21753
+ const queryClient = useQueryClient14();
21418
21754
  return useMutation6({
21419
21755
  mutationFn: async (terminalId) => {
21420
21756
  const response = await deleteTerminalsById({
@@ -21504,7 +21840,7 @@ var TerminalTabBar = memo26(function TerminalTabBar2({
21504
21840
  });
21505
21841
 
21506
21842
  // src/components/terminals/TerminalViewer.tsx
21507
- import { useEffect as useEffect36, useRef as useRef23, useState as useState40, useCallback as useCallback24 } from "react";
21843
+ import { useEffect as useEffect37, useRef as useRef23, useState as useState40, useCallback as useCallback24 } from "react";
21508
21844
  import { init, Terminal as Terminal7, FitAddon } from "ghostty-web";
21509
21845
  import { client as client3 } from "@ottocode/api";
21510
21846
  import { jsx as jsx88, jsxs as jsxs76 } from "react/jsx-runtime";
@@ -21669,7 +22005,7 @@ function TerminalViewer({
21669
22005
  }
21670
22006
  };
21671
22007
  }, [terminalId]);
21672
- useEffect36(() => {
22008
+ useEffect37(() => {
21673
22009
  if (!containerRef.current || !terminalId)
21674
22010
  return;
21675
22011
  let disposed = false;
@@ -21839,7 +22175,7 @@ function TerminalViewer({
21839
22175
  fitAddonRef.current = null;
21840
22176
  };
21841
22177
  }, [terminalId, connectWebSocket]);
21842
- useEffect36(() => {
22178
+ useEffect37(() => {
21843
22179
  const term = termRef.current;
21844
22180
  if (!term)
21845
22181
  return;
@@ -21857,7 +22193,7 @@ function TerminalViewer({
21857
22193
  }
21858
22194
  }
21859
22195
  }, [isActive, fitTerminal]);
21860
- useEffect36(() => {
22196
+ useEffect37(() => {
21861
22197
  fitTerminal();
21862
22198
  }, [fitTerminal]);
21863
22199
  return /* @__PURE__ */ jsx88("div", {
@@ -21914,12 +22250,12 @@ var TerminalsPanel = memo27(function TerminalsPanel2() {
21914
22250
  const autoCreatingRef = useRef24(false);
21915
22251
  const terminalsListRef = useRef24(terminalsList);
21916
22252
  terminalsListRef.current = terminalsList;
21917
- useEffect37(() => {
22253
+ useEffect38(() => {
21918
22254
  if (isOpen && terminalsListRef.current.length > 0 && (!activeTabId || !terminalsListRef.current.find((t) => t.id === activeTabId))) {
21919
22255
  selectTab(terminalsListRef.current[0].id);
21920
22256
  }
21921
22257
  }, [isOpen, terminalsList.length, activeTabId, selectTab]);
21922
- useEffect37(() => {
22258
+ useEffect38(() => {
21923
22259
  if (isOpen && terminals && terminalsList.length === 0 && !autoCreatingRef.current && !createTerminal.isPending) {
21924
22260
  autoCreatingRef.current = true;
21925
22261
  createTerminal.mutateAsync({
@@ -21955,7 +22291,7 @@ var TerminalsPanel = memo27(function TerminalsPanel2() {
21955
22291
  }
21956
22292
  } catch {}
21957
22293
  }, [killTerminal, activeTabId, selectTab, closePanel]);
21958
- useEffect37(() => {
22294
+ useEffect38(() => {
21959
22295
  const handleKeyDown = (e) => {
21960
22296
  if (e.key === "`" && e.ctrlKey) {
21961
22297
  e.preventDefault();
@@ -22401,7 +22737,7 @@ var SessionFilesSidebarToggle = memo30(function SessionFilesSidebarToggle2({
22401
22737
  });
22402
22738
  });
22403
22739
  // src/components/session-files/SessionFilesDiffPanel.tsx
22404
- import { useEffect as useEffect38, useMemo as useMemo23, memo as memo31 } from "react";
22740
+ import { useEffect as useEffect39, useMemo as useMemo23, memo as memo31 } from "react";
22405
22741
  import { X as X15, ChevronLeft, ChevronRight as ChevronRight12 } from "lucide-react";
22406
22742
  import { jsx as jsx93, jsxs as jsxs81 } from "react/jsx-runtime";
22407
22743
  function transformToUnifiedDiff(patch) {
@@ -22482,13 +22818,17 @@ function getPatchLineTones(patch) {
22482
22818
  }
22483
22819
  return tones;
22484
22820
  }
22485
- function FullHeightDiffView({ patch }) {
22821
+ function FullHeightDiffView({
22822
+ patch,
22823
+ filePath
22824
+ }) {
22486
22825
  return /* @__PURE__ */ jsx93("div", {
22487
22826
  className: "bg-card/60 border border-border rounded-lg overflow-hidden h-full",
22488
22827
  children: /* @__PURE__ */ jsx93(CodeMirrorViewer, {
22489
22828
  content: patch,
22490
- path: "session.patch",
22491
- lineTones: getPatchLineTones(patch)
22829
+ path: filePath,
22830
+ lineTones: getPatchLineTones(patch),
22831
+ disableMarkdownSyntax: true
22492
22832
  })
22493
22833
  });
22494
22834
  }
@@ -22541,7 +22881,7 @@ ${contentLines.map((line) => `+${line}`).join(`
22541
22881
  }
22542
22882
  return rawPatch;
22543
22883
  }, [selectedOperation, selectedFile]);
22544
- useEffect38(() => {
22884
+ useEffect39(() => {
22545
22885
  const handleKeyDown = (e) => {
22546
22886
  const target = e.target;
22547
22887
  const isInInput = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
@@ -22686,7 +23026,8 @@ ${contentLines.map((line) => `+${line}`).join(`
22686
23026
  /* @__PURE__ */ jsx93("div", {
22687
23027
  className: "flex-1 overflow-hidden p-4",
22688
23028
  children: patchContent ? /* @__PURE__ */ jsx93(FullHeightDiffView, {
22689
- patch: patchContent
23029
+ patch: patchContent,
23030
+ filePath: selectedFile
22690
23031
  }) : /* @__PURE__ */ jsx93("div", {
22691
23032
  className: "h-full flex items-center justify-center text-muted-foreground",
22692
23033
  children: "No diff content available"
@@ -22696,7 +23037,7 @@ ${contentLines.map((line) => `+${line}`).join(`
22696
23037
  });
22697
23038
  });
22698
23039
  // src/components/research/ResearchSidebar.tsx
22699
- import { memo as memo32, useState as useState41, useEffect as useEffect39, useCallback as useCallback26, useRef as useRef25, useMemo as useMemo24 } from "react";
23040
+ import { memo as memo32, useState as useState41, useEffect as useEffect40, useCallback as useCallback26, useRef as useRef25, useMemo as useMemo24 } from "react";
22700
23041
  import {
22701
23042
  FlaskConical as FlaskConical3,
22702
23043
  Plus as Plus5,
@@ -22710,7 +23051,7 @@ import {
22710
23051
  } from "lucide-react";
22711
23052
 
22712
23053
  // src/hooks/useResearch.ts
22713
- import { useQuery as useQuery11, useMutation as useMutation7, useQueryClient as useQueryClient14 } from "@tanstack/react-query";
23054
+ import { useQuery as useQuery11, useMutation as useMutation7, useQueryClient as useQueryClient15 } from "@tanstack/react-query";
22714
23055
  import {
22715
23056
  createResearchSession as apiCreateResearchSession,
22716
23057
  deleteResearchSession as apiDeleteResearchSession,
@@ -22773,7 +23114,7 @@ function useResearchSessions(parentSessionId, enabled = true) {
22773
23114
  });
22774
23115
  }
22775
23116
  function useCreateResearchSession() {
22776
- const queryClient = useQueryClient14();
23117
+ const queryClient = useQueryClient15();
22777
23118
  return useMutation7({
22778
23119
  mutationFn: ({
22779
23120
  parentSessionId,
@@ -22787,7 +23128,7 @@ function useCreateResearchSession() {
22787
23128
  });
22788
23129
  }
22789
23130
  function useDeleteResearchSession() {
22790
- const queryClient = useQueryClient14();
23131
+ const queryClient = useQueryClient15();
22791
23132
  return useMutation7({
22792
23133
  mutationFn: (researchId) => researchApi.deleteResearchSession(researchId),
22793
23134
  onSuccess: () => {
@@ -22814,7 +23155,7 @@ function useInjectContext() {
22814
23155
  });
22815
23156
  }
22816
23157
  function useExportToSession() {
22817
- const queryClient = useQueryClient14();
23158
+ const queryClient = useQueryClient15();
22818
23159
  return useMutation7({
22819
23160
  mutationFn: ({
22820
23161
  researchId,
@@ -22827,7 +23168,7 @@ function useExportToSession() {
22827
23168
  }
22828
23169
 
22829
23170
  // src/components/research/ResearchSidebar.tsx
22830
- import { useMutation as useMutation8, useQueryClient as useQueryClient15 } from "@tanstack/react-query";
23171
+ import { useMutation as useMutation8, useQueryClient as useQueryClient16 } from "@tanstack/react-query";
22831
23172
  import { jsx as jsx94, jsxs as jsxs82, Fragment as Fragment36 } from "react/jsx-runtime";
22832
23173
  var PANEL_KEY3 = "research";
22833
23174
  var DEFAULT_WIDTH3 = 384;
@@ -22861,7 +23202,7 @@ var ResearchSidebar = memo32(function ResearchSidebar2({
22861
23202
  const { data: parentMessagesData } = useMessages(parentSessionId ?? undefined, { enabled: isExpanded });
22862
23203
  useSessionStream(activeResearchSessionId ?? undefined, isExpanded);
22863
23204
  const updateSession = useUpdateSession(activeResearchSessionId ?? "");
22864
- const queryClient = useQueryClient15();
23205
+ const queryClient = useQueryClient16();
22865
23206
  const sendMessage = useMutation8({
22866
23207
  mutationFn: async ({
22867
23208
  sessionId,
@@ -22871,12 +23212,12 @@ var ResearchSidebar = memo32(function ResearchSidebar2({
22871
23212
  queryClient.invalidateQueries({ queryKey: ["messages", sessionId] });
22872
23213
  }
22873
23214
  });
22874
- useEffect39(() => {
23215
+ useEffect40(() => {
22875
23216
  if (parentSessionId) {
22876
23217
  useResearchStore.getState().setParentSessionId(parentSessionId);
22877
23218
  }
22878
23219
  }, [parentSessionId]);
22879
- useEffect39(() => {
23220
+ useEffect40(() => {
22880
23221
  if (researchData?.sessions?.length) {
22881
23222
  const currentIsValid = researchData.sessions.some((s) => s.id === activeResearchSessionId);
22882
23223
  if (!currentIsValid) {
@@ -22886,7 +23227,7 @@ var ResearchSidebar = memo32(function ResearchSidebar2({
22886
23227
  selectResearchSession(null);
22887
23228
  }
22888
23229
  }, [researchData, activeResearchSessionId, selectResearchSession]);
22889
- useEffect39(() => {
23230
+ useEffect40(() => {
22890
23231
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
22891
23232
  }, []);
22892
23233
  const adjustTextareaHeight = useCallback26(() => {
@@ -22896,7 +23237,7 @@ var ResearchSidebar = memo32(function ResearchSidebar2({
22896
23237
  textarea.style.height = "auto";
22897
23238
  textarea.style.height = `${Math.min(textarea.scrollHeight, 120)}px`;
22898
23239
  }, []);
22899
- useEffect39(() => {
23240
+ useEffect40(() => {
22900
23241
  adjustTextareaHeight();
22901
23242
  }, [adjustTextareaHeight]);
22902
23243
  const handleCreateNew = useCallback26(async () => {
@@ -23390,7 +23731,7 @@ var ResearchSidebarToggle = memo33(function ResearchSidebarToggle2({
23390
23731
  });
23391
23732
  });
23392
23733
  // src/components/settings/SettingsSidebar.tsx
23393
- import { memo as memo34, useState as useState43, useMemo as useMemo25, useCallback as useCallback28, useEffect as useEffect42, useRef as useRef28 } from "react";
23734
+ import { memo as memo34, useState as useState43, useMemo as useMemo25, useCallback as useCallback28, useEffect as useEffect43, useRef as useRef28 } from "react";
23394
23735
  import { createPortal } from "react-dom";
23395
23736
  import {
23396
23737
  Settings as Settings2,
@@ -23451,8 +23792,8 @@ var useOnboardingStore = create22((set, get) => ({
23451
23792
  }));
23452
23793
 
23453
23794
  // src/hooks/useAuthStatus.ts
23454
- import { useEffect as useEffect40, useCallback as useCallback27, useState as useState42, useRef as useRef26 } from "react";
23455
- import { useQueryClient as useQueryClient16 } from "@tanstack/react-query";
23795
+ import { useEffect as useEffect41, useCallback as useCallback27, useState as useState42, useRef as useRef26 } from "react";
23796
+ import { useQueryClient as useQueryClient17 } from "@tanstack/react-query";
23456
23797
  var isInIframe = typeof window !== "undefined" && window.self !== window.top;
23457
23798
  function useAuthStatus() {
23458
23799
  const setAuthStatus = useOnboardingStore((s) => s.setAuthStatus);
@@ -23461,7 +23802,7 @@ function useAuthStatus() {
23461
23802
  const setError = useOnboardingStore((s) => s.setError);
23462
23803
  const authStatus = useOnboardingStore((s) => s.authStatus);
23463
23804
  const isOpen = useOnboardingStore((s) => s.isOpen);
23464
- const queryClient = useQueryClient16();
23805
+ const queryClient = useQueryClient17();
23465
23806
  const [initialized, setInitialized] = useState42(false);
23466
23807
  const [oauthPolling, setOauthPolling] = useState42(false);
23467
23808
  const oauthPollingRef = useRef26(null);
@@ -23655,7 +23996,7 @@ function useAuthStatus() {
23655
23996
  setLoading(false);
23656
23997
  }
23657
23998
  }, [fetchAuthStatus, setLoading, setError]);
23658
- useEffect40(() => {
23999
+ useEffect41(() => {
23659
24000
  if (!oauthPolling || !isInIframe)
23660
24001
  return;
23661
24002
  oauthPollingRef.current = setInterval(() => {
@@ -23669,7 +24010,7 @@ function useAuthStatus() {
23669
24010
  clearTimeout(timeout);
23670
24011
  };
23671
24012
  }, [oauthPolling, fetchAuthStatus]);
23672
- useEffect40(() => {
24013
+ useEffect41(() => {
23673
24014
  if (!oauthPolling || !authStatus)
23674
24015
  return;
23675
24016
  const currentConfigured = Object.entries(authStatus.providers).filter(([, p]) => p.configured);
@@ -23678,7 +24019,7 @@ function useAuthStatus() {
23678
24019
  setOauthPolling(false);
23679
24020
  }
23680
24021
  }, [authStatus, oauthPolling]);
23681
- useEffect40(() => {
24022
+ useEffect41(() => {
23682
24023
  const handleOAuthMessage = (event) => {
23683
24024
  if (event.data?.type === "oauth-success") {
23684
24025
  fetchAuthStatus();
@@ -23749,14 +24090,14 @@ function useAuthStatus() {
23749
24090
  }
23750
24091
 
23751
24092
  // src/hooks/useTopupCallback.ts
23752
- import { useEffect as useEffect41, useRef as useRef27 } from "react";
24093
+ import { useEffect as useEffect42, useRef as useRef27 } from "react";
23753
24094
  var STORAGE_KEY2 = "pendingPolarCheckout";
23754
24095
  function useTopupCallback() {
23755
24096
  const hasHandled = useRef27(false);
23756
24097
  const loadingToastId = useRef27(null);
23757
24098
  const setBalance = useOttoRouterStore((s) => s.setBalance);
23758
24099
  const removeToast = useToastStore((s) => s.removeToast);
23759
- useEffect41(() => {
24100
+ useEffect42(() => {
23760
24101
  if (hasHandled.current)
23761
24102
  return;
23762
24103
  const params = new URLSearchParams(window.location.search);
@@ -23950,7 +24291,7 @@ var SelectRow = memo34(function SelectRow2({
23950
24291
  const [menuStyle, setMenuStyle] = useState43(null);
23951
24292
  const buttonRef = useRef28(null);
23952
24293
  const selectedOption = options.find((o) => o.id === value);
23953
- useEffect42(() => {
24294
+ useEffect43(() => {
23954
24295
  if (!isOpen || !buttonRef.current)
23955
24296
  return;
23956
24297
  const update = () => {
@@ -24167,7 +24508,7 @@ var NumberInputRow = memo34(function NumberInputRow2({
24167
24508
  disabled
24168
24509
  }) {
24169
24510
  const [draft, setDraft] = useState43(value !== null && value !== undefined ? String(value) : "");
24170
- useEffect42(() => {
24511
+ useEffect43(() => {
24171
24512
  setDraft(value !== null && value !== undefined ? String(value) : "");
24172
24513
  }, [value]);
24173
24514
  const persistedValue = value !== null && value !== undefined ? String(value) : "";
@@ -24898,8 +25239,8 @@ import {
24898
25239
  import { QRCodeSVG as QRCodeSVG2 } from "qrcode.react";
24899
25240
 
24900
25241
  // src/hooks/useTunnel.ts
24901
- import { useQuery as useQuery12, useMutation as useMutation9, useQueryClient as useQueryClient17 } from "@tanstack/react-query";
24902
- import { useEffect as useEffect43, useCallback as useCallback29, useRef as useRef29 } from "react";
25242
+ import { useQuery as useQuery12, useMutation as useMutation9, useQueryClient as useQueryClient18 } from "@tanstack/react-query";
25243
+ import { useEffect as useEffect44, useCallback as useCallback29, useRef as useRef29 } from "react";
24903
25244
  import {
24904
25245
  client as client4,
24905
25246
  getTunnelQr,
@@ -24942,7 +25283,7 @@ function useTunnelStatus() {
24942
25283
  queryFn: fetchTunnelStatus,
24943
25284
  refetchInterval: 3000
24944
25285
  });
24945
- useEffect43(() => {
25286
+ useEffect44(() => {
24946
25287
  if (query.data) {
24947
25288
  setStatus(query.data.status);
24948
25289
  setUrl(query.data.url);
@@ -24952,7 +25293,7 @@ function useTunnelStatus() {
24952
25293
  return query;
24953
25294
  }
24954
25295
  function useStartTunnel() {
24955
- const queryClient = useQueryClient17();
25296
+ const queryClient = useQueryClient18();
24956
25297
  const setStatus = useTunnelStore((s) => s.setStatus);
24957
25298
  const setUrl = useTunnelStore((s) => s.setUrl);
24958
25299
  const setError = useTunnelStore((s) => s.setError);
@@ -24984,7 +25325,7 @@ function useStartTunnel() {
24984
25325
  });
24985
25326
  }
24986
25327
  function useStopTunnel() {
24987
- const queryClient = useQueryClient17();
25328
+ const queryClient = useQueryClient18();
24988
25329
  const reset = useTunnelStore((s) => s.reset);
24989
25330
  return useMutation9({
24990
25331
  mutationFn: stopTunnel,
@@ -25002,7 +25343,7 @@ function useTunnelQr() {
25002
25343
  queryFn: fetchTunnelQr,
25003
25344
  enabled: !!url
25004
25345
  });
25005
- useEffect43(() => {
25346
+ useEffect44(() => {
25006
25347
  if (query.data?.ok && query.data.qrCode) {
25007
25348
  setQrCode(query.data.qrCode);
25008
25349
  }
@@ -25042,7 +25383,7 @@ function useTunnelStream() {
25042
25383
  eventSourceRef.current = null;
25043
25384
  };
25044
25385
  }, [setStatus, setUrl, setError, setProgress]);
25045
- useEffect43(() => {
25386
+ useEffect44(() => {
25046
25387
  if (isExpanded) {
25047
25388
  const cleanup = connect();
25048
25389
  return cleanup;
@@ -25301,7 +25642,7 @@ var TunnelSidebarToggle = memo37(function TunnelSidebarToggle2() {
25301
25642
  });
25302
25643
  });
25303
25644
  // src/components/mcp/MCPSidebar.tsx
25304
- import { memo as memo40, useState as useState46, useCallback as useCallback32, useMemo as useMemo26, useEffect as useEffect46, useRef as useRef32 } from "react";
25645
+ import { memo as memo40, useState as useState46, useCallback as useCallback32, useMemo as useMemo26, useEffect as useEffect47, useRef as useRef32 } from "react";
25305
25646
  import {
25306
25647
  ChevronDown as ChevronDown12,
25307
25648
  ChevronRight as ChevronRight14,
@@ -25318,11 +25659,11 @@ import {
25318
25659
  Wrench,
25319
25660
  X as X17
25320
25661
  } from "lucide-react";
25321
- import { useQueryClient as useQueryClient19 } from "@tanstack/react-query";
25662
+ import { useQueryClient as useQueryClient20 } from "@tanstack/react-query";
25322
25663
 
25323
25664
  // src/hooks/useMCP.ts
25324
- import { useQuery as useQuery13, useMutation as useMutation10, useQueryClient as useQueryClient18 } from "@tanstack/react-query";
25325
- import { useEffect as useEffect44, useRef as useRef30, useCallback as useCallback30 } from "react";
25665
+ import { useQuery as useQuery13, useMutation as useMutation10, useQueryClient as useQueryClient19 } from "@tanstack/react-query";
25666
+ import { useEffect as useEffect45, useRef as useRef30, useCallback as useCallback30 } from "react";
25326
25667
  import {
25327
25668
  listMcpServers,
25328
25669
  startMcpServer,
@@ -25344,7 +25685,7 @@ function useMCPServers() {
25344
25685
  },
25345
25686
  refetchInterval: 1e4
25346
25687
  });
25347
- useEffect44(() => {
25688
+ useEffect45(() => {
25348
25689
  if (query.data?.servers) {
25349
25690
  setServers(query.data.servers);
25350
25691
  }
@@ -25352,7 +25693,7 @@ function useMCPServers() {
25352
25693
  return query;
25353
25694
  }
25354
25695
  function useStartMCPServer() {
25355
- const queryClient = useQueryClient18();
25696
+ const queryClient = useQueryClient19();
25356
25697
  return useMutation10({
25357
25698
  mutationFn: async (name) => {
25358
25699
  const { data, error } = await startMcpServer({
@@ -25371,7 +25712,7 @@ function useStartMCPServer() {
25371
25712
  });
25372
25713
  }
25373
25714
  function useStopMCPServer() {
25374
- const queryClient = useQueryClient18();
25715
+ const queryClient = useQueryClient19();
25375
25716
  const setLoading = useMCPStore((s) => s.setLoading);
25376
25717
  return useMutation10({
25377
25718
  mutationFn: async (name) => {
@@ -25393,7 +25734,7 @@ function useStopMCPServer() {
25393
25734
  });
25394
25735
  }
25395
25736
  function useAddMCPServer() {
25396
- const queryClient = useQueryClient18();
25737
+ const queryClient = useQueryClient19();
25397
25738
  return useMutation10({
25398
25739
  mutationFn: async (params) => {
25399
25740
  const { data, error } = await addMcpServer({
@@ -25412,7 +25753,7 @@ function useAddMCPServer() {
25412
25753
  });
25413
25754
  }
25414
25755
  function useRemoveMCPServer() {
25415
- const queryClient = useQueryClient18();
25756
+ const queryClient = useQueryClient19();
25416
25757
  return useMutation10({
25417
25758
  mutationFn: async (name) => {
25418
25759
  const { data, error } = await removeMcpServer({
@@ -25431,7 +25772,7 @@ function useRemoveMCPServer() {
25431
25772
  });
25432
25773
  }
25433
25774
  function useAuthenticateMCPServer() {
25434
- const queryClient = useQueryClient18();
25775
+ const queryClient = useQueryClient19();
25435
25776
  return useMutation10({
25436
25777
  mutationFn: async (name) => {
25437
25778
  const { data, error } = await initiateMcpAuth({
@@ -25450,7 +25791,7 @@ function useAuthenticateMCPServer() {
25450
25791
  });
25451
25792
  }
25452
25793
  function useRevokeMCPAuth() {
25453
- const queryClient = useQueryClient18();
25794
+ const queryClient = useQueryClient19();
25454
25795
  return useMutation10({
25455
25796
  mutationFn: async (name) => {
25456
25797
  const { data, error } = await revokeMcpAuth({
@@ -25487,7 +25828,7 @@ function useCopilotDevicePoller() {
25487
25828
  const copilotDevice = useMCPStore((s) => s.copilotDevice);
25488
25829
  const setCopilotDevice = useMCPStore((s) => s.setCopilotDevice);
25489
25830
  const setLoading = useMCPStore((s) => s.setLoading);
25490
- const queryClient = useQueryClient18();
25831
+ const queryClient = useQueryClient19();
25491
25832
  const timerRef = useRef30(null);
25492
25833
  const stopPolling = useCallback30(() => {
25493
25834
  if (timerRef.current) {
@@ -25495,7 +25836,7 @@ function useCopilotDevicePoller() {
25495
25836
  timerRef.current = null;
25496
25837
  }
25497
25838
  }, []);
25498
- useEffect44(() => {
25839
+ useEffect45(() => {
25499
25840
  if (!copilotDevice) {
25500
25841
  stopPolling();
25501
25842
  return;
@@ -25531,7 +25872,7 @@ function useCopilotDevicePoller() {
25531
25872
  }
25532
25873
 
25533
25874
  // src/components/mcp/AddMCPServerModal.tsx
25534
- import { memo as memo38, useState as useState45, useCallback as useCallback31, useRef as useRef31, useEffect as useEffect45 } from "react";
25875
+ import { memo as memo38, useState as useState45, useCallback as useCallback31, useRef as useRef31, useEffect as useEffect46 } from "react";
25535
25876
  import { Globe as Globe5, Laptop, FolderDot, Terminal as Terminal9 } from "lucide-react";
25536
25877
  import { jsx as jsx100, jsxs as jsxs88, Fragment as Fragment39 } from "react/jsx-runtime";
25537
25878
  function parseCommandString(input) {
@@ -25654,7 +25995,7 @@ var AddMCPServerModal = memo38(function AddMCPServerModal2({
25654
25995
  ]);
25655
25996
  const contentRef = useRef31(null);
25656
25997
  const [contentHeight, setContentHeight] = useState45(undefined);
25657
- useEffect45(() => {
25998
+ useEffect46(() => {
25658
25999
  const el = contentRef.current;
25659
26000
  if (!el)
25660
26001
  return;
@@ -26198,7 +26539,7 @@ var MCPServerCard = memo40(function MCPServerCard2({
26198
26539
  function useAuthPoller(name, onAuthenticated) {
26199
26540
  const { data } = useMCPAuthStatus(name);
26200
26541
  const prevAuth = useRef32(false);
26201
- useEffect46(() => {
26542
+ useEffect47(() => {
26202
26543
  if (data?.authenticated && !prevAuth.current) {
26203
26544
  onAuthenticated();
26204
26545
  }
@@ -26224,7 +26565,7 @@ var MCPSidebar = memo40(function MCPSidebar2() {
26224
26565
  const [pollingServer, setPollingServer] = useState46(null);
26225
26566
  const [deleteTarget, setDeleteTarget] = useState46(null);
26226
26567
  const [searchQuery, setSearchQuery] = useState46("");
26227
- const queryClient = useQueryClient19();
26568
+ const queryClient = useQueryClient20();
26228
26569
  const handleAuthCompleted = useCallback32(() => {
26229
26570
  if (pollingServer) {
26230
26571
  setAuthUrl(pollingServer, null);
@@ -26233,7 +26574,7 @@ var MCPSidebar = memo40(function MCPSidebar2() {
26233
26574
  }
26234
26575
  }, [pollingServer, setAuthUrl, queryClient]);
26235
26576
  useAuthPoller(pollingServer, handleAuthCompleted);
26236
- useEffect46(() => {
26577
+ useEffect47(() => {
26237
26578
  for (const name of loading) {
26238
26579
  const server = servers.find((s) => s.name === name);
26239
26580
  if (server?.connected) {
@@ -26530,8 +26871,8 @@ import {
26530
26871
  } from "lucide-react";
26531
26872
 
26532
26873
  // src/hooks/useSkills.ts
26533
- import { useMutation as useMutation11, useQuery as useQuery14, useQueryClient as useQueryClient20 } from "@tanstack/react-query";
26534
- import { useEffect as useEffect47 } from "react";
26874
+ import { useMutation as useMutation11, useQuery as useQuery14, useQueryClient as useQueryClient21 } from "@tanstack/react-query";
26875
+ import { useEffect as useEffect48 } from "react";
26535
26876
  function useSkills() {
26536
26877
  const setSkillsConfig = useSkillsStore((s) => s.setSkillsConfig);
26537
26878
  const query = useQuery14({
@@ -26541,7 +26882,7 @@ function useSkills() {
26541
26882
  },
26542
26883
  refetchInterval: 30000
26543
26884
  });
26544
- useEffect47(() => {
26885
+ useEffect48(() => {
26545
26886
  if (query.data?.items) {
26546
26887
  setSkillsConfig({
26547
26888
  skills: query.data.items,
@@ -26554,7 +26895,7 @@ function useSkills() {
26554
26895
  return query;
26555
26896
  }
26556
26897
  function useUpdateSkillsConfig() {
26557
- const queryClient = useQueryClient20();
26898
+ const queryClient = useQueryClient21();
26558
26899
  const setSkillsConfig = useSkillsStore((s) => s.setSkillsConfig);
26559
26900
  return useMutation11({
26560
26901
  mutationFn: (input) => apiClient.updateSkillsConfig(input),
@@ -26997,7 +27338,7 @@ var SkillsSidebarToggle = memo43(function SkillsSidebarToggle2() {
26997
27338
  });
26998
27339
  });
26999
27340
  // src/components/skills/SkillViewerPanel.tsx
27000
- import { memo as memo44, useEffect as useEffect48 } from "react";
27341
+ import { memo as memo44, useEffect as useEffect49 } from "react";
27001
27342
  import { X as X19 } from "lucide-react";
27002
27343
  import { jsx as jsx106, jsxs as jsxs93 } from "react/jsx-runtime";
27003
27344
  var SkillViewerPanel = memo44(function SkillViewerPanel2({
@@ -27021,7 +27362,7 @@ var SkillViewerPanel = memo44(function SkillViewerPanel2({
27021
27362
  const content = isMainFile ? skillDetail?.content : fileData?.content;
27022
27363
  const isLoading = isMainFile ? !skillDetail : fileLoading;
27023
27364
  const displayPath = isMainFile ? "SKILL.md" : viewingFile ?? "";
27024
- useEffect48(() => {
27365
+ useEffect49(() => {
27025
27366
  const handleEscape = (e) => {
27026
27367
  const target = e.target;
27027
27368
  const isInInput = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
@@ -27091,7 +27432,7 @@ var SkillViewerPanel = memo44(function SkillViewerPanel2({
27091
27432
  });
27092
27433
  });
27093
27434
  // src/components/file-browser/FileBrowserSidebar.tsx
27094
- import { memo as memo45, useCallback as useCallback33, useEffect as useEffect49, useRef as useRef33 } from "react";
27435
+ import { memo as memo45, useCallback as useCallback33, useEffect as useEffect50, useRef as useRef33 } from "react";
27095
27436
  import {
27096
27437
  ChevronRight as ChevronRight15,
27097
27438
  ChevronDown as ChevronDown13,
@@ -27160,7 +27501,7 @@ function TreeItem({
27160
27501
  const activeViewerTabPath = getViewerTabPath3(activeViewerTab);
27161
27502
  const isSelected = selectedFile === path || activeViewerTabPath === path;
27162
27503
  const itemRef = useRef33(null);
27163
- useEffect49(() => {
27504
+ useEffect50(() => {
27164
27505
  if (isSelected) {
27165
27506
  itemRef.current?.scrollIntoView({ block: "nearest" });
27166
27507
  }
@@ -27225,7 +27566,7 @@ var FileBrowserSidebar = memo45(function FileBrowserSidebar2() {
27225
27566
  });
27226
27567
  const panelWidth = usePanelWidthStore((s) => s.widths[PANEL_KEY4] ?? DEFAULT_WIDTH4);
27227
27568
  const { data: rootData, isLoading, refetch } = useFileTree(".");
27228
- useEffect49(() => {
27569
+ useEffect50(() => {
27229
27570
  if (isExpanded && activeFileTabPath) {
27230
27571
  revealFile(activeFileTabPath);
27231
27572
  }
@@ -27330,7 +27671,7 @@ var FileBrowserSidebarToggle = memo46(function FileBrowserSidebarToggle2() {
27330
27671
  // src/components/file-browser/FileViewerPanel.tsx
27331
27672
  import {
27332
27673
  memo as memo47,
27333
- useEffect as useEffect51,
27674
+ useEffect as useEffect52,
27334
27675
  useMemo as useMemo29,
27335
27676
  useRef as useRef35
27336
27677
  } from "react";
@@ -27340,7 +27681,7 @@ import remarkGfm3 from "remark-gfm";
27340
27681
 
27341
27682
  // src/components/workspace/ToolPreviewPanel.tsx
27342
27683
  import { CheckCircle2 as CheckCircle22, XCircle as XCircle4 } from "lucide-react";
27343
- import { useEffect as useEffect50, useMemo as useMemo28, useRef as useRef34 } from "react";
27684
+ import { useEffect as useEffect51, useMemo as useMemo28, useRef as useRef34 } from "react";
27344
27685
  import { jsx as jsx109, jsxs as jsxs96 } from "react/jsx-runtime";
27345
27686
  var LARGE_WRITE_PREVIEW_CHARS = 24000;
27346
27687
  var LARGE_WRITE_PREVIEW_LINES = 500;
@@ -27412,6 +27753,35 @@ function getStablePatchLines(patch) {
27412
27753
  lines.pop();
27413
27754
  return lines;
27414
27755
  }
27756
+ function getEnvelopedPatchPath(line) {
27757
+ const directive = line.match(/^\*\*\* (?:Update|Add|Delete) File: (.+)$/);
27758
+ const replaceDirective = line.match(/^\*\*\* Replace in: (.+)$/);
27759
+ const lineDirective = line.match(/^\*\*\* (?:Delete Lines in|Replace Lines in|Insert Before in|Insert After in): (.+)$/);
27760
+ return directive?.[1] ?? replaceDirective?.[1] ?? lineDirective?.[1];
27761
+ }
27762
+ function parsePatchLineNumber(value) {
27763
+ const trimmed = value.trim();
27764
+ if (!/^\d+$/.test(trimmed))
27765
+ return;
27766
+ const line = Number.parseInt(trimmed, 10);
27767
+ return line > 0 ? line : undefined;
27768
+ }
27769
+ function parsePatchLineRange(value) {
27770
+ const match = /^(\d+)(?:\s*-\s*(\d+|end|eof|\$))?$/i.exec(value.trim());
27771
+ if (!match)
27772
+ return;
27773
+ const startLine = parsePatchLineNumber(match[1]);
27774
+ if (!startLine)
27775
+ return;
27776
+ if (!match[2])
27777
+ return { startLine, endLine: startLine };
27778
+ const endLine = /^(end|eof|\$)$/i.test(match[2]) ? "end" : parsePatchLineNumber(match[2]);
27779
+ if (!endLine)
27780
+ return;
27781
+ if (typeof endLine === "number" && endLine < startLine)
27782
+ return;
27783
+ return { startLine, endLine };
27784
+ }
27415
27785
  function getPatchLineHighlights(patch, targetPath, fallbackLines) {
27416
27786
  const highlighted = new Set(fallbackLines ?? []);
27417
27787
  if (!patch)
@@ -27427,9 +27797,9 @@ function getPatchLineHighlights(patch, targetPath, fallbackLines) {
27427
27797
  let inHunk = false;
27428
27798
  let newLine = 0;
27429
27799
  for (const line of getStablePatchLines(patch)) {
27430
- const directive = line.match(/^\*\*\* (?:Update|Add|Delete) File: (.+)$/);
27431
- if (directive?.[1]) {
27432
- activeFile = patchPathMatches(directive[1], targetPath);
27800
+ const directivePath = getEnvelopedPatchPath(line);
27801
+ if (directivePath) {
27802
+ activeFile = patchPathMatches(directivePath, targetPath);
27433
27803
  sawFileDirective = true;
27434
27804
  inHunk = false;
27435
27805
  newLine = 0;
@@ -27523,10 +27893,10 @@ function collectEnvelopedPatchHunks(patch, targetPath) {
27523
27893
  current = [];
27524
27894
  };
27525
27895
  for (const line of getStablePatchLines(patch)) {
27526
- const directive = line.match(/^\*\*\* (?:Update|Add|Delete) File: (.+)$/);
27527
- if (directive?.[1]) {
27896
+ const directivePath = getEnvelopedPatchPath(line);
27897
+ if (directivePath) {
27528
27898
  flush();
27529
- activeFile = patchPathMatches(directive[1], targetPath);
27899
+ activeFile = patchPathMatches(directivePath, targetPath);
27530
27900
  sawFileDirective = true;
27531
27901
  inHunk = activeFile;
27532
27902
  continue;
@@ -27615,9 +27985,152 @@ function buildEnvelopedPatchPreview(content, patch, targetPath) {
27615
27985
  latestLine: changedLines.length > 0 ? Math.max(...changedLines) : undefined
27616
27986
  };
27617
27987
  }
27988
+ function collectLineNumberPatchOperations(patch, targetPath) {
27989
+ const operations = [];
27990
+ let current = null;
27991
+ let activeFile = false;
27992
+ let phase = "directives";
27993
+ const flush = () => {
27994
+ if (current && activeFile)
27995
+ operations.push(current);
27996
+ current = null;
27997
+ phase = "directives";
27998
+ };
27999
+ for (const line of getStablePatchLines(patch)) {
28000
+ const directive = line.match(/^\*\*\* (Delete Lines in|Replace Lines in|Insert Before in|Insert After in): (.+)$/);
28001
+ if (directive?.[1] && directive[2]) {
28002
+ flush();
28003
+ activeFile = patchPathMatches(directive[2], targetPath);
28004
+ if (directive[1] === "Delete Lines in") {
28005
+ current = { kind: "delete", filePath: directive[2], lines: [] };
28006
+ } else if (directive[1] === "Replace Lines in") {
28007
+ current = { kind: "replace", filePath: directive[2], lines: [] };
28008
+ } else {
28009
+ current = {
28010
+ kind: "insert",
28011
+ filePath: directive[2],
28012
+ position: directive[1] === "Insert Before in" ? "before" : "after",
28013
+ lines: []
28014
+ };
28015
+ }
28016
+ continue;
28017
+ }
28018
+ if (!current)
28019
+ continue;
28020
+ if (line.startsWith("*** End Patch")) {
28021
+ flush();
28022
+ continue;
28023
+ }
28024
+ if (line.startsWith("*** Begin Patch"))
28025
+ continue;
28026
+ if (phase === "with") {
28027
+ current.lines.push(line);
28028
+ continue;
28029
+ }
28030
+ if (line.startsWith("*** Lines:") && current.kind !== "insert") {
28031
+ const range = parsePatchLineRange(line.slice("*** Lines:".length));
28032
+ if (range) {
28033
+ current.startLine = range.startLine;
28034
+ current.endLine = range.endLine;
28035
+ }
28036
+ continue;
28037
+ }
28038
+ if (line.startsWith("*** Line:") && current.kind === "insert") {
28039
+ current.line = parsePatchLineNumber(line.slice("*** Line:".length));
28040
+ continue;
28041
+ }
28042
+ if (line.startsWith("*** With:")) {
28043
+ phase = "with";
28044
+ }
28045
+ }
28046
+ flush();
28047
+ return operations;
28048
+ }
28049
+ function getCurrentRecordPosition(records, currentIndex) {
28050
+ let seen = 0;
28051
+ for (let index = 0;index < records.length; index += 1) {
28052
+ if (records[index].removed)
28053
+ continue;
28054
+ if (seen === currentIndex)
28055
+ return index;
28056
+ seen += 1;
28057
+ }
28058
+ return records.length;
28059
+ }
28060
+ function buildLineNumberPatchPreview(content, patch, targetPath) {
28061
+ const operations = collectLineNumberPatchOperations(patch, targetPath);
28062
+ if (operations.length === 0)
28063
+ return null;
28064
+ const contentLines = content.split(`
28065
+ `);
28066
+ if (contentLines.at(-1) === "")
28067
+ contentLines.pop();
28068
+ const records = contentLines.map((line) => ({
28069
+ text: line
28070
+ }));
28071
+ for (const operation of operations) {
28072
+ const currentLines = records.filter((record) => !record.removed);
28073
+ if (operation.kind === "insert") {
28074
+ if (!operation.line)
28075
+ continue;
28076
+ const insertIndex = operation.position === "before" ? operation.line - 1 : operation.line;
28077
+ if (insertIndex < 0 || insertIndex > currentLines.length)
28078
+ continue;
28079
+ const recordIndex2 = getCurrentRecordPosition(records, insertIndex);
28080
+ records.splice(recordIndex2, 0, ...operation.lines.map((line) => ({
28081
+ text: line,
28082
+ tone: "add"
28083
+ })));
28084
+ continue;
28085
+ }
28086
+ if (!operation.startLine || !operation.endLine)
28087
+ continue;
28088
+ const endLine = operation.endLine === "end" ? currentLines.length : operation.endLine;
28089
+ if (operation.startLine > currentLines.length || endLine > currentLines.length)
28090
+ continue;
28091
+ const startIndex = operation.startLine - 1;
28092
+ const endIndex = endLine - 1;
28093
+ const recordIndex = getCurrentRecordPosition(records, startIndex);
28094
+ for (let index = startIndex;index <= endIndex; index += 1) {
28095
+ const position = getCurrentRecordPosition(records, startIndex);
28096
+ if (!records[position])
28097
+ continue;
28098
+ records[position].tone = "remove";
28099
+ records[position].removed = true;
28100
+ }
28101
+ if (operation.kind === "replace") {
28102
+ records.splice(recordIndex, 0, ...operation.lines.map((line) => ({
28103
+ text: line,
28104
+ tone: "add"
28105
+ })));
28106
+ }
28107
+ }
28108
+ const lineTones = new Map;
28109
+ const renderedLines = records.map((record, index) => {
28110
+ if (record.tone)
28111
+ lineTones.set(index + 1, record.tone);
28112
+ return record.text;
28113
+ });
28114
+ if (lineTones.size === 0)
28115
+ return null;
28116
+ const resultLines = records.filter((record) => !record.removed).map((record) => record.text);
28117
+ const changedLines = [...lineTones.keys()];
28118
+ return {
28119
+ content: renderedLines.join(`
28120
+ `),
28121
+ resultContent: resultLines.join(`
28122
+ `),
28123
+ lineTones,
28124
+ firstLine: Math.min(...changedLines),
28125
+ latestLine: Math.max(...changedLines)
28126
+ };
28127
+ }
27618
28128
  function buildLivePatchPreview(content, patch, targetPath) {
27619
28129
  if (!patch)
27620
28130
  return null;
28131
+ const lineNumberPreview = buildLineNumberPatchPreview(content, patch, targetPath);
28132
+ if (lineNumberPreview)
28133
+ return lineNumberPreview;
27621
28134
  const insertions = new Map;
27622
28135
  const removals = new Set;
27623
28136
  let activeFile = false;
@@ -27747,7 +28260,8 @@ function PlainSourceViewer({
27747
28260
  }),
27748
28261
  /* @__PURE__ */ jsx109(CodeMirrorViewer, {
27749
28262
  content,
27750
- path
28263
+ path,
28264
+ disableMarkdownSyntax: true
27751
28265
  })
27752
28266
  ]
27753
28267
  });
@@ -27768,7 +28282,8 @@ function SourceViewer({
27768
28282
  highlightTone,
27769
28283
  lineTones,
27770
28284
  scrollToLine,
27771
- scrollToEndSignal
28285
+ scrollToEndSignal,
28286
+ disableMarkdownSyntax: true
27772
28287
  });
27773
28288
  }
27774
28289
  function ToolPreviewPanel({ tab }) {
@@ -27822,7 +28337,7 @@ function ToolPreviewPanel({ tab }) {
27822
28337
  };
27823
28338
  }
27824
28339
  const stablePatchPreview = lastPatchPreviewRef.current?.key === patchPreviewKey ? livePatchPreview ?? lastPatchPreviewRef.current.preview : livePatchPreview ?? persistedPatchPreview;
27825
- useEffect50(() => {
28340
+ useEffect51(() => {
27826
28341
  if (tab.toolName !== "apply_patch" || !livePatchPreview)
27827
28342
  return;
27828
28343
  const baseContent = tab.baseContent ?? appliedFile?.content;
@@ -27847,11 +28362,11 @@ function ToolPreviewPanel({ tab }) {
27847
28362
  error: tab.error
27848
28363
  });
27849
28364
  }, [tab, appliedFile?.content, livePatchPreview]);
27850
- useEffect50(() => {
28365
+ useEffect51(() => {
27851
28366
  if (shouldLoadAppliedFile)
27852
28367
  refetchAppliedFile();
27853
28368
  }, [shouldLoadAppliedFile, refetchAppliedFile]);
27854
- useEffect50(() => {
28369
+ useEffect51(() => {
27855
28370
  if (scrollSignal.length === 0)
27856
28371
  return;
27857
28372
  const frame = window.requestAnimationFrame(() => {
@@ -27885,26 +28400,6 @@ function ToolPreviewPanel({ tab }) {
27885
28400
  return /* @__PURE__ */ jsxs96("div", {
27886
28401
  className: "h-full w-full bg-transparent flex flex-col",
27887
28402
  children: [
27888
- /* @__PURE__ */ jsxs96("div", {
27889
- className: "shrink-0 border-b border-sidebar-border bg-sidebar-accent/30 px-3 py-1.5 text-[12px] text-muted-foreground flex items-center gap-2",
27890
- children: [
27891
- /* @__PURE__ */ jsx109(StatusIcon, {
27892
- status: tab.status
27893
- }),
27894
- /* @__PURE__ */ jsx109("span", {
27895
- children: statusLabel
27896
- }),
27897
- /* @__PURE__ */ jsx109("span", {
27898
- className: "text-muted-foreground/60",
27899
- children: "·"
27900
- }),
27901
- /* @__PURE__ */ jsx109("span", {
27902
- className: "font-mono truncate",
27903
- title: tab.path,
27904
- children: tab.path
27905
- })
27906
- ]
27907
- }),
27908
28403
  tab.error && /* @__PURE__ */ jsx109("div", {
27909
28404
  className: "shrink-0 border-b border-red-500/20 bg-red-500/10 px-3 py-2 text-[12px] text-red-700 dark:text-red-300",
27910
28405
  children: tab.error
@@ -27952,6 +28447,26 @@ function ToolPreviewPanel({ tab }) {
27952
28447
  className: "h-full flex items-center justify-center text-sm text-muted-foreground",
27953
28448
  children: "Waiting for write content..."
27954
28449
  })
28450
+ }),
28451
+ /* @__PURE__ */ jsxs96("div", {
28452
+ className: "shrink-0 border-t border-sidebar-border bg-sidebar-accent/30 px-3 py-1.5 text-[12px] text-muted-foreground flex items-center gap-2",
28453
+ children: [
28454
+ /* @__PURE__ */ jsx109(StatusIcon, {
28455
+ status: tab.status
28456
+ }),
28457
+ /* @__PURE__ */ jsx109("span", {
28458
+ children: statusLabel
28459
+ }),
28460
+ /* @__PURE__ */ jsx109("span", {
28461
+ className: "text-muted-foreground/60",
28462
+ children: "·"
28463
+ }),
28464
+ /* @__PURE__ */ jsx109("span", {
28465
+ className: "font-mono truncate",
28466
+ title: tab.path,
28467
+ children: tab.path
28468
+ })
28469
+ ]
27955
28470
  })
27956
28471
  ]
27957
28472
  });
@@ -28061,6 +28576,7 @@ var FileViewerPanel = memo47(function FileViewerPanel2({
28061
28576
  open,
28062
28577
  file,
28063
28578
  highlight,
28579
+ annotations,
28064
28580
  patchPreview,
28065
28581
  writePreview,
28066
28582
  onClose
@@ -28073,7 +28589,7 @@ var FileViewerPanel = memo47(function FileViewerPanel2({
28073
28589
  const selectedFile = file ?? storeSelectedFile;
28074
28590
  const closeViewer = onClose ?? storeCloseViewer;
28075
28591
  const { data, isLoading } = useFileContent(selectedFile);
28076
- useEffect51(() => {
28592
+ useEffect52(() => {
28077
28593
  const handleEscape = (e) => {
28078
28594
  const target = e.target;
28079
28595
  const isInInput = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
@@ -28100,7 +28616,7 @@ var FileViewerPanel = memo47(function FileViewerPanel2({
28100
28616
  const patchBaseContent = patchPreview ? patchPreview.baseContent ?? data?.content ?? "" : undefined;
28101
28617
  const livePatchPreview = useMemo29(() => selectedFile && patchPreview?.patch && patchBaseContent !== undefined ? buildLivePatchPreview(patchBaseContent, patchPreview.patch, selectedFile) : null, [selectedFile, patchPreview?.patch, patchBaseContent]);
28102
28618
  const activePatchPreview = patchPreview?.status === "success" ? persistedPatchPreview ?? livePatchPreview : livePatchPreview ?? persistedPatchPreview;
28103
- useEffect51(() => {
28619
+ useEffect52(() => {
28104
28620
  if (!selectedFile || !patchPreview || !livePatchPreview)
28105
28621
  return;
28106
28622
  const baseContent = patchPreview.baseContent ?? data?.content;
@@ -28125,7 +28641,7 @@ var FileViewerPanel = memo47(function FileViewerPanel2({
28125
28641
  error: patchPreview.error
28126
28642
  });
28127
28643
  }, [selectedFile, data?.content, patchPreview, livePatchPreview]);
28128
- useEffect51(() => {
28644
+ useEffect52(() => {
28129
28645
  if (!data || !effectiveHighlight?.startLine)
28130
28646
  return;
28131
28647
  const frame = window.requestAnimationFrame(() => {
@@ -28139,6 +28655,28 @@ var FileViewerPanel = memo47(function FileViewerPanel2({
28139
28655
  const highlightStart = effectiveHighlight?.startLine;
28140
28656
  const highlightEnd = effectiveHighlight?.endLine ?? highlightStart;
28141
28657
  const patchChangedLines = patchPreview?.changedLines;
28658
+ const persistentLineTones = useMemo29(() => {
28659
+ if (!annotations?.length)
28660
+ return;
28661
+ const tones = new Map;
28662
+ for (const annotation of annotations) {
28663
+ for (const [line, tone] of annotation.lineTones) {
28664
+ if (line > 0)
28665
+ tones.set(line, tone);
28666
+ }
28667
+ }
28668
+ return tones.size > 0 ? tones : undefined;
28669
+ }, [annotations]);
28670
+ const writePreviewLineTones = useMemo29(() => {
28671
+ if (writePreview?.content === undefined)
28672
+ return;
28673
+ const tones = new Map;
28674
+ const lineCount = writePreview.content.length === 0 ? 1 : writePreview.content.split(`
28675
+ `).length;
28676
+ for (let line = 1;line <= lineCount; line += 1)
28677
+ tones.set(line, "add");
28678
+ return tones;
28679
+ }, [writePreview?.content]);
28142
28680
  const highlightedLines = useMemo29(() => {
28143
28681
  if (highlightStart && highlightEnd) {
28144
28682
  return new Set(Array.from({ length: highlightEnd - highlightStart + 1 }, (_, index) => highlightStart + index));
@@ -28152,18 +28690,21 @@ var FileViewerPanel = memo47(function FileViewerPanel2({
28152
28690
  const scrollToHighlightLine = highlightStart ?? fallbackPatchHighlightStart;
28153
28691
  const activePatchLineTones = useMemo29(() => {
28154
28692
  if (!activePatchPreview)
28155
- return;
28156
- const tones = new Map(activePatchPreview.lineTones);
28693
+ return persistentLineTones;
28694
+ const tones = new Map(persistentLineTones);
28695
+ for (const [line, tone] of activePatchPreview.lineTones) {
28696
+ tones.set(line, tone);
28697
+ }
28157
28698
  for (const line of patchChangedLines ?? []) {
28158
28699
  if (!tones.has(line))
28159
28700
  tones.set(line, "add");
28160
28701
  }
28161
28702
  return tones;
28162
- }, [activePatchPreview, patchChangedLines]);
28703
+ }, [activePatchPreview, patchChangedLines, persistentLineTones]);
28163
28704
  if (!isViewerOpen || !selectedFile)
28164
28705
  return null;
28165
28706
  const language = inferLanguage(selectedFile);
28166
- const renderMarkdown = isMarkdownFile(selectedFile) && !effectiveHighlight && !activePatchPreview && !writePreview?.content;
28707
+ const renderMarkdown = isMarkdownFile(selectedFile) && !effectiveHighlight && !persistentLineTones && !activePatchPreview && !writePreview?.content;
28167
28708
  return /* @__PURE__ */ jsxs97("div", {
28168
28709
  className: mode === "pane" ? "h-full w-full bg-transparent flex flex-col" : "absolute inset-0 bg-background z-50 flex flex-col animate-in slide-in-from-left duration-300",
28169
28710
  children: [
@@ -28208,12 +28749,15 @@ var FileViewerPanel = memo47(function FileViewerPanel2({
28208
28749
  className: "flex-1 overflow-auto",
28209
28750
  children: writePreview?.content !== undefined ? /* @__PURE__ */ jsx110(CodeMirrorViewer, {
28210
28751
  content: writePreview.content,
28211
- path: selectedFile
28752
+ path: selectedFile,
28753
+ lineTones: writePreviewLineTones,
28754
+ disableMarkdownSyntax: true
28212
28755
  }) : activePatchPreview ? /* @__PURE__ */ jsx110(CodeMirrorViewer, {
28213
28756
  content: activePatchPreview.content,
28214
28757
  path: selectedFile,
28215
28758
  lineTones: activePatchLineTones,
28216
- scrollToLine: activePatchPreview.latestLine
28759
+ scrollToLine: activePatchPreview.latestLine,
28760
+ disableMarkdownSyntax: true
28217
28761
  }) : isLoading ? /* @__PURE__ */ jsx110("div", {
28218
28762
  className: "h-full flex items-center justify-center text-muted-foreground",
28219
28763
  children: "Loading file..."
@@ -28259,7 +28803,9 @@ var FileViewerPanel = memo47(function FileViewerPanel2({
28259
28803
  content: data.content,
28260
28804
  path: selectedFile,
28261
28805
  highlightedLines,
28262
- scrollToLine: scrollToHighlightLine
28806
+ lineTones: persistentLineTones,
28807
+ scrollToLine: scrollToHighlightLine,
28808
+ disableMarkdownSyntax: isMarkdownFile(selectedFile)
28263
28809
  }) : /* @__PURE__ */ jsx110("div", {
28264
28810
  className: "h-full flex items-center justify-center text-muted-foreground",
28265
28811
  children: "Unable to load file"
@@ -28294,7 +28840,7 @@ var FileViewerPanel = memo47(function FileViewerPanel2({
28294
28840
  });
28295
28841
  });
28296
28842
  // src/components/file-browser/QuickFilePicker.tsx
28297
- import { memo as memo48, useState as useState48, useEffect as useEffect52, useRef as useRef36, useCallback as useCallback34, useMemo as useMemo30 } from "react";
28843
+ import { memo as memo48, useState as useState48, useEffect as useEffect53, useRef as useRef36, useCallback as useCallback34, useMemo as useMemo30 } from "react";
28298
28844
  import { FileCode as FileCode4, Search as Search9 } from "lucide-react";
28299
28845
 
28300
28846
  // src/stores/filePickerStore.ts
@@ -28349,14 +28895,14 @@ var QuickFilePicker = memo48(function QuickFilePicker2() {
28349
28895
  const results = filesData.files.map((file) => ({ file, ...fuzzyMatch(query, file) })).filter((r) => r.match).sort((a, b) => b.score - a.score).slice(0, 50);
28350
28896
  return results.map((r) => r.file);
28351
28897
  }, [filesData?.files, query]);
28352
- useEffect52(() => {
28898
+ useEffect53(() => {
28353
28899
  if (isOpen) {
28354
28900
  setQuery("");
28355
28901
  setSelectedIndex(0);
28356
28902
  setTimeout(() => inputRef.current?.focus(), 0);
28357
28903
  }
28358
28904
  }, [isOpen]);
28359
- useEffect52(() => {
28905
+ useEffect53(() => {
28360
28906
  const item = listRef.current?.children[selectedIndex];
28361
28907
  item?.scrollIntoView({ block: "nearest" });
28362
28908
  }, [selectedIndex]);
@@ -28514,20 +29060,170 @@ function HighlightedPath({ path, query }) {
28514
29060
  });
28515
29061
  }
28516
29062
  // src/components/workspace/ViewerTabs.tsx
28517
- import { memo as memo49, useEffect as useEffect53 } from "react";
28518
- import {
28519
- Braces as Braces2,
28520
- File as File3,
28521
- FileCode as FileCode5,
28522
- FileJson as FileJson2,
28523
- FileText as FileText8,
28524
- FileType as FileType2,
28525
- GitCommit as GitCommit5,
28526
- Image as Image2,
28527
- Settings as Settings4,
28528
- X as X21
28529
- } from "lucide-react";
29063
+ import { Icon, addCollection } from "@iconify/react";
29064
+ import { icons as materialIconTheme } from "@iconify-json/material-icon-theme";
29065
+ import { memo as memo49, useEffect as useEffect54 } from "react";
29066
+ import { GitCommit as GitCommit5, X as X21 } from "lucide-react";
28530
29067
  import { jsx as jsx112, jsxs as jsxs99 } from "react/jsx-runtime";
29068
+ addCollection(materialIconTheme);
29069
+ var ICON_CLASS = "h-3.5 w-3.5 shrink-0";
29070
+ var FILENAME_ICON_MAP = {
29071
+ ".dockerignore": "docker",
29072
+ ".editorconfig": "editorconfig",
29073
+ ".env": "settings",
29074
+ ".env.example": "settings",
29075
+ ".env.local": "settings",
29076
+ ".eslintignore": "eslint",
29077
+ ".eslintrc": "eslint",
29078
+ ".gitattributes": "git",
29079
+ ".gitignore": "git",
29080
+ ".prettierrc": "prettier",
29081
+ "astro.config.mjs": "astro-config",
29082
+ "astro.config.ts": "astro-config",
29083
+ "biome.json": "biome",
29084
+ "bun.lock": "bun",
29085
+ "bun.lockb": "bun",
29086
+ "bunfig.toml": "bun",
29087
+ "cargo.lock": "lock",
29088
+ "cargo.toml": "rust",
29089
+ "compose.yaml": "docker",
29090
+ "compose.yml": "docker",
29091
+ "docker-compose.yaml": "docker",
29092
+ "docker-compose.yml": "docker",
29093
+ dockerfile: "docker",
29094
+ "eslint.config.js": "eslint",
29095
+ "eslint.config.mjs": "eslint",
29096
+ "eslint.config.ts": "eslint",
29097
+ gemfile: "gemfile",
29098
+ "go.mod": "go-mod",
29099
+ "go.sum": "go-mod",
29100
+ "jsconfig.json": "jsconfig",
29101
+ license: "license",
29102
+ makefile: "makefile",
29103
+ "next.config.js": "next",
29104
+ "next.config.mjs": "next",
29105
+ "next.config.ts": "next",
29106
+ "package-lock.json": "npm",
29107
+ "package.json": "npm",
29108
+ "pnpm-lock.yaml": "pnpm",
29109
+ "postcss.config.js": "postcss",
29110
+ "postcss.config.mjs": "postcss",
29111
+ "postcss.config.ts": "postcss",
29112
+ "prettier.config.js": "prettier",
29113
+ "prettier.config.mjs": "prettier",
29114
+ "prettier.config.ts": "prettier",
29115
+ "readme.md": "readme",
29116
+ "readme.mdx": "readme",
29117
+ "rollup.config.js": "rollup",
29118
+ "rollup.config.mjs": "rollup",
29119
+ "rollup.config.ts": "rollup",
29120
+ "svelte.config.js": "svelte",
29121
+ "svelte.config.ts": "svelte",
29122
+ "tailwind.config.js": "tailwindcss",
29123
+ "tailwind.config.ts": "tailwindcss",
29124
+ "tauri.conf.json": "tauri",
29125
+ "tsconfig.json": "tsconfig",
29126
+ "vite.config.js": "vite",
29127
+ "vite.config.mjs": "vite",
29128
+ "vite.config.ts": "vite",
29129
+ "vitest.config.js": "vitest",
29130
+ "vitest.config.mjs": "vitest",
29131
+ "vitest.config.ts": "vitest",
29132
+ "vue.config.js": "vue-config",
29133
+ "vue.config.ts": "vue-config",
29134
+ "yarn.lock": "lock"
29135
+ };
29136
+ var EXTENSION_ICON_MAP = {
29137
+ ai: "image",
29138
+ astro: "astro",
29139
+ avif: "image",
29140
+ bash: "console",
29141
+ bmp: "image",
29142
+ c: "c",
29143
+ cc: "cpp",
29144
+ clj: "clojure",
29145
+ cljc: "clojure",
29146
+ cljs: "clojure",
29147
+ cpp: "cpp",
29148
+ cs: "csharp",
29149
+ css: "css",
29150
+ cxx: "cpp",
29151
+ dart: "dart",
29152
+ dockerfile: "docker",
29153
+ ex: "elixir",
29154
+ exs: "elixir",
29155
+ erl: "erlang",
29156
+ fish: "console",
29157
+ gif: "image",
29158
+ go: "go",
29159
+ graphql: "graphql",
29160
+ gql: "graphql",
29161
+ groovy: "groovy",
29162
+ h: "c",
29163
+ hpp: "cpp",
29164
+ hrl: "erlang",
29165
+ hs: "haskell",
29166
+ htm: "html",
29167
+ html: "html",
29168
+ hxx: "cpp",
29169
+ ico: "favicon",
29170
+ java: "java",
29171
+ jpeg: "image",
29172
+ jpg: "image",
29173
+ js: "javascript",
29174
+ json: "json",
29175
+ jsonc: "json",
29176
+ jsx: "react",
29177
+ jl: "julia",
29178
+ kt: "kotlin",
29179
+ kts: "kotlin",
29180
+ less: "less",
29181
+ log: "log",
29182
+ lua: "lua",
29183
+ luau: "luau",
29184
+ mjs: "javascript",
29185
+ ml: "ocaml",
29186
+ mli: "ocaml",
29187
+ mov: "video",
29188
+ mp3: "audio",
29189
+ mp4: "video",
29190
+ md: "markdown",
29191
+ mdx: "markdown",
29192
+ nim: "nim",
29193
+ pdf: "pdf",
29194
+ php: "php-elephant",
29195
+ png: "image",
29196
+ prisma: "prisma",
29197
+ proto: "proto",
29198
+ ps1: "powershell",
29199
+ py: "python",
29200
+ pyw: "python",
29201
+ r: "r",
29202
+ rb: "ruby",
29203
+ rs: "rust",
29204
+ sass: "sass",
29205
+ scala: "scala",
29206
+ scss: "sass",
29207
+ sh: "console",
29208
+ sql: "database",
29209
+ svg: "svg",
29210
+ svelte: "svelte",
29211
+ swift: "swift",
29212
+ toml: "toml",
29213
+ ts: "typescript",
29214
+ tsx: "react-ts",
29215
+ txt: "document",
29216
+ vue: "vue",
29217
+ wav: "audio",
29218
+ webm: "video",
29219
+ webp: "image",
29220
+ xml: "xml",
29221
+ yaml: "yaml",
29222
+ yml: "yaml",
29223
+ zig: "zig",
29224
+ zip: "zip",
29225
+ zsh: "console"
29226
+ };
28531
29227
  function tabKindLabel(tab) {
28532
29228
  switch (tab.type) {
28533
29229
  case "git-diff":
@@ -28557,86 +29253,34 @@ function getFileExtension2(path) {
28557
29253
  const extension = path.split(".").pop()?.toLowerCase() ?? "";
28558
29254
  return extension && extension !== path.toLowerCase() ? extension : "";
28559
29255
  }
28560
- function renderLanguageIcon(extension) {
28561
- switch (extension) {
28562
- case "ts":
28563
- case "tsx":
28564
- return /* @__PURE__ */ jsx112("span", {
28565
- className: "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center bg-[#3178c6] text-[7px] font-bold leading-none text-white",
28566
- children: "TS"
28567
- });
28568
- case "js":
28569
- case "jsx":
28570
- case "mjs":
28571
- case "cjs":
28572
- return /* @__PURE__ */ jsx112("span", {
28573
- className: "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center bg-[#f7df1e] text-[7px] font-bold leading-none text-black",
28574
- children: "JS"
28575
- });
28576
- case "py":
28577
- return /* @__PURE__ */ jsx112("span", {
28578
- className: "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-[3px] bg-gradient-to-br from-[#3776ab] to-[#ffd43b] text-[7px] font-bold leading-none text-white",
28579
- children: "Py"
28580
- });
28581
- case "go":
28582
- return /* @__PURE__ */ jsx112("span", {
28583
- className: "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full bg-[#00add8] text-[7px] font-bold leading-none text-white",
28584
- children: "Go"
28585
- });
28586
- case "rs":
28587
- return /* @__PURE__ */ jsx112("span", {
28588
- className: "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full bg-[#ce422b] text-[8px] font-bold leading-none text-white",
28589
- children: "R"
28590
- });
28591
- case "json":
28592
- return /* @__PURE__ */ jsx112(FileJson2, {
28593
- className: "h-3.5 w-3.5 shrink-0 text-yellow-500"
28594
- });
28595
- case "md":
28596
- case "mdx":
28597
- return /* @__PURE__ */ jsx112(FileText8, {
28598
- className: "h-3.5 w-3.5 shrink-0 text-sky-500"
28599
- });
28600
- case "env":
28601
- case "toml":
28602
- case "yaml":
28603
- case "yml":
28604
- return /* @__PURE__ */ jsx112(Settings4, {
28605
- className: "h-3.5 w-3.5 shrink-0 text-violet-500"
28606
- });
28607
- case "css":
28608
- case "scss":
28609
- case "sass":
28610
- case "less":
28611
- return /* @__PURE__ */ jsx112(Braces2, {
28612
- className: "h-3.5 w-3.5 shrink-0 text-blue-500"
28613
- });
28614
- case "html":
28615
- case "xml":
28616
- return /* @__PURE__ */ jsx112(FileType2, {
28617
- className: "h-3.5 w-3.5 shrink-0 text-orange-500"
28618
- });
28619
- case "png":
28620
- case "jpg":
28621
- case "jpeg":
28622
- case "gif":
28623
- case "svg":
28624
- case "webp":
28625
- return /* @__PURE__ */ jsx112(Image2, {
28626
- className: "h-3.5 w-3.5 shrink-0 text-pink-500"
28627
- });
28628
- case "txt":
28629
- case "log":
28630
- return /* @__PURE__ */ jsx112(FileText8, {
28631
- className: "h-3.5 w-3.5 shrink-0 text-muted-foreground"
28632
- });
28633
- default:
28634
- return extension ? /* @__PURE__ */ jsx112(FileCode5, {
28635
- className: "h-3.5 w-3.5 shrink-0 text-muted-foreground/80"
28636
- }) : /* @__PURE__ */ jsx112(File3, {
28637
- className: "h-3.5 w-3.5 shrink-0 text-muted-foreground/80"
28638
- });
29256
+ function getFileName3(path) {
29257
+ return path.split(/[\\/]/).pop()?.toLowerCase() ?? path.toLowerCase();
29258
+ }
29259
+ function getIconNameForPath(path) {
29260
+ const fileName = getFileName3(path);
29261
+ const fileIcon = FILENAME_ICON_MAP[fileName];
29262
+ if (fileIcon)
29263
+ return fileIcon;
29264
+ if (fileName.endsWith(".d.ts"))
29265
+ return "typescript-def";
29266
+ if (fileName.endsWith(".test.ts") || fileName.endsWith(".spec.ts")) {
29267
+ return "test-ts";
28639
29268
  }
29269
+ if (fileName.endsWith(".test.js") || fileName.endsWith(".spec.js")) {
29270
+ return "test-js";
29271
+ }
29272
+ if (fileName.endsWith(".test.jsx") || fileName.endsWith(".spec.jsx") || fileName.endsWith(".test.tsx") || fileName.endsWith(".spec.tsx")) {
29273
+ return "test-jsx";
29274
+ }
29275
+ const extension = getFileExtension2(fileName);
29276
+ return EXTENSION_ICON_MAP[extension] ?? "document";
29277
+ }
29278
+ function renderFileIcon(path) {
29279
+ return /* @__PURE__ */ jsx112(Icon, {
29280
+ "aria-hidden": "true",
29281
+ className: ICON_CLASS,
29282
+ icon: `material-icon-theme:${getIconNameForPath(path)}`
29283
+ });
28640
29284
  }
28641
29285
  function renderTabIcon(tab) {
28642
29286
  if (tab.type === "git-diff") {
@@ -28654,10 +29298,10 @@ function renderTabIcon(tab) {
28654
29298
  className: `h-3.5 w-3.5 shrink-0 ${tab.status === "error" ? "text-red-500" : tab.status === "success" ? "text-emerald-500" : "text-blue-500"}`
28655
29299
  });
28656
29300
  }
28657
- const extension = getFileExtension2(getTabPath(tab));
29301
+ const path = getTabPath(tab);
28658
29302
  return /* @__PURE__ */ jsx112("span", {
28659
29303
  className: "shrink-0 inline-flex items-center text-muted-foreground/80",
28660
- children: renderLanguageIcon(extension)
29304
+ children: renderFileIcon(path)
28661
29305
  });
28662
29306
  }
28663
29307
  function renderTabContent(tab, closeTab, updateSessionFileOperationIndex) {
@@ -28686,6 +29330,7 @@ function renderTabContent(tab, closeTab, updateSessionFileOperationIndex) {
28686
29330
  open: true,
28687
29331
  file: tab.path,
28688
29332
  highlight: tab.highlight,
29333
+ annotations: tab.annotations,
28689
29334
  patchPreview: tab.patchPreview,
28690
29335
  writePreview: tab.writePreview,
28691
29336
  onClose: () => closeTab(tab.id)
@@ -28710,7 +29355,7 @@ var ViewerTabs = memo49(function ViewerTabs2() {
28710
29355
  const setActiveTab = useViewerTabsStore((state) => state.setActiveTab);
28711
29356
  const closeTab = useViewerTabsStore((state) => state.closeTab);
28712
29357
  const updateSessionFileOperationIndex = useViewerTabsStore((state) => state.updateSessionFileOperationIndex);
28713
- useEffect53(() => {
29358
+ useEffect54(() => {
28714
29359
  const handleKeyDown = (event) => {
28715
29360
  const target = event.target;
28716
29361
  const isInInput = target?.tagName === "INPUT" || target?.tagName === "TEXTAREA" || target?.isContentEditable;
@@ -28734,12 +29379,12 @@ var ViewerTabs = memo49(function ViewerTabs2() {
28734
29379
  className: "h-full w-full min-w-0 bg-sidebar flex flex-col",
28735
29380
  children: [
28736
29381
  /* @__PURE__ */ jsxs99("div", {
28737
- className: "h-12 shrink-0 bg-background flex overflow-x-auto overflow-y-hidden",
29382
+ className: "h-12 shrink-0 bg-background flex overflow-x-auto overflow-y-hidden overscroll-x-contain scrollbar-hide",
28738
29383
  children: [
28739
29384
  tabs.map((tab) => {
28740
29385
  const isActive = tab.id === activeTab.id;
28741
29386
  return /* @__PURE__ */ jsxs99("div", {
28742
- className: `group h-12 max-w-56 min-w-0 px-3 border-r border-sidebar-border flex items-center gap-2 text-left transition-colors ${isActive ? "bg-sidebar text-sidebar-foreground" : "border-b bg-background text-muted-foreground/70 hover:text-foreground hover:bg-sidebar-accent/40"}`,
29387
+ className: `group h-12 w-44 max-w-56 shrink-0 px-3 border-r border-sidebar-border flex items-center gap-2 text-left transition-colors ${isActive ? "bg-sidebar text-sidebar-foreground" : "border-b bg-background text-muted-foreground/70 hover:text-foreground hover:bg-sidebar-accent/40"}`,
28743
29388
  title: `${tab.title}
28744
29389
  ${tabKindLabel(tab)}`,
28745
29390
  children: [
@@ -28783,10 +29428,10 @@ ${tabKindLabel(tab)}`,
28783
29428
  });
28784
29429
  });
28785
29430
  // src/components/onboarding/OnboardingModal.tsx
28786
- import { memo as memo52, useEffect as useEffect56 } from "react";
29431
+ import { memo as memo52, useEffect as useEffect57 } from "react";
28787
29432
 
28788
29433
  // src/components/onboarding/steps/ProviderSetupStep.tsx
28789
- import { memo as memo50, useEffect as useEffect54, useState as useState49, useRef as useRef37 } from "react";
29434
+ import { memo as memo50, useEffect as useEffect55, useState as useState49, useRef as useRef37 } from "react";
28790
29435
  import {
28791
29436
  Copy as Copy5,
28792
29437
  Check as Check13,
@@ -28893,34 +29538,34 @@ var ProviderSetupStep = memo50(function ProviderSetupStep2({
28893
29538
  const { fetchBalance } = useOttoRouterBalance("ottorouter");
28894
29539
  const effectivePayg = payg?.effectiveSpendableUsd ?? balance ?? 0;
28895
29540
  const setuStatusLabel = subscription?.active ? `GO ${(subscription.creditsRemaining ?? 0).toFixed(1)} credits` : `$${effectivePayg.toFixed(2)}`;
28896
- useEffect54(() => {
29541
+ useEffect55(() => {
28897
29542
  if (prevTopupModalOpen.current && !isTopupModalOpen) {
28898
29543
  fetchBalance();
28899
29544
  }
28900
29545
  prevTopupModalOpen.current = isTopupModalOpen;
28901
29546
  }, [isTopupModalOpen, fetchBalance]);
28902
- useEffect54(() => {
29547
+ useEffect55(() => {
28903
29548
  if (!authStatus.ottorouter.configured && !isSettingUp) {
28904
29549
  setIsSettingUp(true);
28905
29550
  onSetupWallet().finally(() => setIsSettingUp(false));
28906
29551
  }
28907
29552
  }, [authStatus.ottorouter.configured, onSetupWallet, isSettingUp]);
28908
- useEffect54(() => {
29553
+ useEffect55(() => {
28909
29554
  if (addingProvider && apiKeyInputRef.current) {
28910
29555
  apiKeyInputRef.current.focus();
28911
29556
  }
28912
29557
  }, [addingProvider]);
28913
- useEffect54(() => {
29558
+ useEffect55(() => {
28914
29559
  if (oauthSession && oauthCodeInputRef.current) {
28915
29560
  oauthCodeInputRef.current.focus();
28916
29561
  }
28917
29562
  }, [oauthSession]);
28918
- useEffect54(() => {
29563
+ useEffect55(() => {
28919
29564
  if (isImportModalOpen && importPrivateKeyRef.current) {
28920
29565
  importPrivateKeyRef.current.focus();
28921
29566
  }
28922
29567
  }, [isImportModalOpen]);
28923
- useEffect54(() => {
29568
+ useEffect55(() => {
28924
29569
  if (!copilotPolling || !copilotDevice || !copilotPollFnRef.current)
28925
29570
  return;
28926
29571
  copilotCancelledRef.current = false;
@@ -29218,7 +29863,7 @@ var ProviderSetupStep = memo50(function ProviderSetupStep2({
29218
29863
  setImportWalletError(null);
29219
29864
  setImportPrivateKey("");
29220
29865
  };
29221
- useEffect54(() => {
29866
+ useEffect55(() => {
29222
29867
  const handleNativeBack = (event) => {
29223
29868
  const customEvent = event;
29224
29869
  if (!customEvent.detail || customEvent.detail.handled)
@@ -30408,7 +31053,7 @@ var ProviderSetupStep = memo50(function ProviderSetupStep2({
30408
31053
  });
30409
31054
 
30410
31055
  // src/components/onboarding/steps/DefaultsStep.tsx
30411
- import { memo as memo51, useState as useState50, useEffect as useEffect55, useId as useId3, useRef as useRef38 } from "react";
31056
+ import { memo as memo51, useState as useState50, useEffect as useEffect56, useId as useId3, useRef as useRef38 } from "react";
30412
31057
  import { ArrowLeft, Sparkles as Sparkles9, ChevronDown as ChevronDown14 } from "lucide-react";
30413
31058
  import { jsx as jsx114, jsxs as jsxs101, Fragment as Fragment43 } from "react/jsx-runtime";
30414
31059
  var DefaultsStep = memo51(function DefaultsStep2({
@@ -30431,7 +31076,7 @@ var DefaultsStep = memo51(function DefaultsStep2({
30431
31076
  const modelId = useId3();
30432
31077
  const agentId = useId3();
30433
31078
  const approvalId = useId3();
30434
- useEffect55(() => {
31079
+ useEffect56(() => {
30435
31080
  const loadConfig = async () => {
30436
31081
  try {
30437
31082
  const [configData, modelsData] = await Promise.all([
@@ -30462,7 +31107,7 @@ var DefaultsStep = memo51(function DefaultsStep2({
30462
31107
  };
30463
31108
  loadConfig();
30464
31109
  }, []);
30465
- useEffect55(() => {
31110
+ useEffect56(() => {
30466
31111
  if (config2?.agents?.length) {
30467
31112
  const agents = config2.agents;
30468
31113
  if (!selectedAgent || !agents.includes(selectedAgent)) {
@@ -30470,7 +31115,7 @@ var DefaultsStep = memo51(function DefaultsStep2({
30470
31115
  }
30471
31116
  }
30472
31117
  }, [config2, selectedAgent]);
30473
- useEffect55(() => {
31118
+ useEffect56(() => {
30474
31119
  if (allModels?.[selectedProvider] && hasUserChangedProvider.current) {
30475
31120
  const providerModels = allModels[selectedProvider];
30476
31121
  if (!providerModels.models.some((m) => m.id === selectedModel)) {
@@ -30799,7 +31444,7 @@ var OnboardingModal = memo52(function OnboardingModal2({
30799
31444
  importCopilotTokenFromGh,
30800
31445
  getCopilotDiagnostics
30801
31446
  } = useAuthStatus();
30802
- useEffect56(() => {
31447
+ useEffect57(() => {
30803
31448
  if (!isOpen)
30804
31449
  return;
30805
31450
  const handleNativeBack = (event) => {
@@ -30853,8 +31498,8 @@ var OnboardingModal = memo52(function OnboardingModal2({
30853
31498
  });
30854
31499
  });
30855
31500
  // src/hooks/useClientEvents.ts
30856
- import { useEffect as useEffect57, useRef as useRef39 } from "react";
30857
- import { useQueryClient as useQueryClient21 } from "@tanstack/react-query";
31501
+ import { useEffect as useEffect58, useRef as useRef39 } from "react";
31502
+ import { useQueryClient as useQueryClient22 } from "@tanstack/react-query";
30858
31503
  import {
30859
31504
  buildClientEventsStreamUrl,
30860
31505
  createClientEventsStream
@@ -31039,12 +31684,12 @@ async function maybeShowLocalAccessToast(baseUrl) {
31039
31684
  });
31040
31685
  }
31041
31686
  function useClientEvents(activeSessionId) {
31042
- const queryClient = useQueryClient21();
31687
+ const queryClient = useQueryClient22();
31043
31688
  const activeSessionIdRef = useRef39(activeSessionId);
31044
- useEffect57(() => {
31689
+ useEffect58(() => {
31045
31690
  activeSessionIdRef.current = activeSessionId;
31046
31691
  }, [activeSessionId]);
31047
- useEffect57(() => {
31692
+ useEffect58(() => {
31048
31693
  if (typeof window === "undefined" || window.parent !== window)
31049
31694
  return;
31050
31695
  if (!("Notification" in window))
@@ -31073,7 +31718,7 @@ function useClientEvents(activeSessionId) {
31073
31718
  }
31074
31719
  });
31075
31720
  }, []);
31076
- useEffect57(() => {
31721
+ useEffect58(() => {
31077
31722
  const controller = new AbortController;
31078
31723
  const baseUrl = getBaseUrl();
31079
31724
  createClientEventsStream({
@@ -31123,7 +31768,7 @@ function useClientEvents(activeSessionId) {
31123
31768
  return buildClientEventsStreamUrl({ baseUrl: getBaseUrl() });
31124
31769
  }
31125
31770
  // src/hooks/useTheme.ts
31126
- import { useEffect as useEffect58, useState as useState51, useCallback as useCallback35, useMemo as useMemo31 } from "react";
31771
+ import { useEffect as useEffect59, useState as useState51, useCallback as useCallback35, useMemo as useMemo31 } from "react";
31127
31772
  var STORAGE_KEY3 = "otto-theme";
31128
31773
  function resolveInitialTheme() {
31129
31774
  if (typeof window === "undefined") {
@@ -31140,7 +31785,7 @@ function resolveInitialTheme() {
31140
31785
  }
31141
31786
  function useTheme() {
31142
31787
  const [theme, setTheme] = useState51(() => resolveInitialTheme());
31143
- useEffect58(() => {
31788
+ useEffect59(() => {
31144
31789
  if (typeof document === "undefined")
31145
31790
  return;
31146
31791
  const root = document.documentElement;
@@ -31158,7 +31803,7 @@ function useTheme() {
31158
31803
  window.parent.postMessage({ type: "otto-set-theme", theme }, "*");
31159
31804
  }
31160
31805
  }, [theme]);
31161
- useEffect58(() => {
31806
+ useEffect59(() => {
31162
31807
  if (typeof window === "undefined")
31163
31808
  return;
31164
31809
  const handler = (e) => {
@@ -31175,11 +31820,11 @@ function useTheme() {
31175
31820
  return useMemo31(() => ({ theme, setTheme, toggleTheme }), [theme, toggleTheme]);
31176
31821
  }
31177
31822
  // src/hooks/useWorkingDirectory.ts
31178
- import { useEffect as useEffect59, useState as useState52 } from "react";
31823
+ import { useEffect as useEffect60, useState as useState52 } from "react";
31179
31824
  import { getCwd } from "@ottocode/api";
31180
31825
  function useWorkingDirectory() {
31181
31826
  const [dirName, setDirName] = useState52(null);
31182
- useEffect59(() => {
31827
+ useEffect60(() => {
31183
31828
  const fetchWorkingDirectory = async () => {
31184
31829
  try {
31185
31830
  const response = await getCwd({ baseURL: getBaseUrl() });
@@ -31203,7 +31848,7 @@ function useWorkingDirectory() {
31203
31848
  return dirName;
31204
31849
  }
31205
31850
  // src/hooks/useKeyboardShortcuts.ts
31206
- import { useEffect as useEffect60, useCallback as useCallback36 } from "react";
31851
+ import { useEffect as useEffect61, useCallback as useCallback36 } from "react";
31207
31852
 
31208
31853
  // src/stores/sidebarStore.ts
31209
31854
  import { create as create24 } from "zustand";
@@ -31507,7 +32152,7 @@ function useKeyboardShortcuts({
31507
32152
  onReturnToInput,
31508
32153
  closeDiff
31509
32154
  ]);
31510
- useEffect60(() => {
32155
+ useEffect61(() => {
31511
32156
  window.addEventListener("keydown", handleKeyDown, true);
31512
32157
  return () => window.removeEventListener("keydown", handleKeyDown, true);
31513
32158
  }, [handleKeyDown]);
@@ -31521,7 +32166,7 @@ function useKeyboardShortcuts({
31521
32166
  import {
31522
32167
  useState as useState53,
31523
32168
  useCallback as useCallback37,
31524
- useEffect as useEffect61
32169
+ useEffect as useEffect62
31525
32170
  } from "react";
31526
32171
  var SUPPORTED_TYPES2 = ["image/png", "image/jpeg", "image/gif", "image/webp"];
31527
32172
  function generateId2() {
@@ -31657,7 +32302,7 @@ function useImageUpload(options = {}) {
31657
32302
  addImages(imageFiles);
31658
32303
  }
31659
32304
  }, [addImages]);
31660
- useEffect61(() => {
32305
+ useEffect62(() => {
31661
32306
  if (!pageWide)
31662
32307
  return;
31663
32308
  let dragCounter = 0;
@@ -31718,7 +32363,7 @@ function useImageUpload(options = {}) {
31718
32363
  };
31719
32364
  }
31720
32365
  // src/hooks/useSetuPayments.ts
31721
- import { useEffect as useEffect62, useRef as useRef40 } from "react";
32366
+ import { useEffect as useEffect63, useRef as useRef40 } from "react";
31722
32367
  function useSetuPayments(sessionId) {
31723
32368
  const clientRef = useRef40(null);
31724
32369
  const loadingToastIdRef = useRef40(null);
@@ -31728,7 +32373,7 @@ function useSetuPayments(sessionId) {
31728
32373
  const updateToast = useToastStore((s) => s.updateToast);
31729
32374
  const setPendingTopup = useTopupApprovalStore((s) => s.setPendingTopup);
31730
32375
  const clearPendingTopup = useTopupApprovalStore((s) => s.clearPendingTopup);
31731
- useEffect62(() => {
32376
+ useEffect63(() => {
31732
32377
  if (!sessionId)
31733
32378
  return;
31734
32379
  const client5 = new SSEClient;
@@ -31967,6 +32612,7 @@ export {
31967
32612
  openPlatformUrl,
31968
32613
  openPlatformSession,
31969
32614
  notifyPlatformFontFamilyChanged,
32615
+ normalizeQueueState,
31970
32616
  listPlatformSystemFonts,
31971
32617
  hasPlatformSystemFonts,
31972
32618
  hasPlatformOpenUrl,
@@ -32052,4 +32698,4 @@ export {
32052
32698
  API_BASE_URL
32053
32699
  };
32054
32700
 
32055
- //# debugId=1239796033F84F8A64756E2164756E21
32701
+ //# debugId=EA8B7C332CFA367264756E2164756E21