parallel-codex-tui 0.3.1 → 0.3.2

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/dist/tui/App.js CHANGED
@@ -119,6 +119,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
119
119
  const [taskSessionsError, setTaskSessionsError] = useState(null);
120
120
  const [taskSessionsNotice, setTaskSessionsNotice] = useState(null);
121
121
  const [taskSessionsIncludeArchived, setTaskSessionsIncludeArchived] = useState(false);
122
+ const [taskSessionQuery, setTaskSessionQuery] = useState("");
122
123
  const [taskSessionAction, setTaskSessionAction] = useState(null);
123
124
  const [taskSessionDetails, setTaskSessionDetails] = useState(null);
124
125
  const [taskSessionDetailSelectedWorkerIndex, setTaskSessionDetailSelectedWorkerIndex] = useState(0);
@@ -174,6 +175,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
174
175
  const mainConversationActionRef = useRef(mainConversationAction);
175
176
  const taskSessionsLoadingRef = useRef(taskSessionsLoading);
176
177
  const taskSessionsIncludeArchivedRef = useRef(taskSessionsIncludeArchived);
178
+ const taskSessionQueryRef = useRef("");
177
179
  const taskSessionActionRef = useRef(taskSessionAction);
178
180
  const taskSessionDetailsRef = useRef(null);
179
181
  const taskSessionDetailSelectedWorkerIndexRef = useRef(0);
@@ -453,6 +455,9 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
453
455
  useEffect(() => {
454
456
  taskSessionsIncludeArchivedRef.current = taskSessionsIncludeArchived;
455
457
  }, [taskSessionsIncludeArchived]);
458
+ useEffect(() => {
459
+ taskSessionQueryRef.current = taskSessionQuery;
460
+ }, [taskSessionQuery]);
456
461
  useEffect(() => {
457
462
  taskSessionActionRef.current = taskSessionAction;
458
463
  }, [taskSessionAction]);
@@ -1246,10 +1251,38 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
1246
1251
  const sessionAction = taskSessionActionRef.current;
