parallel-codex-tui 0.2.7 → 0.2.9
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.
- package/README.md +13 -11
- package/dist/cli.js +7 -1
- package/dist/core/session-manager.js +330 -3
- package/dist/domain/schemas.js +19 -1
- package/dist/orchestrator/orchestrator.js +2 -0
- package/dist/orchestrator/prompts.js +3 -1
- package/dist/tui/App.js +205 -26
- package/dist/tui/InputBar.js +36 -13
- package/dist/tui/MainConversationsView.js +142 -0
- package/dist/tui/keyboard.js +2 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/tui/App.js
CHANGED
|
@@ -19,7 +19,7 @@ import { TerminalOutput } from "./TerminalOutput.js";
|
|
|
19
19
|
import { NativeTerminalScreen } from "./terminal-screen.js";
|
|
20
20
|
import { WorkerOutputView } from "./WorkerOutputView.js";
|
|
21
21
|
import { compactEndByDisplayWidth, displayWidth, wrapByDisplayWidth } from "./display-width.js";
|
|
22
|
-
import { isAttachShortcut, isCopyShortcut, isDiagnosticsShortcut, isExitShortcut, isLogsShortcut,
|
|
22
|
+
import { isAttachShortcut, isCopyShortcut, isDiagnosticsShortcut, isExitShortcut, isLogsShortcut, isNewConversationShortcut, isRouterDiagnosticsShortcut, isStatusDetailsShortcut, isTaskResultShortcut, isTaskSessionsShortcut, isWorkerOverviewShortcut, isWorkerSearchShortcut, isWorkspaceShortcut, mouseScrollDelta, rawChatScrollArrowDelta, rawHistoryDelta, rawPageScrollDelta, rawPlainArrowDelta, scrollDelta, workerLogJumpKind } from "./keyboard.js";
|
|
23
23
|
import { createRawInputDecoder, tokenizeRawInput } from "./raw-input-decoder.js";
|
|
24
24
|
import { decodeHtmlEntities } from "./markdown-text.js";
|
|
25
25
|
import { configureTuiTheme, TUI_THEME } from "./theme.js";
|
|
@@ -29,6 +29,7 @@ import { moveWorkerSelection, WorkerOverviewView } from "./WorkerOverviewView.js
|
|
|
29
29
|
import { FeatureBoardView, moveFeatureBoardSelection } from "./FeatureBoardView.js";
|
|
30
30
|
import { CollaborationTimelineView, collaborationSelectionScrollOffset, collaborationTimelineEvents, moveCollaborationEventSelection, nextCollaborationFeatureIndex } from "./CollaborationTimelineView.js";
|
|
31
31
|
import { moveTaskSessionSelection, TaskSessionsView } from "./TaskSessionsView.js";
|
|
32
|
+
import { MainConversationsView, moveMainConversationSelection } from "./MainConversationsView.js";
|
|
32
33
|
import { moveTaskSessionDetailSelection, TaskSessionDetailView } from "./TaskSessionDetailView.js";
|
|
33
34
|
import { latestTaskResultMessageIndex, parseTaskResultSummary } from "./task-result.js";
|
|
34
35
|
import { buildNativeAttachLaunch, buildNativeForkLaunch, startNativeAttachProcess } from "../workers/native-attach.js";
|
|
@@ -49,7 +50,7 @@ const EMPTY_WORKER_NAVIGATION_TARGETS = {
|
|
|
49
50
|
errorOffsets: [],
|
|
50
51
|
diffOffsets: []
|
|
51
52
|
};
|
|
52
|
-
export function App({ config, orchestrator, cwd, initialTaskId = null, initialRoute = null, initialWorkers, initialCanRetryTask = false, initialMessages = [], persistChatMessage, reloadConfig, workspaceChoices = [], switchWorkspace, loadRouterDiagnostics, loadTaskSessions, loadTaskSessionDetails, renameTaskSession, setTaskSessionArchived, deleteTaskSession, exportTaskSession, exportDiagnostics, loadCollaborationTimeline, activateTaskSession, prepareNativeAttach, prepareNativeFork, startNativeAttach, copyToClipboard = copyTextToClipboard, shutdownSignal }) {
|
|
53
|
+
export function App({ config, orchestrator, cwd, initialTaskId = null, initialRoute = null, initialWorkers, initialCanRetryTask = false, initialMessages = [], persistChatMessage, reloadConfig, workspaceChoices = [], switchWorkspace, loadRouterDiagnostics, loadTaskSessions, loadMainConversations, activateMainConversation, loadTaskSessionDetails, renameTaskSession, setTaskSessionArchived, deleteTaskSession, exportTaskSession, exportDiagnostics, loadCollaborationTimeline, activateTaskSession, startMainConversation, prepareNativeAttach, prepareNativeFork, startNativeAttach, copyToClipboard = copyTextToClipboard, shutdownSignal }) {
|
|
53
54
|
configureTuiTheme({
|
|
54
55
|
theme: config.ui.theme,
|
|
55
56
|
colors: config.ui.colors
|
|
@@ -97,6 +98,9 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
97
98
|
const [routerMaxScrollOffset, setRouterMaxScrollOffset] = useState(0);
|
|
98
99
|
const [taskSessions, setTaskSessions] = useState([]);
|
|
99
100
|
const [selectedTaskSessionIndex, setSelectedTaskSessionIndex] = useState(0);
|
|
101
|
+
const [sessionCenterMode, setSessionCenterMode] = useState("tasks");
|
|
102
|
+
const [mainConversations, setMainConversations] = useState([]);
|
|
103
|
+
const [selectedMainConversationIndex, setSelectedMainConversationIndex] = useState(0);
|
|
100
104
|
const [taskSessionsLoading, setTaskSessionsLoading] = useState(false);
|
|
101
105
|
const [taskSessionsError, setTaskSessionsError] = useState(null);
|
|
102
106
|
const [taskSessionsNotice, setTaskSessionsNotice] = useState(null);
|
|
@@ -149,6 +153,9 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
149
153
|
const routerMaxScrollOffsetRef = useRef(routerMaxScrollOffset);
|
|
150
154
|
const taskSessionsRef = useRef(taskSessions);
|
|
151
155
|
const selectedTaskSessionIndexRef = useRef(selectedTaskSessionIndex);
|
|
156
|
+
const sessionCenterModeRef = useRef("tasks");
|
|
157
|
+
const mainConversationsRef = useRef(mainConversations);
|
|
158
|
+
const selectedMainConversationIndexRef = useRef(selectedMainConversationIndex);
|
|
152
159
|
const taskSessionsLoadingRef = useRef(taskSessionsLoading);
|
|
153
160
|
const taskSessionsIncludeArchivedRef = useRef(taskSessionsIncludeArchived);
|
|
154
161
|
const taskSessionActionRef = useRef(taskSessionAction);
|
|
@@ -168,7 +175,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
168
175
|
const attachSelectedWorkerRef = useRef(attachSelectedWorker);
|
|
169
176
|
const submitRef = useRef(submit);
|
|
170
177
|
const retryRef = useRef(retryActiveTask);
|
|
171
|
-
const
|
|
178
|
+
const newConversationRef = useRef(startNewConversation);
|
|
172
179
|
const openWorkspacePickerRef = useRef(openWorkspacePicker);
|
|
173
180
|
const openRouterDiagnosticsRef = useRef(openRouterDiagnostics);
|
|
174
181
|
const openWorkerOverviewRef = useRef(openWorkerOverview);
|
|
@@ -180,6 +187,8 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
180
187
|
const refreshCollaborationTimelineRef = useRef(refreshCollaborationTimeline);
|
|
181
188
|
const activateSelectedTaskSessionRef = useRef(activateSelectedTaskSession);
|
|
182
189
|
const refreshTaskSessionsRef = useRef(refreshTaskSessions);
|
|
190
|
+
const openMainConversationsRef = useRef(openMainConversations);
|
|
191
|
+
const restoreSelectedMainConversationRef = useRef(restoreSelectedMainConversation);
|
|
183
192
|
const renameSelectedTaskSessionRef = useRef(renameSelectedTaskSession);
|
|
184
193
|
const archiveSelectedTaskSessionRef = useRef(archiveSelectedTaskSession);
|
|
185
194
|
const deleteSelectedTaskSessionRef = useRef(deleteSelectedTaskSession);
|
|
@@ -334,6 +343,15 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
334
343
|
useEffect(() => {
|
|
335
344
|
selectedTaskSessionIndexRef.current = selectedTaskSessionIndex;
|
|
336
345
|
}, [selectedTaskSessionIndex]);
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
sessionCenterModeRef.current = sessionCenterMode;
|
|
348
|
+
}, [sessionCenterMode]);
|
|
349
|
+
useEffect(() => {
|
|
350
|
+
mainConversationsRef.current = mainConversations;
|
|
351
|
+
}, [mainConversations]);
|
|
352
|
+
useEffect(() => {
|
|
353
|
+
selectedMainConversationIndexRef.current = selectedMainConversationIndex;
|
|
354
|
+
}, [selectedMainConversationIndex]);
|
|
337
355
|
useEffect(() => {
|
|
338
356
|
taskSessionsLoadingRef.current = taskSessionsLoading;
|
|
339
357
|
}, [taskSessionsLoading]);
|
|
@@ -394,7 +412,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
394
412
|
attachSelectedWorkerRef.current = attachSelectedWorker;
|
|
395
413
|
submitRef.current = submit;
|
|
396
414
|
retryRef.current = retryActiveTask;
|
|
397
|
-
|
|
415
|
+
newConversationRef.current = startNewConversation;
|
|
398
416
|
openWorkspacePickerRef.current = openWorkspacePicker;
|
|
399
417
|
openRouterDiagnosticsRef.current = openRouterDiagnostics;
|
|
400
418
|
openWorkerOverviewRef.current = openWorkerOverview;
|
|
@@ -405,6 +423,8 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
405
423
|
refreshCollaborationTimelineRef.current = refreshCollaborationTimeline;
|
|
406
424
|
activateSelectedTaskSessionRef.current = activateSelectedTaskSession;
|
|
407
425
|
refreshTaskSessionsRef.current = refreshTaskSessions;
|
|
426
|
+
openMainConversationsRef.current = openMainConversations;
|
|
427
|
+
restoreSelectedMainConversationRef.current = restoreSelectedMainConversation;
|
|
408
428
|
renameSelectedTaskSessionRef.current = renameSelectedTaskSession;
|
|
409
429
|
archiveSelectedTaskSessionRef.current = archiveSelectedTaskSession;
|
|
410
430
|
deleteSelectedTaskSessionRef.current = deleteSelectedTaskSession;
|
|
@@ -685,6 +705,13 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
685
705
|
setTaskSessionsError(null);
|
|
686
706
|
setSelectedTaskSessionIndex(nextIndex);
|
|
687
707
|
};
|
|
708
|
+
const moveSelectedMainConversation = (delta, wrap = false) => {
|
|
709
|
+
const nextIndex = moveMainConversationSelection(selectedMainConversationIndexRef.current, delta, mainConversationsRef.current.length, wrap);
|
|
710
|
+
selectedMainConversationIndexRef.current = nextIndex;
|
|
711
|
+
setTaskSessionsError(null);
|
|
712
|
+
setTaskSessionsNotice(null);
|
|
713
|
+
setSelectedMainConversationIndex(nextIndex);
|
|
714
|
+
};
|
|
688
715
|
const moveSelectedTaskSessionDetailWorker = (delta, wrap = false) => {
|
|
689
716
|
const nextIndex = moveTaskSessionDetailSelection(taskSessionDetailSelectedWorkerIndexRef.current, delta, taskSessionDetailsRef.current?.workers.length ?? 0, wrap);
|
|
690
717
|
taskSessionDetailSelectedWorkerIndexRef.current = nextIndex;
|
|
@@ -847,6 +874,49 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
847
874
|
}
|
|
848
875
|
return;
|
|
849
876
|
}
|
|
877
|
+
if (sessionCenterModeRef.current === "conversations") {
|
|
878
|
+
if (isTaskSessionsShortcut(chunk, {}) || chunk === "\x1b") {
|
|
879
|
+
setTaskSessionsError(null);
|
|
880
|
+
setTaskSessionsNotice(null);
|
|
881
|
+
viewRef.current = taskSessionsReturnViewRef.current;
|
|
882
|
+
setView(taskSessionsReturnViewRef.current);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
if (taskSessionsLoadingRef.current) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (isNewConversationShortcut(chunk, {}) || chunk === "n" || chunk === "N") {
|
|
889
|
+
void newConversationRef.current();
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
if (chunk === "t" || chunk === "T") {
|
|
893
|
+
sessionCenterModeRef.current = "tasks";
|
|
894
|
+
setSessionCenterMode("tasks");
|
|
895
|
+
setTaskSessionsError(null);
|
|
896
|
+
setTaskSessionsNotice(null);
|
|
897
|
+
void refreshTaskSessionsRef.current(activeTaskIdRef.current);
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
if (chunk === "c" || chunk === "C") {
|
|
901
|
+
void openMainConversationsRef.current();
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
if (chunk === "\r" || chunk === "\n") {
|
|
905
|
+
void restoreSelectedMainConversationRef.current();
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
if (chunk === "\t") {
|
|
909
|
+
moveSelectedMainConversation(1, true);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
const conversationSelectionDelta = -(rawHistoryDelta(chunk)
|
|
913
|
+
+ rawPageScrollDelta(chunk, Math.max(1, outputHeight - 2))
|
|
914
|
+
+ mouseScrollDelta(chunk, 1));
|
|
915
|
+
if (conversationSelectionDelta !== 0) {
|
|
916
|
+
moveSelectedMainConversation(conversationSelectionDelta);
|
|
917
|
+
}
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
850
920
|
const sessionAction = taskSessionActionRef.current;
|
|
851
921
|
if (sessionAction) {
|
|
852
922
|
if (chunk === "\x1b") {
|
|
@@ -898,14 +968,12 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
898
968
|
openWorkspacePickerRef.current();
|
|
899
969
|
return;
|
|
900
970
|
}
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
setView("chat");
|
|
908
|
-
}
|
|
971
|
+
if (isNewConversationShortcut(chunk, {}) && !busyRef.current) {
|
|
972
|
+
void newConversationRef.current();
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (chunk === "c" || chunk === "C") {
|
|
976
|
+
void openMainConversationsRef.current();
|
|
909
977
|
return;
|
|
910
978
|
}
|
|
911
979
|
const selectedSession = taskSessionsRef.current[selectedTaskSessionIndexRef.current];
|
|
@@ -1217,8 +1285,8 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1217
1285
|
openWorkspacePickerRef.current();
|
|
1218
1286
|
return;
|
|
1219
1287
|
}
|
|
1220
|
-
if (
|
|
1221
|
-
void
|
|
1288
|
+
if (isNewConversationShortcut(chunk, {}) && !busyRef.current) {
|
|
1289
|
+
void newConversationRef.current();
|
|
1222
1290
|
return;
|
|
1223
1291
|
}
|
|
1224
1292
|
if (chunk === "f" || chunk === "F") {
|
|
@@ -1336,8 +1404,8 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1336
1404
|
jumpWorkerLog(jumpKind);
|
|
1337
1405
|
continue;
|
|
1338
1406
|
}
|
|
1339
|
-
if (
|
|
1340
|
-
void
|
|
1407
|
+
if (isNewConversationShortcut(workerChunk, {}) && !busyRef.current) {
|
|
1408
|
+
void newConversationRef.current();
|
|
1341
1409
|
return;
|
|
1342
1410
|
}
|
|
1343
1411
|
if (isWorkspaceShortcut(workerChunk, {}) && !busyRef.current) {
|
|
@@ -1413,8 +1481,8 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1413
1481
|
}
|
|
1414
1482
|
return;
|
|
1415
1483
|
}
|
|
1416
|
-
if (
|
|
1417
|
-
void
|
|
1484
|
+
if (isNewConversationShortcut(chunk, {}) && !busyRef.current) {
|
|
1485
|
+
void newConversationRef.current();
|
|
1418
1486
|
return;
|
|
1419
1487
|
}
|
|
1420
1488
|
if (chunk === "\x1b") {
|
|
@@ -1688,7 +1756,15 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1688
1756
|
label: "sessions",
|
|
1689
1757
|
text: details
|
|
1690
1758
|
? details.workers.map((worker) => [worker.id, worker.role, worker.engine, worker.state].join(" · ")).join("\n")
|
|
1691
|
-
:
|
|
1759
|
+
: sessionCenterModeRef.current === "conversations"
|
|
1760
|
+
? mainConversationsRef.current.map((conversation) => [
|
|
1761
|
+
conversation.current ? "current" : "saved",
|
|
1762
|
+
conversation.id ?? "legacy",
|
|
1763
|
+
conversation.title,
|
|
1764
|
+
`${conversation.messageCount} messages`,
|
|
1765
|
+
`${conversation.nativeSessionCount} native`
|
|
1766
|
+
].join(" · ")).join("\n")
|
|
1767
|
+
: taskSessionsRef.current.map((task) => [task.id, task.title, task.status].join(" · ")).join("\n")
|
|
1692
1768
|
};
|
|
1693
1769
|
}
|
|
1694
1770
|
if (currentView === "router") {
|
|
@@ -1712,8 +1788,8 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1712
1788
|
exitRef.current();
|
|
1713
1789
|
return;
|
|
1714
1790
|
}
|
|
1715
|
-
if (
|
|
1716
|
-
void
|
|
1791
|
+
if (isNewConversationShortcut(inputKey, key) && !busy) {
|
|
1792
|
+
void startNewConversation();
|
|
1717
1793
|
return;
|
|
1718
1794
|
}
|
|
1719
1795
|
const delta = scrollDelta(inputKey, key, outputHeight - 1);
|
|
@@ -2104,17 +2180,22 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2104
2180
|
setBusy(false);
|
|
2105
2181
|
}
|
|
2106
2182
|
}
|
|
2107
|
-
async function
|
|
2108
|
-
if (busyRef.current
|
|
2183
|
+
async function startNewConversation() {
|
|
2184
|
+
if (busyRef.current) {
|
|
2109
2185
|
return;
|
|
2110
2186
|
}
|
|
2111
2187
|
try {
|
|
2188
|
+
await startMainConversation?.();
|
|
2112
2189
|
await activateTaskSession?.(null);
|
|
2113
2190
|
}
|
|
2114
2191
|
catch (error) {
|
|
2115
2192
|
setAttachError(error instanceof Error ? error.message : String(error));
|
|
2116
2193
|
return;
|
|
2117
2194
|
}
|
|
2195
|
+
resetActiveTaskUiForMainConversation();
|
|
2196
|
+
await appendVisibleMessage({ from: "system", text: "new conversation · ready" });
|
|
2197
|
+
}
|
|
2198
|
+
function resetActiveTaskUiForMainConversation() {
|
|
2118
2199
|
const nextMemory = newTaskMemoryState();
|
|
2119
2200
|
activeTaskIdRef.current = nextMemory.activeTaskId;
|
|
2120
2201
|
setActiveTaskId(nextMemory.activeTaskId);
|
|
@@ -2139,7 +2220,6 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2139
2220
|
offset: 0,
|
|
2140
2221
|
draft: { value: inputRef.current, cursor: inputCursorRef.current }
|
|
2141
2222
|
};
|
|
2142
|
-
await appendVisibleMessage({ from: "system", text: "new task · ready" });
|
|
2143
2223
|
}
|
|
2144
2224
|
async function openRouterDiagnostics() {
|
|
2145
2225
|
const currentView = viewRef.current;
|
|
@@ -2205,6 +2285,8 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2205
2285
|
setTaskSessionDetailSelectedWorkerIndex(0);
|
|
2206
2286
|
setTaskSessionDetailNotice(null);
|
|
2207
2287
|
updateTaskSessionAction(null);
|
|
2288
|
+
sessionCenterModeRef.current = "tasks";
|
|
2289
|
+
setSessionCenterMode("tasks");
|
|
2208
2290
|
await refreshTaskSessions(activeTaskIdRef.current);
|
|
2209
2291
|
}
|
|
2210
2292
|
async function refreshTaskSessions(preferredTaskId = null) {
|
|
@@ -2245,6 +2327,103 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2245
2327
|
}
|
|
2246
2328
|
}
|
|
2247
2329
|
}
|
|
2330
|
+
async function openMainConversations() {
|
|
2331
|
+
const currentView = viewRef.current;
|
|
2332
|
+
if (busyRef.current || currentView === "native" || currentView === "workspace") {
|
|
2333
|
+
return;
|
|
2334
|
+
}
|
|
2335
|
+
if (currentView !== "sessions") {
|
|
2336
|
+
taskSessionsReturnViewRef.current = currentView === "worker" || currentView === "workers" || currentView === "router"
|
|
2337
|
+
? currentView
|
|
2338
|
+
: "chat";
|
|
2339
|
+
viewRef.current = "sessions";
|
|
2340
|
+
setView("sessions");
|
|
2341
|
+
}
|
|
2342
|
+
const previousId = sessionCenterModeRef.current === "conversations"
|
|
2343
|
+
? mainConversationsRef.current[selectedMainConversationIndexRef.current]?.id
|
|
2344
|
+
: undefined;
|
|
2345
|
+
sessionCenterModeRef.current = "conversations";
|
|
2346
|
+
setSessionCenterMode("conversations");
|
|
2347
|
+
taskSessionDetailsRef.current = null;
|
|
2348
|
+
setTaskSessionDetails(null);
|
|
2349
|
+
updateTaskSessionAction(null);
|
|
2350
|
+
setTaskSessionsError(null);
|
|
2351
|
+
setTaskSessionsNotice(null);
|
|
2352
|
+
taskSessionsLoadingRef.current = true;
|
|
2353
|
+
setTaskSessionsLoading(true);
|
|
2354
|
+
const sequence = taskSessionsLoadSequenceRef.current + 1;
|
|
2355
|
+
taskSessionsLoadSequenceRef.current = sequence;
|
|
2356
|
+
try {
|
|
2357
|
+
if (!loadMainConversations) {
|
|
2358
|
+
throw new Error("Main conversations are unavailable");
|
|
2359
|
+
}
|
|
2360
|
+
const conversations = await loadMainConversations();
|
|
2361
|
+
if (taskSessionsLoadSequenceRef.current !== sequence) {
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
mainConversationsRef.current = conversations;
|
|
2365
|
+
setMainConversations(conversations);
|
|
2366
|
+
const preservedIndex = previousId !== undefined
|
|
2367
|
+
? conversations.findIndex((conversation) => conversation.id === previousId)
|
|
2368
|
+
: -1;
|
|
2369
|
+
const currentIndex = conversations.findIndex((conversation) => conversation.current);
|
|
2370
|
+
const selectedIndex = preservedIndex >= 0
|
|
2371
|
+
? preservedIndex
|
|
2372
|
+
: currentIndex >= 0
|
|
2373
|
+
? currentIndex
|
|
2374
|
+
: 0;
|
|
2375
|
+
selectedMainConversationIndexRef.current = selectedIndex;
|
|
2376
|
+
setSelectedMainConversationIndex(selectedIndex);
|
|
2377
|
+
}
|
|
2378
|
+
catch (error) {
|
|
2379
|
+
if (taskSessionsLoadSequenceRef.current === sequence) {
|
|
2380
|
+
setTaskSessionsError(error instanceof Error ? error.message : String(error));
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
finally {
|
|
2384
|
+
if (taskSessionsLoadSequenceRef.current === sequence) {
|
|
2385
|
+
taskSessionsLoadingRef.current = false;
|
|
2386
|
+
setTaskSessionsLoading(false);
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
async function restoreSelectedMainConversation() {
|
|
2391
|
+
if (busyRef.current || taskSessionsLoadingRef.current) {
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
const selected = mainConversationsRef.current[selectedMainConversationIndexRef.current];
|
|
2395
|
+
if (!selected) {
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
taskSessionsLoadingRef.current = true;
|
|
2399
|
+
setTaskSessionsLoading(true);
|
|
2400
|
+
setTaskSessionsError(null);
|
|
2401
|
+
setTaskSessionsNotice(null);
|
|
2402
|
+
try {
|
|
2403
|
+
if (!activateMainConversation) {
|
|
2404
|
+
throw new Error("Main conversation restore is unavailable");
|
|
2405
|
+
}
|
|
2406
|
+
const result = await activateMainConversation(selected.id);
|
|
2407
|
+
if (!result.changed) {
|
|
2408
|
+
await openMainConversations();
|
|
2409
|
+
setTaskSessionsNotice(`Already current · ${result.conversation.title}`);
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2412
|
+
await activateTaskSession?.(null);
|
|
2413
|
+
resetActiveTaskUiForMainConversation();
|
|
2414
|
+
await appendVisibleMessage({
|
|
2415
|
+
from: "system",
|
|
2416
|
+
text: `conversation restored · ${result.conversation.title} · ${result.restoredNativeSessions} native`
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
catch (error) {
|
|
2420
|
+
setTaskSessionsError(error instanceof Error ? error.message : String(error));
|
|
2421
|
+
}
|
|
2422
|
+
finally {
|
|
2423
|
+
taskSessionsLoadingRef.current = false;
|
|
2424
|
+
setTaskSessionsLoading(false);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2248
2427
|
function updateTaskSessionAction(next) {
|
|
2249
2428
|
taskSessionActionRef.current = next;
|
|
2250
2429
|
setTaskSessionAction(next);
|
|
@@ -2732,11 +2911,11 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2732
2911
|
? { type: "rename", value: taskSessionAction.value, cursor: taskSessionAction.cursor }
|
|
2733
2912
|
: taskSessionAction?.type === "delete"
|
|
2734
2913
|
? { type: "delete", title: taskSessionAction.title }
|
|
2735
|
-
: null, taskSessionsIncludeArchived: taskSessionsIncludeArchived, taskSessionDetail: Boolean(taskSessionDetails), taskSessionDetailHasNative: taskSessionDetailHasNative, taskSessionDetailCanFork: taskSessionDetailCanFork, canRetry: canRetryTask, hasWorkers: workers.length > 0, hasActiveTask: Boolean(activeTaskId), hasTaskResult: hasTaskResult, taskResultExpanded: taskResultExpanded, chatScrollOffset: chatScrollOffset, chatMaxScrollOffset: chatMaxScrollOffset, nativeClosed: view === "native" && nativeAttach?.closedCode !== null, searchMatchIndex: workerSearch.matchIndex, searchMatchCount: workerNavigationTargets.searchOffsets.length, clipboardNotice: clipboardNotice, value: workerSearch.open && view === "worker"
|
|
2914
|
+
: null, taskSessionsIncludeArchived: taskSessionsIncludeArchived, mainConversationSessions: sessionCenterMode === "conversations" && !taskSessionDetails, taskSessionDetail: Boolean(taskSessionDetails), taskSessionDetailHasNative: taskSessionDetailHasNative, taskSessionDetailCanFork: taskSessionDetailCanFork, canRetry: canRetryTask, hasWorkers: workers.length > 0, hasActiveTask: Boolean(activeTaskId), hasTaskResult: hasTaskResult, taskResultExpanded: taskResultExpanded, chatScrollOffset: chatScrollOffset, chatMaxScrollOffset: chatMaxScrollOffset, nativeClosed: view === "native" && nativeAttach?.closedCode !== null, searchMatchIndex: workerSearch.matchIndex, searchMatchCount: workerNavigationTargets.searchOffsets.length, clipboardNotice: clipboardNotice, value: workerSearch.open && view === "worker"
|
|
2736
2915
|
? workerSearch.query
|
|
2737
2916
|
: view === "native" || view === "router" || view === "workers" || view === "features" || view === "sessions" || view === "collaboration" || view === "status" ? "" : input, cursor: workerSearch.open && view === "worker"
|
|
2738
2917
|
? workerSearch.cursor
|
|
2739
|
-
: view === "chat" ? inputCursor : undefined, terminalWidth: terminalWidth, onChange: view === "native" ? setNativeInput : setInput, onSubmit: view === "native" ? undefined : submit }), error: attachError, children: view === "status" ? (_jsx(StatusDetailView, { cwd: cwd, taskId: activeTaskId, mode: activeMode, busy: busy, canRetry: canRetryTask, taskStatus: visibleTaskStatus, routeStatus: visibleRouteStatus, routeReason: routePending ? undefined : lastRoute?.reason, pairing: config.pairing, configStatus: configChange?.detail, configRestartRequired: configChange?.kind === "restart", workers: workers, selectedWorkerIndex: selectedWorkerIndex, height: contentHeight, terminalWidth: terminalWidth })) : view === "native" ? (_jsx(NativeAttachView, { attach: nativeAttach, viewportHeight: contentHeight })) : view === "sessions" ? (taskSessionDetails ? (_jsx(TaskSessionDetailView, { details: taskSessionDetails, selectedWorkerIndex: taskSessionDetailSelectedWorkerIndex, loading: taskSessionsLoading, error: taskSessionsError, notice: taskSessionDetailNotice, height: contentHeight, terminalWidth: terminalWidth })) : (_jsx(TaskSessionsView, { tasks: taskSessions, activeTaskId: activeTaskId, selectedIndex: selectedTaskSessionIndex, includeArchived: taskSessionsIncludeArchived, notice: taskSessionsNotice, action: taskSessionAction?.type === "rename"
|
|
2918
|
+
: view === "chat" ? inputCursor : undefined, terminalWidth: terminalWidth, onChange: view === "native" ? setNativeInput : setInput, onSubmit: view === "native" ? undefined : submit }), error: attachError, children: view === "status" ? (_jsx(StatusDetailView, { cwd: cwd, taskId: activeTaskId, mode: activeMode, busy: busy, canRetry: canRetryTask, taskStatus: visibleTaskStatus, routeStatus: visibleRouteStatus, routeReason: routePending ? undefined : lastRoute?.reason, pairing: config.pairing, configStatus: configChange?.detail, configRestartRequired: configChange?.kind === "restart", workers: workers, selectedWorkerIndex: selectedWorkerIndex, height: contentHeight, terminalWidth: terminalWidth })) : view === "native" ? (_jsx(NativeAttachView, { attach: nativeAttach, viewportHeight: contentHeight })) : view === "sessions" ? (taskSessionDetails ? (_jsx(TaskSessionDetailView, { details: taskSessionDetails, selectedWorkerIndex: taskSessionDetailSelectedWorkerIndex, loading: taskSessionsLoading, error: taskSessionsError, notice: taskSessionDetailNotice, height: contentHeight, terminalWidth: terminalWidth })) : sessionCenterMode === "conversations" ? (_jsx(MainConversationsView, { conversations: mainConversations, selectedIndex: selectedMainConversationIndex, notice: taskSessionsNotice, loading: taskSessionsLoading, error: taskSessionsError, height: contentHeight, terminalWidth: terminalWidth })) : (_jsx(TaskSessionsView, { tasks: taskSessions, activeTaskId: activeTaskId, selectedIndex: selectedTaskSessionIndex, includeArchived: taskSessionsIncludeArchived, notice: taskSessionsNotice, action: taskSessionAction?.type === "rename"
|
|
2740
2919
|
? { type: "rename", title: taskSessionAction.title }
|
|
2741
2920
|
: taskSessionAction?.type === "delete"
|
|
2742
2921
|
? { type: "delete", title: taskSessionAction.title }
|
package/dist/tui/InputBar.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { compactEndByDisplayWidth, compactTailByDisplayWidth, displayWidth } from "./display-width.js";
|
|
4
4
|
import { TUI_THEME } from "./theme.js";
|
|
5
|
-
export function InputBar({ mode, ready = true, busy = false, routeFallback = false, collaborationDetail = false, collaborationUnresolved = false, collaborationBack = "workers", featureCanCancel = false, featureCanPause = false, featureCanReassign = false, featureCancelConfirm = false, featurePauseConfirm = false, featureAssignment = false, taskSessionAction = null, taskSessionsIncludeArchived = false, taskSessionDetail = false, taskSessionDetailHasNative = false, taskSessionDetailCanFork = false, canRetry = false, hasWorkers = false, hasActiveTask = false, hasTaskResult = false, taskResultExpanded = false, chatScrollOffset = 0, chatMaxScrollOffset = 0, nativeClosed = false, searchMatchIndex = 0, searchMatchCount = 0, clipboardNotice = null, value, cursor, terminalWidth: providedTerminalWidth, onChange, onSubmit }) {
|
|
5
|
+
export function InputBar({ mode, ready = true, busy = false, routeFallback = false, collaborationDetail = false, collaborationUnresolved = false, collaborationBack = "workers", featureCanCancel = false, featureCanPause = false, featureCanReassign = false, featureCancelConfirm = false, featurePauseConfirm = false, featureAssignment = false, taskSessionAction = null, taskSessionsIncludeArchived = false, mainConversationSessions = false, taskSessionDetail = false, taskSessionDetailHasNative = false, taskSessionDetailCanFork = false, canRetry = false, hasWorkers = false, hasActiveTask = false, hasTaskResult = false, taskResultExpanded = false, chatScrollOffset = 0, chatMaxScrollOffset = 0, nativeClosed = false, searchMatchIndex = 0, searchMatchCount = 0, clipboardNotice = null, value, cursor, terminalWidth: providedTerminalWidth, onChange, onSubmit }) {
|
|
6
6
|
const terminalWidth = providedTerminalWidth ?? process.stdout.columns ?? 120;
|
|
7
7
|
const fillRail = providedTerminalWidth !== undefined || typeof process.stdout.columns === "number";
|
|
8
8
|
if (clipboardNotice) {
|
|
@@ -39,6 +39,10 @@ export function InputBar({ mode, ready = true, busy = false, routeFallback = fal
|
|
|
39
39
|
const hints = taskSessionDetailInputHints(terminalWidth, taskSessionDetailHasNative, taskSessionDetailCanFork);
|
|
40
40
|
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: displayWidth(`${hints.label}${hints.detail}`), fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: hints.label }), hints.detail ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: hints.detail }) : null] }));
|
|
41
41
|
}
|
|
42
|
+
if (mainConversationSessions) {
|
|
43
|
+
const hints = mainConversationSessionsInputHints(terminalWidth);
|
|
44
|
+
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: displayWidth(`${hints.label}${hints.detail}`), fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: hints.label }), hints.detail ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: hints.detail }) : null] }));
|
|
45
|
+
}
|
|
42
46
|
const hints = taskSessionsInputHints(terminalWidth, taskSessionsIncludeArchived);
|
|
43
47
|
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: displayWidth(`${hints.label}${hints.detail}`), fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: hints.label }), hints.detail ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: hints.detail }) : null] }));
|
|
44
48
|
}
|
|
@@ -208,7 +212,7 @@ export function chatPlaceholderDisplayValue(terminalWidth, options = {}) {
|
|
|
208
212
|
const maxScrollOffset = Math.max(0, options.maxScrollOffset ?? 0);
|
|
209
213
|
const scrollOffset = Math.min(Math.max(0, options.scrollOffset ?? 0), maxScrollOffset);
|
|
210
214
|
if (options.hasTaskResult) {
|
|
211
|
-
return chatTaskResultPlaceholderDisplayValue(terminalWidth, Boolean(options.taskResultExpanded), Boolean(options.hasWorkers),
|
|
215
|
+
return chatTaskResultPlaceholderDisplayValue(terminalWidth, Boolean(options.taskResultExpanded), Boolean(options.hasWorkers), scrollOffset, maxScrollOffset);
|
|
212
216
|
}
|
|
213
217
|
if (scrollOffset > 0) {
|
|
214
218
|
return chatHistoryPlaceholderDisplayValue(terminalWidth, scrollOffset, maxScrollOffset);
|
|
@@ -224,20 +228,21 @@ export function chatPlaceholderDisplayValue(terminalWidth, options = {}) {
|
|
|
224
228
|
]);
|
|
225
229
|
}
|
|
226
230
|
if (options.hasWorkers) {
|
|
227
|
-
return chatTaskPlaceholderDisplayValue(terminalWidth, maxScrollOffset > 0
|
|
231
|
+
return chatTaskPlaceholderDisplayValue(terminalWidth, maxScrollOffset > 0);
|
|
228
232
|
}
|
|
229
233
|
if (maxScrollOffset > 0 && terminalWidth >= 22) {
|
|
230
234
|
return selectChatPlaceholder(terminalWidth, ["message · scroll", "message", "msg"]);
|
|
231
235
|
}
|
|
232
236
|
return selectChatPlaceholder(terminalWidth, [
|
|
233
|
-
"message · ^P project · ^T tasks · ^G routes",
|
|
234
|
-
"message · ^P project · ^G routes",
|
|
235
|
-
"message · ^P project",
|
|
237
|
+
"message · ^N new · ^P project · ^T tasks · ^G routes",
|
|
238
|
+
"message · ^N new · ^P project · ^G routes",
|
|
239
|
+
"message · ^N new · ^P project",
|
|
240
|
+
"message · ^N new",
|
|
236
241
|
"message",
|
|
237
242
|
"msg"
|
|
238
243
|
]);
|
|
239
244
|
}
|
|
240
|
-
function chatTaskResultPlaceholderDisplayValue(terminalWidth, expanded, hasWorkers,
|
|
245
|
+
function chatTaskResultPlaceholderDisplayValue(terminalWidth, expanded, hasWorkers, scrollOffset, maxScrollOffset) {
|
|
241
246
|
const toggle = expanded ? "^D compact" : "^D details";
|
|
242
247
|
const position = expanded && maxScrollOffset > 0
|
|
243
248
|
? scrollOffset > 0 ? `result ${scrollOffset}/${maxScrollOffset}` : "result · scroll"
|
|
@@ -255,7 +260,7 @@ function chatTaskResultPlaceholderDisplayValue(terminalWidth, expanded, hasWorke
|
|
|
255
260
|
"^D"
|
|
256
261
|
]
|
|
257
262
|
: [
|
|
258
|
-
`${position} · ${toggle}
|
|
263
|
+
`${position} · ${toggle} · ^N new`,
|
|
259
264
|
`${position} · ${toggle}`,
|
|
260
265
|
toggle,
|
|
261
266
|
"^D"
|
|
@@ -286,15 +291,17 @@ function chatHistoryPlaceholderDisplayValue(terminalWidth, offset, maxOffset) {
|
|
|
286
291
|
"back"
|
|
287
292
|
]);
|
|
288
293
|
}
|
|
289
|
-
function chatTaskPlaceholderDisplayValue(terminalWidth, scrollable = false
|
|
290
|
-
if (
|
|
294
|
+
function chatTaskPlaceholderDisplayValue(terminalWidth, scrollable = false) {
|
|
295
|
+
if (terminalWidth >= 72) {
|
|
291
296
|
const activeCandidates = scrollable
|
|
292
297
|
? [
|
|
293
298
|
"message · scroll · ^N new · ^W logs · ^B workers · ^T tasks · Tab · ^O attach · ^G routes",
|
|
299
|
+
"scroll · ^N new · ^W logs · ^B workers · ^T tasks · Tab · ^O attach · ^G routes",
|
|
294
300
|
"message · scroll · ^N new · ^W logs · ^B workers · Tab · ^O attach · ^G routes"
|
|
295
301
|
]
|
|
296
302
|
: [
|
|
297
303
|
"message · ^N new · ^W logs · ^B workers · ^T tasks · Tab · ^O attach · ^G routes",
|
|
304
|
+
"^N new · ^W logs · ^B workers · ^T tasks · Tab · ^O attach · ^G routes",
|
|
298
305
|
"message · ^N new · ^W logs · ^B workers · Tab · ^O attach · ^G routes"
|
|
299
306
|
];
|
|
300
307
|
const active = activeCandidates.find((candidate) => displayWidth(candidate) <= chatPlaceholderValueWidth(terminalWidth));
|
|
@@ -561,9 +568,13 @@ function collaborationDetailInputHints(width) {
|
|
|
561
568
|
function taskSessionsInputHints(width, includeArchived) {
|
|
562
569
|
const archivedAction = includeArchived ? "H hide archived" : "H archived";
|
|
563
570
|
return selectInputHints(width, [
|
|
564
|
-
{ label: "sessions", detail: ` · Up/Dn select · Enter restore · I inspect · R rename · A archive · D delete · E export · ${archivedAction} · Esc back` },
|
|
565
|
-
{ label: "sessions", detail: " · Up/Dn select · Enter restore · I inspect · R rename · A archive · D delete · E export · Esc back" },
|
|
566
|
-
{ label: "sessions", detail: " · Up/Dn select · Enter restore · I inspect · R rename · A archive · D delete · Esc back" },
|
|
571
|
+
{ label: "sessions", detail: ` · Up/Dn select · Enter restore · C conversations · I inspect · R rename · A archive · D delete · E export · ${archivedAction} · Esc back` },
|
|
572
|
+
{ label: "sessions", detail: " · Up/Dn select · Enter restore · C conversations · I inspect · R rename · A archive · D delete · E export · Esc back" },
|
|
573
|
+
{ label: "sessions", detail: " · Up/Dn select · Enter restore · C chats · I inspect · R rename · A archive · D delete · Esc back" },
|
|
574
|
+
{ label: "sessions", detail: " · Up/Dn select · Enter restore · C chats · I inspect · R rename · A archive · Esc back" },
|
|
575
|
+
{ label: "sessions", detail: " · Up/Dn select · Enter restore · C chats · I inspect · R rename · Esc back" },
|
|
576
|
+
{ label: "sessions", detail: " · Up/Dn select · Enter restore · C chats · R rename · Esc back" },
|
|
577
|
+
{ label: "sessions", detail: " · Up/Dn select · Enter restore · C chats · Esc back" },
|
|
567
578
|
{ label: "sessions", detail: " · Up/Dn select · Enter restore · I inspect · R rename · A archive · Esc back" },
|
|
568
579
|
{ label: "sessions", detail: " · Up/Dn select · Enter restore · I inspect · R rename · Esc back" },
|
|
569
580
|
{ label: "sessions", detail: " · Up/Dn select · Enter restore · R rename · Esc back" },
|
|
@@ -577,6 +588,18 @@ function taskSessionsInputHints(width, includeArchived) {
|
|
|
577
588
|
{ label: "s", detail: "" }
|
|
578
589
|
]);
|
|
579
590
|
}
|
|
591
|
+
function mainConversationSessionsInputHints(width) {
|
|
592
|
+
return selectInputHints(width, [
|
|
593
|
+
{ label: "conversations", detail: " · Up/Dn select · Enter restore · N new · T tasks · Esc back" },
|
|
594
|
+
{ label: "conversations", detail: " · Up/Dn select · Enter restore · T tasks · Esc back" },
|
|
595
|
+
{ label: "conversations", detail: " · Up/Dn select · Enter restore · Esc back" },
|
|
596
|
+
{ label: "conversations", detail: " · Up/Dn select · Esc back" },
|
|
597
|
+
{ label: "conversations", detail: " · Esc back" },
|
|
598
|
+
{ label: "Esc back", detail: "" },
|
|
599
|
+
{ label: "chats", detail: "" },
|
|
600
|
+
{ label: "c", detail: "" }
|
|
601
|
+
]);
|
|
602
|
+
}
|
|
580
603
|
function taskSessionDetailInputHints(width, hasNative, canFork) {
|
|
581
604
|
const nativeActions = hasNative
|
|
582
605
|
? `${canFork ? "C continue · B branch" : "C continue"} · `
|