replicas-cli 0.2.51 → 0.2.53

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.
@@ -11,15 +11,15 @@ import {
11
11
  isAgentBackendEvent,
12
12
  parseAgentEvents,
13
13
  parseUserMessage
14
- } from "./chunk-QIZFFIVG.mjs";
14
+ } from "./chunk-SDULF4LX.mjs";
15
15
 
16
16
  // src/interactive/index.tsx
17
17
  import { createCliRenderer } from "@opentui/core";
18
18
  import { createRoot } from "@opentui/react";
19
19
 
20
20
  // src/interactive/App.tsx
21
- import { useState as useState7, useEffect as useEffect3, useMemo as useMemo5, useCallback as useCallback6, useRef as useRef3 } from "react";
22
- import { useKeyboard as useKeyboard4, useRenderer, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
21
+ import { useState as useState8, useEffect as useEffect3, useMemo as useMemo6, useCallback as useCallback7, useRef as useRef5 } from "react";
22
+ import { useKeyboard as useKeyboard5, useRenderer, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
23
23
  import { QueryClient } from "@tanstack/react-query";
24
24
 
25
25
  // ../shared/src/hooks/auth-context.ts
@@ -493,7 +493,18 @@ var KEYBINDS = {
493
493
  "chat-input": "\u21B5 send \u21E5 plan/build",
494
494
  info: "j/k nav \u21B5 open o dashboard w wake"
495
495
  };
496
- function StatusBar({ focusPanel }) {
496
+ var DIFF_KEYBINDS = "j/k nav \u21E5 switch pane";
497
+ var MODE_HINT = "1 chat 2 diff";
498
+ var MODE_HINT_PANELS = /* @__PURE__ */ new Set([
499
+ "chat-tabs",
500
+ "chat-history",
501
+ "info"
502
+ ]);
503
+ function StatusBar({ focusPanel, viewingDiff, hasDiffAvailable }) {
504
+ const showingDiffHints = viewingDiff && focusPanel !== "sidebar" && focusPanel !== "info";
505
+ const baseHints = showingDiffHints ? DIFF_KEYBINDS : KEYBINDS[focusPanel];
506
+ const showModeHint = hasDiffAvailable && (showingDiffHints || MODE_HINT_PANELS.has(focusPanel));
507
+ const hints = showModeHint ? `${baseHints} ${MODE_HINT}` : baseHints;
497
508
  return /* @__PURE__ */ jsxs(
498
509
  "box",
499
510
  {
@@ -507,7 +518,7 @@ function StatusBar({ focusPanel }) {
507
518
  /* @__PURE__ */ jsx("text", { children: /* @__PURE__ */ jsx("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx("strong", { children: "Replicas" }) }) }),
508
519
  /* @__PURE__ */ jsxs("text", { children: [
509
520
  /* @__PURE__ */ jsxs("span", { fg: "#555555", children: [
510
- KEYBINDS[focusPanel],
521
+ hints,
511
522
  " "
512
523
  ] }),
513
524
  /* @__PURE__ */ jsxs("span", { fg: "#888888", children: [
@@ -527,11 +538,11 @@ function StatusBar({ focusPanel }) {
527
538
  import { useState as useState2, useMemo as useMemo2, useCallback as useCallback3 } from "react";
528
539
  import { useKeyboard, useTerminalDimensions } from "@opentui/react";
529
540
  import { jsx as jsx2, jsxs as jsxs2 } from "@opentui/react/jsx-runtime";
530
- function flattenGroups(groups, expandedGroups) {
541
+ function flattenGroups(groups, collapsedGroups) {
531
542
  const items = [];
532
543
  for (const group of groups) {
533
544
  if (group.id === "__ungrouped__" && group.workspaces.length === 0) continue;
534
- const expanded = expandedGroups.has(group.id);
545
+ const expanded = !collapsedGroups.has(group.id);
535
546
  items.push({ type: "group", id: group.id, name: group.name, groupType: group.type, expanded });
536
547
  if (expanded) {
537
548
  for (const ws of group.workspaces) {
@@ -566,20 +577,11 @@ function WorkspaceSidebar({
566
577
  loading
567
578
  }) {
568
579
  const { height: termHeight } = useTerminalDimensions();
569
- const [expandedGroups, setExpandedGroups] = useState2(
570
- () => new Set(groups.map((g) => g.id))
571
- );
580
+ const [collapsedGroups, setCollapsedGroups] = useState2(/* @__PURE__ */ new Set());
572
581
  const [cursorIndex, setCursorIndex] = useState2(0);
573
582
  const [scrollOffset, setScrollOffset] = useState2(0);
574
583
  const [confirmDelete, setConfirmDelete] = useState2(null);
575
- const expandedWithNew = useMemo2(() => {
576
- const next = new Set(expandedGroups);
577
- for (const g of groups) {
578
- if (!next.has(g.id)) next.add(g.id);
579
- }
580
- return next;
581
- }, [expandedGroups, groups]);
582
- const items = useMemo2(() => flattenGroups(groups, expandedWithNew), [groups, expandedWithNew]);
584
+ const items = useMemo2(() => flattenGroups(groups, collapsedGroups), [groups, collapsedGroups]);
583
585
  const clampedCursor = Math.min(cursorIndex, Math.max(0, items.length - 1));
584
586
  if (clampedCursor !== cursorIndex) {
585
587
  setCursorIndex(clampedCursor);
@@ -610,7 +612,7 @@ function WorkspaceSidebar({
610
612
  const handleAction = useCallback3(
611
613
  (item) => {
612
614
  if (item.type === "group") {
613
- setExpandedGroups((prev) => {
615
+ setCollapsedGroups((prev) => {
614
616
  const next = new Set(prev);
615
617
  if (next.has(item.id)) {
616
618
  next.delete(item.id);
@@ -853,12 +855,11 @@ function WorkspaceSidebar({
853
855
  }
854
856
 
855
857
  // src/interactive/components/ChatArea.tsx
856
- import { useRef, useEffect as useEffect2 } from "react";
857
- import { useKeyboard as useKeyboard2 } from "@opentui/react";
858
+ import { useRef as useRef2, useEffect as useEffect2 } from "react";
859
+ import { useKeyboard as useKeyboard3 } from "@opentui/react";
858
860
 
859
861
  // src/interactive/components/ChatMessage.tsx
860
- import { useState as useState4, useMemo as useMemo3 } from "react";
861
- import { SyntaxStyle } from "@opentui/core";
862
+ import { useState as useState5, useMemo as useMemo4 } from "react";
862
863
 
863
864
  // src/interactive/components/Spinner.tsx
864
865
  import "opentui-spinner/react";
@@ -873,168 +874,678 @@ function SpinnerLabel({ color, label }) {
873
874
  }
874
875
 
875
876
  // src/interactive/components/StructuredUserMessage.tsx
876
- import { useState as useState3 } from "react";
877
+ import { useState as useState4 } from "react";
878
+
879
+ // src/interactive/components/diff-utils.tsx
880
+ import React2, { useState as useState3, useMemo as useMemo3, useCallback as useCallback4, useRef } from "react";
881
+ import { SyntaxStyle } from "@opentui/core";
882
+ import { useKeyboard as useKeyboard2 } from "@opentui/react";
877
883
  import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
884
+ var sharedSyntaxStyle = null;
885
+ function getSyntaxStyle() {
886
+ if (!sharedSyntaxStyle) {
887
+ sharedSyntaxStyle = SyntaxStyle.fromTheme([
888
+ { scope: ["markup.heading", "markup.heading.1", "markup.heading.2", "markup.heading.3"], style: { foreground: "#66bb6a", bold: true } },
889
+ { scope: ["markup.bold", "markup.strong"], style: { foreground: "#ffffff", bold: true } },
890
+ { scope: ["markup.italic", "markup.emphasis"], style: { foreground: "#e0e0e0", italic: true } },
891
+ { scope: ["markup.link"], style: { foreground: "#7dcfff", underline: true } },
892
+ { scope: ["markup.link.url"], style: { foreground: "#7dcfff", underline: true } },
893
+ { scope: ["markup.link.text"], style: { foreground: "#66bb6a", underline: true } },
894
+ { scope: ["markup.link.label"], style: { foreground: "#66bb6a", underline: true } },
895
+ { scope: ["markup.list"], style: { foreground: "#66bb6a" } },
896
+ { scope: ["markup.quote"], style: { foreground: "#888888", italic: true } },
897
+ { scope: ["markup.raw", "markup.raw.block"], style: { foreground: "#bb9af7" } },
898
+ { scope: ["markup.raw.inline"], style: { foreground: "#bb9af7", background: "#1a1a2e" } },
899
+ { scope: ["keyword", "keyword.control", "keyword.operator"], style: { foreground: "#bb9af7" } },
900
+ { scope: ["string", "string.quoted"], style: { foreground: "#9ece6a" } },
901
+ { scope: ["comment", "comment.line", "comment.block"], style: { foreground: "#555555", italic: true } },
902
+ { scope: ["constant", "constant.numeric", "constant.language"], style: { foreground: "#ff9e64" } },
903
+ { scope: ["entity.name.function", "support.function"], style: { foreground: "#7aa2f7" } },
904
+ { scope: ["entity.name.type", "support.type"], style: { foreground: "#2ac3de" } },
905
+ { scope: ["variable", "variable.other"], style: { foreground: "#c0caf5" } },
906
+ { scope: ["punctuation"], style: { foreground: "#888888" } }
907
+ ]);
908
+ }
909
+ return sharedSyntaxStyle;
910
+ }
911
+ function truncateName(text, maxLen) {
912
+ if (maxLen <= 0) return "";
913
+ if (text.length <= maxLen) return text;
914
+ if (maxLen === 1) return "\u2026";
915
+ return text.slice(0, maxLen - 1) + "\u2026";
916
+ }
917
+ function countDiffStats(diff) {
918
+ let added = 0;
919
+ let removed = 0;
920
+ for (const line of diff.split("\n")) {
921
+ if (line.startsWith("+") && !line.startsWith("+++")) added++;
922
+ else if (line.startsWith("-") && !line.startsWith("---")) removed++;
923
+ }
924
+ return { added, removed };
925
+ }
926
+ var EXT_TO_FILETYPE = {
927
+ ts: "typescript",
928
+ tsx: "typescriptreact",
929
+ js: "javascript",
930
+ jsx: "javascriptreact",
931
+ cts: "typescript",
932
+ mts: "typescript",
933
+ cjs: "javascript",
934
+ mjs: "javascript",
935
+ py: "python",
936
+ rs: "rust",
937
+ go: "go",
938
+ rb: "ruby",
939
+ java: "java",
940
+ kt: "kotlin",
941
+ swift: "swift",
942
+ c: "c",
943
+ cpp: "cpp",
944
+ h: "c",
945
+ hpp: "cpp",
946
+ cs: "csharp",
947
+ css: "css",
948
+ scss: "scss",
949
+ html: "html",
950
+ json: "json",
951
+ yaml: "yaml",
952
+ yml: "yaml",
953
+ toml: "toml",
954
+ md: "markdown",
955
+ sh: "bash",
956
+ bash: "bash",
957
+ zsh: "bash",
958
+ sql: "sql"
959
+ };
960
+ function extFromPath(path) {
961
+ const ext = path.split(".").pop()?.toLowerCase();
962
+ return ext ? EXT_TO_FILETYPE[ext] : void 0;
963
+ }
964
+ function ensureUnifiedHeaders(diff, filePath) {
965
+ if (/^--- /m.test(diff) && /^@@ /m.test(diff)) return diff;
966
+ const file = filePath ?? "a";
967
+ const fileHeaders = `--- a/${file}
968
+ +++ b/${file}
969
+ `;
970
+ if (/^@@ -\d+/m.test(diff)) {
971
+ return fileHeaders + diff;
972
+ }
973
+ const lines = diff.split("\n");
974
+ const diffLines = lines.filter((l) => !l.startsWith("@@"));
975
+ let oldCount = 0;
976
+ let newCount = 0;
977
+ for (const line of diffLines) {
978
+ if (line.startsWith("+") && !line.startsWith("+++")) newCount++;
979
+ else if (line.startsWith("-") && !line.startsWith("---")) oldCount++;
980
+ else if (line.startsWith(" ") || !line.startsWith("+") && !line.startsWith("-") && line.length > 0) {
981
+ oldCount++;
982
+ newCount++;
983
+ }
984
+ }
985
+ const hunk = `@@ -1,${oldCount} +1,${newCount} @@`;
986
+ return `${fileHeaders}${hunk}
987
+ ${diffLines.join("\n")}`;
988
+ }
989
+ function diffProps(filePath) {
990
+ return {
991
+ view: "split",
992
+ filetype: filePath ? extFromPath(filePath) : void 0,
993
+ syntaxStyle: getSyntaxStyle(),
994
+ showLineNumbers: true,
995
+ width: "100%",
996
+ wrapMode: "word",
997
+ fg: "#cccccc",
998
+ addedBg: "#0d2b0d",
999
+ removedBg: "#2b0d0d",
1000
+ contextBg: "#0a0a0a",
1001
+ addedSignColor: "#66bb6a",
1002
+ removedSignColor: "#ff4444",
1003
+ lineNumberFg: "#555555",
1004
+ lineNumberBg: "#0a0a0a",
1005
+ addedLineNumberBg: "#0d2b0d",
1006
+ removedLineNumberBg: "#2b0d0d"
1007
+ };
1008
+ }
1009
+ function DiffView({ diff, filePath }) {
1010
+ const hunks = useMemo3(() => splitDiffByHunks(diff), [diff]);
1011
+ const props = diffProps(filePath);
1012
+ if (hunks.length <= 1) {
1013
+ return /* @__PURE__ */ jsx4("diff", { diff: ensureUnifiedHeaders(diff, filePath), ...props });
1014
+ }
1015
+ return /* @__PURE__ */ jsx4("box", { flexDirection: "column", children: hunks.map((hunk, i) => {
1016
+ const prev = i > 0 ? hunks[i - 1] : null;
1017
+ const gap = prev ? hunk.newStart - (prev.newStart + prev.newCount) : 0;
1018
+ return /* @__PURE__ */ jsxs4(React2.Fragment, { children: [
1019
+ prev && gap > 0 && /* @__PURE__ */ jsx4("box", { backgroundColor: "#151515", paddingX: 1, height: 1, children: /* @__PURE__ */ jsx4("text", { children: /* @__PURE__ */ jsxs4("span", { fg: "#666666", children: [
1020
+ gap,
1021
+ " unmodified ",
1022
+ gap === 1 ? "line" : "lines"
1023
+ ] }) }) }),
1024
+ /* @__PURE__ */ jsx4("diff", { diff: ensureUnifiedHeaders(hunk.body, filePath), ...props })
1025
+ ] }, i);
1026
+ }) });
1027
+ }
1028
+ function splitDiffByFile(fullDiff) {
1029
+ const chunks = [];
1030
+ const lines = fullDiff.split("\n");
1031
+ let currentLines = [];
1032
+ let currentPath = "";
1033
+ for (const line of lines) {
1034
+ if (line.startsWith("diff --git ")) {
1035
+ if (currentPath && currentLines.length > 0) {
1036
+ const diff = currentLines.join("\n");
1037
+ chunks.push({ path: currentPath, diff, ...countDiffStats(diff) });
1038
+ }
1039
+ const match = line.match(/^diff --git a\/(.+) b\/(.+)$/);
1040
+ currentPath = match ? match[2] : "";
1041
+ currentLines = [line];
1042
+ } else {
1043
+ currentLines.push(line);
1044
+ }
1045
+ }
1046
+ if (currentPath && currentLines.length > 0) {
1047
+ const diff = currentLines.join("\n");
1048
+ chunks.push({ path: currentPath, diff, ...countDiffStats(diff) });
1049
+ }
1050
+ return chunks;
1051
+ }
1052
+ function splitDiffByHunks(fileDiff) {
1053
+ const hunks = [];
1054
+ const lines = fileDiff.split("\n");
1055
+ let current = null;
1056
+ for (const line of lines) {
1057
+ const match = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/);
1058
+ if (match) {
1059
+ if (current) hunks.push({ body: current.lines.join("\n"), newStart: current.newStart, newCount: current.newCount });
1060
+ current = {
1061
+ lines: [line],
1062
+ newStart: parseInt(match[1], 10),
1063
+ newCount: match[2] ? parseInt(match[2], 10) : 1
1064
+ };
1065
+ } else if (current) {
1066
+ current.lines.push(line);
1067
+ }
1068
+ }
1069
+ if (current) hunks.push({ body: current.lines.join("\n"), newStart: current.newStart, newCount: current.newCount });
1070
+ return hunks;
1071
+ }
1072
+ function buildFileTree(files) {
1073
+ const root = { name: "", children: /* @__PURE__ */ new Map() };
1074
+ files.forEach((file, idx) => {
1075
+ const segments = file.path.split("/");
1076
+ let node = root;
1077
+ for (const seg of segments) {
1078
+ if (!node.children.has(seg)) {
1079
+ node.children.set(seg, { name: seg, children: /* @__PURE__ */ new Map() });
1080
+ }
1081
+ node = node.children.get(seg);
1082
+ }
1083
+ node.fileIndex = idx;
1084
+ node.added = file.added;
1085
+ node.removed = file.removed;
1086
+ });
1087
+ const result = [];
1088
+ function walk(node, depth, accumulatedPath, parentPath) {
1089
+ if (node.fileIndex !== void 0) {
1090
+ const fullPath = parentPath ? `${parentPath}/${node.name}` : node.name;
1091
+ result.push({
1092
+ type: "file",
1093
+ name: node.name,
1094
+ path: fullPath,
1095
+ depth,
1096
+ fileIndex: node.fileIndex,
1097
+ added: node.added,
1098
+ removed: node.removed
1099
+ });
1100
+ return;
1101
+ }
1102
+ const children = Array.from(node.children.values());
1103
+ if (children.length === 1 && children[0].fileIndex === void 0) {
1104
+ walk(children[0], depth, [...accumulatedPath, node.name], parentPath);
1105
+ return;
1106
+ }
1107
+ let folderFullPath = parentPath;
1108
+ if (node.name !== "") {
1109
+ const displayName = [...accumulatedPath, node.name].filter(Boolean).join("/");
1110
+ folderFullPath = parentPath ? `${parentPath}/${displayName}` : displayName;
1111
+ result.push({ type: "folder", name: displayName, path: folderFullPath, depth });
1112
+ }
1113
+ const childDepth = node.name === "" ? depth : depth + 1;
1114
+ for (const child of children) {
1115
+ walk(child, childDepth, [], folderFullPath);
1116
+ }
1117
+ }
1118
+ walk(root, 0, [], "");
1119
+ return result;
1120
+ }
1121
+ var FILE_PANEL_WIDTH = 32;
1122
+ function DiffViewer({ diff, repoName, focused }) {
1123
+ const files = useMemo3(() => splitDiffByFile(diff), [diff]);
1124
+ const tree = useMemo3(() => buildFileTree(files), [files]);
1125
+ const [selectedFileIndex, setSelectedFileIndex] = useState3(0);
1126
+ const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
1127
+ const [cursorIndex, setCursorIndex] = useState3(0);
1128
+ const [innerFocus, setInnerFocus] = useState3("files");
1129
+ const safeFileIndex = Math.min(selectedFileIndex, Math.max(0, files.length - 1));
1130
+ const diffScrollRef = useRef(null);
1131
+ const fileTreeScrollRef = useRef(null);
1132
+ const visibleTree = useMemo3(() => {
1133
+ const result = [];
1134
+ let hideUntilDepth = null;
1135
+ for (const node of tree) {
1136
+ if (hideUntilDepth !== null && node.depth <= hideUntilDepth) {
1137
+ hideUntilDepth = null;
1138
+ }
1139
+ if (hideUntilDepth !== null) continue;
1140
+ result.push(node);
1141
+ if (node.type === "folder" && collapsedFolders.has(node.path)) {
1142
+ hideUntilDepth = node.depth;
1143
+ }
1144
+ }
1145
+ return result;
1146
+ }, [tree, collapsedFolders]);
1147
+ const safeCursorIndex = Math.min(cursorIndex, Math.max(0, visibleTree.length - 1));
1148
+ const scrollCursorIntoView = useCallback4((treeIdx) => {
1149
+ const node = visibleTree[treeIdx];
1150
+ if (!node) return;
1151
+ fileTreeScrollRef.current?.scrollChildIntoView(`tree-${node.path}`);
1152
+ }, [visibleTree]);
1153
+ const moveCursor = useCallback4((next) => {
1154
+ const len = visibleTree.length;
1155
+ if (len === 0) return;
1156
+ const wrapped = (next % len + len) % len;
1157
+ setCursorIndex(wrapped);
1158
+ scrollCursorIntoView(wrapped);
1159
+ const node = visibleTree[wrapped];
1160
+ if (node?.type === "file" && node.fileIndex !== void 0) {
1161
+ setSelectedFileIndex(node.fileIndex);
1162
+ }
1163
+ }, [visibleTree, scrollCursorIntoView]);
1164
+ const toggleFolder = useCallback4((folderPath) => {
1165
+ setCollapsedFolders((prev) => {
1166
+ const next = new Set(prev);
1167
+ if (next.has(folderPath)) next.delete(folderPath);
1168
+ else next.add(folderPath);
1169
+ return next;
1170
+ });
1171
+ }, []);
1172
+ const selectFile = useCallback4((fileIndex, treeIndex) => {
1173
+ setSelectedFileIndex(fileIndex);
1174
+ setCursorIndex(treeIndex);
1175
+ scrollCursorIntoView(treeIndex);
1176
+ }, [scrollCursorIntoView]);
1177
+ useKeyboard2((key) => {
1178
+ if (!focused) return;
1179
+ if (key.name === "tab" && !key.shift) {
1180
+ setInnerFocus((f) => f === "files" ? "diff" : "files");
1181
+ return;
1182
+ }
1183
+ if (innerFocus === "files") {
1184
+ if (visibleTree.length > 1 && (key.name === "up" || key.name === "k" && !key.ctrl)) {
1185
+ moveCursor(safeCursorIndex - 1);
1186
+ return;
1187
+ }
1188
+ if (visibleTree.length > 1 && (key.name === "down" || key.name === "j" && !key.ctrl)) {
1189
+ moveCursor(safeCursorIndex + 1);
1190
+ return;
1191
+ }
1192
+ if (key.name === "return" || key.name === "enter") {
1193
+ const node = visibleTree[safeCursorIndex];
1194
+ if (node?.type === "folder") toggleFolder(node.path);
1195
+ return;
1196
+ }
1197
+ return;
1198
+ }
1199
+ if (innerFocus === "diff") {
1200
+ const sb = diffScrollRef.current;
1201
+ if (!sb) return;
1202
+ if (key.ctrl && key.name === "d") {
1203
+ sb.scrollBy((sb.viewport?.height ?? 20) / 2);
1204
+ } else if (key.ctrl && key.name === "u") {
1205
+ sb.scrollBy(-((sb.viewport?.height ?? 20) / 2));
1206
+ }
1207
+ }
1208
+ });
1209
+ const statsColumnWidth = useMemo3(() => {
1210
+ let max = 0;
1211
+ for (const f of files) {
1212
+ const a = f.added > 0 ? `+${f.added}`.length : 0;
1213
+ const r = f.removed > 0 ? `-${f.removed}`.length : 0;
1214
+ const len = a + r + (a > 0 && r > 0 ? 1 : 0);
1215
+ if (len > max) max = len;
1216
+ }
1217
+ return max;
1218
+ }, [files]);
1219
+ if (files.length === 0) {
1220
+ return /* @__PURE__ */ jsxs4("box", { flexGrow: 1, flexDirection: "column", children: [
1221
+ /* @__PURE__ */ jsxs4("box", { paddingX: 1, marginBottom: 1, flexDirection: "row", justifyContent: "space-between", flexShrink: 0, height: 1, children: [
1222
+ /* @__PURE__ */ jsxs4("text", { children: [
1223
+ /* @__PURE__ */ jsx4("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx4("strong", { children: "Diff" }) }),
1224
+ /* @__PURE__ */ jsxs4("span", { fg: "#555555", children: [
1225
+ " ",
1226
+ "\u2502",
1227
+ " "
1228
+ ] }),
1229
+ /* @__PURE__ */ jsx4("span", { fg: "#cccccc", children: repoName })
1230
+ ] }),
1231
+ /* @__PURE__ */ jsx4("text", { fg: "#555555", children: "1 file" })
1232
+ ] }),
1233
+ /* @__PURE__ */ jsx4("scrollbox", { flexGrow: 1, focused, children: /* @__PURE__ */ jsx4("box", { paddingX: 1, children: /* @__PURE__ */ jsx4(DiffView, { diff }) }) })
1234
+ ] });
1235
+ }
1236
+ const selected = files[safeFileIndex];
1237
+ const totalAdded = files.reduce((s, f) => s + f.added, 0);
1238
+ const totalRemoved = files.reduce((s, f) => s + f.removed, 0);
1239
+ const filesPaneActive = focused === true && innerFocus === "files";
1240
+ const diffPaneActive = focused === true && innerFocus === "diff";
1241
+ return /* @__PURE__ */ jsxs4("box", { flexGrow: 1, flexDirection: "row", children: [
1242
+ /* @__PURE__ */ jsxs4(
1243
+ "box",
1244
+ {
1245
+ width: FILE_PANEL_WIDTH,
1246
+ flexDirection: "column",
1247
+ backgroundColor: "#0a0a0a",
1248
+ flexShrink: 0,
1249
+ children: [
1250
+ /* @__PURE__ */ jsx4("box", { paddingX: 1, flexShrink: 0, height: 1, children: /* @__PURE__ */ jsxs4("text", { children: [
1251
+ /* @__PURE__ */ jsx4("span", { fg: filesPaneActive ? "#66bb6a" : "#333333", children: filesPaneActive ? "\u25B8 " : " " }),
1252
+ /* @__PURE__ */ jsx4("span", { fg: filesPaneActive ? "#66bb6a" : "#888888", children: filesPaneActive ? /* @__PURE__ */ jsxs4("strong", { children: [
1253
+ files.length,
1254
+ " ",
1255
+ files.length === 1 ? "file" : "files",
1256
+ " changed"
1257
+ ] }) : `${files.length} ${files.length === 1 ? "file" : "files"} changed` })
1258
+ ] }) }),
1259
+ /* @__PURE__ */ jsx4("scrollbox", { ref: fileTreeScrollRef, flexGrow: 1, focused: false, children: visibleTree.map((node, i) => {
1260
+ const indent = " ".repeat(node.depth);
1261
+ const usable = FILE_PANEL_WIDTH - 2;
1262
+ const isCursor = filesPaneActive && i === safeCursorIndex;
1263
+ if (node.type === "folder") {
1264
+ const isCollapsed = collapsedFolders.has(node.path);
1265
+ const chevron = isCollapsed ? "\u25B8" : "\u25BE";
1266
+ const overhead2 = indent.length + 2;
1267
+ const nameMax2 = Math.max(1, usable - overhead2);
1268
+ const displayName2 = truncateName(node.name, nameMax2);
1269
+ return /* @__PURE__ */ jsx4(
1270
+ "box",
1271
+ {
1272
+ id: `tree-${node.path}`,
1273
+ paddingX: 1,
1274
+ height: 1,
1275
+ backgroundColor: isCursor ? "#1a1a1a" : void 0,
1276
+ onMouseDown: () => {
1277
+ setCursorIndex(i);
1278
+ toggleFolder(node.path);
1279
+ },
1280
+ children: /* @__PURE__ */ jsxs4("text", { children: [
1281
+ /* @__PURE__ */ jsx4("span", { fg: "#555555", children: indent }),
1282
+ /* @__PURE__ */ jsxs4("span", { fg: isCursor ? "#cccccc" : "#888888", children: [
1283
+ chevron,
1284
+ " ",
1285
+ displayName2
1286
+ ] })
1287
+ ] })
1288
+ },
1289
+ `folder-${node.path}`
1290
+ );
1291
+ }
1292
+ const isSelectedFile = node.fileIndex === safeFileIndex;
1293
+ const added = node.added ?? 0;
1294
+ const removed = node.removed ?? 0;
1295
+ const addedPart = added > 0 ? `+${added}` : "";
1296
+ const removedPart = removed > 0 ? `-${removed}` : "";
1297
+ const bubbleColor = added > 0 && removed > 0 ? "#ffaa00" : removed > 0 ? "#ff4444" : "#66bb6a";
1298
+ const overhead = indent.length + 2 + statsColumnWidth + 1;
1299
+ const nameMax = Math.max(1, usable - overhead);
1300
+ const displayName = truncateName(node.name, nameMax);
1301
+ const bg = isCursor ? "#0d2b0d" : isSelectedFile ? "#0a1a0a" : void 0;
1302
+ return /* @__PURE__ */ jsxs4(
1303
+ "box",
1304
+ {
1305
+ id: `tree-${node.path}`,
1306
+ paddingX: 1,
1307
+ height: 1,
1308
+ backgroundColor: bg,
1309
+ onMouseDown: () => selectFile(node.fileIndex, i),
1310
+ flexDirection: "row",
1311
+ justifyContent: "space-between",
1312
+ children: [
1313
+ /* @__PURE__ */ jsxs4("text", { children: [
1314
+ /* @__PURE__ */ jsx4("span", { fg: "#555555", children: indent }),
1315
+ /* @__PURE__ */ jsxs4("span", { fg: bubbleColor, children: [
1316
+ "\u25CF",
1317
+ " "
1318
+ ] }),
1319
+ /* @__PURE__ */ jsx4("span", { fg: isSelectedFile || isCursor ? "#66bb6a" : "#cccccc", children: displayName })
1320
+ ] }),
1321
+ /* @__PURE__ */ jsx4("box", { width: statsColumnWidth, flexShrink: 0, justifyContent: "flex-end", flexDirection: "row", children: /* @__PURE__ */ jsxs4("text", { children: [
1322
+ addedPart && /* @__PURE__ */ jsx4("span", { fg: "#66bb6a", children: addedPart }),
1323
+ addedPart && removedPart && /* @__PURE__ */ jsx4("span", { fg: "#444444", children: " " }),
1324
+ removedPart && /* @__PURE__ */ jsx4("span", { fg: "#ff4444", children: removedPart })
1325
+ ] }) })
1326
+ ]
1327
+ },
1328
+ `file-${node.path}`
1329
+ );
1330
+ }) })
1331
+ ]
1332
+ }
1333
+ ),
1334
+ /* @__PURE__ */ jsxs4("box", { flexGrow: 1, flexDirection: "column", children: [
1335
+ /* @__PURE__ */ jsxs4("box", { paddingX: 1, flexDirection: "column", flexShrink: 0, children: [
1336
+ /* @__PURE__ */ jsxs4("box", { flexDirection: "row", justifyContent: "space-between", height: 1, children: [
1337
+ /* @__PURE__ */ jsxs4("text", { children: [
1338
+ /* @__PURE__ */ jsx4("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx4("strong", { children: repoName }) }),
1339
+ totalAdded > 0 && /* @__PURE__ */ jsxs4("span", { fg: "#66bb6a", children: [
1340
+ " +",
1341
+ totalAdded
1342
+ ] }),
1343
+ totalAdded > 0 && totalRemoved > 0 && /* @__PURE__ */ jsx4("span", { fg: "#444444", children: " " }),
1344
+ totalRemoved > 0 && /* @__PURE__ */ jsxs4("span", { fg: "#ff4444", children: [
1345
+ "-",
1346
+ totalRemoved
1347
+ ] })
1348
+ ] }),
1349
+ /* @__PURE__ */ jsxs4("text", { fg: "#555555", children: [
1350
+ safeFileIndex + 1,
1351
+ "/",
1352
+ files.length
1353
+ ] })
1354
+ ] }),
1355
+ /* @__PURE__ */ jsx4("box", { flexDirection: "row", height: 1, backgroundColor: "#0d0d0d", paddingX: 1, children: /* @__PURE__ */ jsxs4("text", { children: [
1356
+ /* @__PURE__ */ jsx4("span", { fg: diffPaneActive ? "#66bb6a" : "#444444", children: diffPaneActive ? "\u25B8 " : " " }),
1357
+ (() => {
1358
+ const lastSlash = selected.path.lastIndexOf("/");
1359
+ const dir = lastSlash >= 0 ? selected.path.slice(0, lastSlash + 1) : "";
1360
+ const base = lastSlash >= 0 ? selected.path.slice(lastSlash + 1) : selected.path;
1361
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
1362
+ dir && /* @__PURE__ */ jsx4("span", { fg: "#666666", children: dir }),
1363
+ /* @__PURE__ */ jsx4("span", { fg: diffPaneActive ? "#66bb6a" : "#ffffff", children: /* @__PURE__ */ jsx4("strong", { children: base }) })
1364
+ ] });
1365
+ })()
1366
+ ] }) })
1367
+ ] }),
1368
+ /* @__PURE__ */ jsx4("scrollbox", { ref: diffScrollRef, flexGrow: 1, focused: focused && innerFocus === "diff", children: /* @__PURE__ */ jsx4("box", { paddingX: 1, children: /* @__PURE__ */ jsx4(DiffView, { diff: selected.diff, filePath: selected.path }) }) })
1369
+ ] })
1370
+ ] });
1371
+ }
1372
+
1373
+ // src/interactive/components/StructuredUserMessage.tsx
1374
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "@opentui/react/jsx-runtime";
878
1375
  function SourceBadge({ source }) {
879
1376
  const config = SOURCE_CONFIG[source];
880
- return /* @__PURE__ */ jsx4("text", { children: /* @__PURE__ */ jsx4("span", { fg: "#000000", bg: config.color, children: ` ${config.label} ` }) });
1377
+ return /* @__PURE__ */ jsx5("text", { children: /* @__PURE__ */ jsx5("span", { fg: "#000000", bg: config.color, children: ` ${config.label} ` }) });
881
1378
  }
882
1379
  function MetadataPill({ label, value }) {
883
- return /* @__PURE__ */ jsx4("text", { children: label ? /* @__PURE__ */ jsxs4(Fragment, { children: [
884
- /* @__PURE__ */ jsxs4("span", { fg: "#888888", children: [
1380
+ return /* @__PURE__ */ jsx5("text", { children: label ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1381
+ /* @__PURE__ */ jsxs5("span", { fg: "#888888", children: [
885
1382
  label,
886
1383
  ": "
887
1384
  ] }),
888
- /* @__PURE__ */ jsx4("span", { fg: "#cccccc", children: value })
889
- ] }) : /* @__PURE__ */ jsx4("span", { fg: "#cccccc", children: value }) });
1385
+ /* @__PURE__ */ jsx5("span", { fg: "#cccccc", children: value })
1386
+ ] }) : /* @__PURE__ */ jsx5("span", { fg: "#cccccc", children: value }) });
890
1387
  }
891
1388
  function MessageBody({ children }) {
892
1389
  if (!children) return null;
893
- return /* @__PURE__ */ jsx4("text", { fg: "#ffffff", selectable: true, children });
1390
+ return /* @__PURE__ */ jsx5("text", { fg: "#ffffff", selectable: true, children });
894
1391
  }
895
1392
  function ExpandableSection({ label, children }) {
896
- const [expanded, setExpanded] = useState3(false);
1393
+ const [expanded, setExpanded] = useState4(false);
897
1394
  if (!children) return null;
898
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", children: [
899
- /* @__PURE__ */ jsx4("box", { onMouseDown: () => setExpanded((e) => !e), children: /* @__PURE__ */ jsxs4("text", { children: [
900
- /* @__PURE__ */ jsxs4("span", { fg: "#555555", children: [
1395
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
1396
+ /* @__PURE__ */ jsx5("box", { onMouseDown: () => setExpanded((e) => !e), children: /* @__PURE__ */ jsxs5("text", { children: [
1397
+ /* @__PURE__ */ jsxs5("span", { fg: "#555555", children: [
901
1398
  expanded ? "\u25BE" : "\u25B8",
902
1399
  " "
903
1400
  ] }),
904
- /* @__PURE__ */ jsx4("span", { fg: "#888888", children: label })
1401
+ /* @__PURE__ */ jsx5("span", { fg: "#888888", children: label })
905
1402
  ] }) }),
906
- expanded && /* @__PURE__ */ jsx4("box", { paddingLeft: 2, children: /* @__PURE__ */ jsx4("text", { fg: "#888888", selectable: true, children }) })
1403
+ expanded && /* @__PURE__ */ jsx5("box", { paddingLeft: 2, children: /* @__PURE__ */ jsx5("text", { fg: "#888888", selectable: true, children }) })
1404
+ ] });
1405
+ }
1406
+ function ExpandableDiffSection({ label, diffContent, filePath }) {
1407
+ const [expanded, setExpanded] = useState4(false);
1408
+ if (!diffContent) return null;
1409
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
1410
+ /* @__PURE__ */ jsx5("box", { onMouseDown: () => setExpanded((e) => !e), children: /* @__PURE__ */ jsxs5("text", { children: [
1411
+ /* @__PURE__ */ jsxs5("span", { fg: "#555555", children: [
1412
+ expanded ? "\u25BE" : "\u25B8",
1413
+ " "
1414
+ ] }),
1415
+ /* @__PURE__ */ jsx5("span", { fg: "#888888", children: label })
1416
+ ] }) }),
1417
+ expanded && /* @__PURE__ */ jsx5("box", { paddingLeft: 1, children: /* @__PURE__ */ jsx5(DiffView, { diff: diffContent, filePath }) })
907
1418
  ] });
908
1419
  }
909
1420
  function CIFailureMessage({ data }) {
910
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", gap: 0, children: [
911
- /* @__PURE__ */ jsxs4("box", { flexDirection: "row", gap: 1, children: [
912
- /* @__PURE__ */ jsx4(SourceBadge, { source: "ci_failure" }),
913
- /* @__PURE__ */ jsx4(MetadataPill, { label: "PR", value: `#${data.prNumber}` }),
914
- /* @__PURE__ */ jsx4(MetadataPill, { label: "Workflow", value: data.workflowName }),
915
- /* @__PURE__ */ jsx4(MetadataPill, { label: "Status", value: data.conclusion })
1421
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", gap: 0, children: [
1422
+ /* @__PURE__ */ jsxs5("box", { flexDirection: "row", gap: 1, children: [
1423
+ /* @__PURE__ */ jsx5(SourceBadge, { source: "ci_failure" }),
1424
+ /* @__PURE__ */ jsx5(MetadataPill, { label: "PR", value: `#${data.prNumber}` }),
1425
+ /* @__PURE__ */ jsx5(MetadataPill, { label: "Workflow", value: data.workflowName }),
1426
+ /* @__PURE__ */ jsx5(MetadataPill, { label: "Status", value: data.conclusion })
916
1427
  ] }),
917
- /* @__PURE__ */ jsxs4("text", { fg: "#ffffff", children: [
1428
+ /* @__PURE__ */ jsxs5("text", { fg: "#ffffff", children: [
918
1429
  "Workflow ",
919
- /* @__PURE__ */ jsx4("span", { fg: "#ff4444", children: data.conclusionText }),
1430
+ /* @__PURE__ */ jsx5("span", { fg: "#ff4444", children: data.conclusionText }),
920
1431
  " on PR #" + data.prNumber
921
1432
  ] }),
922
- data.workflowFile && /* @__PURE__ */ jsx4(MetadataPill, { label: "File", value: data.workflowFile }),
923
- data.runUrl && /* @__PURE__ */ jsx4(MetadataPill, { label: "Run", value: data.runUrl })
1433
+ data.workflowFile && /* @__PURE__ */ jsx5(MetadataPill, { label: "File", value: data.workflowFile }),
1434
+ data.runUrl && /* @__PURE__ */ jsx5(MetadataPill, { label: "Run", value: data.runUrl })
924
1435
  ] });
925
1436
  }
926
1437
  function LinearIssueMessage({ data }) {
927
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", gap: 0, children: [
928
- /* @__PURE__ */ jsxs4("box", { flexDirection: "row", gap: 1, children: [
929
- /* @__PURE__ */ jsx4(SourceBadge, { source: "linear_issue" }),
930
- /* @__PURE__ */ jsx4(MetadataPill, { label: "", value: data.identifier })
1438
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", gap: 0, children: [
1439
+ /* @__PURE__ */ jsxs5("box", { flexDirection: "row", gap: 1, children: [
1440
+ /* @__PURE__ */ jsx5(SourceBadge, { source: "linear_issue" }),
1441
+ /* @__PURE__ */ jsx5(MetadataPill, { label: "", value: data.identifier })
931
1442
  ] }),
932
- /* @__PURE__ */ jsx4("text", { fg: "#ffffff", children: /* @__PURE__ */ jsx4("strong", { children: data.title }) }),
933
- data.description && data.description !== "No description provided." && /* @__PURE__ */ jsx4(MessageBody, { children: data.description }),
934
- /* @__PURE__ */ jsx4(ExpandableSection, { label: "Additional context", children: data.additionalContext }),
935
- data.parentIssue && /* @__PURE__ */ jsx4(ExpandableSection, { label: `Parent: ${data.parentIssue.identifier} - ${data.parentIssue.title}`, children: data.parentIssue.description })
1443
+ /* @__PURE__ */ jsx5("text", { fg: "#ffffff", children: /* @__PURE__ */ jsx5("strong", { children: data.title }) }),
1444
+ data.description && data.description !== "No description provided." && /* @__PURE__ */ jsx5(MessageBody, { children: data.description }),
1445
+ /* @__PURE__ */ jsx5(ExpandableSection, { label: "Additional context", children: data.additionalContext }),
1446
+ data.parentIssue && /* @__PURE__ */ jsx5(ExpandableSection, { label: `Parent: ${data.parentIssue.identifier} - ${data.parentIssue.title}`, children: data.parentIssue.description })
936
1447
  ] });
937
1448
  }
938
1449
  function GitHubIssueNewMessage({ data }) {
939
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", gap: 0, children: [
940
- /* @__PURE__ */ jsxs4("box", { flexDirection: "row", gap: 1, children: [
941
- /* @__PURE__ */ jsx4(SourceBadge, { source: "github_issue_new" }),
942
- /* @__PURE__ */ jsx4(MetadataPill, { label: "Issue", value: `#${data.issueNumber}` }),
943
- data.triggeringUser && /* @__PURE__ */ jsx4(MetadataPill, { label: "From", value: `@${data.triggeringUser}` })
1450
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", gap: 0, children: [
1451
+ /* @__PURE__ */ jsxs5("box", { flexDirection: "row", gap: 1, children: [
1452
+ /* @__PURE__ */ jsx5(SourceBadge, { source: "github_issue_new" }),
1453
+ /* @__PURE__ */ jsx5(MetadataPill, { label: "Issue", value: `#${data.issueNumber}` }),
1454
+ data.triggeringUser && /* @__PURE__ */ jsx5(MetadataPill, { label: "From", value: `@${data.triggeringUser}` })
944
1455
  ] }),
945
- /* @__PURE__ */ jsx4("text", { fg: "#ffffff", children: /* @__PURE__ */ jsx4("strong", { children: data.issueTitle }) }),
946
- data.triggeringComment && /* @__PURE__ */ jsx4(MessageBody, { children: data.triggeringComment }),
947
- /* @__PURE__ */ jsx4(ExpandableSection, { label: "Issue description", children: data.issueDescription })
1456
+ /* @__PURE__ */ jsx5("text", { fg: "#ffffff", children: /* @__PURE__ */ jsx5("strong", { children: data.issueTitle }) }),
1457
+ data.triggeringComment && /* @__PURE__ */ jsx5(MessageBody, { children: data.triggeringComment }),
1458
+ /* @__PURE__ */ jsx5(ExpandableSection, { label: "Issue description", children: data.issueDescription })
948
1459
  ] });
949
1460
  }
950
1461
  function GitHubIssueExistingMessage({ data }) {
951
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", gap: 0, children: [
952
- /* @__PURE__ */ jsxs4("box", { flexDirection: "row", gap: 1, children: [
953
- /* @__PURE__ */ jsx4(SourceBadge, { source: "github_issue_existing" }),
954
- /* @__PURE__ */ jsx4(MetadataPill, { label: "Issue", value: `#${data.issueNumber}` }),
955
- data.commentUser && /* @__PURE__ */ jsx4(MetadataPill, { label: "From", value: `@${data.commentUser}` })
1462
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", gap: 0, children: [
1463
+ /* @__PURE__ */ jsxs5("box", { flexDirection: "row", gap: 1, children: [
1464
+ /* @__PURE__ */ jsx5(SourceBadge, { source: "github_issue_existing" }),
1465
+ /* @__PURE__ */ jsx5(MetadataPill, { label: "Issue", value: `#${data.issueNumber}` }),
1466
+ data.commentUser && /* @__PURE__ */ jsx5(MetadataPill, { label: "From", value: `@${data.commentUser}` })
956
1467
  ] }),
957
- /* @__PURE__ */ jsx4(MessageBody, { children: data.commentBody })
1468
+ /* @__PURE__ */ jsx5(MessageBody, { children: data.commentBody })
958
1469
  ] });
959
1470
  }
960
1471
  function GitHubPRNewMessage({ data }) {
961
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", gap: 0, children: [
962
- /* @__PURE__ */ jsxs4("box", { flexDirection: "row", gap: 1, children: [
963
- /* @__PURE__ */ jsx4(SourceBadge, { source: "github_pr_new" }),
964
- /* @__PURE__ */ jsx4(MetadataPill, { label: "PR", value: `#${data.prNumber}` }),
965
- data.mentioningUser && /* @__PURE__ */ jsx4(MetadataPill, { label: "From", value: `@${data.mentioningUser}` })
1472
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", gap: 0, children: [
1473
+ /* @__PURE__ */ jsxs5("box", { flexDirection: "row", gap: 1, children: [
1474
+ /* @__PURE__ */ jsx5(SourceBadge, { source: "github_pr_new" }),
1475
+ /* @__PURE__ */ jsx5(MetadataPill, { label: "PR", value: `#${data.prNumber}` }),
1476
+ data.mentioningUser && /* @__PURE__ */ jsx5(MetadataPill, { label: "From", value: `@${data.mentioningUser}` })
966
1477
  ] }),
967
- /* @__PURE__ */ jsx4(MessageBody, { children: data.contextMessage })
1478
+ /* @__PURE__ */ jsx5(MessageBody, { children: data.contextMessage })
968
1479
  ] });
969
1480
  }
970
1481
  function GitHubPRCodeReviewMessage({ data }) {
971
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", gap: 0, children: [
972
- /* @__PURE__ */ jsxs4("box", { flexDirection: "row", gap: 1, children: [
973
- /* @__PURE__ */ jsx4(SourceBadge, { source: "github_pr_existing_review" }),
974
- /* @__PURE__ */ jsx4(MetadataPill, { label: "PR", value: `#${data.prNumber}` }),
975
- data.commentUser && /* @__PURE__ */ jsx4(MetadataPill, { label: "From", value: `@${data.commentUser}` }),
976
- data.filePath && /* @__PURE__ */ jsx4(MetadataPill, { label: "File", value: data.filePath })
1482
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", gap: 0, children: [
1483
+ /* @__PURE__ */ jsxs5("box", { flexDirection: "row", gap: 1, children: [
1484
+ /* @__PURE__ */ jsx5(SourceBadge, { source: "github_pr_existing_review" }),
1485
+ /* @__PURE__ */ jsx5(MetadataPill, { label: "PR", value: `#${data.prNumber}` }),
1486
+ data.commentUser && /* @__PURE__ */ jsx5(MetadataPill, { label: "From", value: `@${data.commentUser}` }),
1487
+ data.filePath && /* @__PURE__ */ jsx5(MetadataPill, { label: "File", value: data.filePath })
977
1488
  ] }),
978
- /* @__PURE__ */ jsx4(MessageBody, { children: data.commentBody }),
979
- /* @__PURE__ */ jsx4(ExpandableSection, { label: "Diff context", children: data.diffHunk })
1489
+ /* @__PURE__ */ jsx5(MessageBody, { children: data.commentBody }),
1490
+ /* @__PURE__ */ jsx5(ExpandableDiffSection, { label: "Diff context", diffContent: data.diffHunk, filePath: data.filePath })
980
1491
  ] });
981
1492
  }
982
1493
  function GitHubPRReviewMessage({ data }) {
983
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", gap: 0, children: [
984
- /* @__PURE__ */ jsxs4("box", { flexDirection: "row", gap: 1, children: [
985
- /* @__PURE__ */ jsx4(SourceBadge, { source: "github_pr_existing_pr_review" }),
986
- /* @__PURE__ */ jsx4(MetadataPill, { label: "PR", value: `#${data.prNumber}` }),
987
- data.reviewUser && /* @__PURE__ */ jsx4(MetadataPill, { label: "From", value: `@${data.reviewUser}` })
1494
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", gap: 0, children: [
1495
+ /* @__PURE__ */ jsxs5("box", { flexDirection: "row", gap: 1, children: [
1496
+ /* @__PURE__ */ jsx5(SourceBadge, { source: "github_pr_existing_pr_review" }),
1497
+ /* @__PURE__ */ jsx5(MetadataPill, { label: "PR", value: `#${data.prNumber}` }),
1498
+ data.reviewUser && /* @__PURE__ */ jsx5(MetadataPill, { label: "From", value: `@${data.reviewUser}` })
988
1499
  ] }),
989
- /* @__PURE__ */ jsxs4("text", { fg: "#888888", children: [
1500
+ /* @__PURE__ */ jsxs5("text", { fg: "#888888", children: [
990
1501
  "@" + data.reviewUser + " ",
991
- /* @__PURE__ */ jsx4("span", { fg: "#ffffff", children: data.reviewAction })
1502
+ /* @__PURE__ */ jsx5("span", { fg: "#ffffff", children: data.reviewAction })
992
1503
  ] }),
993
- data.commentBody && /* @__PURE__ */ jsx4(MessageBody, { children: data.commentBody })
1504
+ data.commentBody && /* @__PURE__ */ jsx5(MessageBody, { children: data.commentBody })
994
1505
  ] });
995
1506
  }
996
1507
  function GitHubPRGeneralMessage({ data }) {
997
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", gap: 0, children: [
998
- /* @__PURE__ */ jsxs4("box", { flexDirection: "row", gap: 1, children: [
999
- /* @__PURE__ */ jsx4(SourceBadge, { source: "github_pr_existing_general" }),
1000
- /* @__PURE__ */ jsx4(MetadataPill, { label: "PR", value: `#${data.prNumber}` }),
1001
- data.commentUser && /* @__PURE__ */ jsx4(MetadataPill, { label: "From", value: `@${data.commentUser}` })
1508
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", gap: 0, children: [
1509
+ /* @__PURE__ */ jsxs5("box", { flexDirection: "row", gap: 1, children: [
1510
+ /* @__PURE__ */ jsx5(SourceBadge, { source: "github_pr_existing_general" }),
1511
+ /* @__PURE__ */ jsx5(MetadataPill, { label: "PR", value: `#${data.prNumber}` }),
1512
+ data.commentUser && /* @__PURE__ */ jsx5(MetadataPill, { label: "From", value: `@${data.commentUser}` })
1002
1513
  ] }),
1003
- /* @__PURE__ */ jsx4(MessageBody, { children: data.commentBody })
1514
+ /* @__PURE__ */ jsx5(MessageBody, { children: data.commentBody })
1004
1515
  ] });
1005
1516
  }
1006
1517
  function SlackTaskMessage({ data }) {
1007
1518
  const hasDistinctContext = data.threadContext && data.threadContext !== `@${data.latestMessageUser}: ${data.latestMessageText}`;
1008
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", gap: 0, children: [
1009
- /* @__PURE__ */ jsxs4("box", { flexDirection: "row", gap: 1, children: [
1010
- /* @__PURE__ */ jsx4(SourceBadge, { source: "slack_task" }),
1011
- data.workspaceTarget && /* @__PURE__ */ jsx4(MetadataPill, { label: "Target", value: data.workspaceTarget }),
1012
- data.latestMessageUser && /* @__PURE__ */ jsx4(MetadataPill, { label: "From", value: `@${data.latestMessageUser}` })
1519
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", gap: 0, children: [
1520
+ /* @__PURE__ */ jsxs5("box", { flexDirection: "row", gap: 1, children: [
1521
+ /* @__PURE__ */ jsx5(SourceBadge, { source: "slack_task" }),
1522
+ data.workspaceTarget && /* @__PURE__ */ jsx5(MetadataPill, { label: "Target", value: data.workspaceTarget }),
1523
+ data.latestMessageUser && /* @__PURE__ */ jsx5(MetadataPill, { label: "From", value: `@${data.latestMessageUser}` })
1013
1524
  ] }),
1014
- /* @__PURE__ */ jsx4(MessageBody, { children: data.latestMessageText }),
1015
- hasDistinctContext && /* @__PURE__ */ jsx4(ExpandableSection, { label: "Thread context", children: data.threadContext })
1525
+ /* @__PURE__ */ jsx5(MessageBody, { children: data.latestMessageText }),
1526
+ hasDistinctContext && /* @__PURE__ */ jsx5(ExpandableSection, { label: "Thread context", children: data.threadContext })
1016
1527
  ] });
1017
1528
  }
1018
1529
  function StructuredUserMessage({ parsed }) {
1019
1530
  switch (parsed.source) {
1020
1531
  case "ci_failure":
1021
- return /* @__PURE__ */ jsx4(CIFailureMessage, { data: parsed });
1532
+ return /* @__PURE__ */ jsx5(CIFailureMessage, { data: parsed });
1022
1533
  case "linear_issue":
1023
- return /* @__PURE__ */ jsx4(LinearIssueMessage, { data: parsed });
1534
+ return /* @__PURE__ */ jsx5(LinearIssueMessage, { data: parsed });
1024
1535
  case "github_issue_new":
1025
- return /* @__PURE__ */ jsx4(GitHubIssueNewMessage, { data: parsed });
1536
+ return /* @__PURE__ */ jsx5(GitHubIssueNewMessage, { data: parsed });
1026
1537
  case "github_issue_existing":
1027
- return /* @__PURE__ */ jsx4(GitHubIssueExistingMessage, { data: parsed });
1538
+ return /* @__PURE__ */ jsx5(GitHubIssueExistingMessage, { data: parsed });
1028
1539
  case "github_pr_new":
1029
- return /* @__PURE__ */ jsx4(GitHubPRNewMessage, { data: parsed });
1540
+ return /* @__PURE__ */ jsx5(GitHubPRNewMessage, { data: parsed });
1030
1541
  case "github_pr_existing_review":
1031
- return /* @__PURE__ */ jsx4(GitHubPRCodeReviewMessage, { data: parsed });
1542
+ return /* @__PURE__ */ jsx5(GitHubPRCodeReviewMessage, { data: parsed });
1032
1543
  case "github_pr_existing_pr_review":
1033
- return /* @__PURE__ */ jsx4(GitHubPRReviewMessage, { data: parsed });
1544
+ return /* @__PURE__ */ jsx5(GitHubPRReviewMessage, { data: parsed });
1034
1545
  case "github_pr_existing_general":
1035
- return /* @__PURE__ */ jsx4(GitHubPRGeneralMessage, { data: parsed });
1546
+ return /* @__PURE__ */ jsx5(GitHubPRGeneralMessage, { data: parsed });
1036
1547
  case "slack_task":
1037
- return /* @__PURE__ */ jsx4(SlackTaskMessage, { data: parsed });
1548
+ return /* @__PURE__ */ jsx5(SlackTaskMessage, { data: parsed });
1038
1549
  default: {
1039
1550
  const _exhaustive = parsed;
1040
1551
  throw new Error(`Unhandled prompt source: ${_exhaustive.source}`);
@@ -1043,36 +1554,7 @@ function StructuredUserMessage({ parsed }) {
1043
1554
  }
1044
1555
 
1045
1556
  // src/interactive/components/ChatMessage.tsx
1046
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "@opentui/react/jsx-runtime";
1047
- var sharedSyntaxStyle = null;
1048
- function getSyntaxStyle() {
1049
- if (!sharedSyntaxStyle) {
1050
- sharedSyntaxStyle = SyntaxStyle.fromTheme([
1051
- // Markdown text styling
1052
- { scope: ["markup.heading", "markup.heading.1", "markup.heading.2", "markup.heading.3"], style: { foreground: "#66bb6a", bold: true } },
1053
- { scope: ["markup.bold", "markup.strong"], style: { foreground: "#ffffff", bold: true } },
1054
- { scope: ["markup.italic", "markup.emphasis"], style: { foreground: "#e0e0e0", italic: true } },
1055
- { scope: ["markup.link"], style: { foreground: "#7dcfff", underline: true } },
1056
- { scope: ["markup.link.url"], style: { foreground: "#7dcfff", underline: true } },
1057
- { scope: ["markup.link.text"], style: { foreground: "#66bb6a", underline: true } },
1058
- { scope: ["markup.link.label"], style: { foreground: "#66bb6a", underline: true } },
1059
- { scope: ["markup.list"], style: { foreground: "#66bb6a" } },
1060
- { scope: ["markup.quote"], style: { foreground: "#888888", italic: true } },
1061
- { scope: ["markup.raw", "markup.raw.block"], style: { foreground: "#bb9af7" } },
1062
- { scope: ["markup.raw.inline"], style: { foreground: "#bb9af7", background: "#1a1a2e" } },
1063
- // Code syntax highlighting
1064
- { scope: ["keyword", "keyword.control", "keyword.operator"], style: { foreground: "#bb9af7" } },
1065
- { scope: ["string", "string.quoted"], style: { foreground: "#9ece6a" } },
1066
- { scope: ["comment", "comment.line", "comment.block"], style: { foreground: "#555555", italic: true } },
1067
- { scope: ["constant", "constant.numeric", "constant.language"], style: { foreground: "#ff9e64" } },
1068
- { scope: ["entity.name.function", "support.function"], style: { foreground: "#7aa2f7" } },
1069
- { scope: ["entity.name.type", "support.type"], style: { foreground: "#2ac3de" } },
1070
- { scope: ["variable", "variable.other"], style: { foreground: "#c0caf5" } },
1071
- { scope: ["punctuation"], style: { foreground: "#888888" } }
1072
- ]);
1073
- }
1074
- return sharedSyntaxStyle;
1075
- }
1557
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
1076
1558
  var AGENT_LABELS = {
1077
1559
  claude: "Claude",
1078
1560
  codex: "Codex"
@@ -1081,15 +1563,15 @@ function truncate2(text, maxLen) {
1081
1563
  return text.length > maxLen ? text.slice(0, maxLen - 1) + "\u2026" : text;
1082
1564
  }
1083
1565
  function StatusIcon({ status }) {
1084
- if (status === "completed") return /* @__PURE__ */ jsxs5("span", { fg: "#66bb6a", children: [
1566
+ if (status === "completed") return /* @__PURE__ */ jsxs6("span", { fg: "#66bb6a", children: [
1085
1567
  " ",
1086
1568
  "\u2713"
1087
1569
  ] });
1088
- if (status === "failed") return /* @__PURE__ */ jsxs5("span", { fg: "#ff4444", children: [
1570
+ if (status === "failed") return /* @__PURE__ */ jsxs6("span", { fg: "#ff4444", children: [
1089
1571
  " ",
1090
1572
  "\u2717"
1091
1573
  ] });
1092
- if (status === "in_progress") return /* @__PURE__ */ jsxs5("span", { fg: "#ffaa00", children: [
1574
+ if (status === "in_progress") return /* @__PURE__ */ jsxs6("span", { fg: "#ffaa00", children: [
1093
1575
  " ",
1094
1576
  "\u2026"
1095
1577
  ] });
@@ -1103,7 +1585,7 @@ function ActionLine({
1103
1585
  onToggle,
1104
1586
  expanded
1105
1587
  }) {
1106
- return /* @__PURE__ */ jsxs5(
1588
+ return /* @__PURE__ */ jsxs6(
1107
1589
  "box",
1108
1590
  {
1109
1591
  paddingX: 1,
@@ -1111,15 +1593,15 @@ function ActionLine({
1111
1593
  justifyContent: "space-between",
1112
1594
  onMouseDown: onToggle,
1113
1595
  children: [
1114
- /* @__PURE__ */ jsxs5("text", { children: [
1115
- /* @__PURE__ */ jsx5("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx5("strong", { children: label }) }),
1116
- /* @__PURE__ */ jsxs5("span", { fg: "#cccccc", children: [
1596
+ /* @__PURE__ */ jsxs6("text", { children: [
1597
+ /* @__PURE__ */ jsx6("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx6("strong", { children: label }) }),
1598
+ /* @__PURE__ */ jsxs6("span", { fg: "#cccccc", children: [
1117
1599
  " ",
1118
1600
  arg.replace(/\n/g, " ").replace(/\s+/g, " ")
1119
1601
  ] }),
1120
- /* @__PURE__ */ jsx5(StatusIcon, { status })
1602
+ /* @__PURE__ */ jsx6(StatusIcon, { status })
1121
1603
  ] }),
1122
- expandable && /* @__PURE__ */ jsx5("text", { children: /* @__PURE__ */ jsx5("span", { fg: "#555555", children: expanded ? "\u25BE" : "\u25B8" }) })
1604
+ expandable && /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#555555", children: expanded ? "\u25BE" : "\u25B8" }) })
1123
1605
  ]
1124
1606
  }
1125
1607
  );
@@ -1130,9 +1612,9 @@ function ExpandableAction({
1130
1612
  status,
1131
1613
  children
1132
1614
  }) {
1133
- const [expanded, setExpanded] = useState4(false);
1134
- return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
1135
- /* @__PURE__ */ jsx5(
1615
+ const [expanded, setExpanded] = useState5(false);
1616
+ return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", children: [
1617
+ /* @__PURE__ */ jsx6(
1136
1618
  ActionLine,
1137
1619
  {
1138
1620
  label,
@@ -1146,12 +1628,59 @@ function ExpandableAction({
1146
1628
  expanded && children
1147
1629
  ] });
1148
1630
  }
1631
+ var CHANGE_STYLE = {
1632
+ add: { color: "#66bb6a", sign: "+" },
1633
+ delete: { color: "#ff4444", sign: "-" }
1634
+ };
1635
+ var DEFAULT_CHANGE_STYLE = { color: "#ffaa00", sign: "~" };
1636
+ function getChangeStyle(action) {
1637
+ return CHANGE_STYLE[action] ?? DEFAULT_CHANGE_STYLE;
1638
+ }
1639
+ function PatchOperation({ op, defaultExpanded = false }) {
1640
+ const [expanded, setExpanded] = useState5(defaultExpanded);
1641
+ const { color, sign } = getChangeStyle(op.action);
1642
+ const stats = useMemo4(() => op.diff ? countDiffStats(op.diff) : null, [op.diff]);
1643
+ if (!op.diff) {
1644
+ return /* @__PURE__ */ jsxs6("text", { children: [
1645
+ /* @__PURE__ */ jsx6("span", { fg: color, children: sign }),
1646
+ /* @__PURE__ */ jsxs6("span", { fg: "#cccccc", children: [
1647
+ " ",
1648
+ op.path
1649
+ ] })
1650
+ ] });
1651
+ }
1652
+ return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", children: [
1653
+ /* @__PURE__ */ jsxs6("box", { onMouseDown: () => setExpanded((e) => !e), flexDirection: "row", justifyContent: "space-between", children: [
1654
+ /* @__PURE__ */ jsxs6("text", { children: [
1655
+ /* @__PURE__ */ jsx6("span", { fg: color, children: sign }),
1656
+ /* @__PURE__ */ jsxs6("span", { fg: "#cccccc", children: [
1657
+ " ",
1658
+ op.path
1659
+ ] }),
1660
+ stats && /* @__PURE__ */ jsxs6(Fragment3, { children: [
1661
+ /* @__PURE__ */ jsx6("span", { fg: "#444444", children: " " }),
1662
+ stats.removed > 0 && /* @__PURE__ */ jsxs6("span", { fg: "#ff4444", children: [
1663
+ "-",
1664
+ stats.removed
1665
+ ] }),
1666
+ stats.removed > 0 && stats.added > 0 && /* @__PURE__ */ jsx6("span", { fg: "#444444", children: " " }),
1667
+ stats.added > 0 && /* @__PURE__ */ jsxs6("span", { fg: "#66bb6a", children: [
1668
+ "+",
1669
+ stats.added
1670
+ ] })
1671
+ ] })
1672
+ ] }),
1673
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#555555", children: expanded ? "\u25BE" : "\u25B8" }) })
1674
+ ] }),
1675
+ expanded && /* @__PURE__ */ jsx6("box", { paddingLeft: 1, children: /* @__PURE__ */ jsx6(DiffView, { diff: op.diff, filePath: op.path }) })
1676
+ ] });
1677
+ }
1149
1678
  function isStructuredPrompt(p) {
1150
1679
  return p.source !== "raw";
1151
1680
  }
1152
1681
  function UserMessageContent({ content }) {
1153
- const parsed = useMemo3(() => parseUserMessage(content), [content]);
1154
- return /* @__PURE__ */ jsx5(
1682
+ const parsed = useMemo4(() => parseUserMessage(content), [content]);
1683
+ return /* @__PURE__ */ jsx6(
1155
1684
  "box",
1156
1685
  {
1157
1686
  flexDirection: "column",
@@ -1159,9 +1688,9 @@ function UserMessageContent({ content }) {
1159
1688
  paddingX: 2,
1160
1689
  paddingY: 1,
1161
1690
  marginX: 1,
1162
- children: isStructuredPrompt(parsed) ? /* @__PURE__ */ jsx5(StructuredUserMessage, { parsed }) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
1163
- /* @__PURE__ */ jsx5("text", { children: /* @__PURE__ */ jsx5("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx5("strong", { children: "You" }) }) }),
1164
- /* @__PURE__ */ jsx5("text", { fg: "#ffffff", selectable: true, children: content })
1691
+ children: isStructuredPrompt(parsed) ? /* @__PURE__ */ jsx6(StructuredUserMessage, { parsed }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
1692
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx6("strong", { children: "You" }) }) }),
1693
+ /* @__PURE__ */ jsx6("text", { fg: "#ffffff", selectable: true, children: content })
1165
1694
  ] })
1166
1695
  }
1167
1696
  );
@@ -1170,14 +1699,14 @@ function ChatMessage({ message, provider }) {
1170
1699
  switch (message.type) {
1171
1700
  case "user": {
1172
1701
  if (message.content === "Request interrupted") {
1173
- return /* @__PURE__ */ jsx5("box", { paddingX: 1, justifyContent: "center", children: /* @__PURE__ */ jsx5("text", { fg: "#555555", children: "--- Request interrupted ---" }) });
1702
+ return /* @__PURE__ */ jsx6("box", { paddingX: 1, justifyContent: "center", children: /* @__PURE__ */ jsx6("text", { fg: "#555555", children: "--- Request interrupted ---" }) });
1174
1703
  }
1175
- return /* @__PURE__ */ jsx5(UserMessageContent, { content: message.content });
1704
+ return /* @__PURE__ */ jsx6(UserMessageContent, { content: message.content });
1176
1705
  }
1177
1706
  case "agent":
1178
- return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", paddingX: 1, children: [
1179
- /* @__PURE__ */ jsx5("text", { children: /* @__PURE__ */ jsx5("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx5("strong", { children: AGENT_LABELS[provider] }) }) }),
1180
- /* @__PURE__ */ jsx5(
1707
+ return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", paddingX: 1, children: [
1708
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx6("strong", { children: AGENT_LABELS[provider] }) }) }),
1709
+ /* @__PURE__ */ jsx6(
1181
1710
  "markdown",
1182
1711
  {
1183
1712
  content: message.content,
@@ -1190,57 +1719,54 @@ function ChatMessage({ message, provider }) {
1190
1719
  )
1191
1720
  ] });
1192
1721
  case "reasoning":
1193
- return /* @__PURE__ */ jsx5("box", { paddingX: 1, children: /* @__PURE__ */ jsx5(SpinnerLabel, { color: "#555555", label: "thinking" }) });
1722
+ return /* @__PURE__ */ jsx6("box", { paddingX: 1, children: /* @__PURE__ */ jsx6(SpinnerLabel, { color: "#555555", label: "thinking" }) });
1194
1723
  case "command": {
1195
1724
  const cmdArg = truncate2(message.command, 60);
1196
1725
  const exitSuffix = message.exitCode !== void 0 && message.exitCode !== 0 ? ` (exit ${message.exitCode})` : "";
1197
1726
  if (!message.output) {
1198
- return /* @__PURE__ */ jsx5(ActionLine, { label: "Command", arg: cmdArg + exitSuffix, status: message.status });
1727
+ return /* @__PURE__ */ jsx6(ActionLine, { label: "Command", arg: cmdArg + exitSuffix, status: message.status });
1199
1728
  }
1200
- return /* @__PURE__ */ jsx5(ExpandableAction, { label: "Command", arg: cmdArg + exitSuffix, status: message.status, children: /* @__PURE__ */ jsx5("box", { paddingLeft: 2, paddingX: 1, children: /* @__PURE__ */ jsx5("text", { fg: "#888888", selectable: true, children: message.output.length > 500 ? message.output.slice(0, 500) + "\n\u2026" : message.output }) }) });
1729
+ return /* @__PURE__ */ jsx6(ExpandableAction, { label: "Command", arg: cmdArg + exitSuffix, status: message.status, children: /* @__PURE__ */ jsx6("box", { paddingLeft: 2, paddingX: 1, children: /* @__PURE__ */ jsx6("text", { fg: "#888888", selectable: true, children: message.output.length > 500 ? message.output.slice(0, 500) + "\n\u2026" : message.output }) }) });
1201
1730
  }
1202
1731
  case "file_change": {
1203
1732
  const count = message.changes.length;
1204
- return /* @__PURE__ */ jsx5(ExpandableAction, { label: "Files", arg: `${count}`, status: message.status, children: /* @__PURE__ */ jsx5("box", { flexDirection: "column", paddingLeft: 2, paddingX: 1, children: message.changes.map((change, i) => /* @__PURE__ */ jsxs5("text", { children: [
1205
- /* @__PURE__ */ jsx5(
1206
- "span",
1207
- {
1208
- fg: change.kind === "add" ? "#66bb6a" : change.kind === "delete" ? "#ff4444" : "#ffaa00",
1209
- children: change.kind === "add" ? "+" : change.kind === "delete" ? "-" : "~"
1210
- }
1211
- ),
1212
- /* @__PURE__ */ jsxs5("span", { fg: "#cccccc", children: [
1213
- " ",
1214
- change.path
1215
- ] })
1216
- ] }, i)) }) });
1733
+ return /* @__PURE__ */ jsx6(ExpandableAction, { label: "Files", arg: `${count}`, status: message.status, children: /* @__PURE__ */ jsx6("box", { flexDirection: "column", paddingLeft: 2, paddingX: 1, children: message.changes.map((change, i) => {
1734
+ const { color, sign } = getChangeStyle(change.kind);
1735
+ return /* @__PURE__ */ jsxs6("text", { children: [
1736
+ /* @__PURE__ */ jsx6("span", { fg: color, children: sign }),
1737
+ /* @__PURE__ */ jsxs6("span", { fg: "#cccccc", children: [
1738
+ " ",
1739
+ change.path
1740
+ ] })
1741
+ ] }, i);
1742
+ }) }) });
1217
1743
  }
1218
1744
  case "tool_call": {
1219
1745
  const toolArg = truncate2(message.tool, 50);
1220
1746
  if (message.output || message.input) {
1221
- return /* @__PURE__ */ jsx5(ExpandableAction, { label: "Tool", arg: toolArg, status: message.status, children: /* @__PURE__ */ jsxs5("box", { flexDirection: "column", paddingLeft: 2, paddingX: 1, children: [
1222
- message.input && /* @__PURE__ */ jsx5("text", { fg: "#888888", selectable: true, children: typeof message.input === "string" ? truncate2(message.input, 300) : truncate2(JSON.stringify(message.input, null, 2), 300) }),
1223
- message.output && /* @__PURE__ */ jsx5("text", { fg: "#888888", selectable: true, children: truncate2(message.output, 300) })
1747
+ return /* @__PURE__ */ jsx6(ExpandableAction, { label: "Tool", arg: toolArg, status: message.status, children: /* @__PURE__ */ jsxs6("box", { flexDirection: "column", paddingLeft: 2, paddingX: 1, children: [
1748
+ message.input && /* @__PURE__ */ jsx6("text", { fg: "#888888", selectable: true, children: typeof message.input === "string" ? truncate2(message.input, 300) : truncate2(JSON.stringify(message.input, null, 2), 300) }),
1749
+ message.output && /* @__PURE__ */ jsx6("text", { fg: "#888888", selectable: true, children: truncate2(message.output, 300) })
1224
1750
  ] }) });
1225
1751
  }
1226
- return /* @__PURE__ */ jsx5(ActionLine, { label: "Tool", arg: toolArg, status: message.status });
1752
+ return /* @__PURE__ */ jsx6(ActionLine, { label: "Tool", arg: toolArg, status: message.status });
1227
1753
  }
1228
1754
  case "web_search":
1229
- return /* @__PURE__ */ jsx5(ActionLine, { label: "Search", arg: truncate2(message.query, 50), status: message.status });
1755
+ return /* @__PURE__ */ jsx6(ActionLine, { label: "Search", arg: truncate2(message.query, 50), status: message.status });
1230
1756
  case "todo_list": {
1231
1757
  const completed = message.items.filter((i) => i.completed).length;
1232
1758
  const total = message.items.length;
1233
- return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", paddingX: 1, children: [
1234
- /* @__PURE__ */ jsx5("text", { children: /* @__PURE__ */ jsx5("span", { fg: "#888888", children: /* @__PURE__ */ jsxs5("strong", { children: [
1759
+ return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", paddingX: 1, children: [
1760
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#888888", children: /* @__PURE__ */ jsxs6("strong", { children: [
1235
1761
  "Plan (",
1236
1762
  completed,
1237
1763
  "/",
1238
1764
  total,
1239
1765
  ")"
1240
1766
  ] }) }) }),
1241
- message.items.map((item, i) => /* @__PURE__ */ jsxs5("text", { children: [
1242
- /* @__PURE__ */ jsx5("span", { fg: item.completed ? "#66bb6a" : "#555555", children: item.completed ? " \u2611" : " \u2610" }),
1243
- /* @__PURE__ */ jsxs5("span", { fg: "#ffffff", children: [
1767
+ message.items.map((item, i) => /* @__PURE__ */ jsxs6("text", { children: [
1768
+ /* @__PURE__ */ jsx6("span", { fg: item.completed ? "#66bb6a" : "#555555", children: item.completed ? " \u2611" : " \u2610" }),
1769
+ /* @__PURE__ */ jsxs6("span", { fg: "#ffffff", children: [
1244
1770
  " ",
1245
1771
  item.text
1246
1772
  ] })
@@ -1248,26 +1774,15 @@ function ChatMessage({ message, provider }) {
1248
1774
  ] });
1249
1775
  }
1250
1776
  case "subagent":
1251
- return /* @__PURE__ */ jsx5(ActionLine, { label: "Agent", arg: truncate2(message.description, 50), status: message.status });
1777
+ return /* @__PURE__ */ jsx6(ActionLine, { label: "Agent", arg: truncate2(message.description, 50), status: message.status });
1252
1778
  case "error":
1253
- return /* @__PURE__ */ jsx5("box", { paddingX: 1, children: /* @__PURE__ */ jsx5("text", { fg: "#ff4444", selectable: true, children: message.message }) });
1779
+ return /* @__PURE__ */ jsx6("box", { paddingX: 1, children: /* @__PURE__ */ jsx6("text", { fg: "#ff4444", selectable: true, children: message.message }) });
1254
1780
  case "skill":
1255
- return /* @__PURE__ */ jsx5(ActionLine, { label: "Skill", arg: message.skillName + (message.args ? ` ${message.args}` : ""), status: message.status });
1781
+ return /* @__PURE__ */ jsx6(ActionLine, { label: "Skill", arg: message.skillName + (message.args ? ` ${message.args}` : ""), status: message.status });
1256
1782
  case "patch": {
1257
1783
  const opCount = message.operations.length;
1258
- return /* @__PURE__ */ jsx5(ExpandableAction, { label: "Patch", arg: `${opCount}`, children: /* @__PURE__ */ jsx5("box", { flexDirection: "column", paddingLeft: 2, paddingX: 1, children: message.operations.map((op, i) => /* @__PURE__ */ jsxs5("text", { children: [
1259
- /* @__PURE__ */ jsx5(
1260
- "span",
1261
- {
1262
- fg: op.action === "add" ? "#66bb6a" : op.action === "delete" ? "#ff4444" : "#ffaa00",
1263
- children: op.action === "add" ? "+" : op.action === "delete" ? "-" : "~"
1264
- }
1265
- ),
1266
- /* @__PURE__ */ jsxs5("span", { fg: "#cccccc", children: [
1267
- " ",
1268
- op.path
1269
- ] })
1270
- ] }, i)) }) });
1784
+ const autoExpand = opCount === 1 && !!message.operations[0].diff;
1785
+ return /* @__PURE__ */ jsx6(ExpandableAction, { label: "Patch", arg: `${opCount}`, children: /* @__PURE__ */ jsx6("box", { flexDirection: "column", paddingLeft: 2, paddingX: 1, children: message.operations.map((op, i) => /* @__PURE__ */ jsx6(PatchOperation, { op, defaultExpanded: autoExpand }, i)) }) });
1271
1786
  }
1272
1787
  default:
1273
1788
  return null;
@@ -1275,7 +1790,7 @@ function ChatMessage({ message, provider }) {
1275
1790
  }
1276
1791
 
1277
1792
  // src/interactive/components/ChatArea.tsx
1278
- import { jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
1793
+ import { jsx as jsx7, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
1279
1794
  var textareaKeyBindings = [
1280
1795
  { name: "return", action: "submit" },
1281
1796
  { name: "return", shift: true, action: "newline" },
@@ -1298,10 +1813,10 @@ function ChatArea({
1298
1813
  isProcessing,
1299
1814
  loading
1300
1815
  }) {
1301
- const textareaRef = useRef(null);
1302
- const scrollboxRef = useRef(null);
1303
- const isAtBottomRef = useRef(true);
1304
- const onSendMessageRef = useRef(onSendMessage);
1816
+ const textareaRef = useRef2(null);
1817
+ const scrollboxRef = useRef2(null);
1818
+ const isAtBottomRef = useRef2(true);
1819
+ const onSendMessageRef = useRef2(onSendMessage);
1305
1820
  onSendMessageRef.current = onSendMessage;
1306
1821
  const inputFocused = focusPanel === "chat-input";
1307
1822
  const tabsFocused = focusPanel === "chat-tabs";
@@ -1343,20 +1858,7 @@ function ChatArea({
1343
1858
  textarea.onSubmit = void 0;
1344
1859
  };
1345
1860
  }, []);
1346
- useKeyboard2((key) => {
1347
- if (!historyFocused) return;
1348
- const scrollbox = scrollboxRef.current;
1349
- if (!scrollbox) return;
1350
- if (key.name === "j" || key.name === "down") {
1351
- scrollbox.scrollBy(3);
1352
- return;
1353
- }
1354
- if (key.name === "k" || key.name === "up") {
1355
- scrollbox.scrollBy(-3);
1356
- return;
1357
- }
1358
- });
1359
- useKeyboard2((key) => {
1861
+ useKeyboard3((key) => {
1360
1862
  if (!tabsFocused || chats.length <= 1 || !selectedChatId) return;
1361
1863
  if (key.name === "left" || key.name === "right" || key.name === "tab" && !key.shift) {
1362
1864
  const currentIdx = chats.findIndex((c) => c.id === selectedChatId);
@@ -1366,7 +1868,7 @@ function ChatArea({
1366
1868
  }
1367
1869
  }
1368
1870
  });
1369
- return /* @__PURE__ */ jsxs6(
1871
+ return /* @__PURE__ */ jsxs7(
1370
1872
  "box",
1371
1873
  {
1372
1874
  flexGrow: 1,
@@ -1378,7 +1880,7 @@ function ChatArea({
1378
1880
  flexDirection: "column",
1379
1881
  backgroundColor: "#000000",
1380
1882
  children: [
1381
- chats.length > 1 && /* @__PURE__ */ jsx6(
1883
+ chats.length > 1 && /* @__PURE__ */ jsx7(
1382
1884
  "box",
1383
1885
  {
1384
1886
  height: 3,
@@ -1394,7 +1896,7 @@ function ChatArea({
1394
1896
  children: chats.map((chat) => {
1395
1897
  const isSelected = chat.id === selectedChatId;
1396
1898
  const label = `${chat.title}${chat.processing ? " ..." : ""}`;
1397
- return /* @__PURE__ */ jsx6(
1899
+ return /* @__PURE__ */ jsx7(
1398
1900
  "box",
1399
1901
  {
1400
1902
  backgroundColor: isSelected ? "#1a1a1a" : void 0,
@@ -1403,14 +1905,14 @@ function ChatArea({
1403
1905
  onSelectChat(chat.id);
1404
1906
  onFocus("chat-tabs");
1405
1907
  },
1406
- children: /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: isSelected ? modeColors.text : "#666666", children: label }) })
1908
+ children: /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: isSelected ? modeColors.text : "#666666", children: label }) })
1407
1909
  },
1408
1910
  chat.id
1409
1911
  );
1410
1912
  })
1411
1913
  }
1412
1914
  ),
1413
- /* @__PURE__ */ jsx6(
1915
+ /* @__PURE__ */ jsx7(
1414
1916
  "scrollbox",
1415
1917
  {
1416
1918
  ref: scrollboxRef,
@@ -1419,22 +1921,22 @@ function ChatArea({
1419
1921
  paddingX: 1,
1420
1922
  paddingY: 1,
1421
1923
  onMouseDown: () => onFocus("chat-history"),
1422
- children: loading ? /* @__PURE__ */ jsx6("box", { paddingX: 1, children: /* @__PURE__ */ jsx6("text", { fg: "#666666", children: "Loading messages..." }) }) : displayMessages.length === 0 ? /* @__PURE__ */ jsx6("box", { paddingX: 1, children: /* @__PURE__ */ jsx6("text", { fg: "#666666", children: "No messages yet. Send a message to start chatting." }) }) : (() => {
1924
+ children: loading ? /* @__PURE__ */ jsx7("box", { paddingX: 1, children: /* @__PURE__ */ jsx7("text", { fg: "#666666", children: "Loading messages..." }) }) : displayMessages.length === 0 ? /* @__PURE__ */ jsx7("box", { paddingX: 1, children: /* @__PURE__ */ jsx7("text", { fg: "#666666", children: "No messages yet. Send a message to start chatting." }) }) : (() => {
1423
1925
  const activeChat = chats.find((c) => c.id === selectedChatId);
1424
1926
  const provider = activeChat?.provider ?? "claude";
1425
1927
  const primaryTypes = /* @__PURE__ */ new Set(["user", "agent", "todo_list"]);
1426
1928
  return displayMessages.map((msg, i) => {
1427
1929
  const prev = i > 0 ? displayMessages[i - 1] : null;
1428
1930
  const needsSpacing = prev && (primaryTypes.has(msg.type) || primaryTypes.has(prev.type));
1429
- return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", children: [
1430
- needsSpacing && /* @__PURE__ */ jsx6("box", { height: 1 }),
1431
- /* @__PURE__ */ jsx6(ChatMessage, { message: msg, provider })
1931
+ return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", children: [
1932
+ needsSpacing && /* @__PURE__ */ jsx7("box", { height: 1 }),
1933
+ /* @__PURE__ */ jsx7(ChatMessage, { message: msg, provider })
1432
1934
  ] }, msg.id || `msg-${i}`);
1433
1935
  });
1434
1936
  })()
1435
1937
  }
1436
1938
  ),
1437
- /* @__PURE__ */ jsxs6(
1939
+ /* @__PURE__ */ jsxs7(
1438
1940
  "box",
1439
1941
  {
1440
1942
  flexDirection: "column",
@@ -1444,7 +1946,7 @@ function ChatArea({
1444
1946
  backgroundColor: "#111111",
1445
1947
  onMouseDown: () => onFocus("chat-input"),
1446
1948
  children: [
1447
- /* @__PURE__ */ jsx6("box", { height: 6, paddingX: 1, children: /* @__PURE__ */ jsx6(
1949
+ /* @__PURE__ */ jsx7("box", { height: 6, paddingX: 1, children: /* @__PURE__ */ jsx7(
1448
1950
  "textarea",
1449
1951
  {
1450
1952
  ref: textareaRef,
@@ -1459,7 +1961,7 @@ function ChatArea({
1459
1961
  height: 5
1460
1962
  }
1461
1963
  ) }),
1462
- /* @__PURE__ */ jsxs6(
1964
+ /* @__PURE__ */ jsxs7(
1463
1965
  "box",
1464
1966
  {
1465
1967
  height: 1,
@@ -1467,8 +1969,8 @@ function ChatArea({
1467
1969
  flexDirection: "row",
1468
1970
  justifyContent: "space-between",
1469
1971
  children: [
1470
- /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: modeColors.text, children: taskMode === "build" ? "Build" : "Plan" }) }),
1471
- isProcessing && /* @__PURE__ */ jsx6(SpinnerLabel, { color: "#ffaa00", label: "thinking" })
1972
+ /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: modeColors.text, children: taskMode === "build" ? "Build" : "Plan" }) }),
1973
+ isProcessing && /* @__PURE__ */ jsx7(SpinnerLabel, { color: "#ffaa00", label: "thinking" })
1472
1974
  ]
1473
1975
  }
1474
1976
  )
@@ -1481,19 +1983,37 @@ function ChatArea({
1481
1983
  }
1482
1984
 
1483
1985
  // src/interactive/components/WorkspaceInfo.tsx
1484
- import React5, { useState as useState5, useMemo as useMemo4, useCallback as useCallback4 } from "react";
1986
+ import React6, { useState as useState6, useMemo as useMemo5, useCallback as useCallback5, useRef as useRef3 } from "react";
1485
1987
  import open from "open";
1486
- import { useKeyboard as useKeyboard3 } from "@opentui/react";
1487
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
1988
+ import { useKeyboard as useKeyboard4 } from "@opentui/react";
1989
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
1488
1990
  var WEB_APP_URL = process.env.REPLICAS_WEB_URL || "https://replicas.dev";
1489
1991
  function buildInteractiveItems(workspaceId, status, previews, wakingWorkspaceId) {
1490
1992
  const items = [];
1993
+ const hasAnyPr = status.repoStatuses?.some((repo) => repo.prUrl);
1994
+ const githubConfigured = status.environmentDetails?.githubAccessConfigured;
1995
+ if (workspaceId && status.status === "active" && !hasAnyPr && githubConfigured) {
1996
+ items.push({ type: "createPr" });
1997
+ }
1491
1998
  if (workspaceId) {
1492
1999
  items.push({ type: "dashboard", workspaceId });
1493
2000
  }
1494
2001
  if (workspaceId && status.status === "sleeping" && wakingWorkspaceId !== workspaceId) {
1495
2002
  items.push({ type: "wake", workspaceId });
1496
2003
  }
2004
+ const repoDiffs = [];
2005
+ if (status.repoStatuses) {
2006
+ for (const repo of status.repoStatuses) {
2007
+ if (repo.gitDiff?.fullDiff && (repo.gitDiff.added > 0 || repo.gitDiff.removed > 0)) {
2008
+ repoDiffs.push({ type: "diff", repoName: repo.name, diff: repo.gitDiff.fullDiff, added: repo.gitDiff.added, removed: repo.gitDiff.removed });
2009
+ }
2010
+ }
2011
+ }
2012
+ items.push({ type: "view-chat" });
2013
+ if (repoDiffs.length > 0) {
2014
+ items.push({ type: "view-diff" });
2015
+ items.push(...repoDiffs);
2016
+ }
1497
2017
  for (const preview of previews) {
1498
2018
  if (preview.publicUrl) {
1499
2019
  items.push({ type: "preview", url: preview.publicUrl, port: preview.port });
@@ -1506,11 +2026,6 @@ function buildInteractiveItems(workspaceId, status, previews, wakingWorkspaceId)
1506
2026
  }
1507
2027
  }
1508
2028
  }
1509
- const hasAnyPr = status.repoStatuses?.some((repo) => repo.prUrl);
1510
- const githubConfigured = status.environmentDetails?.githubAccessConfigured;
1511
- if (workspaceId && status.status === "active" && !hasAnyPr && githubConfigured) {
1512
- items.push({ type: "createPr" });
1513
- }
1514
2029
  return items;
1515
2030
  }
1516
2031
  function getItemLabel(item) {
@@ -1523,10 +2038,36 @@ function getItemLabel(item) {
1523
2038
  return `\u2197 Preview :${item.port}`;
1524
2039
  case "pr":
1525
2040
  return `\u2197 View PR (${item.repoName})`;
2041
+ case "diff":
2042
+ return `\u25B8 Diff +${item.added} -${item.removed}`;
2043
+ case "view-chat":
2044
+ return "1 Chat";
2045
+ case "view-diff":
2046
+ return "2 Diff";
1526
2047
  case "createPr":
1527
2048
  return "+ Create PR";
1528
2049
  }
1529
2050
  }
2051
+ function getItemId(item) {
2052
+ switch (item.type) {
2053
+ case "dashboard":
2054
+ return "info-dashboard";
2055
+ case "wake":
2056
+ return "info-wake";
2057
+ case "preview":
2058
+ return `info-preview-${item.port}`;
2059
+ case "pr":
2060
+ return `info-pr-${item.repoName}`;
2061
+ case "diff":
2062
+ return `info-diff-${item.repoName}`;
2063
+ case "view-chat":
2064
+ return "info-view-chat";
2065
+ case "view-diff":
2066
+ return "info-view-diff";
2067
+ case "createPr":
2068
+ return "info-create-pr";
2069
+ }
2070
+ }
1530
2071
  var AUTH_METHOD_LABELS = {
1531
2072
  oauth: "OAuth",
1532
2073
  api_key: "API Key",
@@ -1535,24 +2076,24 @@ var AUTH_METHOD_LABELS = {
1535
2076
  };
1536
2077
  function StatusDot({ status }) {
1537
2078
  if (status === true || status === "yes") {
1538
- return /* @__PURE__ */ jsx7("span", { fg: "#66bb6a", children: "\u2713" });
2079
+ return /* @__PURE__ */ jsx8("span", { fg: "#66bb6a", children: "\u2713" });
1539
2080
  }
1540
2081
  if (status === false || status === "no") {
1541
- return /* @__PURE__ */ jsx7("span", { fg: "#ff4444", children: "\u2717" });
2082
+ return /* @__PURE__ */ jsx8("span", { fg: "#ff4444", children: "\u2717" });
1542
2083
  }
1543
- return /* @__PURE__ */ jsx7("span", { fg: "#555555", children: "-" });
2084
+ return /* @__PURE__ */ jsx8("span", { fg: "#555555", children: "-" });
1544
2085
  }
1545
2086
  function SectionLabel({ title }) {
1546
- return /* @__PURE__ */ jsx7("box", { backgroundColor: "#151515", paddingX: 1, children: /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: "#666666", children: /* @__PURE__ */ jsx7("strong", { children: title.toUpperCase() }) }) }) });
2087
+ return /* @__PURE__ */ jsx8("box", { backgroundColor: "#151515", paddingX: 1, children: /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8("span", { fg: "#666666", children: /* @__PURE__ */ jsx8("strong", { children: title.toUpperCase() }) }) }) });
1547
2088
  }
1548
2089
  function Section({ title, children }) {
1549
- return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", marginBottom: 1, children: [
1550
- /* @__PURE__ */ jsx7(SectionLabel, { title }),
1551
- /* @__PURE__ */ jsx7("box", { flexDirection: "column", backgroundColor: "#0a0a0a", children })
2090
+ return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", marginBottom: 1, children: [
2091
+ /* @__PURE__ */ jsx8(SectionLabel, { title }),
2092
+ /* @__PURE__ */ jsx8("box", { flexDirection: "column", backgroundColor: "#0a0a0a", children })
1552
2093
  ] });
1553
2094
  }
1554
2095
  function CardItem({ label, status }) {
1555
- return /* @__PURE__ */ jsxs7(
2096
+ return /* @__PURE__ */ jsxs8(
1556
2097
  "box",
1557
2098
  {
1558
2099
  flexDirection: "row",
@@ -1561,8 +2102,8 @@ function CardItem({ label, status }) {
1561
2102
  backgroundColor: "#111111",
1562
2103
  marginBottom: 0,
1563
2104
  children: [
1564
- /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: "#cccccc", children: label }) }),
1565
- /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7(StatusDot, { status }) })
2105
+ /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8("span", { fg: "#cccccc", children: label }) }),
2106
+ /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8(StatusDot, { status }) })
1566
2107
  ]
1567
2108
  }
1568
2109
  );
@@ -1575,40 +2116,77 @@ function DetailList({
1575
2116
  const actualSet = new Set(actual);
1576
2117
  const rows = uniqueExpected.length > 0 ? uniqueExpected : Array.from(actualSet).sort((a, b) => a.localeCompare(b));
1577
2118
  if (rows.length === 0) return null;
1578
- return /* @__PURE__ */ jsx7(Fragment3, { children: rows.map((item, i) => /* @__PURE__ */ jsx7(CardItem, { label: item, status: actualSet.has(item) }, i)) });
2119
+ return /* @__PURE__ */ jsx8(Fragment4, { children: rows.map((item, i) => /* @__PURE__ */ jsx8(CardItem, { label: item, status: actualSet.has(item) }, i)) });
1579
2120
  }
1580
- function InteractiveRow({ label, highlighted, disabled, onClick }) {
2121
+ function InteractiveRow({ id, label, highlighted, disabled, onClick }) {
1581
2122
  if (disabled) {
1582
- return /* @__PURE__ */ jsx7("box", { paddingX: 1, backgroundColor: "#111111", children: /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: "#444444", children: label }) }) });
2123
+ return /* @__PURE__ */ jsx8("box", { id, paddingX: 1, backgroundColor: "#111111", children: /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8("span", { fg: "#444444", children: label }) }) });
1583
2124
  }
1584
- return /* @__PURE__ */ jsx7(
2125
+ return /* @__PURE__ */ jsx8(
1585
2126
  "box",
1586
2127
  {
2128
+ id,
1587
2129
  paddingX: 1,
1588
2130
  backgroundColor: highlighted ? "#1a2a1a" : "#111111",
1589
2131
  onMouseDown: onClick,
1590
- children: /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: highlighted ? "#66bb6a" : "#7dcfff", children: label }) })
2132
+ children: /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8("span", { fg: highlighted ? "#66bb6a" : "#7dcfff", children: label }) })
2133
+ }
2134
+ );
2135
+ }
2136
+ function ViewModeRow({
2137
+ id,
2138
+ label,
2139
+ active,
2140
+ highlighted,
2141
+ disabled,
2142
+ onClick
2143
+ }) {
2144
+ const bg = highlighted ? "#1a2a1a" : "#111111";
2145
+ const fg = disabled ? "#444444" : active ? "#66bb6a" : "#cccccc";
2146
+ const marker = active ? "\u25B8" : " ";
2147
+ return /* @__PURE__ */ jsx8(
2148
+ "box",
2149
+ {
2150
+ id,
2151
+ paddingX: 1,
2152
+ backgroundColor: bg,
2153
+ onMouseDown: disabled ? void 0 : onClick,
2154
+ children: /* @__PURE__ */ jsxs8("text", { children: [
2155
+ /* @__PURE__ */ jsxs8("span", { fg: active ? "#66bb6a" : "#555555", children: [
2156
+ marker,
2157
+ " "
2158
+ ] }),
2159
+ active ? /* @__PURE__ */ jsx8("span", { fg, children: /* @__PURE__ */ jsx8("strong", { children: label }) }) : /* @__PURE__ */ jsx8("span", { fg, children: label })
2160
+ ] })
1591
2161
  }
1592
2162
  );
1593
2163
  }
1594
- function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, envConfig, previews, onWakeWorkspace, onCreatePr, isPlanMode, wakingWorkspaceId }) {
2164
+ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, envConfig, previews, onWakeWorkspace, onViewDiff, wakingWorkspaceId, viewMode, viewingDiffRepoName, onSelectChatMode, onSelectDiffMode, onCreatePr, isPlanMode }) {
1595
2165
  const borderColor = focused ? "#66bb6a" : "#333333";
1596
- const [cursorIndex, setCursorIndex] = useState5(0);
1597
- const interactiveItems = useMemo4(() => {
2166
+ const [cursorIndex, setCursorIndex] = useState6(0);
2167
+ const interactiveItems = useMemo5(() => {
1598
2168
  if (!status || !workspaceName) return [];
1599
2169
  return buildInteractiveItems(workspaceId, status, previews, wakingWorkspaceId);
1600
2170
  }, [workspaceId, workspaceName, status, previews, wakingWorkspaceId]);
2171
+ const diffItems = useMemo5(
2172
+ () => interactiveItems.filter((item) => item.type === "diff"),
2173
+ [interactiveItems]
2174
+ );
2175
+ const hasAnyDiff = diffItems.length > 0;
1601
2176
  const safeCursor = interactiveItems.length > 0 ? Math.min(cursorIndex, interactiveItems.length - 1) : 0;
1602
- const moveCursor = useCallback4(
2177
+ const scrollboxRef = useRef3(null);
2178
+ const moveCursor = useCallback5(
1603
2179
  (next) => {
1604
2180
  const len = interactiveItems.length;
1605
2181
  if (len === 0) return;
1606
2182
  const wrapped = (next % len + len) % len;
1607
2183
  setCursorIndex(wrapped);
2184
+ const item = interactiveItems[wrapped];
2185
+ if (item) scrollboxRef.current?.scrollChildIntoView(getItemId(item));
1608
2186
  },
1609
- [interactiveItems.length]
2187
+ [interactiveItems]
1610
2188
  );
1611
- const handleAction = useCallback4(
2189
+ const handleAction = useCallback5(
1612
2190
  (item) => {
1613
2191
  if (!item) return;
1614
2192
  switch (item.type) {
@@ -1627,6 +2205,15 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1627
2205
  open(item.url).catch(() => {
1628
2206
  });
1629
2207
  break;
2208
+ case "diff":
2209
+ onViewDiff?.(item.diff, item.repoName);
2210
+ break;
2211
+ case "view-chat":
2212
+ onSelectChatMode();
2213
+ break;
2214
+ case "view-diff":
2215
+ onSelectDiffMode();
2216
+ break;
1630
2217
  case "createPr":
1631
2218
  if (!isPlanMode) onCreatePr();
1632
2219
  break;
@@ -1634,7 +2221,7 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1634
2221
  },
1635
2222
  [onWakeWorkspace, onCreatePr]
1636
2223
  );
1637
- useKeyboard3((key) => {
2224
+ useKeyboard4((key) => {
1638
2225
  if (!focused) return;
1639
2226
  if (interactiveItems.length === 0) return;
1640
2227
  if (key.name === "j" || key.name === "down") {
@@ -1668,7 +2255,7 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1668
2255
  return;
1669
2256
  }
1670
2257
  });
1671
- const isHighlighted = useCallback4(
2258
+ const isHighlighted = useCallback5(
1672
2259
  (item) => {
1673
2260
  if (!focused) return false;
1674
2261
  const idx = interactiveItems.indexOf(item);
@@ -1676,19 +2263,20 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1676
2263
  },
1677
2264
  [focused, interactiveItems, safeCursor]
1678
2265
  );
1679
- const findItem = useCallback4(
2266
+ const findItem = useCallback5(
1680
2267
  (type, key) => {
1681
2268
  return interactiveItems.find((item) => {
1682
2269
  if (item.type !== type) return false;
1683
2270
  if (type === "preview" && key) return item.port === Number(key);
1684
2271
  if (type === "pr" && key) return item.repoName === key;
2272
+ if (type === "diff" && key) return item.repoName === key;
1685
2273
  return true;
1686
2274
  });
1687
2275
  },
1688
2276
  [interactiveItems]
1689
2277
  );
1690
2278
  if (!workspaceName) {
1691
- return /* @__PURE__ */ jsx7(
2279
+ return /* @__PURE__ */ jsx8(
1692
2280
  "box",
1693
2281
  {
1694
2282
  width: 30,
@@ -1700,12 +2288,12 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1700
2288
  flexDirection: "column",
1701
2289
  paddingX: 1,
1702
2290
  backgroundColor: "#000000",
1703
- children: /* @__PURE__ */ jsx7("text", { fg: "#666666", children: "Select a workspace" })
2291
+ children: /* @__PURE__ */ jsx8("text", { fg: "#666666", children: "Select a workspace" })
1704
2292
  }
1705
2293
  );
1706
2294
  }
1707
2295
  if (loading || !status) {
1708
- return /* @__PURE__ */ jsx7(
2296
+ return /* @__PURE__ */ jsx8(
1709
2297
  "box",
1710
2298
  {
1711
2299
  width: 30,
@@ -1717,7 +2305,7 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1717
2305
  flexDirection: "column",
1718
2306
  paddingX: 1,
1719
2307
  backgroundColor: "#000000",
1720
- children: /* @__PURE__ */ jsx7("text", { fg: "#666666", children: "Loading..." })
2308
+ children: /* @__PURE__ */ jsx8("text", { fg: "#666666", children: "Loading..." })
1721
2309
  }
1722
2310
  );
1723
2311
  }
@@ -1729,7 +2317,7 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1729
2317
  const dashboardItem = findItem("dashboard");
1730
2318
  const wakeItem = findItem("wake");
1731
2319
  const createPrItem = findItem("createPr");
1732
- return /* @__PURE__ */ jsx7(
2320
+ return /* @__PURE__ */ jsx8(
1733
2321
  "box",
1734
2322
  {
1735
2323
  width: 30,
@@ -1740,16 +2328,16 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1740
2328
  titleAlignment: "center",
1741
2329
  flexDirection: "column",
1742
2330
  backgroundColor: "#000000",
1743
- children: /* @__PURE__ */ jsxs7("scrollbox", { focused, flexGrow: 1, children: [
1744
- /* @__PURE__ */ jsx7("box", { backgroundColor: "#111111", paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ jsxs7("box", { flexDirection: "column", children: [
1745
- /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx7("strong", { children: workspaceName }) }) }),
1746
- /* @__PURE__ */ jsxs7("text", { children: [
1747
- /* @__PURE__ */ jsxs7("span", { fg: statusColor, children: [
2331
+ children: /* @__PURE__ */ jsxs8("scrollbox", { ref: scrollboxRef, focused: false, flexGrow: 1, children: [
2332
+ /* @__PURE__ */ jsx8("box", { backgroundColor: "#111111", paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ jsxs8("box", { flexDirection: "column", children: [
2333
+ /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx8("strong", { children: workspaceName }) }) }),
2334
+ /* @__PURE__ */ jsxs8("text", { children: [
2335
+ /* @__PURE__ */ jsxs8("span", { fg: statusColor, children: [
1748
2336
  "\u25CF",
1749
2337
  " ",
1750
2338
  status.status
1751
2339
  ] }),
1752
- env && /* @__PURE__ */ jsxs7("span", { fg: "#555555", children: [
2340
+ env && /* @__PURE__ */ jsxs8("span", { fg: "#555555", children: [
1753
2341
  " ",
1754
2342
  "\u2502",
1755
2343
  " ",
@@ -1757,94 +2345,158 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1757
2345
  ] })
1758
2346
  ] })
1759
2347
  ] }) }),
1760
- dashboardItem && /* @__PURE__ */ jsx7(
2348
+ createPrItem && /* @__PURE__ */ jsx8(
2349
+ InteractiveRow,
2350
+ {
2351
+ id: getItemId(createPrItem),
2352
+ label: getItemLabel(createPrItem),
2353
+ highlighted: isHighlighted(createPrItem),
2354
+ disabled: isPlanMode,
2355
+ onClick: () => handleAction(createPrItem)
2356
+ }
2357
+ ),
2358
+ dashboardItem && /* @__PURE__ */ jsx8(
1761
2359
  InteractiveRow,
1762
2360
  {
2361
+ id: getItemId(dashboardItem),
1763
2362
  label: getItemLabel(dashboardItem),
1764
2363
  highlighted: isHighlighted(dashboardItem),
1765
2364
  onClick: () => handleAction(dashboardItem)
1766
2365
  }
1767
2366
  ),
1768
- wakeItem && /* @__PURE__ */ jsx7(
2367
+ wakeItem && /* @__PURE__ */ jsx8(
1769
2368
  InteractiveRow,
1770
2369
  {
2370
+ id: getItemId(wakeItem),
1771
2371
  label: getItemLabel(wakeItem),
1772
2372
  highlighted: isHighlighted(wakeItem),
1773
2373
  onClick: () => handleAction(wakeItem)
1774
2374
  }
1775
2375
  ),
1776
- (status.isClaudeProcessing || status.isCodexProcessing) && /* @__PURE__ */ jsxs7("box", { backgroundColor: "#1a1500", paddingX: 1, marginX: 1, marginBottom: 1, children: [
1777
- status.isClaudeProcessing && /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsxs7("span", { fg: "#ffaa00", children: [
2376
+ (status.isClaudeProcessing || status.isCodexProcessing) && /* @__PURE__ */ jsxs8("box", { backgroundColor: "#1a1500", paddingX: 1, marginX: 1, marginBottom: 1, children: [
2377
+ status.isClaudeProcessing && /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsxs8("span", { fg: "#ffaa00", children: [
1778
2378
  "\u25C6",
1779
2379
  " Claude thinking..."
1780
2380
  ] }) }),
1781
- status.isCodexProcessing && /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsxs7("span", { fg: "#ffaa00", children: [
2381
+ status.isCodexProcessing && /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsxs8("span", { fg: "#ffaa00", children: [
1782
2382
  "\u25C6",
1783
2383
  " Codex thinking..."
1784
2384
  ] }) })
1785
2385
  ] }),
1786
- env && /* @__PURE__ */ jsxs7(Section, { title: "Agents", children: [
1787
- /* @__PURE__ */ jsxs7("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
1788
- /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: "#cccccc", children: "Claude" }) }),
1789
- /* @__PURE__ */ jsx7("text", { children: AUTH_METHOD_LABELS[env.claudeAuthMethod] ? /* @__PURE__ */ jsx7("span", { fg: "#66bb6a", children: AUTH_METHOD_LABELS[env.claudeAuthMethod] }) : /* @__PURE__ */ jsx7("span", { fg: "#ff4444", children: "\u2717" }) })
2386
+ env && /* @__PURE__ */ jsxs8(Section, { title: "Agents", children: [
2387
+ /* @__PURE__ */ jsxs8("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
2388
+ /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8("span", { fg: "#cccccc", children: "Claude" }) }),
2389
+ /* @__PURE__ */ jsx8("text", { children: AUTH_METHOD_LABELS[env.claudeAuthMethod] ? /* @__PURE__ */ jsx8("span", { fg: "#66bb6a", children: AUTH_METHOD_LABELS[env.claudeAuthMethod] }) : /* @__PURE__ */ jsx8("span", { fg: "#ff4444", children: "\u2717" }) })
1790
2390
  ] }),
1791
- /* @__PURE__ */ jsxs7("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
1792
- /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: "#cccccc", children: "Codex" }) }),
1793
- /* @__PURE__ */ jsx7("text", { children: AUTH_METHOD_LABELS[env.codexAuthMethod] ? /* @__PURE__ */ jsx7("span", { fg: "#66bb6a", children: AUTH_METHOD_LABELS[env.codexAuthMethod] }) : /* @__PURE__ */ jsx7("span", { fg: "#ff4444", children: "\u2717" }) })
2391
+ /* @__PURE__ */ jsxs8("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
2392
+ /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8("span", { fg: "#cccccc", children: "Codex" }) }),
2393
+ /* @__PURE__ */ jsx8("text", { children: AUTH_METHOD_LABELS[env.codexAuthMethod] ? /* @__PURE__ */ jsx8("span", { fg: "#66bb6a", children: AUTH_METHOD_LABELS[env.codexAuthMethod] }) : /* @__PURE__ */ jsx8("span", { fg: "#ff4444", children: "\u2717" }) })
1794
2394
  ] })
1795
2395
  ] }),
1796
- env && /* @__PURE__ */ jsxs7(Section, { title: "Integrations", children: [
1797
- /* @__PURE__ */ jsx7(CardItem, { label: "GitHub", status: env.githubAccessConfigured }),
1798
- /* @__PURE__ */ jsx7(CardItem, { label: "Slack", status: env.slackAccessConfigured }),
1799
- /* @__PURE__ */ jsx7(CardItem, { label: "Linear", status: env.linearAccessConfigured })
2396
+ /* @__PURE__ */ jsx8(Section, { title: "View", children: (() => {
2397
+ const chatItem = findItem("view-chat");
2398
+ const diffItem = findItem("view-diff");
2399
+ return /* @__PURE__ */ jsxs8(Fragment4, { children: [
2400
+ chatItem && /* @__PURE__ */ jsx8(
2401
+ ViewModeRow,
2402
+ {
2403
+ id: getItemId(chatItem),
2404
+ label: "1 Chat",
2405
+ active: viewMode === "chat",
2406
+ highlighted: isHighlighted(chatItem),
2407
+ onClick: () => handleAction(chatItem)
2408
+ }
2409
+ ),
2410
+ diffItem ? /* @__PURE__ */ jsx8(
2411
+ ViewModeRow,
2412
+ {
2413
+ id: getItemId(diffItem),
2414
+ label: "2 Diff",
2415
+ active: viewMode === "diff",
2416
+ highlighted: isHighlighted(diffItem),
2417
+ onClick: () => handleAction(diffItem)
2418
+ }
2419
+ ) : /* @__PURE__ */ jsx8(ViewModeRow, { label: "2 Diff", active: false, highlighted: false, disabled: true, onClick: () => {
2420
+ } }),
2421
+ hasAnyDiff && diffItems.map((item, i) => {
2422
+ const isActive = viewMode === "diff" && viewingDiffRepoName === item.repoName;
2423
+ const cursorOn = isHighlighted(item);
2424
+ const bg = cursorOn ? "#1a2a1a" : isActive ? "#0d2b0d" : "#111111";
2425
+ const nameColor = isActive || cursorOn ? "#66bb6a" : "#cccccc";
2426
+ return /* @__PURE__ */ jsxs8(
2427
+ "box",
2428
+ {
2429
+ id: getItemId(item),
2430
+ paddingX: 1,
2431
+ backgroundColor: bg,
2432
+ onMouseDown: () => handleAction(item),
2433
+ flexDirection: "row",
2434
+ justifyContent: "space-between",
2435
+ children: [
2436
+ /* @__PURE__ */ jsxs8("text", { children: [
2437
+ /* @__PURE__ */ jsx8("span", { fg: isActive ? "#66bb6a" : "#555555", children: isActive ? " \u25B8 " : " \u2514 " }),
2438
+ isActive ? /* @__PURE__ */ jsx8("span", { fg: nameColor, children: /* @__PURE__ */ jsx8("strong", { children: item.repoName }) }) : /* @__PURE__ */ jsx8("span", { fg: nameColor, children: item.repoName })
2439
+ ] }),
2440
+ /* @__PURE__ */ jsxs8("text", { children: [
2441
+ item.added > 0 && /* @__PURE__ */ jsxs8("span", { fg: "#66bb6a", children: [
2442
+ "+",
2443
+ item.added
2444
+ ] }),
2445
+ item.added > 0 && item.removed > 0 && /* @__PURE__ */ jsx8("span", { fg: "#444444", children: " " }),
2446
+ item.removed > 0 && /* @__PURE__ */ jsxs8("span", { fg: "#ff4444", children: [
2447
+ "-",
2448
+ item.removed
2449
+ ] })
2450
+ ] })
2451
+ ]
2452
+ },
2453
+ `diff-${i}`
2454
+ );
2455
+ })
2456
+ ] });
2457
+ })() }),
2458
+ env && /* @__PURE__ */ jsxs8(Section, { title: "Integrations", children: [
2459
+ /* @__PURE__ */ jsx8(CardItem, { label: "GitHub", status: env.githubAccessConfigured }),
2460
+ /* @__PURE__ */ jsx8(CardItem, { label: "Slack", status: env.slackAccessConfigured }),
2461
+ /* @__PURE__ */ jsx8(CardItem, { label: "Linear", status: env.linearAccessConfigured })
1800
2462
  ] }),
1801
- previews.length > 0 && /* @__PURE__ */ jsx7(Section, { title: "Previews", children: previews.map((preview, i) => {
2463
+ previews.length > 0 && /* @__PURE__ */ jsx8(Section, { title: "Previews", children: previews.map((preview, i) => {
1802
2464
  const previewItem = findItem("preview", String(preview.port));
1803
- return /* @__PURE__ */ jsxs7(
2465
+ return /* @__PURE__ */ jsxs8(
1804
2466
  "box",
1805
2467
  {
2468
+ id: previewItem ? getItemId(previewItem) : void 0,
1806
2469
  flexDirection: "row",
1807
2470
  justifyContent: "space-between",
1808
2471
  paddingX: 1,
1809
2472
  backgroundColor: previewItem && isHighlighted(previewItem) ? "#1a2a1a" : "#111111",
1810
2473
  onMouseDown: () => previewItem && handleAction(previewItem),
1811
2474
  children: [
1812
- /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsxs7("span", { fg: "#cccccc", children: [
2475
+ /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsxs8("span", { fg: "#cccccc", children: [
1813
2476
  ":",
1814
2477
  preview.port
1815
2478
  ] }) }),
1816
- /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: previewItem && isHighlighted(previewItem) ? "#66bb6a" : "#7dcfff", children: "\u2197" }) })
2479
+ /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8("span", { fg: previewItem && isHighlighted(previewItem) ? "#66bb6a" : "#7dcfff", children: "\u2197" }) })
1817
2480
  ]
1818
2481
  },
1819
2482
  i
1820
2483
  );
1821
2484
  }) }),
1822
- status.repoStatuses && status.repoStatuses.length > 0 && /* @__PURE__ */ jsx7(Section, { title: "Repositories", children: status.repoStatuses.map((repo, i) => {
2485
+ status.repoStatuses && status.repoStatuses.length > 0 && /* @__PURE__ */ jsx8(Section, { title: "Repositories", children: status.repoStatuses.map((repo, i) => {
1823
2486
  const prItem = findItem("pr", repo.name);
1824
- return /* @__PURE__ */ jsxs7(React5.Fragment, { children: [
1825
- /* @__PURE__ */ jsx7("box", { backgroundColor: "#111111", paddingX: 1, children: /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx7("strong", { children: repo.name }) }) }) }),
1826
- /* @__PURE__ */ jsx7("box", { backgroundColor: "#111111", paddingX: 1, flexDirection: "row", justifyContent: "space-between", children: /* @__PURE__ */ jsxs7("text", { children: [
1827
- /* @__PURE__ */ jsxs7("span", { fg: "#555555", children: [
2487
+ return /* @__PURE__ */ jsxs8(React6.Fragment, { children: [
2488
+ /* @__PURE__ */ jsx8("box", { backgroundColor: "#111111", paddingX: 1, children: /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx8("strong", { children: repo.name }) }) }) }),
2489
+ /* @__PURE__ */ jsx8("box", { backgroundColor: "#111111", paddingX: 1, flexDirection: "row", justifyContent: "space-between", children: /* @__PURE__ */ jsxs8("text", { children: [
2490
+ /* @__PURE__ */ jsxs8("span", { fg: "#555555", children: [
1828
2491
  "\u2514",
1829
2492
  " "
1830
2493
  ] }),
1831
- /* @__PURE__ */ jsx7("span", { fg: repo.currentBranch !== repo.defaultBranch ? "#ffaa00" : "#66bb6a", children: repo.currentBranch })
2494
+ /* @__PURE__ */ jsx8("span", { fg: repo.currentBranch !== repo.defaultBranch ? "#ffaa00" : "#66bb6a", children: repo.currentBranch })
1832
2495
  ] }) }),
1833
- repo.gitDiff && (repo.gitDiff.added > 0 || repo.gitDiff.removed > 0) && /* @__PURE__ */ jsx7("box", { backgroundColor: "#111111", paddingX: 1, flexDirection: "row", children: /* @__PURE__ */ jsxs7("text", { children: [
1834
- /* @__PURE__ */ jsx7("span", { fg: "#555555", children: " " }),
1835
- /* @__PURE__ */ jsxs7("span", { fg: "#66bb6a", children: [
1836
- "+",
1837
- repo.gitDiff.added
1838
- ] }),
1839
- /* @__PURE__ */ jsx7("span", { fg: "#444444", children: " / " }),
1840
- /* @__PURE__ */ jsxs7("span", { fg: "#ff4444", children: [
1841
- "-",
1842
- repo.gitDiff.removed
1843
- ] })
1844
- ] }) }),
1845
- prItem && /* @__PURE__ */ jsx7(
2496
+ prItem && /* @__PURE__ */ jsx8(
1846
2497
  InteractiveRow,
1847
2498
  {
2499
+ id: getItemId(prItem),
1848
2500
  label: ` ${getItemLabel(prItem)}`,
1849
2501
  highlighted: isHighlighted(prItem),
1850
2502
  onClick: () => handleAction(prItem)
@@ -1852,44 +2504,35 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1852
2504
  )
1853
2505
  ] }, i);
1854
2506
  }) }),
1855
- createPrItem && /* @__PURE__ */ jsx7(
1856
- InteractiveRow,
1857
- {
1858
- label: getItemLabel(createPrItem),
1859
- highlighted: isHighlighted(createPrItem),
1860
- disabled: isPlanMode,
1861
- onClick: () => handleAction(createPrItem)
1862
- }
1863
- ),
1864
- env && /* @__PURE__ */ jsxs7(Section, { title: "Hooks", children: [
1865
- /* @__PURE__ */ jsx7(CardItem, { label: "Global warm", status: env.globalWarmHookCompleted.status }),
1866
- env.repositories.map((repo, i) => /* @__PURE__ */ jsxs7(React5.Fragment, { children: [
1867
- /* @__PURE__ */ jsx7("box", { backgroundColor: "#111111", paddingX: 1, children: /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx7("strong", { children: repo.repositoryName }) }) }) }),
1868
- /* @__PURE__ */ jsxs7("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
1869
- /* @__PURE__ */ jsxs7("text", { children: [
1870
- /* @__PURE__ */ jsxs7("span", { fg: "#555555", children: [
2507
+ env && /* @__PURE__ */ jsxs8(Section, { title: "Hooks", children: [
2508
+ /* @__PURE__ */ jsx8(CardItem, { label: "Global warm", status: env.globalWarmHookCompleted.status }),
2509
+ env.repositories.map((repo, i) => /* @__PURE__ */ jsxs8(React6.Fragment, { children: [
2510
+ /* @__PURE__ */ jsx8("box", { backgroundColor: "#111111", paddingX: 1, children: /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx8("strong", { children: repo.repositoryName }) }) }) }),
2511
+ /* @__PURE__ */ jsxs8("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
2512
+ /* @__PURE__ */ jsxs8("text", { children: [
2513
+ /* @__PURE__ */ jsxs8("span", { fg: "#555555", children: [
1871
2514
  "\u251C",
1872
2515
  " "
1873
2516
  ] }),
1874
- /* @__PURE__ */ jsx7("span", { fg: "#cccccc", children: "warm" })
2517
+ /* @__PURE__ */ jsx8("span", { fg: "#cccccc", children: "warm" })
1875
2518
  ] }),
1876
- /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7(StatusDot, { status: repo.warmHookCompleted }) })
2519
+ /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8(StatusDot, { status: repo.warmHookCompleted }) })
1877
2520
  ] }),
1878
- /* @__PURE__ */ jsxs7("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
1879
- /* @__PURE__ */ jsxs7("text", { children: [
1880
- /* @__PURE__ */ jsxs7("span", { fg: "#555555", children: [
2521
+ /* @__PURE__ */ jsxs8("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
2522
+ /* @__PURE__ */ jsxs8("text", { children: [
2523
+ /* @__PURE__ */ jsxs8("span", { fg: "#555555", children: [
1881
2524
  "\u2514",
1882
2525
  " "
1883
2526
  ] }),
1884
- /* @__PURE__ */ jsx7("span", { fg: "#cccccc", children: "start" })
2527
+ /* @__PURE__ */ jsx8("span", { fg: "#cccccc", children: "start" })
1885
2528
  ] }),
1886
- /* @__PURE__ */ jsx7("text", { children: /* @__PURE__ */ jsx7(StatusDot, { status: repo.startHookCompleted }) })
2529
+ /* @__PURE__ */ jsx8("text", { children: /* @__PURE__ */ jsx8(StatusDot, { status: repo.startHookCompleted }) })
1887
2530
  ] })
1888
2531
  ] }, i))
1889
2532
  ] }),
1890
- env && (expectedSkills.length > 0 || env.skillsInstalled.length > 0) && /* @__PURE__ */ jsx7(Section, { title: "Skills", children: /* @__PURE__ */ jsx7(DetailList, { expected: expectedSkills, actual: env.skillsInstalled }) }),
1891
- env && (expectedGlobalVars.length > 0 || env.envVarsSet.length > 0) && /* @__PURE__ */ jsx7(Section, { title: "Env Vars", children: /* @__PURE__ */ jsx7(DetailList, { expected: expectedGlobalVars, actual: env.envVarsSet }) }),
1892
- env && (expectedGlobalFiles.length > 0 || env.filesUploaded.length > 0) && /* @__PURE__ */ jsx7(Section, { title: "Files", children: /* @__PURE__ */ jsx7(DetailList, { expected: expectedGlobalFiles, actual: env.filesUploaded }) })
2533
+ env && (expectedSkills.length > 0 || env.skillsInstalled.length > 0) && /* @__PURE__ */ jsx8(Section, { title: "Skills", children: /* @__PURE__ */ jsx8(DetailList, { expected: expectedSkills, actual: env.skillsInstalled }) }),
2534
+ env && (expectedGlobalVars.length > 0 || env.envVarsSet.length > 0) && /* @__PURE__ */ jsx8(Section, { title: "Env Vars", children: /* @__PURE__ */ jsx8(DetailList, { expected: expectedGlobalVars, actual: env.envVarsSet }) }),
2535
+ env && (expectedGlobalFiles.length > 0 || env.filesUploaded.length > 0) && /* @__PURE__ */ jsx8(Section, { title: "Files", children: /* @__PURE__ */ jsx8(DetailList, { expected: expectedGlobalFiles, actual: env.filesUploaded }) })
1893
2536
  ] })
1894
2537
  }
1895
2538
  );
@@ -1899,8 +2542,8 @@ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, e
1899
2542
  import { useTerminalDimensions as useTerminalDimensions2 } from "@opentui/react";
1900
2543
 
1901
2544
  // src/interactive/toast-context.tsx
1902
- import { createContext as createContext2, useContext as useContext2, useState as useState6, useCallback as useCallback5, useRef as useRef2 } from "react";
1903
- import { jsx as jsx8 } from "@opentui/react/jsx-runtime";
2545
+ import { createContext as createContext2, useContext as useContext2, useState as useState7, useCallback as useCallback6, useRef as useRef4 } from "react";
2546
+ import { jsx as jsx9 } from "@opentui/react/jsx-runtime";
1904
2547
  var ToastContext = createContext2(null);
1905
2548
  function useToast() {
1906
2549
  const ctx = useContext2(ToastContext);
@@ -1908,9 +2551,9 @@ function useToast() {
1908
2551
  return ctx;
1909
2552
  }
1910
2553
  function ToastProvider({ children }) {
1911
- const [current, setCurrent] = useState6(null);
1912
- const timeoutRef = useRef2(null);
1913
- const show = useCallback5((options) => {
2554
+ const [current, setCurrent] = useState7(null);
2555
+ const timeoutRef = useRef4(null);
2556
+ const show = useCallback6((options) => {
1914
2557
  const { message, variant = "info", duration = 3e3 } = options;
1915
2558
  setCurrent({ message, variant });
1916
2559
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
@@ -1918,15 +2561,15 @@ function ToastProvider({ children }) {
1918
2561
  setCurrent(null);
1919
2562
  }, duration);
1920
2563
  }, []);
1921
- const error = useCallback5((err) => {
2564
+ const error = useCallback6((err) => {
1922
2565
  const message = err instanceof Error ? err.message : "An error occurred";
1923
2566
  show({ message, variant: "error", duration: 5e3 });
1924
2567
  }, [show]);
1925
- return /* @__PURE__ */ jsx8(ToastContext.Provider, { value: { current, show, error }, children });
2568
+ return /* @__PURE__ */ jsx9(ToastContext.Provider, { value: { current, show, error }, children });
1926
2569
  }
1927
2570
 
1928
2571
  // src/interactive/components/Toast.tsx
1929
- import { jsx as jsx9 } from "@opentui/react/jsx-runtime";
2572
+ import { jsx as jsx10 } from "@opentui/react/jsx-runtime";
1930
2573
  var VARIANT_COLORS = {
1931
2574
  info: { border: "#66bb6a", bg: "#0a1a0a" },
1932
2575
  success: { border: "#66bb6a", bg: "#0a1a0a" },
@@ -1939,7 +2582,7 @@ function Toast() {
1939
2582
  if (!current) return null;
1940
2583
  const colors = VARIANT_COLORS[current.variant] ?? VARIANT_COLORS.info;
1941
2584
  const toastWidth = Math.min(60, width - 4);
1942
- return /* @__PURE__ */ jsx9(
2585
+ return /* @__PURE__ */ jsx10(
1943
2586
  "box",
1944
2587
  {
1945
2588
  position: "absolute",
@@ -1953,13 +2596,13 @@ function Toast() {
1953
2596
  border: true,
1954
2597
  borderStyle: "rounded",
1955
2598
  zIndex: 100,
1956
- children: /* @__PURE__ */ jsx9("text", { fg: "#ffffff", selectable: true, children: current.message })
2599
+ children: /* @__PURE__ */ jsx10("text", { fg: "#ffffff", selectable: true, children: current.message })
1957
2600
  }
1958
2601
  );
1959
2602
  }
1960
2603
 
1961
2604
  // src/interactive/App.tsx
1962
- import { jsx as jsx10, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
2605
+ import { jsx as jsx11, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
1963
2606
  var FOCUS_ORDER = ["sidebar", "chat-tabs", "chat-history", "chat-input", "info"];
1964
2607
  var MOCK_CHATS = [
1965
2608
  { id: "mock-claude", provider: "claude", title: "Claude Code", createdAt: "", updatedAt: "", processing: false },
@@ -1981,9 +2624,9 @@ var authValue = {
1981
2624
  queryClient
1982
2625
  };
1983
2626
  function App() {
1984
- return /* @__PURE__ */ jsx10(ReplicasAuthProvider, { value: authValue, children: /* @__PURE__ */ jsxs8(ToastProvider, { children: [
1985
- /* @__PURE__ */ jsx10(AppInner, {}),
1986
- /* @__PURE__ */ jsx10(Toast, {})
2627
+ return /* @__PURE__ */ jsx11(ReplicasAuthProvider, { value: authValue, children: /* @__PURE__ */ jsxs9(ToastProvider, { children: [
2628
+ /* @__PURE__ */ jsx11(AppInner, {}),
2629
+ /* @__PURE__ */ jsx11(Toast, {})
1987
2630
  ] }) });
1988
2631
  }
1989
2632
  function AppInner() {
@@ -2003,17 +2646,19 @@ function AppInner() {
2003
2646
  renderer.off("selection", handler);
2004
2647
  };
2005
2648
  }, [renderer, toast]);
2006
- const [selectedWorkspaceId, setSelectedWorkspaceId] = useState7(null);
2007
- const [selectedChatId, setSelectedChatId] = useState7(null);
2008
- const [focusPanel, setFocusPanel] = useState7("sidebar");
2009
- const [taskMode, setTaskMode] = useState7("build");
2010
- const [mockWorkspaces, setMockWorkspaces] = useState7([]);
2011
- const mockToRealRef = useRef3(/* @__PURE__ */ new Map());
2012
- const mockGroupRef = useRef3(/* @__PURE__ */ new Map());
2013
- const [mockMessages, setMockMessages] = useState7([]);
2014
- const [mockThinking, setMockThinking] = useState7(false);
2015
- const pendingMessageRef = useRef3(/* @__PURE__ */ new Map());
2016
- const mockIds = useMemo5(() => new Set(mockWorkspaces.map((m) => m.id)), [mockWorkspaces]);
2649
+ const [selectedWorkspaceId, setSelectedWorkspaceId] = useState8(null);
2650
+ const [selectedChatId, setSelectedChatId] = useState8(null);
2651
+ const [focusPanel, setFocusPanel] = useState8("sidebar");
2652
+ const [taskMode, setTaskMode] = useState8("build");
2653
+ const [mockWorkspaces, setMockWorkspaces] = useState8([]);
2654
+ const mockToRealRef = useRef5(/* @__PURE__ */ new Map());
2655
+ const mockGroupRef = useRef5(/* @__PURE__ */ new Map());
2656
+ const [mockMessages, setMockMessages] = useState8([]);
2657
+ const [mockThinking, setMockThinking] = useState8(false);
2658
+ const pendingMessageRef = useRef5(/* @__PURE__ */ new Map());
2659
+ const [viewingDiff, setViewingDiff] = useState8(null);
2660
+ const [lastViewedDiff, setLastViewedDiff] = useState8(null);
2661
+ const mockIds = useMemo6(() => new Set(mockWorkspaces.map((m) => m.id)), [mockWorkspaces]);
2017
2662
  const isMockSelected = selectedWorkspaceId ? mockIds.has(selectedWorkspaceId) : false;
2018
2663
  const { data: workspacesData, isLoading: loadingWorkspaces } = useWorkspaces(1, 100, "organization");
2019
2664
  const { data: reposData } = useRepositories();
@@ -2027,7 +2672,7 @@ function AppInner() {
2027
2672
  );
2028
2673
  const { data: chatsData } = useWorkspaceChats(isMockSelected ? null : selectedWorkspaceId);
2029
2674
  const chats = isMockSelected ? MOCK_CHATS : chatsData?.chats ?? [];
2030
- const resolvedChatId = useMemo5(() => {
2675
+ const resolvedChatId = useMemo6(() => {
2031
2676
  if (selectedChatId && chats.find((c) => c.id === selectedChatId)) {
2032
2677
  return selectedChatId;
2033
2678
  }
@@ -2045,22 +2690,22 @@ function AppInner() {
2045
2690
  const deleteWorkspaceMutation = useDeleteWorkspace();
2046
2691
  const wakeWorkspaceMutation = useWakeWorkspace();
2047
2692
  const generateNameMutation = useGenerateWorkspaceName();
2048
- const [wakingWorkspaceId, setWakingWorkspaceId] = useState7(null);
2693
+ const [wakingWorkspaceId, setWakingWorkspaceId] = useState8(null);
2049
2694
  const sendMessageMutation = useSendChatMessage(selectedWorkspaceId, resolvedChatId);
2050
2695
  const interruptMutation = useInterruptChat(selectedWorkspaceId, resolvedChatId);
2051
2696
  const workspaces = workspacesData?.workspaces ?? [];
2052
2697
  const repositories = reposData?.repositories ?? [];
2053
2698
  const repositorySets = setsData?.repository_sets ?? [];
2054
2699
  const previews = previewsData?.previews ?? [];
2055
- const allWorkspaces = useMemo5(() => [...mockWorkspaces, ...workspaces], [mockWorkspaces, workspaces]);
2056
- const groups = useMemo5(
2700
+ const allWorkspaces = useMemo6(() => [...mockWorkspaces, ...workspaces], [mockWorkspaces, workspaces]);
2701
+ const groups = useMemo6(
2057
2702
  () => buildGroups(allWorkspaces, workspacesData, repositories, repositorySets, mockGroupRef.current),
2058
2703
  [allWorkspaces, workspacesData, repositories, repositorySets]
2059
2704
  );
2060
2705
  const selectedWorkspace = allWorkspaces.find((ws) => ws.id === selectedWorkspaceId) ?? null;
2061
2706
  const selectedChat = chats.find((c) => c.id === resolvedChatId) ?? null;
2062
2707
  const isProcessing = mockThinking || (selectedChat?.processing ?? false);
2063
- const displayMessages = useMemo5(() => {
2708
+ const displayMessages = useMemo6(() => {
2064
2709
  if (isMockSelected) return mockMessages;
2065
2710
  const rawEvents = historyData?.events ?? [];
2066
2711
  const events = rawEvents.filter(isAgentBackendEvent);
@@ -2124,23 +2769,27 @@ function AppInner() {
2124
2769
  }, 5e3);
2125
2770
  return () => clearInterval(interval);
2126
2771
  }, [mockWorkspaces.length]);
2127
- const handleSelectWorkspace = useCallback6((workspaceId) => {
2772
+ const handleSelectWorkspace = useCallback7((workspaceId) => {
2128
2773
  setSelectedWorkspaceId(workspaceId);
2129
2774
  setSelectedChatId(null);
2130
2775
  setMockMessages([]);
2131
2776
  setMockThinking(false);
2777
+ setViewingDiff(null);
2778
+ setLastViewedDiff(null);
2132
2779
  setFocusPanel("chat-input");
2133
2780
  }, []);
2134
- const handleFocus = useCallback6((panel) => {
2781
+ const handleFocus = useCallback7((panel) => {
2135
2782
  setFocusPanel(panel);
2136
2783
  }, []);
2137
- const handleCreateWorkspace = useCallback6(
2784
+ const handleCreateWorkspace = useCallback7(
2138
2785
  async (groupId, groupType) => {
2139
2786
  const orgId = getOrganizationId() ?? "";
2140
2787
  const mock = createMockWorkspaceRecord(orgId);
2141
2788
  mockGroupRef.current.set(mock.id, groupId);
2142
2789
  setMockWorkspaces((prev) => [mock, ...prev]);
2143
2790
  setSelectedWorkspaceId(mock.id);
2791
+ setViewingDiff(null);
2792
+ setLastViewedDiff(null);
2144
2793
  setFocusPanel("chat-input");
2145
2794
  try {
2146
2795
  const request = groupType === "set" ? { repository_set_id: groupId.replace(/^set:/, ""), name: mock.name, placeholder: true } : { repository_ids: [groupId], name: mock.name, placeholder: true };
@@ -2154,18 +2803,26 @@ function AppInner() {
2154
2803
  },
2155
2804
  [createWorkspaceMutation, toast]
2156
2805
  );
2157
- const handleDeleteWorkspace = useCallback6(
2806
+ const handleDeleteWorkspace = useCallback7(
2158
2807
  async (workspaceId) => {
2159
2808
  if (mockIds.has(workspaceId)) {
2160
2809
  mockGroupRef.current.delete(workspaceId);
2161
2810
  mockToRealRef.current.delete(workspaceId);
2162
2811
  setMockWorkspaces((prev) => prev.filter((m) => m.id !== workspaceId));
2163
- if (selectedWorkspaceId === workspaceId) setSelectedWorkspaceId(null);
2812
+ if (selectedWorkspaceId === workspaceId) {
2813
+ setSelectedWorkspaceId(null);
2814
+ setViewingDiff(null);
2815
+ setLastViewedDiff(null);
2816
+ }
2164
2817
  return;
2165
2818
  }
2166
2819
  try {
2167
2820
  await deleteWorkspaceMutation.mutateAsync(workspaceId);
2168
- if (selectedWorkspaceId === workspaceId) setSelectedWorkspaceId(null);
2821
+ if (selectedWorkspaceId === workspaceId) {
2822
+ setSelectedWorkspaceId(null);
2823
+ setViewingDiff(null);
2824
+ setLastViewedDiff(null);
2825
+ }
2169
2826
  toast.show({ message: "Workspace deleted", variant: "info" });
2170
2827
  } catch (err) {
2171
2828
  toast.error(err);
@@ -2173,7 +2830,7 @@ function AppInner() {
2173
2830
  },
2174
2831
  [mockIds, selectedWorkspaceId, deleteWorkspaceMutation]
2175
2832
  );
2176
- const handleWakeWorkspace = useCallback6(
2833
+ const handleWakeWorkspace = useCallback7(
2177
2834
  async (workspaceId) => {
2178
2835
  setWakingWorkspaceId(workspaceId);
2179
2836
  try {
@@ -2187,10 +2844,10 @@ function AppInner() {
2187
2844
  },
2188
2845
  [wakeWorkspaceMutation, toast]
2189
2846
  );
2190
- const handleSelectChat = useCallback6((chatId) => {
2847
+ const handleSelectChat = useCallback7((chatId) => {
2191
2848
  setSelectedChatId(chatId);
2192
2849
  }, []);
2193
- useKeyboard4((key) => {
2850
+ useKeyboard5((key) => {
2194
2851
  if (key.name === "f12") {
2195
2852
  renderer.console.toggle();
2196
2853
  return;
@@ -2210,6 +2867,7 @@ function AppInner() {
2210
2867
  const availablePanels = FOCUS_ORDER.filter((p) => {
2211
2868
  if (p === "sidebar" && !showWorkspacePanel) return false;
2212
2869
  if ((p === "chat-tabs" || p === "chat-history" || p === "chat-input") && (!selectedWorkspaceId || statusData?.status === "sleeping")) return false;
2870
+ if (viewingDiff && (p === "chat-tabs" || p === "chat-input")) return false;
2213
2871
  if (p === "chat-tabs" && chats.length <= 1) return false;
2214
2872
  if (p === "info" && !showInfoPanel) return false;
2215
2873
  return true;
@@ -2223,6 +2881,15 @@ function AppInner() {
2223
2881
  setTaskMode((m) => m === "build" ? "plan" : "build");
2224
2882
  return;
2225
2883
  }
2884
+ if ((key.name === "1" || key.name === "2") && focusPanel !== "chat-input") {
2885
+ if (key.name === "1") {
2886
+ setViewingDiff(null);
2887
+ } else if (firstAvailableDiff) {
2888
+ setViewingDiff(firstAvailableDiff);
2889
+ setLastViewedDiff(firstAvailableDiff);
2890
+ }
2891
+ return;
2892
+ }
2226
2893
  if (key.name === "w" && focusPanel !== "sidebar" && focusPanel !== "info" && selectedWorkspaceId) {
2227
2894
  if (statusData?.status === "sleeping" && !wakingWorkspaceId) {
2228
2895
  handleWakeWorkspace(selectedWorkspaceId);
@@ -2231,7 +2898,33 @@ function AppInner() {
2231
2898
  }
2232
2899
  if (focusPanel === "chat-input") return;
2233
2900
  });
2234
- const handleSendMessage = useCallback6(
2901
+ const handleViewDiff = useCallback7((diff, repoName) => {
2902
+ setViewingDiff({ diff, repoName });
2903
+ setLastViewedDiff({ diff, repoName });
2904
+ setFocusPanel("chat-history");
2905
+ }, []);
2906
+ const handleSelectChatMode = useCallback7(() => {
2907
+ setViewingDiff(null);
2908
+ setFocusPanel("chat-history");
2909
+ }, []);
2910
+ const firstAvailableDiff = useMemo6(() => {
2911
+ if (lastViewedDiff) return lastViewedDiff;
2912
+ const repos = statusData?.repoStatuses ?? [];
2913
+ for (const repo of repos) {
2914
+ if (repo.gitDiff?.fullDiff && (repo.gitDiff.added > 0 || repo.gitDiff.removed > 0)) {
2915
+ return { diff: repo.gitDiff.fullDiff, repoName: repo.name };
2916
+ }
2917
+ }
2918
+ return null;
2919
+ }, [lastViewedDiff, statusData?.repoStatuses]);
2920
+ const handleSelectDiffMode = useCallback7(() => {
2921
+ if (firstAvailableDiff) {
2922
+ setViewingDiff(firstAvailableDiff);
2923
+ setLastViewedDiff(firstAvailableDiff);
2924
+ setFocusPanel("chat-history");
2925
+ }
2926
+ }, [firstAvailableDiff]);
2927
+ const handleSendMessage = useCallback7(
2235
2928
  async (message) => {
2236
2929
  if (!selectedWorkspaceId || !resolvedChatId) return;
2237
2930
  if (isMockSelected) {
@@ -2248,9 +2941,9 @@ function AppInner() {
2248
2941
  },
2249
2942
  [selectedWorkspaceId, resolvedChatId, isMockSelected, sendMessageMutation, taskMode]
2250
2943
  );
2251
- return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", height: "100%", backgroundColor: "#000000", children: [
2252
- /* @__PURE__ */ jsxs8("box", { flexDirection: "row", flexGrow: 1, backgroundColor: "#000000", children: [
2253
- showWorkspacePanel && /* @__PURE__ */ jsx10(
2944
+ return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", height: "100%", backgroundColor: "#000000", children: [
2945
+ /* @__PURE__ */ jsxs9("box", { flexDirection: "row", flexGrow: 1, backgroundColor: "#000000", children: [
2946
+ showWorkspacePanel && /* @__PURE__ */ jsx11(
2254
2947
  WorkspaceSidebar,
2255
2948
  {
2256
2949
  groups,
@@ -2265,7 +2958,20 @@ function AppInner() {
2265
2958
  loading: loadingWorkspaces
2266
2959
  }
2267
2960
  ),
2268
- selectedWorkspaceId && statusData?.status !== "sleeping" ? /* @__PURE__ */ jsx10(
2961
+ /* @__PURE__ */ jsx11("box", { flexDirection: "column", flexGrow: 1, children: viewingDiff ? /* @__PURE__ */ jsx11(
2962
+ "box",
2963
+ {
2964
+ flexGrow: 1,
2965
+ border: true,
2966
+ borderStyle: "rounded",
2967
+ borderColor: focusPanel !== "sidebar" && focusPanel !== "info" ? "#66bb6a" : "#333333",
2968
+ backgroundColor: "#000000",
2969
+ flexDirection: "column",
2970
+ title: "Diff",
2971
+ titleAlignment: "center",
2972
+ children: /* @__PURE__ */ jsx11(DiffViewer, { diff: viewingDiff.diff, repoName: viewingDiff.repoName, focused: focusPanel !== "sidebar" && focusPanel !== "info" })
2973
+ }
2974
+ ) : selectedWorkspaceId && statusData?.status !== "sleeping" ? /* @__PURE__ */ jsx11(
2269
2975
  ChatArea,
2270
2976
  {
2271
2977
  chats,
@@ -2279,7 +2985,7 @@ function AppInner() {
2279
2985
  isProcessing,
2280
2986
  loading: loadingMessages
2281
2987
  }
2282
- ) : selectedWorkspaceId && statusData?.status === "sleeping" ? /* @__PURE__ */ jsxs8(
2988
+ ) : selectedWorkspaceId && statusData?.status === "sleeping" ? /* @__PURE__ */ jsxs9(
2283
2989
  "box",
2284
2990
  {
2285
2991
  flexGrow: 1,
@@ -2292,18 +2998,18 @@ function AppInner() {
2292
2998
  flexDirection: "column",
2293
2999
  gap: 1,
2294
3000
  children: [
2295
- /* @__PURE__ */ jsxs8("text", { fg: "#888888", children: [
3001
+ /* @__PURE__ */ jsxs9("text", { fg: "#888888", children: [
2296
3002
  "\u263E",
2297
3003
  " This workspace is sleeping"
2298
3004
  ] }),
2299
- /* @__PURE__ */ jsxs8("text", { fg: "#555555", children: [
3005
+ /* @__PURE__ */ jsxs9("text", { fg: "#555555", children: [
2300
3006
  "Press ",
2301
- /* @__PURE__ */ jsx10("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx10("strong", { children: "w" }) }),
3007
+ /* @__PURE__ */ jsx11("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx11("strong", { children: "w" }) }),
2302
3008
  " to wake it up"
2303
3009
  ] })
2304
3010
  ]
2305
3011
  }
2306
- ) : /* @__PURE__ */ jsx10(
3012
+ ) : /* @__PURE__ */ jsx11(
2307
3013
  "box",
2308
3014
  {
2309
3015
  flexGrow: 1,
@@ -2313,10 +3019,10 @@ function AppInner() {
2313
3019
  backgroundColor: "#000000",
2314
3020
  justifyContent: "center",
2315
3021
  alignItems: "center",
2316
- children: /* @__PURE__ */ jsx10("text", { fg: "#555555", children: "Create a workspace to begin building!" })
3022
+ children: /* @__PURE__ */ jsx11("text", { fg: "#555555", children: "Create a workspace to begin building!" })
2317
3023
  }
2318
- ),
2319
- showInfoPanel && /* @__PURE__ */ jsx10(
3024
+ ) }),
3025
+ showInfoPanel && /* @__PURE__ */ jsx11(
2320
3026
  WorkspaceInfo,
2321
3027
  {
2322
3028
  status: statusData ?? null,
@@ -2331,31 +3037,38 @@ function AppInner() {
2331
3037
  },
2332
3038
  previews,
2333
3039
  onWakeWorkspace: handleWakeWorkspace,
3040
+ onViewDiff: handleViewDiff,
2334
3041
  onCreatePr: () => {
2335
3042
  if (!resolvedChatId) return;
2336
3043
  sendMessageMutation.mutate({ message: "Push changes and create a GitHub PR" });
2337
3044
  },
2338
3045
  isPlanMode: taskMode === "plan",
2339
- wakingWorkspaceId
3046
+ wakingWorkspaceId,
3047
+ viewMode: viewingDiff ? "diff" : "chat",
3048
+ viewingDiffRepoName: viewingDiff?.repoName ?? null,
3049
+ onSelectChatMode: handleSelectChatMode,
3050
+ onSelectDiffMode: handleSelectDiffMode
2340
3051
  }
2341
3052
  )
2342
3053
  ] }),
2343
- /* @__PURE__ */ jsx10(
3054
+ /* @__PURE__ */ jsx11(
2344
3055
  StatusBar,
2345
3056
  {
2346
- focusPanel
3057
+ focusPanel,
3058
+ viewingDiff: !!viewingDiff,
3059
+ hasDiffAvailable: !!firstAvailableDiff
2347
3060
  }
2348
3061
  )
2349
3062
  ] });
2350
3063
  }
2351
3064
 
2352
3065
  // src/interactive/index.tsx
2353
- import { jsx as jsx11 } from "@opentui/react/jsx-runtime";
3066
+ import { jsx as jsx12 } from "@opentui/react/jsx-runtime";
2354
3067
  async function launchInteractive() {
2355
3068
  const renderer = await createCliRenderer({
2356
3069
  exitOnCtrlC: false
2357
3070
  });
2358
- createRoot(renderer).render(/* @__PURE__ */ jsx11(App, {}));
3071
+ createRoot(renderer).render(/* @__PURE__ */ jsx12(App, {}));
2359
3072
  }
2360
3073
  export {
2361
3074
  launchInteractive