specsmd 0.1.35 → 0.1.36
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/lib/dashboard/tui/app.js +471 -184
- package/lib/dashboard/tui/store.js +11 -5
- package/package.json +1 -1
package/lib/dashboard/tui/app.js
CHANGED
|
@@ -793,13 +793,16 @@ function getPanelTitles(flow, snapshot) {
|
|
|
793
793
|
}
|
|
794
794
|
|
|
795
795
|
function getSectionOrderForView(view) {
|
|
796
|
-
if (view === '
|
|
797
|
-
return ['intent-status'
|
|
796
|
+
if (view === 'intents') {
|
|
797
|
+
return ['intent-status'];
|
|
798
|
+
}
|
|
799
|
+
if (view === 'completed') {
|
|
800
|
+
return ['completed-runs'];
|
|
798
801
|
}
|
|
799
802
|
if (view === 'health') {
|
|
800
|
-
return ['stats', 'warnings', 'error-details'];
|
|
803
|
+
return ['standards', 'stats', 'warnings', 'error-details'];
|
|
801
804
|
}
|
|
802
|
-
return ['current-run'
|
|
805
|
+
return ['current-run'];
|
|
803
806
|
}
|
|
804
807
|
|
|
805
808
|
function cycleSection(view, currentSectionKey, direction = 1, availableSections = null) {
|
|
@@ -1054,24 +1057,221 @@ function getNoCompletedMessage(flow) {
|
|
|
1054
1057
|
return 'No completed runs yet';
|
|
1055
1058
|
}
|
|
1056
1059
|
|
|
1057
|
-
function
|
|
1058
|
-
if (
|
|
1060
|
+
function getNoCurrentMessage(flow) {
|
|
1061
|
+
if (flow === 'aidlc') return 'No active bolt';
|
|
1062
|
+
if (flow === 'simple') return 'No active spec';
|
|
1063
|
+
return 'No active run';
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
function buildCurrentGroups(snapshot, flow) {
|
|
1067
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
1068
|
+
|
|
1069
|
+
if (effectiveFlow === 'aidlc') {
|
|
1070
|
+
const bolt = getCurrentBolt(snapshot);
|
|
1071
|
+
if (!bolt) {
|
|
1072
|
+
return [];
|
|
1073
|
+
}
|
|
1074
|
+
const stages = Array.isArray(bolt.stages) ? bolt.stages : [];
|
|
1075
|
+
const completedStages = stages.filter((stage) => stage.status === 'completed').length;
|
|
1076
|
+
return [{
|
|
1077
|
+
key: `current:bolt:${bolt.id}`,
|
|
1078
|
+
label: `${bolt.id} [${bolt.type}] ${completedStages}/${stages.length} stages`,
|
|
1079
|
+
files: filterExistingFiles([
|
|
1080
|
+
...collectAidlcBoltFiles(bolt),
|
|
1081
|
+
...collectAidlcIntentContextFiles(snapshot, bolt.intent)
|
|
1082
|
+
])
|
|
1083
|
+
}];
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
if (effectiveFlow === 'simple') {
|
|
1087
|
+
const spec = getCurrentSpec(snapshot);
|
|
1088
|
+
if (!spec) {
|
|
1089
|
+
return [];
|
|
1090
|
+
}
|
|
1091
|
+
return [{
|
|
1092
|
+
key: `current:spec:${spec.name}`,
|
|
1093
|
+
label: `${spec.name} [${spec.state}] ${spec.tasksCompleted}/${spec.tasksTotal} tasks`,
|
|
1094
|
+
files: filterExistingFiles(collectSimpleSpecFiles(spec))
|
|
1095
|
+
}];
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const run = getCurrentRun(snapshot);
|
|
1099
|
+
if (!run) {
|
|
1100
|
+
return [];
|
|
1101
|
+
}
|
|
1102
|
+
const workItems = Array.isArray(run.workItems) ? run.workItems : [];
|
|
1103
|
+
const completed = workItems.filter((item) => item.status === 'completed').length;
|
|
1104
|
+
return [{
|
|
1105
|
+
key: `current:run:${run.id}`,
|
|
1106
|
+
label: `${run.id} [${run.scope}] ${completed}/${workItems.length} items`,
|
|
1107
|
+
files: filterExistingFiles(collectFireRunFiles(run).map((file) => ({ ...file, scope: 'active' })))
|
|
1108
|
+
}];
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function buildRunFileGroups(fileEntries) {
|
|
1112
|
+
const order = ['active', 'upcoming', 'completed', 'intent', 'other'];
|
|
1113
|
+
const buckets = new Map(order.map((scope) => [scope, []]));
|
|
1114
|
+
|
|
1115
|
+
for (const fileEntry of Array.isArray(fileEntries) ? fileEntries : []) {
|
|
1116
|
+
const scope = order.includes(fileEntry?.scope) ? fileEntry.scope : 'other';
|
|
1117
|
+
buckets.get(scope).push(fileEntry);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
const groups = [];
|
|
1121
|
+
for (const scope of order) {
|
|
1122
|
+
const files = buckets.get(scope) || [];
|
|
1123
|
+
if (files.length === 0) {
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
1126
|
+
groups.push({
|
|
1127
|
+
key: `run-files:scope:${scope}`,
|
|
1128
|
+
label: `${formatScope(scope)} files (${files.length})`,
|
|
1129
|
+
files: filterExistingFiles(files)
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
return groups;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
function normalizeInfoLine(line) {
|
|
1136
|
+
const normalized = normalizePanelLine(line);
|
|
1137
|
+
return {
|
|
1138
|
+
label: normalized.text,
|
|
1139
|
+
color: normalized.color,
|
|
1140
|
+
bold: normalized.bold
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
function toInfoRows(lines, keyPrefix, emptyLabel = 'No data') {
|
|
1145
|
+
const safe = Array.isArray(lines) ? lines : [];
|
|
1146
|
+
if (safe.length === 0) {
|
|
1059
1147
|
return [{
|
|
1060
1148
|
kind: 'info',
|
|
1061
|
-
key:
|
|
1062
|
-
label:
|
|
1149
|
+
key: `${keyPrefix}:empty`,
|
|
1150
|
+
label: emptyLabel,
|
|
1063
1151
|
selectable: false
|
|
1064
1152
|
}];
|
|
1065
1153
|
}
|
|
1066
1154
|
|
|
1067
|
-
return
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1155
|
+
return safe.map((line, index) => {
|
|
1156
|
+
const normalized = normalizeInfoLine(line);
|
|
1157
|
+
return {
|
|
1158
|
+
kind: 'info',
|
|
1159
|
+
key: `${keyPrefix}:${index}`,
|
|
1160
|
+
label: normalized.label,
|
|
1161
|
+
color: normalized.color,
|
|
1162
|
+
bold: normalized.bold,
|
|
1163
|
+
selectable: true
|
|
1164
|
+
};
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
function buildOverviewIntentGroups(snapshot, flow, filter = 'next') {
|
|
1169
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
1170
|
+
const normalizedFilter = filter === 'completed' ? 'completed' : 'next';
|
|
1171
|
+
const isIncluded = (status) => {
|
|
1172
|
+
if (normalizedFilter === 'completed') {
|
|
1173
|
+
return status === 'completed';
|
|
1174
|
+
}
|
|
1175
|
+
return status !== 'completed';
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
if (effectiveFlow === 'aidlc') {
|
|
1179
|
+
const intents = Array.isArray(snapshot?.intents) ? snapshot.intents : [];
|
|
1180
|
+
return intents
|
|
1181
|
+
.filter((intent) => isIncluded(intent?.status || 'pending'))
|
|
1182
|
+
.map((intent, index) => ({
|
|
1183
|
+
key: `overview:intent:${intent?.id || index}`,
|
|
1184
|
+
label: `${intent?.id || 'unknown'}: ${intent?.status || 'pending'} (${intent?.completedStories || 0}/${intent?.storyCount || 0} stories, ${intent?.completedUnits || 0}/${intent?.unitCount || 0} units)`,
|
|
1185
|
+
files: filterExistingFiles(collectAidlcIntentContextFiles(snapshot, intent?.id))
|
|
1186
|
+
}));
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
if (effectiveFlow === 'simple') {
|
|
1190
|
+
const specs = Array.isArray(snapshot?.specs) ? snapshot.specs : [];
|
|
1191
|
+
return specs
|
|
1192
|
+
.filter((spec) => isIncluded(spec?.state || 'pending'))
|
|
1193
|
+
.map((spec, index) => ({
|
|
1194
|
+
key: `overview:spec:${spec?.name || index}`,
|
|
1195
|
+
label: `${spec?.name || 'unknown'}: ${spec?.state || 'pending'} (${spec?.tasksCompleted || 0}/${spec?.tasksTotal || 0} tasks)`,
|
|
1196
|
+
files: filterExistingFiles(collectSimpleSpecFiles(spec))
|
|
1197
|
+
}));
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
const intents = Array.isArray(snapshot?.intents) ? snapshot.intents : [];
|
|
1201
|
+
return intents
|
|
1202
|
+
.filter((intent) => isIncluded(intent?.status || 'pending'))
|
|
1203
|
+
.map((intent, index) => {
|
|
1204
|
+
const workItems = Array.isArray(intent?.workItems) ? intent.workItems : [];
|
|
1205
|
+
const done = workItems.filter((item) => item.status === 'completed').length;
|
|
1206
|
+
const files = [{
|
|
1207
|
+
label: `${intent?.id || 'intent'}/brief.md`,
|
|
1208
|
+
path: intent?.filePath,
|
|
1209
|
+
scope: 'intent'
|
|
1210
|
+
}, ...workItems.map((item) => ({
|
|
1211
|
+
label: `${intent?.id || 'intent'}/${item?.id || 'work-item'}.md`,
|
|
1212
|
+
path: item?.filePath,
|
|
1213
|
+
scope: item?.status === 'completed' ? 'completed' : 'upcoming'
|
|
1214
|
+
}))];
|
|
1215
|
+
return {
|
|
1216
|
+
key: `overview:intent:${intent?.id || index}`,
|
|
1217
|
+
label: `${intent?.id || 'unknown'}: ${intent?.status || 'pending'} (${done}/${workItems.length} work items)`,
|
|
1218
|
+
files: filterExistingFiles(files)
|
|
1219
|
+
};
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
function buildStandardsGroups(snapshot, flow) {
|
|
1224
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
1225
|
+
if (effectiveFlow === 'simple') {
|
|
1226
|
+
return [];
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
const standards = Array.isArray(snapshot?.standards) ? snapshot.standards : [];
|
|
1230
|
+
return standards.map((standard, index) => {
|
|
1231
|
+
const id = standard?.type || standard?.name || String(index);
|
|
1232
|
+
const name = `${standard?.name || standard?.type || 'standard'}.md`;
|
|
1233
|
+
return {
|
|
1234
|
+
key: `standards:${id}`,
|
|
1235
|
+
label: name,
|
|
1236
|
+
files: filterExistingFiles([{
|
|
1237
|
+
label: name,
|
|
1238
|
+
path: standard?.filePath,
|
|
1239
|
+
scope: 'file'
|
|
1240
|
+
}])
|
|
1241
|
+
};
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
function buildProjectGroups(snapshot, flow) {
|
|
1246
|
+
const effectiveFlow = getEffectiveFlow(flow, snapshot);
|
|
1247
|
+
const files = [];
|
|
1248
|
+
|
|
1249
|
+
if (effectiveFlow === 'aidlc') {
|
|
1250
|
+
files.push({
|
|
1251
|
+
label: 'memory-bank/project.yaml',
|
|
1252
|
+
path: path.join(snapshot?.rootPath || '', 'project.yaml'),
|
|
1253
|
+
scope: 'file'
|
|
1254
|
+
});
|
|
1255
|
+
} else if (effectiveFlow === 'simple') {
|
|
1256
|
+
files.push({
|
|
1257
|
+
label: 'package.json',
|
|
1258
|
+
path: path.join(snapshot?.workspacePath || '', 'package.json'),
|
|
1259
|
+
scope: 'file'
|
|
1260
|
+
});
|
|
1261
|
+
} else {
|
|
1262
|
+
files.push({
|
|
1263
|
+
label: '.specs-fire/state.yaml',
|
|
1264
|
+
path: path.join(snapshot?.rootPath || '', 'state.yaml'),
|
|
1265
|
+
scope: 'file'
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
const projectName = snapshot?.project?.name || 'unknown-project';
|
|
1270
|
+
return [{
|
|
1271
|
+
key: `project:${projectName}`,
|
|
1272
|
+
label: `project ${projectName}`,
|
|
1273
|
+
files: filterExistingFiles(files)
|
|
1274
|
+
}];
|
|
1075
1275
|
}
|
|
1076
1276
|
|
|
1077
1277
|
function collectAidlcIntentContextFiles(snapshot, intentId) {
|
|
@@ -1303,10 +1503,10 @@ function buildInteractiveRowsLines(rows, selectedIndex, icons, width, isFocusedS
|
|
|
1303
1503
|
}
|
|
1304
1504
|
|
|
1305
1505
|
return {
|
|
1306
|
-
text: truncate(` ${row.label || ''}`, width),
|
|
1307
|
-
color: 'gray',
|
|
1308
|
-
bold:
|
|
1309
|
-
selected:
|
|
1506
|
+
text: truncate(`${isSelected ? `${cursor} ` : ' '}${row.label || ''}`, width),
|
|
1507
|
+
color: isSelected ? (isFocusedSection ? 'green' : 'cyan') : (row.color || 'gray'),
|
|
1508
|
+
bold: isSelected || Boolean(row.bold),
|
|
1509
|
+
selected: isSelected
|
|
1310
1510
|
};
|
|
1311
1511
|
});
|
|
1312
1512
|
}
|
|
@@ -1399,19 +1599,24 @@ function openFileWithDefaultApp(filePath) {
|
|
|
1399
1599
|
|
|
1400
1600
|
function buildQuickHelpText(view, options = {}) {
|
|
1401
1601
|
const {
|
|
1602
|
+
flow = 'fire',
|
|
1402
1603
|
previewOpen = false,
|
|
1403
1604
|
availableFlowCount = 1
|
|
1404
1605
|
} = options;
|
|
1606
|
+
const isAidlc = String(flow || '').toLowerCase() === 'aidlc';
|
|
1607
|
+
const isSimple = String(flow || '').toLowerCase() === 'simple';
|
|
1608
|
+
const activeLabel = isAidlc ? 'active bolt' : (isSimple ? 'active spec' : 'active run');
|
|
1405
1609
|
|
|
1406
|
-
const parts = ['1/2/3
|
|
1610
|
+
const parts = ['1/2/3/4 tabs', 'g/G sections'];
|
|
1407
1611
|
|
|
1408
|
-
if (view === 'runs') {
|
|
1612
|
+
if (view === 'runs' || view === 'intents' || view === 'completed' || view === 'health') {
|
|
1409
1613
|
if (previewOpen) {
|
|
1410
1614
|
parts.push('tab pane', '↑/↓ nav/scroll', 'v close');
|
|
1411
1615
|
} else {
|
|
1412
1616
|
parts.push('↑/↓ navigate', 'enter expand', 'v preview');
|
|
1413
1617
|
}
|
|
1414
1618
|
}
|
|
1619
|
+
parts.push(`tab1 ${activeLabel}`);
|
|
1415
1620
|
|
|
1416
1621
|
if (availableFlowCount > 1) {
|
|
1417
1622
|
parts.push('[/] flow');
|
|
@@ -1424,25 +1629,30 @@ function buildQuickHelpText(view, options = {}) {
|
|
|
1424
1629
|
function buildHelpOverlayLines(options = {}) {
|
|
1425
1630
|
const {
|
|
1426
1631
|
view = 'runs',
|
|
1632
|
+
flow = 'fire',
|
|
1427
1633
|
previewOpen = false,
|
|
1428
1634
|
paneFocus = 'main',
|
|
1429
1635
|
availableFlowCount = 1,
|
|
1430
1636
|
showErrorSection = false
|
|
1431
1637
|
} = options;
|
|
1638
|
+
const isAidlc = String(flow || '').toLowerCase() === 'aidlc';
|
|
1639
|
+
const isSimple = String(flow || '').toLowerCase() === 'simple';
|
|
1640
|
+
const itemLabel = isAidlc ? 'bolt' : (isSimple ? 'spec' : 'run');
|
|
1641
|
+
const itemPlural = isAidlc ? 'bolts' : (isSimple ? 'specs' : 'runs');
|
|
1432
1642
|
|
|
1433
1643
|
const lines = [
|
|
1434
1644
|
{ text: 'Global', color: 'cyan', bold: true },
|
|
1435
1645
|
'q or Ctrl+C quit',
|
|
1436
1646
|
'r refresh snapshot',
|
|
1437
|
-
|
|
1647
|
+
`1 active ${itemLabel} | 2 intents | 3 completed ${itemPlural} | 4 standards/health`,
|
|
1438
1648
|
'g next section | G previous section',
|
|
1439
1649
|
'h/? toggle this shortcuts overlay',
|
|
1440
1650
|
'esc close overlays (help/preview/fullscreen)',
|
|
1441
1651
|
{ text: '', color: undefined, bold: false },
|
|
1442
|
-
{ text: '
|
|
1443
|
-
|
|
1652
|
+
{ text: 'Tab 1 Active', color: 'yellow', bold: true },
|
|
1653
|
+
`a focus active ${itemLabel}`,
|
|
1444
1654
|
'up/down or j/k move selection',
|
|
1445
|
-
'enter expand/collapse
|
|
1655
|
+
'enter expand/collapse selected folder row',
|
|
1446
1656
|
'v preview selected file',
|
|
1447
1657
|
'v twice quickly opens fullscreen preview overlay',
|
|
1448
1658
|
'tab switch focus between main and preview panes',
|
|
@@ -1459,12 +1669,16 @@ function buildHelpOverlayLines(options = {}) {
|
|
|
1459
1669
|
|
|
1460
1670
|
lines.push(
|
|
1461
1671
|
{ text: '', color: undefined, bold: false },
|
|
1462
|
-
{ text: '
|
|
1463
|
-
'i intents
|
|
1464
|
-
'
|
|
1672
|
+
{ text: 'Tab 2 Intents', color: 'green', bold: true },
|
|
1673
|
+
'i focus intents',
|
|
1674
|
+
'n next intents | x completed intents',
|
|
1675
|
+
'left/right toggles next/completed when intents is focused',
|
|
1676
|
+
{ text: '', color: undefined, bold: false },
|
|
1677
|
+
{ text: 'Tab 3 Completed', color: 'blue', bold: true },
|
|
1678
|
+
'c focus completed items',
|
|
1465
1679
|
{ text: '', color: undefined, bold: false },
|
|
1466
|
-
{ text: 'Health
|
|
1467
|
-
`t stats | w warnings${showErrorSection ? ' | e errors' : ''}`,
|
|
1680
|
+
{ text: 'Tab 4 Standards/Health', color: 'magenta', bold: true },
|
|
1681
|
+
`s standards | t stats | w warnings${showErrorSection ? ' | e errors' : ''}`,
|
|
1468
1682
|
{ text: '', color: undefined, bold: false },
|
|
1469
1683
|
{ text: `Current view: ${String(view || 'runs').toUpperCase()}`, color: 'gray', bold: false }
|
|
1470
1684
|
);
|
|
@@ -1685,11 +1899,15 @@ function createDashboardApp(deps) {
|
|
|
1685
1899
|
}
|
|
1686
1900
|
|
|
1687
1901
|
function TabsBar(props) {
|
|
1688
|
-
const { view, width, icons } = props;
|
|
1902
|
+
const { view, width, icons, flow: activeFlow } = props;
|
|
1903
|
+
const effectiveFlow = String(activeFlow || '').toLowerCase();
|
|
1904
|
+
const primaryLabel = effectiveFlow === 'aidlc' ? 'BOLTS' : (effectiveFlow === 'simple' ? 'SPECS' : 'RUNS');
|
|
1905
|
+
const completedLabel = effectiveFlow === 'aidlc' ? 'COMPLETED BOLTS' : (effectiveFlow === 'simple' ? 'COMPLETED SPECS' : 'COMPLETED RUNS');
|
|
1689
1906
|
const tabs = [
|
|
1690
|
-
{ id: 'runs', label: ` 1 ${icons.runs}
|
|
1691
|
-
{ id: '
|
|
1692
|
-
{ id: '
|
|
1907
|
+
{ id: 'runs', label: ` 1 ${icons.runs} ${primaryLabel} ` },
|
|
1908
|
+
{ id: 'intents', label: ` 2 ${icons.overview} INTENTS ` },
|
|
1909
|
+
{ id: 'completed', label: ` 3 ${icons.runs} ${completedLabel} ` },
|
|
1910
|
+
{ id: 'health', label: ` 4 ${icons.health} STANDARDS/HEALTH ` }
|
|
1693
1911
|
];
|
|
1694
1912
|
|
|
1695
1913
|
return React.createElement(
|
|
@@ -1757,18 +1975,24 @@ function createDashboardApp(deps) {
|
|
|
1757
1975
|
const [error, setError] = useState(initialNormalizedError);
|
|
1758
1976
|
const [ui, setUi] = useState(createInitialUIState());
|
|
1759
1977
|
const [sectionFocus, setSectionFocus] = useState({
|
|
1760
|
-
runs: 'run
|
|
1761
|
-
|
|
1762
|
-
|
|
1978
|
+
runs: 'current-run',
|
|
1979
|
+
intents: 'intent-status',
|
|
1980
|
+
completed: 'completed-runs',
|
|
1981
|
+
health: 'standards'
|
|
1763
1982
|
});
|
|
1764
1983
|
const [selectionBySection, setSelectionBySection] = useState({
|
|
1765
|
-
'run
|
|
1766
|
-
|
|
1767
|
-
completed: 0
|
|
1984
|
+
'current-run': 0,
|
|
1985
|
+
'intent-status': 0,
|
|
1986
|
+
'completed-runs': 0,
|
|
1987
|
+
standards: 0,
|
|
1988
|
+
stats: 0,
|
|
1989
|
+
warnings: 0,
|
|
1990
|
+
'error-details': 0
|
|
1768
1991
|
});
|
|
1769
1992
|
const [expandedGroups, setExpandedGroups] = useState({});
|
|
1770
1993
|
const [previewTarget, setPreviewTarget] = useState(null);
|
|
1771
1994
|
const [overviewIntentFilter, setOverviewIntentFilter] = useState('next');
|
|
1995
|
+
const [deferredTabsReady, setDeferredTabsReady] = useState(false);
|
|
1772
1996
|
const [previewOpen, setPreviewOpen] = useState(false);
|
|
1773
1997
|
const [paneFocus, setPaneFocus] = useState('main');
|
|
1774
1998
|
const [overlayPreviewOpen, setOverlayPreviewOpen] = useState(false);
|
|
@@ -1804,24 +2028,86 @@ function createDashboardApp(deps) {
|
|
|
1804
2028
|
return base.filter((sectionKey) => sectionKey !== 'error-details' || showErrorPanelForSections);
|
|
1805
2029
|
}, [showErrorPanelForSections]);
|
|
1806
2030
|
|
|
1807
|
-
const
|
|
1808
|
-
const
|
|
1809
|
-
const
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
2031
|
+
const effectiveFlow = getEffectiveFlow(activeFlow, snapshot);
|
|
2032
|
+
const currentGroups = buildCurrentGroups(snapshot, activeFlow);
|
|
2033
|
+
const currentExpandedGroups = { ...expandedGroups };
|
|
2034
|
+
for (const group of currentGroups) {
|
|
2035
|
+
if (currentExpandedGroups[group.key] == null) {
|
|
2036
|
+
currentExpandedGroups[group.key] = true;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
const currentRunRows = toExpandableRows(
|
|
2041
|
+
currentGroups,
|
|
2042
|
+
getNoCurrentMessage(effectiveFlow),
|
|
2043
|
+
currentExpandedGroups
|
|
1818
2044
|
);
|
|
2045
|
+
const shouldHydrateSecondaryTabs = deferredTabsReady || ui.view !== 'runs';
|
|
2046
|
+
const intentRows = shouldHydrateSecondaryTabs
|
|
2047
|
+
? [
|
|
2048
|
+
{
|
|
2049
|
+
kind: 'info',
|
|
2050
|
+
key: 'intent-filter',
|
|
2051
|
+
label: `filter ${overviewIntentFilter === 'completed' ? 'next | [COMPLETED]' : '[NEXT] | completed'} (n/x)`,
|
|
2052
|
+
color: 'cyan',
|
|
2053
|
+
bold: true,
|
|
2054
|
+
selectable: false
|
|
2055
|
+
},
|
|
2056
|
+
...toExpandableRows(
|
|
2057
|
+
buildOverviewIntentGroups(snapshot, activeFlow, overviewIntentFilter),
|
|
2058
|
+
overviewIntentFilter === 'completed' ? 'No completed intents yet' : 'No upcoming intents',
|
|
2059
|
+
expandedGroups
|
|
2060
|
+
)
|
|
2061
|
+
]
|
|
2062
|
+
: toInfoRows(['Loading intents...'], 'intent-loading');
|
|
2063
|
+
const completedRows = shouldHydrateSecondaryTabs
|
|
2064
|
+
? toExpandableRows(
|
|
2065
|
+
buildCompletedGroups(snapshot, activeFlow),
|
|
2066
|
+
getNoCompletedMessage(effectiveFlow),
|
|
2067
|
+
expandedGroups
|
|
2068
|
+
)
|
|
2069
|
+
: toInfoRows(['Loading completed items...'], 'completed-loading');
|
|
2070
|
+
const standardsRows = shouldHydrateSecondaryTabs
|
|
2071
|
+
? toExpandableRows(
|
|
2072
|
+
buildStandardsGroups(snapshot, activeFlow),
|
|
2073
|
+
effectiveFlow === 'simple' ? 'No standards for SIMPLE flow' : 'No standards found',
|
|
2074
|
+
expandedGroups
|
|
2075
|
+
)
|
|
2076
|
+
: toInfoRows(['Loading standards...'], 'standards-loading');
|
|
2077
|
+
const statsRows = shouldHydrateSecondaryTabs
|
|
2078
|
+
? toInfoRows(
|
|
2079
|
+
buildStatsLines(snapshot, 200, activeFlow),
|
|
2080
|
+
'stats',
|
|
2081
|
+
'No stats available'
|
|
2082
|
+
)
|
|
2083
|
+
: toInfoRows(['Loading stats...'], 'stats-loading');
|
|
2084
|
+
const warningsRows = shouldHydrateSecondaryTabs
|
|
2085
|
+
? toInfoRows(
|
|
2086
|
+
buildWarningsLines(snapshot, 200),
|
|
2087
|
+
'warnings',
|
|
2088
|
+
'No warnings'
|
|
2089
|
+
)
|
|
2090
|
+
: toInfoRows(['Loading warnings...'], 'warnings-loading');
|
|
2091
|
+
const errorDetailsRows = shouldHydrateSecondaryTabs
|
|
2092
|
+
? toInfoRows(
|
|
2093
|
+
buildErrorLines(error, 200),
|
|
2094
|
+
'error-details',
|
|
2095
|
+
'No error details'
|
|
2096
|
+
)
|
|
2097
|
+
: toInfoRows(['Loading error details...'], 'error-loading');
|
|
1819
2098
|
|
|
1820
2099
|
const rowsBySection = {
|
|
1821
|
-
'run
|
|
1822
|
-
|
|
1823
|
-
completed: completedRows
|
|
2100
|
+
'current-run': currentRunRows,
|
|
2101
|
+
'intent-status': intentRows,
|
|
2102
|
+
'completed-runs': completedRows,
|
|
2103
|
+
standards: standardsRows,
|
|
2104
|
+
stats: statsRows,
|
|
2105
|
+
warnings: warningsRows,
|
|
2106
|
+
'error-details': errorDetailsRows
|
|
1824
2107
|
};
|
|
2108
|
+
const rowLengthSignature = Object.entries(rowsBySection)
|
|
2109
|
+
.map(([key, rowsForSection]) => `${key}:${Array.isArray(rowsForSection) ? rowsForSection.length : 0}`)
|
|
2110
|
+
.join('|');
|
|
1825
2111
|
|
|
1826
2112
|
const currentSectionOrder = getAvailableSections(ui.view);
|
|
1827
2113
|
const focusedSection = currentSectionOrder.includes(sectionFocus[ui.view])
|
|
@@ -1914,12 +2200,18 @@ function createDashboardApp(deps) {
|
|
|
1914
2200
|
}
|
|
1915
2201
|
|
|
1916
2202
|
if (input === '2') {
|
|
1917
|
-
setUi((previous) => ({ ...previous, view: '
|
|
2203
|
+
setUi((previous) => ({ ...previous, view: 'intents' }));
|
|
1918
2204
|
setPaneFocus('main');
|
|
1919
2205
|
return;
|
|
1920
2206
|
}
|
|
1921
2207
|
|
|
1922
2208
|
if (input === '3') {
|
|
2209
|
+
setUi((previous) => ({ ...previous, view: 'completed' }));
|
|
2210
|
+
setPaneFocus('main');
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
if (input === '4') {
|
|
1923
2215
|
setUi((previous) => ({ ...previous, view: 'health' }));
|
|
1924
2216
|
setPaneFocus('main');
|
|
1925
2217
|
return;
|
|
@@ -1938,14 +2230,19 @@ function createDashboardApp(deps) {
|
|
|
1938
2230
|
return availableFlowIds[nextIndex];
|
|
1939
2231
|
});
|
|
1940
2232
|
setSelectionBySection({
|
|
1941
|
-
'run
|
|
1942
|
-
|
|
1943
|
-
completed: 0
|
|
2233
|
+
'current-run': 0,
|
|
2234
|
+
'intent-status': 0,
|
|
2235
|
+
'completed-runs': 0,
|
|
2236
|
+
standards: 0,
|
|
2237
|
+
stats: 0,
|
|
2238
|
+
warnings: 0,
|
|
2239
|
+
'error-details': 0
|
|
1944
2240
|
});
|
|
1945
2241
|
setSectionFocus({
|
|
1946
|
-
runs: 'run
|
|
1947
|
-
|
|
1948
|
-
|
|
2242
|
+
runs: 'current-run',
|
|
2243
|
+
intents: 'intent-status',
|
|
2244
|
+
completed: 'completed-runs',
|
|
2245
|
+
health: 'standards'
|
|
1949
2246
|
});
|
|
1950
2247
|
setOverviewIntentFilter('next');
|
|
1951
2248
|
setExpandedGroups({});
|
|
@@ -1970,14 +2267,19 @@ function createDashboardApp(deps) {
|
|
|
1970
2267
|
return availableFlowIds[nextIndex];
|
|
1971
2268
|
});
|
|
1972
2269
|
setSelectionBySection({
|
|
1973
|
-
'run
|
|
1974
|
-
|
|
1975
|
-
completed: 0
|
|
2270
|
+
'current-run': 0,
|
|
2271
|
+
'intent-status': 0,
|
|
2272
|
+
'completed-runs': 0,
|
|
2273
|
+
standards: 0,
|
|
2274
|
+
stats: 0,
|
|
2275
|
+
warnings: 0,
|
|
2276
|
+
'error-details': 0
|
|
1976
2277
|
});
|
|
1977
2278
|
setSectionFocus({
|
|
1978
|
-
runs: 'run
|
|
1979
|
-
|
|
1980
|
-
|
|
2279
|
+
runs: 'current-run',
|
|
2280
|
+
intents: 'intent-status',
|
|
2281
|
+
completed: 'completed-runs',
|
|
2282
|
+
health: 'standards'
|
|
1981
2283
|
});
|
|
1982
2284
|
setOverviewIntentFilter('next');
|
|
1983
2285
|
setExpandedGroups({});
|
|
@@ -1994,12 +2296,12 @@ function createDashboardApp(deps) {
|
|
|
1994
2296
|
? sectionFocus[ui.view]
|
|
1995
2297
|
: (availableSections[0] || 'current-run');
|
|
1996
2298
|
|
|
1997
|
-
if (key.tab &&
|
|
2299
|
+
if (key.tab && previewOpen) {
|
|
1998
2300
|
setPaneFocus((previous) => (previous === 'main' ? 'preview' : 'main'));
|
|
1999
2301
|
return;
|
|
2000
2302
|
}
|
|
2001
2303
|
|
|
2002
|
-
if (ui.view === '
|
|
2304
|
+
if (ui.view === 'intents' && activeSection === 'intent-status') {
|
|
2003
2305
|
if (input === 'n') {
|
|
2004
2306
|
setOverviewIntentFilter('next');
|
|
2005
2307
|
return;
|
|
@@ -2038,34 +2340,21 @@ function createDashboardApp(deps) {
|
|
|
2038
2340
|
setPaneFocus('main');
|
|
2039
2341
|
return;
|
|
2040
2342
|
}
|
|
2041
|
-
|
|
2042
|
-
setSectionFocus((previous) => ({ ...previous, runs: 'run-files' }));
|
|
2043
|
-
setPaneFocus('main');
|
|
2044
|
-
return;
|
|
2045
|
-
}
|
|
2046
|
-
if (input === 'p') {
|
|
2047
|
-
setSectionFocus((previous) => ({ ...previous, runs: 'pending' }));
|
|
2048
|
-
setPaneFocus('main');
|
|
2049
|
-
return;
|
|
2050
|
-
}
|
|
2051
|
-
} else if (ui.view === 'overview') {
|
|
2052
|
-
if (input === 'p') {
|
|
2053
|
-
setSectionFocus((previous) => ({ ...previous, overview: 'project' }));
|
|
2054
|
-
return;
|
|
2055
|
-
}
|
|
2343
|
+
} else if (ui.view === 'intents') {
|
|
2056
2344
|
if (input === 'i') {
|
|
2057
|
-
setSectionFocus((previous) => ({ ...previous,
|
|
2058
|
-
return;
|
|
2059
|
-
}
|
|
2060
|
-
if (input === 's') {
|
|
2061
|
-
setSectionFocus((previous) => ({ ...previous, overview: 'standards' }));
|
|
2345
|
+
setSectionFocus((previous) => ({ ...previous, intents: 'intent-status' }));
|
|
2062
2346
|
return;
|
|
2063
2347
|
}
|
|
2348
|
+
} else if (ui.view === 'completed') {
|
|
2064
2349
|
if (input === 'c') {
|
|
2065
|
-
setSectionFocus((previous) => ({ ...previous,
|
|
2350
|
+
setSectionFocus((previous) => ({ ...previous, completed: 'completed-runs' }));
|
|
2066
2351
|
return;
|
|
2067
2352
|
}
|
|
2068
2353
|
} else if (ui.view === 'health') {
|
|
2354
|
+
if (input === 's') {
|
|
2355
|
+
setSectionFocus((previous) => ({ ...previous, health: 'standards' }));
|
|
2356
|
+
return;
|
|
2357
|
+
}
|
|
2069
2358
|
if (input === 't') {
|
|
2070
2359
|
setSectionFocus((previous) => ({ ...previous, health: 'stats' }));
|
|
2071
2360
|
return;
|
|
@@ -2094,7 +2383,7 @@ function createDashboardApp(deps) {
|
|
|
2094
2383
|
}
|
|
2095
2384
|
}
|
|
2096
2385
|
|
|
2097
|
-
if (
|
|
2386
|
+
if (key.upArrow || key.downArrow || input === 'j' || input === 'k') {
|
|
2098
2387
|
const moveDown = key.downArrow || input === 'j';
|
|
2099
2388
|
const moveUp = key.upArrow || input === 'k';
|
|
2100
2389
|
|
|
@@ -2107,11 +2396,7 @@ function createDashboardApp(deps) {
|
|
|
2107
2396
|
return;
|
|
2108
2397
|
}
|
|
2109
2398
|
|
|
2110
|
-
const targetSection = activeSection
|
|
2111
|
-
if (targetSection !== activeSection) {
|
|
2112
|
-
setSectionFocus((previous) => ({ ...previous, runs: targetSection }));
|
|
2113
|
-
}
|
|
2114
|
-
|
|
2399
|
+
const targetSection = activeSection;
|
|
2115
2400
|
const targetRows = rowsBySection[targetSection] || [];
|
|
2116
2401
|
if (targetRows.length === 0) {
|
|
2117
2402
|
return;
|
|
@@ -2129,24 +2414,22 @@ function createDashboardApp(deps) {
|
|
|
2129
2414
|
return;
|
|
2130
2415
|
}
|
|
2131
2416
|
|
|
2132
|
-
if (
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
}));
|
|
2141
|
-
}
|
|
2417
|
+
if (key.return || key.enter) {
|
|
2418
|
+
const rowsForSection = rowsBySection[activeSection] || [];
|
|
2419
|
+
const selectedRow = getSelectedRow(rowsForSection, selectionBySection[activeSection] || 0);
|
|
2420
|
+
if (selectedRow?.kind === 'group' && selectedRow.expandable) {
|
|
2421
|
+
setExpandedGroups((previous) => ({
|
|
2422
|
+
...previous,
|
|
2423
|
+
[selectedRow.key]: !previous[selectedRow.key]
|
|
2424
|
+
}));
|
|
2142
2425
|
}
|
|
2143
2426
|
return;
|
|
2144
2427
|
}
|
|
2145
2428
|
|
|
2146
|
-
if (input === 'v'
|
|
2429
|
+
if (input === 'v') {
|
|
2147
2430
|
const target = selectedFocusedFile || previewTarget;
|
|
2148
2431
|
if (!target) {
|
|
2149
|
-
setStatusLine('Select a file row first
|
|
2432
|
+
setStatusLine('Select a file row first.');
|
|
2150
2433
|
return;
|
|
2151
2434
|
}
|
|
2152
2435
|
|
|
@@ -2184,7 +2467,7 @@ function createDashboardApp(deps) {
|
|
|
2184
2467
|
return;
|
|
2185
2468
|
}
|
|
2186
2469
|
|
|
2187
|
-
if (input === 'o'
|
|
2470
|
+
if (input === 'o') {
|
|
2188
2471
|
const target = selectedFocusedFile || previewTarget;
|
|
2189
2472
|
const result = openFileWithDefaultApp(target?.path);
|
|
2190
2473
|
setStatusLine(result.message);
|
|
@@ -2196,21 +2479,38 @@ function createDashboardApp(deps) {
|
|
|
2196
2479
|
}, [refresh]);
|
|
2197
2480
|
|
|
2198
2481
|
useEffect(() => {
|
|
2199
|
-
setSelectionBySection((previous) =>
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2482
|
+
setSelectionBySection((previous) => {
|
|
2483
|
+
let changed = false;
|
|
2484
|
+
const next = { ...previous };
|
|
2485
|
+
|
|
2486
|
+
for (const [sectionKey, sectionRows] of Object.entries(rowsBySection)) {
|
|
2487
|
+
const previousValue = Number.isFinite(previous[sectionKey]) ? previous[sectionKey] : 0;
|
|
2488
|
+
const clampedValue = clampIndex(previousValue, sectionRows.length);
|
|
2489
|
+
if (previousValue !== clampedValue) {
|
|
2490
|
+
next[sectionKey] = clampedValue;
|
|
2491
|
+
changed = true;
|
|
2492
|
+
} else if (!(sectionKey in next)) {
|
|
2493
|
+
next[sectionKey] = clampedValue;
|
|
2494
|
+
changed = true;
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
return changed ? next : previous;
|
|
2499
|
+
});
|
|
2500
|
+
}, [activeFlow, rowLengthSignature, snapshot?.generatedAt]);
|
|
2206
2501
|
|
|
2207
2502
|
useEffect(() => {
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2503
|
+
setDeferredTabsReady(false);
|
|
2504
|
+
const timer = setTimeout(() => {
|
|
2505
|
+
setDeferredTabsReady(true);
|
|
2506
|
+
}, 0);
|
|
2507
|
+
return () => {
|
|
2508
|
+
clearTimeout(timer);
|
|
2509
|
+
};
|
|
2510
|
+
}, [activeFlow, snapshot?.generatedAt]);
|
|
2511
|
+
|
|
2512
|
+
useEffect(() => {
|
|
2513
|
+
setPaneFocus('main');
|
|
2214
2514
|
}, [ui.view]);
|
|
2215
2515
|
|
|
2216
2516
|
useEffect(() => {
|
|
@@ -2326,14 +2626,15 @@ function createDashboardApp(deps) {
|
|
|
2326
2626
|
const fullWidth = Math.max(40, cols - 1);
|
|
2327
2627
|
const showFooterHelpLine = rows >= 10;
|
|
2328
2628
|
const showErrorPanel = Boolean(error) && rows >= 18;
|
|
2629
|
+
const showGlobalErrorPanel = showErrorPanel && ui.view !== 'health' && !ui.showHelp;
|
|
2329
2630
|
const showErrorInline = Boolean(error) && !showErrorPanel;
|
|
2330
2631
|
const densePanels = rows <= 28 || cols <= 120;
|
|
2331
2632
|
|
|
2332
|
-
const reservedRows = 2 + (showFooterHelpLine ? 1 : 0) + (
|
|
2633
|
+
const reservedRows = 2 + (showFooterHelpLine ? 1 : 0) + (showGlobalErrorPanel ? 5 : 0) + (showErrorInline ? 1 : 0);
|
|
2333
2634
|
const contentRowsBudget = Math.max(4, rows - reservedRows);
|
|
2334
2635
|
const ultraCompact = rows <= 14;
|
|
2335
2636
|
const panelTitles = getPanelTitles(activeFlow, snapshot);
|
|
2336
|
-
const splitPreviewLayout =
|
|
2637
|
+
const splitPreviewLayout = previewOpen && !overlayPreviewOpen && !ui.showHelp && cols >= 110 && rows >= 16;
|
|
2337
2638
|
const mainPaneWidth = splitPreviewLayout
|
|
2338
2639
|
? Math.max(34, Math.floor((fullWidth - 1) * 0.52))
|
|
2339
2640
|
: fullWidth;
|
|
@@ -2343,19 +2644,17 @@ function createDashboardApp(deps) {
|
|
|
2343
2644
|
const mainCompactWidth = Math.max(18, mainPaneWidth - 4);
|
|
2344
2645
|
const previewCompactWidth = Math.max(18, previewPaneWidth - 4);
|
|
2345
2646
|
|
|
2346
|
-
const
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
mainCompactWidth,
|
|
2358
|
-
ui.view === 'runs' && focusedSection === 'pending' && paneFocus === 'main'
|
|
2647
|
+
const sectionLines = Object.fromEntries(
|
|
2648
|
+
Object.entries(rowsBySection).map(([sectionKey, sectionRows]) => [
|
|
2649
|
+
sectionKey,
|
|
2650
|
+
buildInteractiveRowsLines(
|
|
2651
|
+
sectionRows,
|
|
2652
|
+
selectionBySection[sectionKey] || 0,
|
|
2653
|
+
icons,
|
|
2654
|
+
mainCompactWidth,
|
|
2655
|
+
paneFocus === 'main' && focusedSection === sectionKey
|
|
2656
|
+
)
|
|
2657
|
+
])
|
|
2359
2658
|
);
|
|
2360
2659
|
const effectivePreviewTarget = previewTarget || selectedFocusedFile;
|
|
2361
2660
|
const previewLines = previewOpen
|
|
@@ -2366,12 +2665,14 @@ function createDashboardApp(deps) {
|
|
|
2366
2665
|
|
|
2367
2666
|
const shortcutsOverlayLines = buildHelpOverlayLines({
|
|
2368
2667
|
view: ui.view,
|
|
2668
|
+
flow: activeFlow,
|
|
2369
2669
|
previewOpen,
|
|
2370
2670
|
paneFocus,
|
|
2371
2671
|
availableFlowCount: availableFlowIds.length,
|
|
2372
2672
|
showErrorSection: showErrorPanel
|
|
2373
2673
|
});
|
|
2374
2674
|
const quickHelpText = buildQuickHelpText(ui.view, {
|
|
2675
|
+
flow: activeFlow,
|
|
2375
2676
|
previewOpen,
|
|
2376
2677
|
paneFocus,
|
|
2377
2678
|
availableFlowCount: availableFlowIds.length
|
|
@@ -2387,7 +2688,7 @@ function createDashboardApp(deps) {
|
|
|
2387
2688
|
borderColor: 'cyan'
|
|
2388
2689
|
}
|
|
2389
2690
|
];
|
|
2390
|
-
} else if (
|
|
2691
|
+
} else if (previewOpen && overlayPreviewOpen) {
|
|
2391
2692
|
panelCandidates = [
|
|
2392
2693
|
{
|
|
2393
2694
|
key: 'preview-overlay',
|
|
@@ -2396,45 +2697,42 @@ function createDashboardApp(deps) {
|
|
|
2396
2697
|
borderColor: 'magenta'
|
|
2397
2698
|
}
|
|
2398
2699
|
];
|
|
2399
|
-
} else if (ui.view === '
|
|
2700
|
+
} else if (ui.view === 'intents') {
|
|
2400
2701
|
panelCandidates = [
|
|
2401
2702
|
{
|
|
2402
2703
|
key: 'intent-status',
|
|
2403
2704
|
title: 'Intents',
|
|
2404
|
-
lines:
|
|
2705
|
+
lines: sectionLines['intent-status'],
|
|
2405
2706
|
borderColor: 'yellow'
|
|
2406
|
-
}
|
|
2707
|
+
}
|
|
2708
|
+
];
|
|
2709
|
+
} else if (ui.view === 'completed') {
|
|
2710
|
+
panelCandidates = [
|
|
2407
2711
|
{
|
|
2408
2712
|
key: 'completed-runs',
|
|
2409
2713
|
title: panelTitles.completed,
|
|
2410
|
-
lines:
|
|
2714
|
+
lines: sectionLines['completed-runs'],
|
|
2411
2715
|
borderColor: 'blue'
|
|
2412
|
-
}
|
|
2716
|
+
}
|
|
2717
|
+
];
|
|
2718
|
+
} else if (ui.view === 'health') {
|
|
2719
|
+
panelCandidates = [
|
|
2413
2720
|
{
|
|
2414
2721
|
key: 'standards',
|
|
2415
2722
|
title: 'Standards',
|
|
2416
|
-
lines:
|
|
2723
|
+
lines: sectionLines.standards,
|
|
2417
2724
|
borderColor: 'blue'
|
|
2418
2725
|
},
|
|
2419
|
-
{
|
|
2420
|
-
key: 'project',
|
|
2421
|
-
title: 'Project + Workspace',
|
|
2422
|
-
lines: buildOverviewProjectLines(snapshot, mainCompactWidth, activeFlow),
|
|
2423
|
-
borderColor: 'green'
|
|
2424
|
-
}
|
|
2425
|
-
];
|
|
2426
|
-
} else if (ui.view === 'health') {
|
|
2427
|
-
panelCandidates = [
|
|
2428
2726
|
{
|
|
2429
2727
|
key: 'stats',
|
|
2430
2728
|
title: 'Stats',
|
|
2431
|
-
lines:
|
|
2729
|
+
lines: sectionLines.stats,
|
|
2432
2730
|
borderColor: 'magenta'
|
|
2433
2731
|
},
|
|
2434
2732
|
{
|
|
2435
2733
|
key: 'warnings',
|
|
2436
2734
|
title: 'Warnings',
|
|
2437
|
-
lines:
|
|
2735
|
+
lines: sectionLines.warnings,
|
|
2438
2736
|
borderColor: 'red'
|
|
2439
2737
|
}
|
|
2440
2738
|
];
|
|
@@ -2443,47 +2741,36 @@ function createDashboardApp(deps) {
|
|
|
2443
2741
|
panelCandidates.push({
|
|
2444
2742
|
key: 'error-details',
|
|
2445
2743
|
title: 'Error Details',
|
|
2446
|
-
lines:
|
|
2744
|
+
lines: sectionLines['error-details'],
|
|
2447
2745
|
borderColor: 'red'
|
|
2448
2746
|
});
|
|
2449
2747
|
}
|
|
2450
2748
|
} else {
|
|
2451
|
-
const includeInlinePreviewPanel = previewOpen && !splitPreviewLayout;
|
|
2452
2749
|
panelCandidates = [
|
|
2453
2750
|
{
|
|
2454
2751
|
key: 'current-run',
|
|
2455
2752
|
title: panelTitles.current,
|
|
2456
|
-
lines:
|
|
2753
|
+
lines: sectionLines['current-run'],
|
|
2457
2754
|
borderColor: 'green'
|
|
2458
|
-
},
|
|
2459
|
-
{
|
|
2460
|
-
key: 'run-files',
|
|
2461
|
-
title: panelTitles.files,
|
|
2462
|
-
lines: runFileLines,
|
|
2463
|
-
borderColor: 'yellow'
|
|
2464
|
-
},
|
|
2465
|
-
includeInlinePreviewPanel
|
|
2466
|
-
? {
|
|
2467
|
-
key: 'preview',
|
|
2468
|
-
title: `Preview: ${effectivePreviewTarget?.label || 'unknown'}`,
|
|
2469
|
-
lines: previewLines,
|
|
2470
|
-
borderColor: 'magenta'
|
|
2471
|
-
}
|
|
2472
|
-
: null,
|
|
2473
|
-
{
|
|
2474
|
-
key: 'pending',
|
|
2475
|
-
title: panelTitles.pending,
|
|
2476
|
-
lines: pendingLines,
|
|
2477
|
-
borderColor: 'yellow'
|
|
2478
2755
|
}
|
|
2479
2756
|
];
|
|
2480
2757
|
}
|
|
2481
2758
|
|
|
2759
|
+
if (!ui.showHelp && previewOpen && !overlayPreviewOpen && !splitPreviewLayout) {
|
|
2760
|
+
panelCandidates.push({
|
|
2761
|
+
key: 'preview',
|
|
2762
|
+
title: `Preview: ${effectivePreviewTarget?.label || 'unknown'}`,
|
|
2763
|
+
lines: previewLines,
|
|
2764
|
+
borderColor: 'magenta'
|
|
2765
|
+
});
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2482
2768
|
if (ultraCompact && !splitPreviewLayout) {
|
|
2483
2769
|
if (previewOpen) {
|
|
2484
|
-
panelCandidates = panelCandidates.filter((panel) => panel && (panel.key ===
|
|
2770
|
+
panelCandidates = panelCandidates.filter((panel) => panel && (panel.key === focusedSection || panel.key === 'preview'));
|
|
2485
2771
|
} else {
|
|
2486
|
-
|
|
2772
|
+
const focusedPanel = panelCandidates.find((panel) => panel?.key === focusedSection);
|
|
2773
|
+
panelCandidates = [focusedPanel || panelCandidates[0]];
|
|
2487
2774
|
}
|
|
2488
2775
|
}
|
|
2489
2776
|
|
|
@@ -2502,7 +2789,7 @@ function createDashboardApp(deps) {
|
|
|
2502
2789
|
});
|
|
2503
2790
|
|
|
2504
2791
|
let contentNode;
|
|
2505
|
-
if (splitPreviewLayout &&
|
|
2792
|
+
if (splitPreviewLayout && !overlayPreviewOpen) {
|
|
2506
2793
|
const previewPanel = {
|
|
2507
2794
|
key: 'preview-split',
|
|
2508
2795
|
title: `Preview: ${effectivePreviewTarget?.label || 'unknown'}`,
|
|
@@ -2564,11 +2851,11 @@ function createDashboardApp(deps) {
|
|
|
2564
2851
|
{ flexDirection: 'column', width: fullWidth },
|
|
2565
2852
|
React.createElement(Text, { color: 'cyan' }, buildHeaderLine(snapshot, activeFlow, watchEnabled, watchStatus, lastRefreshAt, ui.view, fullWidth)),
|
|
2566
2853
|
React.createElement(FlowBar, { activeFlow, width: fullWidth, flowIds: availableFlowIds }),
|
|
2567
|
-
React.createElement(TabsBar, { view: ui.view, width: fullWidth, icons }),
|
|
2854
|
+
React.createElement(TabsBar, { view: ui.view, width: fullWidth, icons, flow: activeFlow }),
|
|
2568
2855
|
showErrorInline
|
|
2569
2856
|
? React.createElement(Text, { color: 'red' }, truncate(buildErrorLines(error, fullWidth)[0] || 'Error', fullWidth))
|
|
2570
2857
|
: null,
|
|
2571
|
-
|
|
2858
|
+
showGlobalErrorPanel
|
|
2572
2859
|
? React.createElement(SectionPanel, {
|
|
2573
2860
|
title: 'Errors',
|
|
2574
2861
|
lines: buildErrorLines(error, Math.max(18, fullWidth - 4)),
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
function createInitialUIState() {
|
|
2
2
|
return {
|
|
3
3
|
view: 'runs',
|
|
4
|
-
showHelp:
|
|
4
|
+
showHelp: false
|
|
5
5
|
};
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
function cycleView(current) {
|
|
9
9
|
if (current === 'runs') {
|
|
10
|
-
return '
|
|
10
|
+
return 'intents';
|
|
11
11
|
}
|
|
12
|
-
if (current === '
|
|
12
|
+
if (current === 'intents') {
|
|
13
|
+
return 'completed';
|
|
14
|
+
}
|
|
15
|
+
if (current === 'completed') {
|
|
13
16
|
return 'health';
|
|
14
17
|
}
|
|
15
18
|
return 'runs';
|
|
@@ -19,10 +22,13 @@ function cycleViewBackward(current) {
|
|
|
19
22
|
if (current === 'runs') {
|
|
20
23
|
return 'health';
|
|
21
24
|
}
|
|
22
|
-
if (current === '
|
|
25
|
+
if (current === 'intents') {
|
|
23
26
|
return 'runs';
|
|
24
27
|
}
|
|
25
|
-
|
|
28
|
+
if (current === 'completed') {
|
|
29
|
+
return 'intents';
|
|
30
|
+
}
|
|
31
|
+
return 'completed';
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
module.exports = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specsmd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"description": "Multi-agent orchestration system for AI-native software development. Delivers AI-DLC, Agile, and custom SDLC flows as markdown-based agent systems.",
|
|
5
5
|
"main": "lib/installer.js",
|
|
6
6
|
"bin": {
|