1247
1252
  if (sessionAction) {
1248
1253
  if (chunk === "\x1b") {
1254
+ if (sessionAction.type === "search") {
1255
+ const preferred = taskSessionsRef.current[selectedTaskSessionIndexRef.current]?.id ?? null;
1256
+ updateTaskSessionAction(null);
1257
+ setTaskSessionsError(null);
1258
+ void refreshTaskSessionsRef.current(preferred, sessionAction.previousQuery);
1259
+ return;
1260
+ }
1249
1261
  updateTaskSessionAction(null);
1250
1262
  setTaskSessionsError(null);
1251
1263
  return;
1252
1264
  }
1265
+ if (sessionAction.type === "search") {
1266
+ const update = applyChatInputChunk(sessionAction.value, chunk, sessionAction.cursor);
1267
+ const preferred = taskSessionsRef.current[selectedTaskSessionIndexRef.current]?.id ?? null;
1268
+ if (update.submit !== null) {
1269
+ updateTaskSessionAction(null);
1270
+ setTaskSessionsError(null);
1271
+ void refreshTaskSessionsRef.current(preferred, update.submit);
1272
+ }
1273
+ else {
1274
+ updateTaskSessionAction({
1275
+ ...sessionAction,
1276
+ value: update.value,
1277
+ cursor: update.cursor
1278
+ });
1279
+ if (update.value !== sessionAction.value) {
1280
+ setTaskSessionsError(null);
1281
+ void refreshTaskSessionsRef.current(preferred, update.value);
1282
+ }
1283
+ }
1284
+ return;
1285
+ }
1253
1286
  if (taskSessionsLoadingRef.current) {
1254
1287
  return;
1255
1288
  }
@@ -1286,6 +1319,17 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
1286
1319
  if (taskSessionsLoadingRef.current) {
1287
1320
  return;
1288
1321
  }
1322
+ if (isWorkerSearchShortcut(chunk, {})) {
1323
+ updateTaskSessionAction({
1324
+ type: "search",
1325
+ value: taskSessionQueryRef.current,
1326
+ cursor: Array.from(taskSessionQueryRef.current).length,
1327
+ previousQuery: taskSessionQueryRef.current
1328
+ });
1329
+ setTaskSessionsError(null);
1330
+ setTaskSessionsNotice(null);
1331
+ return;
1332
+ }
1289
1333
  if (isRouterDiagnosticsShortcut(chunk, {})) {
1290
1334
  void openRouterDiagnosticsRef.current();
1291
1335
  return;
@@ -1303,6 +1347,11 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
1303
1347
  return;
1304
1348
  }
1305
1349
  const selectedSession = taskSessionsRef.current[selectedTaskSessionIndexRef.current];
1350
+ if ((chunk === "x" || chunk === "X") && taskSessionQueryRef.current.trim()) {
1351
+ void refreshTaskSessionsRef.current(selectedSession?.id ?? null, "");
1352
+ setTaskSessionsNotice("Task search cleared");
1353
+ return;
1354
+ }
1306
1355
  if (chunk === "r" || chunk === "R") {
1307
1356
  if (selectedSession) {
1308
1357
  updateTaskSessionAction({
@@ -2656,11 +2705,16 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
2656
2705
  setTaskSessionDetailNotice(null);
2657
2706
  updateTaskSessionAction(null);
2658
2707
  updateMainConversationAction(null);
2708
+ taskSessionQueryRef.current = "";
2709
+ setTaskSessionQuery("");
2659
2710
  sessionCenterModeRef.current = "tasks";
2660
2711
  setSessionCenterMode("tasks");
2661
2712
  await refreshTaskSessions(activeTaskIdRef.current);
2662
2713
  }
2663
- async function refreshTaskSessions(preferredTaskId = null) {
2714
+ async function refreshTaskSessions(preferredTaskId = null, query = taskSessionQueryRef.current) {
2715
+ const normalizedQuery = query.replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, 1000);
2716
+ taskSessionQueryRef.current = normalizedQuery;
2717
+ setTaskSessionQuery(normalizedQuery);
2664
2718
  taskSessionsLoadingRef.current = true;
2665
2719
  setTaskSessionsLoading(true);
2666
2720
  const sequence = taskSessionsLoadSequenceRef.current + 1;
@@ -2670,7 +2724,8 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
2670
2724
  throw new Error("Task sessions are unavailable");
2671
2725
  }
2672
2726
  const tasks = await loadTaskSessions({
2673
- includeArchived: taskSessionsIncludeArchivedRef.current
2727
+ includeArchived: taskSessionsIncludeArchivedRef.current,
2728
+ ...(normalizedQuery.trim() ? { query: normalizedQuery } : {})
2674
2729
  });
2675
2730
  if (taskSessionsLoadSequenceRef.current !== sequence) {
2676
2731
  return;
@@ -3529,9 +3584,11 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
3529
3584
  ? { type: "rename", value: taskSessionAction.value, cursor: taskSessionAction.cursor }
3530
3585
  : taskSessionAction?.type === "delete"
3531
3586
  ? { type: "delete", title: taskSessionAction.title }
3532
- : null, taskSessionsIncludeArchived: sessionCenterMode === "conversations"
3587
+ : taskSessionAction?.type === "search"
3588
+ ? { type: "search", value: taskSessionAction.value, cursor: taskSessionAction.cursor }
3589
+ : null, taskSessionsIncludeArchived: sessionCenterMode === "conversations"
3533
3590
  ? mainConversationsIncludeArchived
3534
- : 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, roleScope: roleConfigurationScope, roleEditingModel: roleModelEdit, roleCanApply: roleConfigurationScope !== "task" || Boolean(activeTaskId), roleSaving: roleConfigurationSaving, roleHasOverride: roleConfigurationHasOverride, value: workerSearch.open && view === "worker"
3591
+ : taskSessionsIncludeArchived, taskSessionQuery: taskSessionQuery, 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, roleScope: roleConfigurationScope, roleEditingModel: roleModelEdit, roleCanApply: roleConfigurationScope !== "task" || Boolean(activeTaskId), roleSaving: roleConfigurationSaving, roleHasOverride: roleConfigurationHasOverride, value: workerSearch.open && view === "worker"
3535
3592
  ? workerSearch.query
3536
3593
  : view === "native" || view === "router" || view === "workers" || view === "features" || view === "sessions" || view === "collaboration" || view === "status" || view === "roles" ? "" : input, cursor: workerSearch.open && view === "worker"
3537
3594
  ? workerSearch.cursor
@@ -3539,7 +3596,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
3539
3596
  ? { type: "rename", title: mainConversationAction.title }
3540
3597
  : mainConversationAction?.type === "delete"
3541
3598
  ? { type: "delete", title: mainConversationAction.title }
3542
- : null, loading: taskSessionsLoading, error: taskSessionsError, height: contentHeight, terminalWidth: terminalWidth })) : (_jsx(TaskSessionsView, { tasks: taskSessions, activeTaskId: activeTaskId, selectedIndex: selectedTaskSessionIndex, includeArchived: taskSessionsIncludeArchived, notice: taskSessionsNotice, action: taskSessionAction?.type === "rename"
3599
+ : null, loading: taskSessionsLoading, error: taskSessionsError, height: contentHeight, terminalWidth: terminalWidth })) : (_jsx(TaskSessionsView, { tasks: taskSessions, activeTaskId: activeTaskId, selectedIndex: selectedTaskSessionIndex, includeArchived: taskSessionsIncludeArchived, query: taskSessionQuery, notice: taskSessionsNotice, action: taskSessionAction?.type === "rename"
3543
3600
  ? { type: "rename", title: taskSessionAction.title }
3544
3601
  : taskSessionAction?.type === "delete"
3545
3602
  ? { type: "delete", title: taskSessionAction.title }
@@ -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, featureEditingModel = null, 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, roleScope = "next", roleEditingModel = null, roleCanApply = true, roleSaving = false, roleHasOverride = false, 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, featureEditingModel = null, taskSessionAction = null, taskSessionsIncludeArchived = false, taskSessionQuery = "", 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, roleScope = "next", roleEditingModel = null, roleCanApply = true, roleSaving = false, roleHasOverride = false, 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) {
@@ -42,6 +42,13 @@ export function InputBar({ mode, ready = true, busy = false, routeFallback = fal
42
42
  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] }));
43
43
  }
44
44
  if (mode === "sessions") {
45
+ if (taskSessionAction?.type === "search") {
46
+ const prefix = terminalWidth < 12 ? "/ " : "find > ";
47
+ const valueWidth = Math.max(1, terminalWidth - displayWidth(prefix) - 3);
48
+ const display = chatInputDisplayParts(taskSessionAction.value, taskSessionAction.cursor, valueWidth);
49
+ const textWidth = displayWidth(`${prefix}${display.before}|${display.after}`);
50
+ return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: textWidth, fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: prefix }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.text, children: display.before }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: "|" }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.text, children: display.after })] }));
51
+ }
45
52
  if (taskSessionAction?.type === "rename") {
46
53
  const prefix = terminalWidth < 12 ? "> " : "rename > ";
47
54
  const valueWidth = Math.max(1, terminalWidth - displayWidth(prefix) - 3);
@@ -61,7 +68,7 @@ export function InputBar({ mode, ready = true, busy = false, routeFallback = fal
61
68
  const hints = mainConversationSessionsInputHints(terminalWidth, taskSessionsIncludeArchived);
62
69
  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] }));
