specsmd 0.1.46 → 0.1.47
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/bin/cli.js +1 -0
- package/lib/dashboard/git/worktrees.js +248 -0
- package/lib/dashboard/index.js +473 -7
- package/lib/dashboard/runtime/watch-runtime.js +18 -9
- package/lib/dashboard/tui/app.js +423 -29
- package/package.json +1 -1
package/lib/dashboard/tui/app.js
CHANGED
|
@@ -206,11 +206,11 @@ function buildShortStats(snapshot, flow) {
|
|
|
206
206
|
return `runs ${stats.activeRunsCount || 0}/${stats.completedRuns || 0} | intents ${stats.completedIntents || 0}/${stats.totalIntents || 0} | work ${stats.completedWorkItems || 0}/${stats.totalWorkItems || 0}`;
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
function buildHeaderLine(snapshot, flow, watchEnabled, watchStatus, lastRefreshAt, view, width) {
|
|
209
|
+
function buildHeaderLine(snapshot, flow, watchEnabled, watchStatus, lastRefreshAt, view, width, worktreeLabel = null) {
|
|
210
210
|
const projectName = snapshot?.project?.name || 'Unnamed project';
|
|
211
211
|
const shortStats = buildShortStats(snapshot, flow);
|
|
212
|
-
|
|
213
|
-
const line = `${flow.toUpperCase()} | ${projectName} | ${shortStats} | watch:${watchEnabled ? watchStatus : 'off'} | ${view} | ${formatTime(lastRefreshAt)}`;
|
|
212
|
+
const worktreeSegment = worktreeLabel ? ` | wt:${worktreeLabel}` : '';
|
|
213
|
+
const line = `${flow.toUpperCase()} | ${projectName} | ${shortStats} | watch:${watchEnabled ? watchStatus : 'off'}${worktreeSegment} | ${view} | ${formatTime(lastRefreshAt)}`;
|
|
214
214
|
|
|
215
215
|
return truncate(line, width);
|
|
216
216
|
}
|
|
@@ -1102,7 +1102,8 @@ function getPanelTitles(flow, snapshot) {
|
|
|
1102
1102
|
current: 'Current Bolt',
|
|
1103
1103
|
files: 'Bolt Files',
|
|
1104
1104
|
pending: 'Queued Bolts',
|
|
1105
|
-
completed: 'Recent Completed Bolts'
|
|
1105
|
+
completed: 'Recent Completed Bolts',
|
|
1106
|
+
otherWorktrees: 'Other Worktrees: Active Bolts'
|
|
1106
1107
|
};
|
|
1107
1108
|
}
|
|
1108
1109
|
if (effectiveFlow === 'simple') {
|
|
@@ -1110,18 +1111,186 @@ function getPanelTitles(flow, snapshot) {
|
|
|
1110
1111
|
current: 'Current Spec',
|
|
1111
1112
|
files: 'Spec Files',
|
|
1112
1113
|
pending: 'Pending Specs',
|
|
1113
|
-
completed: 'Recent Completed Specs'
|
|
1114
|
+
completed: 'Recent Completed Specs',
|
|
1115
|
+
otherWorktrees: 'Other Worktrees: Active Specs'
|
|
1114
1116
|
};
|
|
1115
1117
|
}
|
|
1116
1118
|
return {
|
|
1117
1119
|
current: 'Current Run',
|
|
1118
1120
|
files: 'Run Files',
|
|
1119
1121
|
pending: 'Pending Queue',
|
|
1120
|
-
completed: 'Recent Completed Runs'
|
|
1122
|
+
completed: 'Recent Completed Runs',
|
|
1123
|
+
otherWorktrees: 'Other Worktrees: Active Runs'
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
function getDashboardWorktreeMeta(snapshot) {
|
|
1128
|
+
if (!snapshot || typeof snapshot !== 'object') {
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1131
|
+
const meta = snapshot.dashboardWorktrees;
|
|
1132
|
+
if (!meta || typeof meta !== 'object') {
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
const items = Array.isArray(meta.items) ? meta.items : [];
|
|
1136
|
+
if (items.length === 0) {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
return {
|
|
1140
|
+
...meta,
|
|
1141
|
+
items
|
|
1121
1142
|
};
|
|
1122
1143
|
}
|
|
1123
1144
|
|
|
1124
|
-
function
|
|
1145
|
+
function getWorktreeItems(snapshot) {
|
|
1146
|
+
return getDashboardWorktreeMeta(snapshot)?.items || [];
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
function getSelectedWorktree(snapshot) {
|
|
1150
|
+
const meta = getDashboardWorktreeMeta(snapshot);
|
|
1151
|
+
if (!meta) {
|
|
1152
|
+
return null;
|
|
1153
|
+
}
|
|
1154
|
+
return meta.items.find((item) => item.id === meta.selectedWorktreeId) || null;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
function hasMultipleWorktrees(snapshot) {
|
|
1158
|
+
return getWorktreeItems(snapshot).length > 1;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
function isSelectedWorktreeMain(snapshot) {
|
|
1162
|
+
const selected = getSelectedWorktree(snapshot);
|
|
1163
|
+
return Boolean(selected?.isMainBranch);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
function getWorktreeDisplayName(worktree) {
|
|
1167
|
+
if (!worktree || typeof worktree !== 'object') {
|
|
1168
|
+
return 'unknown';
|
|
1169
|
+
}
|
|
1170
|
+
if (typeof worktree.displayBranch === 'string' && worktree.displayBranch.trim() !== '') {
|
|
1171
|
+
return worktree.displayBranch;
|
|
1172
|
+
}
|
|
1173
|
+
if (typeof worktree.branch === 'string' && worktree.branch.trim() !== '') {
|
|
1174
|
+
return worktree.branch;
|
|
1175
|
+
}
|
|
1176
|
+
if (typeof worktree.name === 'string' && worktree.name.trim() !== '') {
|
|
1177
|
+
return worktree.name;
|
|
1178
|
+
}
|
|
1179
|
+
return path.basename(worktree.path || '') || 'unknown';
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function buildWorktreeRows(snapshot, flow) {
|
|
1183
|
+
const meta = getDashboardWorktreeMeta(snapshot);
|
|
1184
|
+
if (!meta) {
|
|
1185
|
+
return [{
|
|
1186
|
+
kind: 'info',
|
|
1187
|
+
key: 'worktrees:none',
|
|
1188
|
+
label: 'No git worktrees detected',
|
|
1189
|
+
selectable: false
|
|
1190
|
+
}];
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
1194
|
+
const entityLabel = effectiveFlow === 'aidlc'
|
|
1195
|
+
? 'active bolts'
|
|
1196
|
+
: (effectiveFlow === 'simple' ? 'active specs' : 'active runs');
|
|
1197
|
+
|
|
1198
|
+
const rows = [];
|
|
1199
|
+
for (const item of meta.items) {
|
|
1200
|
+
const currentLabel = item.isSelected ? '[CURRENT] ' : '';
|
|
1201
|
+
const mainLabel = item.isMainBranch && !item.detached ? '[MAIN] ' : '';
|
|
1202
|
+
const availabilityLabel = item.flowAvailable ? '' : ' (flow unavailable)';
|
|
1203
|
+
const statusLabel = item.status === 'loading'
|
|
1204
|
+
? ' loading...'
|
|
1205
|
+
: (item.status === 'error' ? ' error' : ` ${item.activeCount || 0} ${entityLabel}`);
|
|
1206
|
+
const scopeLabel = item.name ? ` (${item.name})` : '';
|
|
1207
|
+
|
|
1208
|
+
rows.push({
|
|
1209
|
+
kind: 'info',
|
|
1210
|
+
key: `worktree:item:${item.id}`,
|
|
1211
|
+
label: `${currentLabel}${mainLabel}${getWorktreeDisplayName(item)}${scopeLabel}${availabilityLabel}${statusLabel}`,
|
|
1212
|
+
color: item.isSelected ? 'green' : (item.flowAvailable ? 'gray' : 'red'),
|
|
1213
|
+
bold: item.isSelected,
|
|
1214
|
+
selectable: true
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
return rows;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
function buildOtherWorktreeActiveGroups(snapshot, flow) {
|
|
1222
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
1223
|
+
if (effectiveFlow === 'simple') {
|
|
1224
|
+
return [];
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
const meta = getDashboardWorktreeMeta(snapshot);
|
|
1228
|
+
if (!meta) {
|
|
1229
|
+
return [];
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
const selectedWorktree = getSelectedWorktree(snapshot);
|
|
1233
|
+
if (!selectedWorktree || !selectedWorktree.isMainBranch) {
|
|
1234
|
+
return [];
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
const groups = [];
|
|
1238
|
+
const otherItems = meta.items.filter((item) => item.id !== meta.selectedWorktreeId);
|
|
1239
|
+
for (const item of otherItems) {
|
|
1240
|
+
if (!item.flowAvailable || item.status === 'unavailable' || item.status === 'error') {
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
if (effectiveFlow === 'aidlc') {
|
|
1245
|
+
const activeBolts = Array.isArray(item.activity?.activeBolts) ? item.activity.activeBolts : [];
|
|
1246
|
+
for (const bolt of activeBolts) {
|
|
1247
|
+
const stages = Array.isArray(bolt?.stages) ? bolt.stages : [];
|
|
1248
|
+
const completedStages = stages.filter((stage) => stage?.status === 'completed').length;
|
|
1249
|
+
groups.push({
|
|
1250
|
+
key: `other:wt:${item.id}:bolt:${bolt.id}`,
|
|
1251
|
+
label: `[WT ${getWorktreeDisplayName(item)}] ${bolt.id} [${bolt.type || 'bolt'}] ${completedStages}/${stages.length || 0} stages`,
|
|
1252
|
+
files: collectAidlcBoltFiles(bolt).map((file) => ({ ...file, scope: 'active' }))
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
const activeRuns = Array.isArray(item.activity?.activeRuns) ? item.activity.activeRuns : [];
|
|
1259
|
+
for (const run of activeRuns) {
|
|
1260
|
+
const workItems = Array.isArray(run?.workItems) ? run.workItems : [];
|
|
1261
|
+
const completed = workItems.filter((workItem) => workItem?.status === 'completed').length;
|
|
1262
|
+
groups.push({
|
|
1263
|
+
key: `other:wt:${item.id}:run:${run.id}`,
|
|
1264
|
+
label: `[WT ${getWorktreeDisplayName(item)}] ${run.id} [${run.scope || 'single'}] ${completed}/${workItems.length} items`,
|
|
1265
|
+
files: collectFireRunFiles(run).map((file) => ({ ...file, scope: 'active' }))
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
return groups;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
function getOtherWorktreeEmptyMessage(snapshot, flow) {
|
|
1274
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
1275
|
+
if (!hasMultipleWorktrees(snapshot)) {
|
|
1276
|
+
return 'No additional worktrees';
|
|
1277
|
+
}
|
|
1278
|
+
if (!isSelectedWorktreeMain(snapshot)) {
|
|
1279
|
+
return 'Switch to main worktree to view active items from other worktrees';
|
|
1280
|
+
}
|
|
1281
|
+
if (effectiveFlow === 'aidlc') {
|
|
1282
|
+
return 'No active bolts in other worktrees';
|
|
1283
|
+
}
|
|
1284
|
+
if (effectiveFlow === 'simple') {
|
|
1285
|
+
return 'No active specs in other worktrees';
|
|
1286
|
+
}
|
|
1287
|
+
return 'No active runs in other worktrees';
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
function getSectionOrderForView(view, options = {}) {
|
|
1291
|
+
const includeWorktrees = options.includeWorktrees === true;
|
|
1292
|
+
const includeOtherWorktrees = options.includeOtherWorktrees === true;
|
|
1293
|
+
|
|
1125
1294
|
if (view === 'intents') {
|
|
1126
1295
|
return ['intent-status'];
|
|
1127
1296
|
}
|
|
@@ -1131,7 +1300,15 @@ function getSectionOrderForView(view) {
|
|
|
1131
1300
|
if (view === 'health') {
|
|
1132
1301
|
return ['standards', 'stats', 'warnings', 'error-details'];
|
|
1133
1302
|
}
|
|
1134
|
-
|
|
1303
|
+
const sections = [];
|
|
1304
|
+
if (includeWorktrees) {
|
|
1305
|
+
sections.push('worktrees');
|
|
1306
|
+
}
|
|
1307
|
+
sections.push('current-run', 'run-files');
|
|
1308
|
+
if (includeOtherWorktrees) {
|
|
1309
|
+
sections.push('other-worktrees-active');
|
|
1310
|
+
}
|
|
1311
|
+
return sections;
|
|
1135
1312
|
}
|
|
1136
1313
|
|
|
1137
1314
|
function cycleSection(view, currentSectionKey, direction = 1, availableSections = null) {
|
|
@@ -2203,7 +2380,8 @@ function buildQuickHelpText(view, options = {}) {
|
|
|
2203
2380
|
const {
|
|
2204
2381
|
flow = 'fire',
|
|
2205
2382
|
previewOpen = false,
|
|
2206
|
-
availableFlowCount = 1
|
|
2383
|
+
availableFlowCount = 1,
|
|
2384
|
+
hasWorktrees = false
|
|
2207
2385
|
} = options;
|
|
2208
2386
|
const isAidlc = String(flow || '').toLowerCase() === 'aidlc';
|
|
2209
2387
|
const isSimple = String(flow || '').toLowerCase() === 'simple';
|
|
@@ -2219,7 +2397,13 @@ function buildQuickHelpText(view, options = {}) {
|
|
|
2219
2397
|
}
|
|
2220
2398
|
}
|
|
2221
2399
|
if (view === 'runs') {
|
|
2400
|
+
if (hasWorktrees) {
|
|
2401
|
+
parts.push('b worktrees', 'u others');
|
|
2402
|
+
}
|
|
2222
2403
|
parts.push('a current', 'f files');
|
|
2404
|
+
if (hasWorktrees) {
|
|
2405
|
+
parts.push('w worktree');
|
|
2406
|
+
}
|
|
2223
2407
|
}
|
|
2224
2408
|
parts.push(`tab1 ${activeLabel}`);
|
|
2225
2409
|
|
|
@@ -2238,7 +2422,8 @@ function buildHelpOverlayLines(options = {}) {
|
|
|
2238
2422
|
previewOpen = false,
|
|
2239
2423
|
paneFocus = 'main',
|
|
2240
2424
|
availableFlowCount = 1,
|
|
2241
|
-
showErrorSection = false
|
|
2425
|
+
showErrorSection = false,
|
|
2426
|
+
hasWorktrees = false
|
|
2242
2427
|
} = options;
|
|
2243
2428
|
const isAidlc = String(flow || '').toLowerCase() === 'aidlc';
|
|
2244
2429
|
const isSimple = String(flow || '').toLowerCase() === 'simple';
|
|
@@ -2255,8 +2440,10 @@ function buildHelpOverlayLines(options = {}) {
|
|
|
2255
2440
|
'esc close overlays (help/preview/fullscreen)',
|
|
2256
2441
|
{ text: '', color: undefined, bold: false },
|
|
2257
2442
|
{ text: 'Tab 1 Active', color: 'yellow', bold: true },
|
|
2443
|
+
...(hasWorktrees ? ['b focus worktrees section', 'u focus other-worktrees section'] : []),
|
|
2258
2444
|
`a focus active ${itemLabel}`,
|
|
2259
2445
|
`f focus ${itemLabel} files`,
|
|
2446
|
+
...(hasWorktrees ? ['w open worktree switcher'] : []),
|
|
2260
2447
|
'up/down or j/k move selection',
|
|
2261
2448
|
'enter expand/collapse selected folder row',
|
|
2262
2449
|
'v or space preview selected file',
|
|
@@ -2292,6 +2479,53 @@ function buildHelpOverlayLines(options = {}) {
|
|
|
2292
2479
|
return lines;
|
|
2293
2480
|
}
|
|
2294
2481
|
|
|
2482
|
+
function buildWorktreeOverlayLines(snapshot, selectedIndex, width) {
|
|
2483
|
+
const meta = getDashboardWorktreeMeta(snapshot);
|
|
2484
|
+
if (!meta) {
|
|
2485
|
+
return [{
|
|
2486
|
+
text: truncate('No worktrees available', width),
|
|
2487
|
+
color: 'gray',
|
|
2488
|
+
bold: false
|
|
2489
|
+
}];
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
const items = Array.isArray(meta.items) ? meta.items : [];
|
|
2493
|
+
const clampedIndex = clampIndex(selectedIndex, items.length || 1);
|
|
2494
|
+
const lines = [{
|
|
2495
|
+
text: truncate('Use ↑/↓ and Enter to switch. Esc closes.', width),
|
|
2496
|
+
color: 'gray',
|
|
2497
|
+
bold: false
|
|
2498
|
+
}];
|
|
2499
|
+
|
|
2500
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
2501
|
+
const item = items[index];
|
|
2502
|
+
const marker = index === clampedIndex ? '>' : ' ';
|
|
2503
|
+
const current = item.isSelected ? '[CURRENT] ' : '';
|
|
2504
|
+
const main = item.isMainBranch && !item.detached ? '[MAIN] ' : '';
|
|
2505
|
+
const status = item.status === 'loading'
|
|
2506
|
+
? 'loading'
|
|
2507
|
+
: (item.status === 'error'
|
|
2508
|
+
? 'error'
|
|
2509
|
+
: (item.flowAvailable ? `${item.activeCount || 0} active` : 'flow unavailable'));
|
|
2510
|
+
const pathLabel = item.path ? path.basename(item.path) : 'unknown';
|
|
2511
|
+
lines.push({
|
|
2512
|
+
text: truncate(`${marker} ${current}${main}${getWorktreeDisplayName(item)} (${pathLabel}) | ${status}`, width),
|
|
2513
|
+
color: index === clampedIndex ? 'green' : (item.isSelected ? 'cyan' : 'gray'),
|
|
2514
|
+
bold: index === clampedIndex || item.isSelected
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
if (meta.hasPendingScans) {
|
|
2519
|
+
lines.push({
|
|
2520
|
+
text: truncate('Background scan in progress for additional worktrees...', width),
|
|
2521
|
+
color: 'yellow',
|
|
2522
|
+
bold: false
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
return lines;
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2295
2529
|
function colorizeMarkdownLine(line, inCodeBlock) {
|
|
2296
2530
|
const text = String(line ?? '');
|
|
2297
2531
|
|
|
@@ -2451,6 +2685,7 @@ function createDashboardApp(deps) {
|
|
|
2451
2685
|
flow,
|
|
2452
2686
|
availableFlows,
|
|
2453
2687
|
resolveRootPathForFlow,
|
|
2688
|
+
resolveRootPathsForFlow,
|
|
2454
2689
|
refreshMs,
|
|
2455
2690
|
watchEnabled,
|
|
2456
2691
|
initialSnapshot,
|
|
@@ -2660,8 +2895,10 @@ function createDashboardApp(deps) {
|
|
|
2660
2895
|
health: 'standards'
|
|
2661
2896
|
});
|
|
2662
2897
|
const [selectionBySection, setSelectionBySection] = useState({
|
|
2898
|
+
worktrees: 0,
|
|
2663
2899
|
'current-run': 0,
|
|
2664
2900
|
'run-files': 0,
|
|
2901
|
+
'other-worktrees-active': 0,
|
|
2665
2902
|
'intent-status': 0,
|
|
2666
2903
|
'completed-runs': 0,
|
|
2667
2904
|
standards: 0,
|
|
@@ -2676,6 +2913,11 @@ function createDashboardApp(deps) {
|
|
|
2676
2913
|
const [previewOpen, setPreviewOpen] = useState(false);
|
|
2677
2914
|
const [paneFocus, setPaneFocus] = useState('main');
|
|
2678
2915
|
const [overlayPreviewOpen, setOverlayPreviewOpen] = useState(false);
|
|
2916
|
+
const [worktreeOverlayOpen, setWorktreeOverlayOpen] = useState(false);
|
|
2917
|
+
const [worktreeOverlayIndex, setWorktreeOverlayIndex] = useState(0);
|
|
2918
|
+
const [selectedWorktreeId, setSelectedWorktreeId] = useState(
|
|
2919
|
+
initialSnapshot?.dashboardWorktrees?.selectedWorktreeId || null
|
|
2920
|
+
);
|
|
2679
2921
|
const [previewScroll, setPreviewScroll] = useState(0);
|
|
2680
2922
|
const [statusLine, setStatusLine] = useState('');
|
|
2681
2923
|
const [lastRefreshAt, setLastRefreshAt] = useState(new Date().toISOString());
|
|
@@ -2685,9 +2927,9 @@ function createDashboardApp(deps) {
|
|
|
2685
2927
|
rows: stdout?.rows || process.stdout.rows || 40
|
|
2686
2928
|
}));
|
|
2687
2929
|
const icons = resolveIconSet();
|
|
2688
|
-
const parseSnapshotForActiveFlow = useCallback(async (flowId) => {
|
|
2930
|
+
const parseSnapshotForActiveFlow = useCallback(async (flowId, context = {}) => {
|
|
2689
2931
|
if (typeof parseSnapshotForFlow === 'function') {
|
|
2690
|
-
return parseSnapshotForFlow(flowId);
|
|
2932
|
+
return parseSnapshotForFlow(flowId, context);
|
|
2691
2933
|
}
|
|
2692
2934
|
if (typeof parseSnapshot === 'function') {
|
|
2693
2935
|
return parseSnapshot();
|
|
@@ -2703,10 +2945,18 @@ function createDashboardApp(deps) {
|
|
|
2703
2945
|
|
|
2704
2946
|
const previewVisibleRows = Number.isFinite(terminalSize.rows) ? terminalSize.rows : (process.stdout.rows || 40);
|
|
2705
2947
|
const showErrorPanelForSections = Boolean(error) && previewVisibleRows >= 18;
|
|
2948
|
+
const worktreeSectionEnabled = hasMultipleWorktrees(snapshot);
|
|
2949
|
+
const otherWorktreesSectionEnabled = worktreeSectionEnabled
|
|
2950
|
+
&& isSelectedWorktreeMain(snapshot)
|
|
2951
|
+
&& getEffectiveFlow(activeFlow, snapshot) !== 'simple';
|
|
2952
|
+
|
|
2706
2953
|
const getAvailableSections = useCallback((viewId) => {
|
|
2707
|
-
const base = getSectionOrderForView(viewId
|
|
2954
|
+
const base = getSectionOrderForView(viewId, {
|
|
2955
|
+
includeWorktrees: worktreeSectionEnabled,
|
|
2956
|
+
includeOtherWorktrees: otherWorktreesSectionEnabled
|
|
2957
|
+
});
|
|
2708
2958
|
return base.filter((sectionKey) => sectionKey !== 'error-details' || showErrorPanelForSections);
|
|
2709
|
-
}, [showErrorPanelForSections]);
|
|
2959
|
+
}, [showErrorPanelForSections, worktreeSectionEnabled, otherWorktreesSectionEnabled]);
|
|
2710
2960
|
|
|
2711
2961
|
const effectiveFlow = getEffectiveFlow(activeFlow, snapshot);
|
|
2712
2962
|
const approvalGate = detectDashboardApprovalGate(snapshot, activeFlow);
|
|
@@ -2739,6 +2989,8 @@ function createDashboardApp(deps) {
|
|
|
2739
2989
|
...currentRunRowsBase
|
|
2740
2990
|
]
|
|
2741
2991
|
: currentRunRowsBase;
|
|
2992
|
+
const worktreeRows = buildWorktreeRows(snapshot, activeFlow);
|
|
2993
|
+
|
|
2742
2994
|
const shouldHydrateSecondaryTabs = deferredTabsReady || ui.view !== 'runs';
|
|
2743
2995
|
const runFileGroups = buildRunFileEntityGroups(snapshot, activeFlow, {
|
|
2744
2996
|
includeBacklog: shouldHydrateSecondaryTabs
|
|
@@ -2754,6 +3006,12 @@ function createDashboardApp(deps) {
|
|
|
2754
3006
|
getNoFileMessage(effectiveFlow),
|
|
2755
3007
|
runFileExpandedGroups
|
|
2756
3008
|
);
|
|
3009
|
+
const otherWorktreeGroups = buildOtherWorktreeActiveGroups(snapshot, activeFlow);
|
|
3010
|
+
const otherWorktreeRows = toExpandableRows(
|
|
3011
|
+
otherWorktreeGroups,
|
|
3012
|
+
getOtherWorktreeEmptyMessage(snapshot, activeFlow),
|
|
3013
|
+
expandedGroups
|
|
3014
|
+
);
|
|
2757
3015
|
const intentRows = shouldHydrateSecondaryTabs
|
|
2758
3016
|
? [
|
|
2759
3017
|
{
|
|
@@ -2804,8 +3062,10 @@ function createDashboardApp(deps) {
|
|
|
2804
3062
|
: toLoadingRows('Loading error details...', 'error-loading');
|
|
2805
3063
|
|
|
2806
3064
|
const rowsBySection = {
|
|
3065
|
+
worktrees: worktreeRows,
|
|
2807
3066
|
'current-run': currentRunRows,
|
|
2808
3067
|
'run-files': runFileRows,
|
|
3068
|
+
'other-worktrees-active': otherWorktreeRows,
|
|
2809
3069
|
'intent-status': intentRows,
|
|
2810
3070
|
'completed-runs': completedRows,
|
|
2811
3071
|
standards: standardsRows,
|
|
@@ -2813,6 +3073,12 @@ function createDashboardApp(deps) {
|
|
|
2813
3073
|
warnings: warningsRows,
|
|
2814
3074
|
'error-details': errorDetailsRows
|
|
2815
3075
|
};
|
|
3076
|
+
const worktreeItems = getWorktreeItems(snapshot);
|
|
3077
|
+
const selectedWorktree = getSelectedWorktree(snapshot);
|
|
3078
|
+
const selectedWorktreeLabel = selectedWorktree ? getWorktreeDisplayName(selectedWorktree) : null;
|
|
3079
|
+
const worktreeWatchSignature = `${snapshot?.dashboardWorktrees?.selectedWorktreeId || ''}|${worktreeItems
|
|
3080
|
+
.map((item) => `${item.id}:${item.status}:${item.activeCount}:${item.flowAvailable ? '1' : '0'}`)
|
|
3081
|
+
.join(',')}`;
|
|
2816
3082
|
const rowLengthSignature = Object.entries(rowsBySection)
|
|
2817
3083
|
.map(([key, rowsForSection]) => `${key}:${Array.isArray(rowsForSection) ? rowsForSection.length : 0}`)
|
|
2818
3084
|
.join('|');
|
|
@@ -2831,7 +3097,9 @@ function createDashboardApp(deps) {
|
|
|
2831
3097
|
const now = new Date().toISOString();
|
|
2832
3098
|
|
|
2833
3099
|
try {
|
|
2834
|
-
const result = await parseSnapshotForActiveFlow(activeFlow
|
|
3100
|
+
const result = await parseSnapshotForActiveFlow(activeFlow, {
|
|
3101
|
+
selectedWorktreeId
|
|
3102
|
+
});
|
|
2835
3103
|
|
|
2836
3104
|
if (result?.ok) {
|
|
2837
3105
|
const nextSnapshot = result.snapshot
|
|
@@ -2845,6 +3113,11 @@ function createDashboardApp(deps) {
|
|
|
2845
3113
|
setLastRefreshAt(now);
|
|
2846
3114
|
}
|
|
2847
3115
|
|
|
3116
|
+
const nextSelectedWorktreeId = nextSnapshot?.dashboardWorktrees?.selectedWorktreeId;
|
|
3117
|
+
if (typeof nextSelectedWorktreeId === 'string' && nextSelectedWorktreeId !== '' && nextSelectedWorktreeId !== selectedWorktreeId) {
|
|
3118
|
+
setSelectedWorktreeId(nextSelectedWorktreeId);
|
|
3119
|
+
}
|
|
3120
|
+
|
|
2848
3121
|
if (errorHashRef.current !== null) {
|
|
2849
3122
|
errorHashRef.current = null;
|
|
2850
3123
|
setError(null);
|
|
@@ -2874,7 +3147,7 @@ function createDashboardApp(deps) {
|
|
|
2874
3147
|
setLastRefreshAt(now);
|
|
2875
3148
|
}
|
|
2876
3149
|
}
|
|
2877
|
-
}, [activeFlow, parseSnapshotForActiveFlow, watchEnabled]);
|
|
3150
|
+
}, [activeFlow, parseSnapshotForActiveFlow, selectedWorktreeId, watchEnabled]);
|
|
2878
3151
|
|
|
2879
3152
|
useInput((input, key) => {
|
|
2880
3153
|
if ((key.ctrl && input === 'c') || input === 'q') {
|
|
@@ -2901,6 +3174,44 @@ function createDashboardApp(deps) {
|
|
|
2901
3174
|
return;
|
|
2902
3175
|
}
|
|
2903
3176
|
|
|
3177
|
+
if (worktreeOverlayOpen) {
|
|
3178
|
+
if (key.escape) {
|
|
3179
|
+
setWorktreeOverlayOpen(false);
|
|
3180
|
+
return;
|
|
3181
|
+
}
|
|
3182
|
+
|
|
3183
|
+
if (key.upArrow || input === 'k') {
|
|
3184
|
+
setWorktreeOverlayIndex((previous) => Math.max(0, previous - 1));
|
|
3185
|
+
return;
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
if (key.downArrow || input === 'j') {
|
|
3189
|
+
setWorktreeOverlayIndex((previous) => Math.min(Math.max(0, worktreeItems.length - 1), previous + 1));
|
|
3190
|
+
return;
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
if (key.return || key.enter) {
|
|
3194
|
+
const selectedOverlayItem = worktreeItems[clampIndex(worktreeOverlayIndex, worktreeItems.length || 1)];
|
|
3195
|
+
if (!selectedOverlayItem) {
|
|
3196
|
+
setStatusLine('No worktree selected.');
|
|
3197
|
+
setWorktreeOverlayOpen(false);
|
|
3198
|
+
return;
|
|
3199
|
+
}
|
|
3200
|
+
setSelectedWorktreeId(selectedOverlayItem.id);
|
|
3201
|
+
setWorktreeOverlayOpen(false);
|
|
3202
|
+
setPreviewTarget(null);
|
|
3203
|
+
setPreviewOpen(false);
|
|
3204
|
+
setOverlayPreviewOpen(false);
|
|
3205
|
+
setPreviewScroll(0);
|
|
3206
|
+
setPaneFocus('main');
|
|
3207
|
+
setStatusLine(`Switched to worktree: ${getWorktreeDisplayName(selectedOverlayItem)}`);
|
|
3208
|
+
void refresh();
|
|
3209
|
+
return;
|
|
3210
|
+
}
|
|
3211
|
+
|
|
3212
|
+
return;
|
|
3213
|
+
}
|
|
3214
|
+
|
|
2904
3215
|
if (input === '1') {
|
|
2905
3216
|
setUi((previous) => ({ ...previous, view: 'runs' }));
|
|
2906
3217
|
setPaneFocus('main');
|
|
@@ -2925,6 +3236,13 @@ function createDashboardApp(deps) {
|
|
|
2925
3236
|
return;
|
|
2926
3237
|
}
|
|
2927
3238
|
|
|
3239
|
+
if (ui.view === 'runs' && input === 'w' && worktreeSectionEnabled) {
|
|
3240
|
+
setWorktreeOverlayIndex(clampIndex(worktreeItems.findIndex((item) => item.id === selectedWorktreeId), worktreeItems.length || 1));
|
|
3241
|
+
setWorktreeOverlayOpen(true);
|
|
3242
|
+
setPaneFocus('main');
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3245
|
+
|
|
2928
3246
|
if ((input === ']' || input === 'm') && availableFlowIds.length > 1) {
|
|
2929
3247
|
snapshotHashRef.current = safeJsonHash(null);
|
|
2930
3248
|
errorHashRef.current = null;
|
|
@@ -2938,8 +3256,10 @@ function createDashboardApp(deps) {
|
|
|
2938
3256
|
return availableFlowIds[nextIndex];
|
|
2939
3257
|
});
|
|
2940
3258
|
setSelectionBySection({
|
|
3259
|
+
worktrees: 0,
|
|
2941
3260
|
'current-run': 0,
|
|
2942
3261
|
'run-files': 0,
|
|
3262
|
+
'other-worktrees-active': 0,
|
|
2943
3263
|
'intent-status': 0,
|
|
2944
3264
|
'completed-runs': 0,
|
|
2945
3265
|
standards: 0,
|
|
@@ -2958,6 +3278,7 @@ function createDashboardApp(deps) {
|
|
|
2958
3278
|
setPreviewTarget(null);
|
|
2959
3279
|
setPreviewOpen(false);
|
|
2960
3280
|
setOverlayPreviewOpen(false);
|
|
3281
|
+
setWorktreeOverlayOpen(false);
|
|
2961
3282
|
setPreviewScroll(0);
|
|
2962
3283
|
setPaneFocus('main');
|
|
2963
3284
|
return;
|
|
@@ -2976,8 +3297,10 @@ function createDashboardApp(deps) {
|
|
|
2976
3297
|
return availableFlowIds[nextIndex];
|
|
2977
3298
|
});
|
|
2978
3299
|
setSelectionBySection({
|
|
3300
|
+
worktrees: 0,
|
|
2979
3301
|
'current-run': 0,
|
|
2980
3302
|
'run-files': 0,
|
|
3303
|
+
'other-worktrees-active': 0,
|
|
2981
3304
|
'intent-status': 0,
|
|
2982
3305
|
'completed-runs': 0,
|
|
2983
3306
|
standards: 0,
|
|
@@ -2996,6 +3319,7 @@ function createDashboardApp(deps) {
|
|
|
2996
3319
|
setPreviewTarget(null);
|
|
2997
3320
|
setPreviewOpen(false);
|
|
2998
3321
|
setOverlayPreviewOpen(false);
|
|
3322
|
+
setWorktreeOverlayOpen(false);
|
|
2999
3323
|
setPreviewScroll(0);
|
|
3000
3324
|
setPaneFocus('main');
|
|
3001
3325
|
return;
|
|
@@ -3045,6 +3369,11 @@ function createDashboardApp(deps) {
|
|
|
3045
3369
|
}
|
|
3046
3370
|
|
|
3047
3371
|
if (ui.view === 'runs') {
|
|
3372
|
+
if (input === 'b' && worktreeSectionEnabled) {
|
|
3373
|
+
setSectionFocus((previous) => ({ ...previous, runs: 'worktrees' }));
|
|
3374
|
+
setPaneFocus('main');
|
|
3375
|
+
return;
|
|
3376
|
+
}
|
|
3048
3377
|
if (input === 'a') {
|
|
3049
3378
|
setSectionFocus((previous) => ({ ...previous, runs: 'current-run' }));
|
|
3050
3379
|
setPaneFocus('main');
|
|
@@ -3055,6 +3384,11 @@ function createDashboardApp(deps) {
|
|
|
3055
3384
|
setPaneFocus('main');
|
|
3056
3385
|
return;
|
|
3057
3386
|
}
|
|
3387
|
+
if (input === 'u' && otherWorktreesSectionEnabled) {
|
|
3388
|
+
setSectionFocus((previous) => ({ ...previous, runs: 'other-worktrees-active' }));
|
|
3389
|
+
setPaneFocus('main');
|
|
3390
|
+
return;
|
|
3391
|
+
}
|
|
3058
3392
|
} else if (ui.view === 'intents') {
|
|
3059
3393
|
if (input === 'i') {
|
|
3060
3394
|
setSectionFocus((previous) => ({ ...previous, intents: 'intent-status' }));
|
|
@@ -3193,6 +3527,28 @@ function createDashboardApp(deps) {
|
|
|
3193
3527
|
void refresh();
|
|
3194
3528
|
}, [refresh]);
|
|
3195
3529
|
|
|
3530
|
+
useEffect(() => {
|
|
3531
|
+
const snapshotSelected = snapshot?.dashboardWorktrees?.selectedWorktreeId;
|
|
3532
|
+
if (typeof snapshotSelected !== 'string' || snapshotSelected === '') {
|
|
3533
|
+
return;
|
|
3534
|
+
}
|
|
3535
|
+
if (snapshotSelected !== selectedWorktreeId) {
|
|
3536
|
+
setSelectedWorktreeId(snapshotSelected);
|
|
3537
|
+
}
|
|
3538
|
+
}, [snapshot?.dashboardWorktrees?.selectedWorktreeId, selectedWorktreeId]);
|
|
3539
|
+
|
|
3540
|
+
useEffect(() => {
|
|
3541
|
+
if (!snapshot?.dashboardWorktrees?.hasPendingScans) {
|
|
3542
|
+
return undefined;
|
|
3543
|
+
}
|
|
3544
|
+
const timer = setTimeout(() => {
|
|
3545
|
+
void refresh();
|
|
3546
|
+
}, 350);
|
|
3547
|
+
return () => {
|
|
3548
|
+
clearTimeout(timer);
|
|
3549
|
+
};
|
|
3550
|
+
}, [snapshot?.dashboardWorktrees?.hasPendingScans, snapshot?.generatedAt, refresh]);
|
|
3551
|
+
|
|
3196
3552
|
useEffect(() => {
|
|
3197
3553
|
setSelectionBySection((previous) => {
|
|
3198
3554
|
let changed = false;
|
|
@@ -3226,6 +3582,7 @@ function createDashboardApp(deps) {
|
|
|
3226
3582
|
|
|
3227
3583
|
useEffect(() => {
|
|
3228
3584
|
setPaneFocus('main');
|
|
3585
|
+
setWorktreeOverlayOpen(false);
|
|
3229
3586
|
}, [ui.view]);
|
|
3230
3587
|
|
|
3231
3588
|
useEffect(() => {
|
|
@@ -3295,12 +3652,17 @@ function createDashboardApp(deps) {
|
|
|
3295
3652
|
return undefined;
|
|
3296
3653
|
}
|
|
3297
3654
|
|
|
3298
|
-
const
|
|
3655
|
+
const resolvedRootCandidates = typeof resolveRootPathsForFlow === 'function'
|
|
3656
|
+
? resolveRootPathsForFlow(activeFlow, snapshot?.dashboardWorktrees, selectedWorktreeId)
|
|
3657
|
+
: null;
|
|
3658
|
+
const candidateRoots = Array.isArray(resolvedRootCandidates) ? resolvedRootCandidates : [];
|
|
3659
|
+
const fallbackRoot = resolveRootPathForFlow
|
|
3299
3660
|
? resolveRootPathForFlow(activeFlow)
|
|
3300
3661
|
: (rootPath || `${workspacePath}/.specs-fire`);
|
|
3662
|
+
const watchRoots = candidateRoots.length > 0 ? candidateRoots : [fallbackRoot];
|
|
3301
3663
|
|
|
3302
3664
|
const runtime = createWatchRuntime({
|
|
3303
|
-
|
|
3665
|
+
rootPaths: watchRoots,
|
|
3304
3666
|
debounceMs: 200,
|
|
3305
3667
|
onRefresh: () => {
|
|
3306
3668
|
void refresh();
|
|
@@ -3329,7 +3691,7 @@ function createDashboardApp(deps) {
|
|
|
3329
3691
|
clearInterval(interval);
|
|
3330
3692
|
void runtime.close();
|
|
3331
3693
|
};
|
|
3332
|
-
}, [watchEnabled, refreshMs, refresh, rootPath, workspacePath, resolveRootPathForFlow, activeFlow]);
|
|
3694
|
+
}, [watchEnabled, refreshMs, refresh, rootPath, workspacePath, resolveRootPathForFlow, resolveRootPathsForFlow, activeFlow, worktreeWatchSignature, selectedWorktreeId]);
|
|
3333
3695
|
|
|
3334
3696
|
useEffect(() => {
|
|
3335
3697
|
if (!stdout || typeof stdout.write !== 'function') {
|
|
@@ -3348,9 +3710,9 @@ function createDashboardApp(deps) {
|
|
|
3348
3710
|
const showFlowBar = availableFlowIds.length > 1;
|
|
3349
3711
|
const showFooterHelpLine = rows >= 10;
|
|
3350
3712
|
const showErrorPanel = Boolean(error) && rows >= 18;
|
|
3351
|
-
const showGlobalErrorPanel = showErrorPanel && ui.view !== 'health' && !ui.showHelp;
|
|
3352
|
-
const showErrorInline = Boolean(error) && !showErrorPanel;
|
|
3353
|
-
const showApprovalBanner = approvalGateLine !== '' && !ui.showHelp;
|
|
3713
|
+
const showGlobalErrorPanel = showErrorPanel && ui.view !== 'health' && !ui.showHelp && !worktreeOverlayOpen;
|
|
3714
|
+
const showErrorInline = Boolean(error) && !showErrorPanel && !worktreeOverlayOpen;
|
|
3715
|
+
const showApprovalBanner = approvalGateLine !== '' && !ui.showHelp && !worktreeOverlayOpen;
|
|
3354
3716
|
const showStatusLine = statusLine !== '';
|
|
3355
3717
|
const densePanels = rows <= 28 || cols <= 120;
|
|
3356
3718
|
|
|
@@ -3366,7 +3728,7 @@ function createDashboardApp(deps) {
|
|
|
3366
3728
|
const contentRowsBudget = Math.max(4, rows - reservedRows - frameSafetyRows);
|
|
3367
3729
|
const ultraCompact = rows <= 14;
|
|
3368
3730
|
const panelTitles = getPanelTitles(activeFlow, snapshot);
|
|
3369
|
-
const splitPreviewLayout = previewOpen && !overlayPreviewOpen && !ui.showHelp && cols >= 110 && rows >= 16;
|
|
3731
|
+
const splitPreviewLayout = previewOpen && !overlayPreviewOpen && !ui.showHelp && !worktreeOverlayOpen && cols >= 110 && rows >= 16;
|
|
3370
3732
|
const mainPaneWidth = splitPreviewLayout
|
|
3371
3733
|
? Math.max(34, Math.floor((fullWidth - 1) * 0.52))
|
|
3372
3734
|
: fullWidth;
|
|
@@ -3401,13 +3763,15 @@ function createDashboardApp(deps) {
|
|
|
3401
3763
|
previewOpen,
|
|
3402
3764
|
paneFocus,
|
|
3403
3765
|
availableFlowCount: availableFlowIds.length,
|
|
3404
|
-
showErrorSection: showErrorPanel
|
|
3766
|
+
showErrorSection: showErrorPanel,
|
|
3767
|
+
hasWorktrees: worktreeSectionEnabled
|
|
3405
3768
|
});
|
|
3406
3769
|
const quickHelpText = buildQuickHelpText(ui.view, {
|
|
3407
3770
|
flow: activeFlow,
|
|
3408
3771
|
previewOpen,
|
|
3409
3772
|
paneFocus,
|
|
3410
|
-
availableFlowCount: availableFlowIds.length
|
|
3773
|
+
availableFlowCount: availableFlowIds.length,
|
|
3774
|
+
hasWorktrees: worktreeSectionEnabled
|
|
3411
3775
|
});
|
|
3412
3776
|
|
|
3413
3777
|
let panelCandidates;
|
|
@@ -3420,6 +3784,15 @@ function createDashboardApp(deps) {
|
|
|
3420
3784
|
borderColor: 'cyan'
|
|
3421
3785
|
}
|
|
3422
3786
|
];
|
|
3787
|
+
} else if (worktreeOverlayOpen) {
|
|
3788
|
+
panelCandidates = [
|
|
3789
|
+
{
|
|
3790
|
+
key: 'worktree-overlay',
|
|
3791
|
+
title: 'Switch Worktree',
|
|
3792
|
+
lines: buildWorktreeOverlayLines(snapshot, worktreeOverlayIndex, Math.max(18, fullWidth - 4)),
|
|
3793
|
+
borderColor: 'yellow'
|
|
3794
|
+
}
|
|
3795
|
+
];
|
|
3423
3796
|
} else if (previewOpen && overlayPreviewOpen) {
|
|
3424
3797
|
panelCandidates = [
|
|
3425
3798
|
{
|
|
@@ -3478,7 +3851,16 @@ function createDashboardApp(deps) {
|
|
|
3478
3851
|
});
|
|
3479
3852
|
}
|
|
3480
3853
|
} else {
|
|
3481
|
-
panelCandidates = [
|
|
3854
|
+
panelCandidates = [];
|
|
3855
|
+
if (worktreeSectionEnabled) {
|
|
3856
|
+
panelCandidates.push({
|
|
3857
|
+
key: 'worktrees',
|
|
3858
|
+
title: 'Worktrees',
|
|
3859
|
+
lines: sectionLines.worktrees,
|
|
3860
|
+
borderColor: 'magenta'
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
panelCandidates.push(
|
|
3482
3864
|
{
|
|
3483
3865
|
key: 'current-run',
|
|
3484
3866
|
title: panelTitles.current,
|
|
@@ -3491,7 +3873,15 @@ function createDashboardApp(deps) {
|
|
|
3491
3873
|
lines: sectionLines['run-files'],
|
|
3492
3874
|
borderColor: 'yellow'
|
|
3493
3875
|
}
|
|
3494
|
-
|
|
3876
|
+
);
|
|
3877
|
+
if (otherWorktreesSectionEnabled) {
|
|
3878
|
+
panelCandidates.push({
|
|
3879
|
+
key: 'other-worktrees-active',
|
|
3880
|
+
title: panelTitles.otherWorktrees,
|
|
3881
|
+
lines: sectionLines['other-worktrees-active'],
|
|
3882
|
+
borderColor: 'blue'
|
|
3883
|
+
});
|
|
3884
|
+
}
|
|
3495
3885
|
}
|
|
3496
3886
|
|
|
3497
3887
|
if (!ui.showHelp && previewOpen && !overlayPreviewOpen && !splitPreviewLayout) {
|
|
@@ -3577,7 +3967,7 @@ function createDashboardApp(deps) {
|
|
|
3577
3967
|
panel,
|
|
3578
3968
|
index,
|
|
3579
3969
|
fullWidth,
|
|
3580
|
-
ui.showHelp
|
|
3970
|
+
(ui.showHelp || worktreeOverlayOpen)
|
|
3581
3971
|
? true
|
|
3582
3972
|
: ((panel.key === 'preview' || panel.key === 'preview-overlay')
|
|
3583
3973
|
? paneFocus === 'preview'
|
|
@@ -3588,7 +3978,11 @@ function createDashboardApp(deps) {
|
|
|
3588
3978
|
return React.createElement(
|
|
3589
3979
|
Box,
|
|
3590
3980
|
{ flexDirection: 'column', width: fullWidth },
|
|
3591
|
-
React.createElement(
|
|
3981
|
+
React.createElement(
|
|
3982
|
+
Text,
|
|
3983
|
+
{ color: 'cyan' },
|
|
3984
|
+
buildHeaderLine(snapshot, activeFlow, watchEnabled, watchStatus, lastRefreshAt, ui.view, fullWidth, selectedWorktreeLabel)
|
|
3985
|
+
),
|
|
3592
3986
|
React.createElement(FlowBar, { activeFlow, width: fullWidth, flowIds: availableFlowIds }),
|
|
3593
3987
|
React.createElement(TabsBar, { view: ui.view, width: fullWidth, icons, flow: activeFlow }),
|
|
3594
3988
|
showApprovalBanner
|