63
70
  }
64
- const hints = taskSessionsInputHints(terminalWidth, taskSessionsIncludeArchived);
71
+ const hints = taskSessionsInputHints(terminalWidth, taskSessionsIncludeArchived, Boolean(taskSessionQuery.trim()));
65
72
  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] }));
66
73
  }
67
74
  if (mode === "collaboration") {
@@ -596,10 +603,11 @@ function collaborationDetailInputHints(width) {
596
603
  { label: "e", detail: "" }
597
604
  ]);
598
605
  }
599
- function taskSessionsInputHints(width, includeArchived) {
606
+ function taskSessionsInputHints(width, includeArchived, hasQuery) {
600
607
  const archivedAction = includeArchived ? "H hide archived" : "H archived";
608
+ const searchAction = hasQuery ? "^F edit · X clear" : "^F find";
601
609
  return selectInputHints(width, [
602
- { label: "sessions", detail: ` · Up/Dn select · Enter restore · C conversations · I inspect · R rename · A archive · D delete · E export · ${archivedAction} · Esc back` },
610
+ { label: "sessions", detail: ` · Up/Dn select · Enter restore · C conversations · I inspect · R rename · A archive · D delete · E export · ${archivedAction} · Esc back · ${searchAction}` },
603
611
  { label: "sessions", detail: " · Up/Dn select · Enter restore · C conversations · I inspect · R rename · A archive · D delete · E export · Esc back" },
604
612
  { label: "sessions", detail: " · Up/Dn select · Enter restore · C chats · I inspect · R rename · A archive · D delete · Esc back" },
605
613
  { label: "sessions", detail: " · Up/Dn select · Enter restore · C chats · I inspect · R rename · A archive · Esc back" },
@@ -2,13 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
3
  import { compactEndByDisplayWidth, displayWidth } from "./display-width.js";
4
4
  import { TUI_THEME } from "./theme.js";
5
- export function TaskSessionsView({ tasks, activeTaskId, selectedIndex, includeArchived = false, notice = null, action = null, loading = false, error = null, height = 20, terminalWidth = process.stdout.columns || 120 }) {
5
+ export function TaskSessionsView({ tasks, activeTaskId, selectedIndex, includeArchived = false, query = "", notice = null, action = null, loading = false, error = null, height = 20, terminalWidth = process.stdout.columns || 120 }) {
6
6
  const viewportHeight = Math.max(1, height);
7
7
  const width = taskSessionsContentWidth(terminalWidth);
8
8
  const lines = taskSessionsDisplayLines(tasks, activeTaskId, selectedIndex, viewportHeight, terminalWidth, {
9
9
  loading,
10
10
  error,
11
11
  includeArchived,
12
+ query,
12
13
  notice,
13
14
  action
14
15
  });
@@ -21,7 +22,10 @@ export function taskSessionsDisplayLines(tasks, activeTaskId, selectedIndex, hei
21
22
  const lines = [
22
23
  {
23
24
  text: fitTaskSessionCandidates([
24
- state.includeArchived ? "Task sessions · archived shown" : "Task sessions",
25
+ taskSessionsHeading(Boolean(state.includeArchived), state.query ?? ""),
26
+ ...((state.query ?? "").trim()
27
+ ? [`Find · ${safeTaskSessionText(state.query ?? "")}`, `/ ${safeTaskSessionText(state.query ?? "")}`]
28
+ : []),
25
29
  state.includeArchived ? "Sessions · all" : "Sessions",
26
30
  "Tasks",
27
31
  "T"
@@ -30,7 +34,7 @@ export function taskSessionsDisplayLines(tasks, activeTaskId, selectedIndex, hei
30
34
  }
31
35
  ];
32
36
  if (viewportHeight >= 3) {
33
- lines.push({ text: taskSessionSummary(tasks, width), tone: "muted" });
37
+ lines.push({ text: taskSessionSummary(tasks, width, state.query ?? ""), tone: "muted" });
34
38
  }
35
39
  if (viewportHeight >= 4 && state.action) {
36
40
  lines.push({
@@ -58,7 +62,10 @@ export function taskSessionsDisplayLines(tasks, activeTaskId, selectedIndex, hei
58
62
  }
59
63
  if (tasks.length === 0) {
60
64
  if (slots > 0) {
61
- lines.push({ text: fitTaskSessionText("No saved task sessions", width), tone: "muted" });
65
+ lines.push({
66
+ text: fitTaskSessionText(state.query?.trim() ? "No matching task sessions" : "No saved task sessions", width),
67
+ tone: "muted"
68
+ });
62
69
  }
63
70
  return lines;
64
71
  }
@@ -94,7 +101,7 @@ function TaskSessionRow({ line, width }) {
94
101
  const theme = taskSessionLineTheme(line.tone);
95
102
  return (_jsxs(Text, { children: [_jsx(Text, { ...theme, children: line.text }), trailingWidth > 0 ? _jsx(Text, { backgroundColor: TUI_THEME.surface, children: " ".repeat(trailingWidth) }) : null] }));
96
103
  }
97
- function taskSessionSummary(tasks, width) {
104
+ function taskSessionSummary(tasks, width, query) {
98
105
  const counts = new Map();
99
106
  let archived = 0;
100
107
  for (const task of tasks) {
@@ -112,11 +119,16 @@ function taskSessionSummary(tasks, width) {
112
119
  if (archived > 0) {
113
120
  parts.push(`${archived} archived`);
114
121
  }
122
+ const searching = Boolean(query.trim());
123
+ const prefix = searching
124
+ ? `${tasks.length} ${tasks.length === 1 ? "match" : "matches"}`
125
+ : `${tasks.length} ${tasks.length === 1 ? "task" : "tasks"}`;
126
+ const compactPrefix = searching ? `${tasks.length} matches` : `${tasks.length} tasks`;
115
127
  return fitTaskSessionCandidates([
116
- [`${tasks.length} ${tasks.length === 1 ? "task" : "tasks"}`, ...parts].join(" · "),
117
- `${tasks.length} tasks · ${counts.get("running") ?? 0} active · ${counts.get("failed") ?? 0} failed`,
118
- `${tasks.length} tasks`,
119
- `${tasks.length}t`
128
+ [prefix, ...parts].join(" · "),
129
+ `${compactPrefix} · ${counts.get("running") ?? 0} active · ${counts.get("failed") ?? 0} failed`,
130
+ compactPrefix,
131
+ `${tasks.length}${searching ? "m" : "t"}`
120
132
  ], width);
121
133
  }
122
134
  function taskSessionRowText(task, selected, active, width) {
@@ -130,7 +142,9 @@ function taskSessionRowText(task, selected, active, width) {
130
142
  const workers = `${task.workerCount} ${task.workerCount === 1 ? "worker" : "workers"}`;
131
143
  const native = `${task.nativeSessionCount} native`;
132
144
  const compactId = task.id.replace(/^task-/, "#");
145
+ const match = safeTaskSessionText(task.searchMatch?.summary ?? "");
133
146
  return fitTaskSessionCandidates([
147
+ ...(match ? [[marker + title, status, match].join(" · ")] : []),
134
148
  [marker + title, status, turns, workers, native, date].join(" · "),
135
149
  [marker + title, status, turns, workers, native].join(" · "),
136
150
  [marker + title, status, turns, workers].join(" · "),
@@ -139,6 +153,13 @@ function taskSessionRowText(task, selected, active, width) {
139
153
  marker.trimEnd()
140
154
  ], width);
141
155
  }
156
+ function taskSessionsHeading(includeArchived, query) {
157
+ const scope = includeArchived ? "archived shown" : "active";
158
+ const cleanQuery = safeTaskSessionText(query);
159
+ return cleanQuery
160
+ ? `Task sessions · ${scope} · find ${cleanQuery}`
161
+ : includeArchived ? "Task sessions · archived shown" : "Task sessions";
162
+ }
142
163
  function taskSessionWindowStart(selected, count, visibleCount) {
143
164
  if (visibleCount <= 0 || count <= visibleCount) {
144
165
  return 0;
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = "0.3.1";
1
+ export const version = "0.3.2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "parallel-codex-tui",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "A TypeScript TUI wrapper for routed parallel coding with Codex and Claude workers.",
5
5
  "license": "MIT",
6
6
  "type": "module",