specsmd 0.1.34 → 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 +573 -188
- 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
|
}
|
|
@@ -1397,6 +1597,95 @@ function openFileWithDefaultApp(filePath) {
|
|
|
1397
1597
|
};
|
|
1398
1598
|
}
|
|
1399
1599
|
|
|
1600
|
+
function buildQuickHelpText(view, options = {}) {
|
|
1601
|
+
const {
|
|
1602
|
+
flow = 'fire',
|
|
1603
|
+
previewOpen = false,
|
|
1604
|
+
availableFlowCount = 1
|
|
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');
|
|
1609
|
+
|
|
1610
|
+
const parts = ['1/2/3/4 tabs', 'g/G sections'];
|
|
1611
|
+
|
|
1612
|
+
if (view === 'runs' || view === 'intents' || view === 'completed' || view === 'health') {
|
|
1613
|
+
if (previewOpen) {
|
|
1614
|
+
parts.push('tab pane', '↑/↓ nav/scroll', 'v close');
|
|
1615
|
+
} else {
|
|
1616
|
+
parts.push('↑/↓ navigate', 'enter expand', 'v preview');
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
parts.push(`tab1 ${activeLabel}`);
|
|
1620
|
+
|
|
1621
|
+
if (availableFlowCount > 1) {
|
|
1622
|
+
parts.push('[/] flow');
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
parts.push('r refresh', '? shortcuts', 'q quit');
|
|
1626
|
+
return parts.join(' | ');
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
function buildHelpOverlayLines(options = {}) {
|
|
1630
|
+
const {
|
|
1631
|
+
view = 'runs',
|
|
1632
|
+
flow = 'fire',
|
|
1633
|
+
previewOpen = false,
|
|
1634
|
+
paneFocus = 'main',
|
|
1635
|
+
availableFlowCount = 1,
|
|
1636
|
+
showErrorSection = false
|
|
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');
|
|
1642
|
+
|
|
1643
|
+
const lines = [
|
|
1644
|
+
{ text: 'Global', color: 'cyan', bold: true },
|
|
1645
|
+
'q or Ctrl+C quit',
|
|
1646
|
+
'r refresh snapshot',
|
|
1647
|
+
`1 active ${itemLabel} | 2 intents | 3 completed ${itemPlural} | 4 standards/health`,
|
|
1648
|
+
'g next section | G previous section',
|
|
1649
|
+
'h/? toggle this shortcuts overlay',
|
|
1650
|
+
'esc close overlays (help/preview/fullscreen)',
|
|
1651
|
+
{ text: '', color: undefined, bold: false },
|
|
1652
|
+
{ text: 'Tab 1 Active', color: 'yellow', bold: true },
|
|
1653
|
+
`a focus active ${itemLabel}`,
|
|
1654
|
+
'up/down or j/k move selection',
|
|
1655
|
+
'enter expand/collapse selected folder row',
|
|
1656
|
+
'v preview selected file',
|
|
1657
|
+
'v twice quickly opens fullscreen preview overlay',
|
|
1658
|
+
'tab switch focus between main and preview panes',
|
|
1659
|
+
'o open selected file in system default app'
|
|
1660
|
+
];
|
|
1661
|
+
|
|
1662
|
+
if (previewOpen) {
|
|
1663
|
+
lines.push(`preview is open (focus: ${paneFocus})`);
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
if (availableFlowCount > 1) {
|
|
1667
|
+
lines.push('[/] (and m) switch flow');
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
lines.push(
|
|
1671
|
+
{ text: '', color: undefined, bold: false },
|
|
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',
|
|
1679
|
+
{ text: '', color: undefined, bold: false },
|
|
1680
|
+
{ text: 'Tab 4 Standards/Health', color: 'magenta', bold: true },
|
|
1681
|
+
`s standards | t stats | w warnings${showErrorSection ? ' | e errors' : ''}`,
|
|
1682
|
+
{ text: '', color: undefined, bold: false },
|
|
1683
|
+
{ text: `Current view: ${String(view || 'runs').toUpperCase()}`, color: 'gray', bold: false }
|
|
1684
|
+
);
|
|
1685
|
+
|
|
1686
|
+
return lines;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1400
1689
|
function colorizeMarkdownLine(line, inCodeBlock) {
|
|
1401
1690
|
const text = String(line ?? '');
|
|
1402
1691
|
|
|
@@ -1610,11 +1899,15 @@ function createDashboardApp(deps) {
|
|
|
1610
1899
|
}
|
|
1611
1900
|
|
|
1612
1901
|
function TabsBar(props) {
|
|
1613
|
-
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');
|
|
1614
1906
|
const tabs = [
|
|
1615
|
-
{ id: 'runs', label: ` 1 ${icons.runs}
|
|
1616
|
-
{ id: '
|
|
1617
|
-
{ 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 ` }
|
|
1618
1911
|
];
|
|
1619
1912
|
|
|
1620
1913
|
return React.createElement(
|
|
@@ -1682,18 +1975,24 @@ function createDashboardApp(deps) {
|
|
|
1682
1975
|
const [error, setError] = useState(initialNormalizedError);
|
|
1683
1976
|
const [ui, setUi] = useState(createInitialUIState());
|
|
1684
1977
|
const [sectionFocus, setSectionFocus] = useState({
|
|
1685
|
-
runs: 'run
|
|
1686
|
-
|
|
1687
|
-
|
|
1978
|
+
runs: 'current-run',
|
|
1979
|
+
intents: 'intent-status',
|
|
1980
|
+
completed: 'completed-runs',
|
|
1981
|
+
health: 'standards'
|
|
1688
1982
|
});
|
|
1689
1983
|
const [selectionBySection, setSelectionBySection] = useState({
|
|
1690
|
-
'run
|
|
1691
|
-
|
|
1692
|
-
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
|
|
1693
1991
|
});
|
|
1694
1992
|
const [expandedGroups, setExpandedGroups] = useState({});
|
|
1695
1993
|
const [previewTarget, setPreviewTarget] = useState(null);
|
|
1696
1994
|
const [overviewIntentFilter, setOverviewIntentFilter] = useState('next');
|
|
1995
|
+
const [deferredTabsReady, setDeferredTabsReady] = useState(false);
|
|
1697
1996
|
const [previewOpen, setPreviewOpen] = useState(false);
|
|
1698
1997
|
const [paneFocus, setPaneFocus] = useState('main');
|
|
1699
1998
|
const [overlayPreviewOpen, setOverlayPreviewOpen] = useState(false);
|
|
@@ -1729,24 +2028,86 @@ function createDashboardApp(deps) {
|
|
|
1729
2028
|
return base.filter((sectionKey) => sectionKey !== 'error-details' || showErrorPanelForSections);
|
|
1730
2029
|
}, [showErrorPanelForSections]);
|
|
1731
2030
|
|
|
1732
|
-
const
|
|
1733
|
-
const
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
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
|
|
1743
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');
|
|
1744
2098
|
|
|
1745
2099
|
const rowsBySection = {
|
|
1746
|
-
'run
|
|
1747
|
-
|
|
1748
|
-
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
|
|
1749
2107
|
};
|
|
2108
|
+
const rowLengthSignature = Object.entries(rowsBySection)
|
|
2109
|
+
.map(([key, rowsForSection]) => `${key}:${Array.isArray(rowsForSection) ? rowsForSection.length : 0}`)
|
|
2110
|
+
.join('|');
|
|
1750
2111
|
|
|
1751
2112
|
const currentSectionOrder = getAvailableSections(ui.view);
|
|
1752
2113
|
const focusedSection = currentSectionOrder.includes(sectionFocus[ui.view])
|
|
@@ -1823,6 +2184,15 @@ function createDashboardApp(deps) {
|
|
|
1823
2184
|
return;
|
|
1824
2185
|
}
|
|
1825
2186
|
|
|
2187
|
+
if (key.escape && ui.showHelp) {
|
|
2188
|
+
setUi((previous) => ({ ...previous, showHelp: false }));
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
if (ui.showHelp) {
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
1826
2196
|
if (input === '1') {
|
|
1827
2197
|
setUi((previous) => ({ ...previous, view: 'runs' }));
|
|
1828
2198
|
setPaneFocus('main');
|
|
@@ -1830,12 +2200,18 @@ function createDashboardApp(deps) {
|
|
|
1830
2200
|
}
|
|
1831
2201
|
|
|
1832
2202
|
if (input === '2') {
|
|
1833
|
-
setUi((previous) => ({ ...previous, view: '
|
|
2203
|
+
setUi((previous) => ({ ...previous, view: 'intents' }));
|
|
1834
2204
|
setPaneFocus('main');
|
|
1835
2205
|
return;
|
|
1836
2206
|
}
|
|
1837
2207
|
|
|
1838
2208
|
if (input === '3') {
|
|
2209
|
+
setUi((previous) => ({ ...previous, view: 'completed' }));
|
|
2210
|
+
setPaneFocus('main');
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
if (input === '4') {
|
|
1839
2215
|
setUi((previous) => ({ ...previous, view: 'health' }));
|
|
1840
2216
|
setPaneFocus('main');
|
|
1841
2217
|
return;
|
|
@@ -1854,14 +2230,19 @@ function createDashboardApp(deps) {
|
|
|
1854
2230
|
return availableFlowIds[nextIndex];
|
|
1855
2231
|
});
|
|
1856
2232
|
setSelectionBySection({
|
|
1857
|
-
'run
|
|
1858
|
-
|
|
1859
|
-
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
|
|
1860
2240
|
});
|
|
1861
2241
|
setSectionFocus({
|
|
1862
|
-
runs: 'run
|
|
1863
|
-
|
|
1864
|
-
|
|
2242
|
+
runs: 'current-run',
|
|
2243
|
+
intents: 'intent-status',
|
|
2244
|
+
completed: 'completed-runs',
|
|
2245
|
+
health: 'standards'
|
|
1865
2246
|
});
|
|
1866
2247
|
setOverviewIntentFilter('next');
|
|
1867
2248
|
setExpandedGroups({});
|
|
@@ -1886,14 +2267,19 @@ function createDashboardApp(deps) {
|
|
|
1886
2267
|
return availableFlowIds[nextIndex];
|
|
1887
2268
|
});
|
|
1888
2269
|
setSelectionBySection({
|
|
1889
|
-
'run
|
|
1890
|
-
|
|
1891
|
-
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
|
|
1892
2277
|
});
|
|
1893
2278
|
setSectionFocus({
|
|
1894
|
-
runs: 'run
|
|
1895
|
-
|
|
1896
|
-
|
|
2279
|
+
runs: 'current-run',
|
|
2280
|
+
intents: 'intent-status',
|
|
2281
|
+
completed: 'completed-runs',
|
|
2282
|
+
health: 'standards'
|
|
1897
2283
|
});
|
|
1898
2284
|
setOverviewIntentFilter('next');
|
|
1899
2285
|
setExpandedGroups({});
|
|
@@ -1910,12 +2296,12 @@ function createDashboardApp(deps) {
|
|
|
1910
2296
|
? sectionFocus[ui.view]
|
|
1911
2297
|
: (availableSections[0] || 'current-run');
|
|
1912
2298
|
|
|
1913
|
-
if (key.tab &&
|
|
2299
|
+
if (key.tab && previewOpen) {
|
|
1914
2300
|
setPaneFocus((previous) => (previous === 'main' ? 'preview' : 'main'));
|
|
1915
2301
|
return;
|
|
1916
2302
|
}
|
|
1917
2303
|
|
|
1918
|
-
if (ui.view === '
|
|
2304
|
+
if (ui.view === 'intents' && activeSection === 'intent-status') {
|
|
1919
2305
|
if (input === 'n') {
|
|
1920
2306
|
setOverviewIntentFilter('next');
|
|
1921
2307
|
return;
|
|
@@ -1954,34 +2340,21 @@ function createDashboardApp(deps) {
|
|
|
1954
2340
|
setPaneFocus('main');
|
|
1955
2341
|
return;
|
|
1956
2342
|
}
|
|
1957
|
-
|
|
1958
|
-
setSectionFocus((previous) => ({ ...previous, runs: 'run-files' }));
|
|
1959
|
-
setPaneFocus('main');
|
|
1960
|
-
return;
|
|
1961
|
-
}
|
|
1962
|
-
if (input === 'p') {
|
|
1963
|
-
setSectionFocus((previous) => ({ ...previous, runs: 'pending' }));
|
|
1964
|
-
setPaneFocus('main');
|
|
1965
|
-
return;
|
|
1966
|
-
}
|
|
1967
|
-
} else if (ui.view === 'overview') {
|
|
1968
|
-
if (input === 'p') {
|
|
1969
|
-
setSectionFocus((previous) => ({ ...previous, overview: 'project' }));
|
|
1970
|
-
return;
|
|
1971
|
-
}
|
|
2343
|
+
} else if (ui.view === 'intents') {
|
|
1972
2344
|
if (input === 'i') {
|
|
1973
|
-
setSectionFocus((previous) => ({ ...previous,
|
|
1974
|
-
return;
|
|
1975
|
-
}
|
|
1976
|
-
if (input === 's') {
|
|
1977
|
-
setSectionFocus((previous) => ({ ...previous, overview: 'standards' }));
|
|
2345
|
+
setSectionFocus((previous) => ({ ...previous, intents: 'intent-status' }));
|
|
1978
2346
|
return;
|
|
1979
2347
|
}
|
|
2348
|
+
} else if (ui.view === 'completed') {
|
|
1980
2349
|
if (input === 'c') {
|
|
1981
|
-
setSectionFocus((previous) => ({ ...previous,
|
|
2350
|
+
setSectionFocus((previous) => ({ ...previous, completed: 'completed-runs' }));
|
|
1982
2351
|
return;
|
|
1983
2352
|
}
|
|
1984
2353
|
} else if (ui.view === 'health') {
|
|
2354
|
+
if (input === 's') {
|
|
2355
|
+
setSectionFocus((previous) => ({ ...previous, health: 'standards' }));
|
|
2356
|
+
return;
|
|
2357
|
+
}
|
|
1985
2358
|
if (input === 't') {
|
|
1986
2359
|
setSectionFocus((previous) => ({ ...previous, health: 'stats' }));
|
|
1987
2360
|
return;
|
|
@@ -2010,7 +2383,7 @@ function createDashboardApp(deps) {
|
|
|
2010
2383
|
}
|
|
2011
2384
|
}
|
|
2012
2385
|
|
|
2013
|
-
if (
|
|
2386
|
+
if (key.upArrow || key.downArrow || input === 'j' || input === 'k') {
|
|
2014
2387
|
const moveDown = key.downArrow || input === 'j';
|
|
2015
2388
|
const moveUp = key.upArrow || input === 'k';
|
|
2016
2389
|
|
|
@@ -2023,11 +2396,7 @@ function createDashboardApp(deps) {
|
|
|
2023
2396
|
return;
|
|
2024
2397
|
}
|
|
2025
2398
|
|
|
2026
|
-
const targetSection = activeSection
|
|
2027
|
-
if (targetSection !== activeSection) {
|
|
2028
|
-
setSectionFocus((previous) => ({ ...previous, runs: targetSection }));
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2399
|
+
const targetSection = activeSection;
|
|
2031
2400
|
const targetRows = rowsBySection[targetSection] || [];
|
|
2032
2401
|
if (targetRows.length === 0) {
|
|
2033
2402
|
return;
|
|
@@ -2045,24 +2414,22 @@ function createDashboardApp(deps) {
|
|
|
2045
2414
|
return;
|
|
2046
2415
|
}
|
|
2047
2416
|
|
|
2048
|
-
if (
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
}));
|
|
2057
|
-
}
|
|
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
|
+
}));
|
|
2058
2425
|
}
|
|
2059
2426
|
return;
|
|
2060
2427
|
}
|
|
2061
2428
|
|
|
2062
|
-
if (input === 'v'
|
|
2429
|
+
if (input === 'v') {
|
|
2063
2430
|
const target = selectedFocusedFile || previewTarget;
|
|
2064
2431
|
if (!target) {
|
|
2065
|
-
setStatusLine('Select a file row first
|
|
2432
|
+
setStatusLine('Select a file row first.');
|
|
2066
2433
|
return;
|
|
2067
2434
|
}
|
|
2068
2435
|
|
|
@@ -2100,7 +2467,7 @@ function createDashboardApp(deps) {
|
|
|
2100
2467
|
return;
|
|
2101
2468
|
}
|
|
2102
2469
|
|
|
2103
|
-
if (input === 'o'
|
|
2470
|
+
if (input === 'o') {
|
|
2104
2471
|
const target = selectedFocusedFile || previewTarget;
|
|
2105
2472
|
const result = openFileWithDefaultApp(target?.path);
|
|
2106
2473
|
setStatusLine(result.message);
|
|
@@ -2112,21 +2479,38 @@ function createDashboardApp(deps) {
|
|
|
2112
2479
|
}, [refresh]);
|
|
2113
2480
|
|
|
2114
2481
|
useEffect(() => {
|
|
2115
|
-
setSelectionBySection((previous) =>
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
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]);
|
|
2122
2501
|
|
|
2123
2502
|
useEffect(() => {
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
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');
|
|
2130
2514
|
}, [ui.view]);
|
|
2131
2515
|
|
|
2132
2516
|
useEffect(() => {
|
|
@@ -2240,16 +2624,17 @@ function createDashboardApp(deps) {
|
|
|
2240
2624
|
const rows = Number.isFinite(terminalSize.rows) ? terminalSize.rows : (process.stdout.rows || 40);
|
|
2241
2625
|
|
|
2242
2626
|
const fullWidth = Math.max(40, cols - 1);
|
|
2243
|
-
const
|
|
2627
|
+
const showFooterHelpLine = rows >= 10;
|
|
2244
2628
|
const showErrorPanel = Boolean(error) && rows >= 18;
|
|
2629
|
+
const showGlobalErrorPanel = showErrorPanel && ui.view !== 'health' && !ui.showHelp;
|
|
2245
2630
|
const showErrorInline = Boolean(error) && !showErrorPanel;
|
|
2246
2631
|
const densePanels = rows <= 28 || cols <= 120;
|
|
2247
2632
|
|
|
2248
|
-
const reservedRows = 2 + (
|
|
2633
|
+
const reservedRows = 2 + (showFooterHelpLine ? 1 : 0) + (showGlobalErrorPanel ? 5 : 0) + (showErrorInline ? 1 : 0);
|
|
2249
2634
|
const contentRowsBudget = Math.max(4, rows - reservedRows);
|
|
2250
2635
|
const ultraCompact = rows <= 14;
|
|
2251
2636
|
const panelTitles = getPanelTitles(activeFlow, snapshot);
|
|
2252
|
-
const splitPreviewLayout =
|
|
2637
|
+
const splitPreviewLayout = previewOpen && !overlayPreviewOpen && !ui.showHelp && cols >= 110 && rows >= 16;
|
|
2253
2638
|
const mainPaneWidth = splitPreviewLayout
|
|
2254
2639
|
? Math.max(34, Math.floor((fullWidth - 1) * 0.52))
|
|
2255
2640
|
: fullWidth;
|
|
@@ -2259,19 +2644,17 @@ function createDashboardApp(deps) {
|
|
|
2259
2644
|
const mainCompactWidth = Math.max(18, mainPaneWidth - 4);
|
|
2260
2645
|
const previewCompactWidth = Math.max(18, previewPaneWidth - 4);
|
|
2261
2646
|
|
|
2262
|
-
const
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
mainCompactWidth,
|
|
2274
|
-
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
|
+
])
|
|
2275
2658
|
);
|
|
2276
2659
|
const effectivePreviewTarget = previewTarget || selectedFocusedFile;
|
|
2277
2660
|
const previewLines = previewOpen
|
|
@@ -2280,8 +2663,32 @@ function createDashboardApp(deps) {
|
|
|
2280
2663
|
})
|
|
2281
2664
|
: [];
|
|
2282
2665
|
|
|
2666
|
+
const shortcutsOverlayLines = buildHelpOverlayLines({
|
|
2667
|
+
view: ui.view,
|
|
2668
|
+
flow: activeFlow,
|
|
2669
|
+
previewOpen,
|
|
2670
|
+
paneFocus,
|
|
2671
|
+
availableFlowCount: availableFlowIds.length,
|
|
2672
|
+
showErrorSection: showErrorPanel
|
|
2673
|
+
});
|
|
2674
|
+
const quickHelpText = buildQuickHelpText(ui.view, {
|
|
2675
|
+
flow: activeFlow,
|
|
2676
|
+
previewOpen,
|
|
2677
|
+
paneFocus,
|
|
2678
|
+
availableFlowCount: availableFlowIds.length
|
|
2679
|
+
});
|
|
2680
|
+
|
|
2283
2681
|
let panelCandidates;
|
|
2284
|
-
if (ui.
|
|
2682
|
+
if (ui.showHelp) {
|
|
2683
|
+
panelCandidates = [
|
|
2684
|
+
{
|
|
2685
|
+
key: 'shortcuts-overlay',
|
|
2686
|
+
title: 'Keyboard Shortcuts',
|
|
2687
|
+
lines: shortcutsOverlayLines,
|
|
2688
|
+
borderColor: 'cyan'
|
|
2689
|
+
}
|
|
2690
|
+
];
|
|
2691
|
+
} else if (previewOpen && overlayPreviewOpen) {
|
|
2285
2692
|
panelCandidates = [
|
|
2286
2693
|
{
|
|
2287
2694
|
key: 'preview-overlay',
|
|
@@ -2290,45 +2697,42 @@ function createDashboardApp(deps) {
|
|
|
2290
2697
|
borderColor: 'magenta'
|
|
2291
2698
|
}
|
|
2292
2699
|
];
|
|
2293
|
-
} else if (ui.view === '
|
|
2700
|
+
} else if (ui.view === 'intents') {
|
|
2294
2701
|
panelCandidates = [
|
|
2295
2702
|
{
|
|
2296
2703
|
key: 'intent-status',
|
|
2297
2704
|
title: 'Intents',
|
|
2298
|
-
lines:
|
|
2705
|
+
lines: sectionLines['intent-status'],
|
|
2299
2706
|
borderColor: 'yellow'
|
|
2300
|
-
}
|
|
2707
|
+
}
|
|
2708
|
+
];
|
|
2709
|
+
} else if (ui.view === 'completed') {
|
|
2710
|
+
panelCandidates = [
|
|
2301
2711
|
{
|
|
2302
2712
|
key: 'completed-runs',
|
|
2303
2713
|
title: panelTitles.completed,
|
|
2304
|
-
lines:
|
|
2714
|
+
lines: sectionLines['completed-runs'],
|
|
2305
2715
|
borderColor: 'blue'
|
|
2306
|
-
}
|
|
2716
|
+
}
|
|
2717
|
+
];
|
|
2718
|
+
} else if (ui.view === 'health') {
|
|
2719
|
+
panelCandidates = [
|
|
2307
2720
|
{
|
|
2308
2721
|
key: 'standards',
|
|
2309
2722
|
title: 'Standards',
|
|
2310
|
-
lines:
|
|
2723
|
+
lines: sectionLines.standards,
|
|
2311
2724
|
borderColor: 'blue'
|
|
2312
2725
|
},
|
|
2313
|
-
{
|
|
2314
|
-
key: 'project',
|
|
2315
|
-
title: 'Project + Workspace',
|
|
2316
|
-
lines: buildOverviewProjectLines(snapshot, mainCompactWidth, activeFlow),
|
|
2317
|
-
borderColor: 'green'
|
|
2318
|
-
}
|
|
2319
|
-
];
|
|
2320
|
-
} else if (ui.view === 'health') {
|
|
2321
|
-
panelCandidates = [
|
|
2322
2726
|
{
|
|
2323
2727
|
key: 'stats',
|
|
2324
2728
|
title: 'Stats',
|
|
2325
|
-
lines:
|
|
2729
|
+
lines: sectionLines.stats,
|
|
2326
2730
|
borderColor: 'magenta'
|
|
2327
2731
|
},
|
|
2328
2732
|
{
|
|
2329
2733
|
key: 'warnings',
|
|
2330
2734
|
title: 'Warnings',
|
|
2331
|
-
lines:
|
|
2735
|
+
lines: sectionLines.warnings,
|
|
2332
2736
|
borderColor: 'red'
|
|
2333
2737
|
}
|
|
2334
2738
|
];
|
|
@@ -2337,61 +2741,40 @@ function createDashboardApp(deps) {
|
|
|
2337
2741
|
panelCandidates.push({
|
|
2338
2742
|
key: 'error-details',
|
|
2339
2743
|
title: 'Error Details',
|
|
2340
|
-
lines:
|
|
2744
|
+
lines: sectionLines['error-details'],
|
|
2341
2745
|
borderColor: 'red'
|
|
2342
2746
|
});
|
|
2343
2747
|
}
|
|
2344
2748
|
} else {
|
|
2345
|
-
const includeInlinePreviewPanel = previewOpen && !splitPreviewLayout;
|
|
2346
2749
|
panelCandidates = [
|
|
2347
2750
|
{
|
|
2348
2751
|
key: 'current-run',
|
|
2349
2752
|
title: panelTitles.current,
|
|
2350
|
-
lines:
|
|
2753
|
+
lines: sectionLines['current-run'],
|
|
2351
2754
|
borderColor: 'green'
|
|
2352
|
-
},
|
|
2353
|
-
{
|
|
2354
|
-
key: 'run-files',
|
|
2355
|
-
title: panelTitles.files,
|
|
2356
|
-
lines: runFileLines,
|
|
2357
|
-
borderColor: 'yellow'
|
|
2358
|
-
},
|
|
2359
|
-
includeInlinePreviewPanel
|
|
2360
|
-
? {
|
|
2361
|
-
key: 'preview',
|
|
2362
|
-
title: `Preview: ${effectivePreviewTarget?.label || 'unknown'}`,
|
|
2363
|
-
lines: previewLines,
|
|
2364
|
-
borderColor: 'magenta'
|
|
2365
|
-
}
|
|
2366
|
-
: null,
|
|
2367
|
-
{
|
|
2368
|
-
key: 'pending',
|
|
2369
|
-
title: panelTitles.pending,
|
|
2370
|
-
lines: pendingLines,
|
|
2371
|
-
borderColor: 'yellow'
|
|
2372
2755
|
}
|
|
2373
2756
|
];
|
|
2374
2757
|
}
|
|
2375
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
|
+
|
|
2376
2768
|
if (ultraCompact && !splitPreviewLayout) {
|
|
2377
2769
|
if (previewOpen) {
|
|
2378
|
-
panelCandidates = panelCandidates.filter((panel) => panel && (panel.key ===
|
|
2770
|
+
panelCandidates = panelCandidates.filter((panel) => panel && (panel.key === focusedSection || panel.key === 'preview'));
|
|
2379
2771
|
} else {
|
|
2380
|
-
|
|
2772
|
+
const focusedPanel = panelCandidates.find((panel) => panel?.key === focusedSection);
|
|
2773
|
+
panelCandidates = [focusedPanel || panelCandidates[0]];
|
|
2381
2774
|
}
|
|
2382
2775
|
}
|
|
2383
2776
|
|
|
2384
2777
|
const panels = allocateSingleColumnPanels(panelCandidates, contentRowsBudget);
|
|
2385
|
-
const flowSwitchHint = availableFlowIds.length > 1 ? ' | [ or ] switch flow' : '';
|
|
2386
|
-
const sectionHint = ui.view === 'runs'
|
|
2387
|
-
? ' | a active | f files | p pending'
|
|
2388
|
-
: (ui.view === 'overview' ? ' | i intents | c completed | s standards | p project | n/x intent filter' : ' | t stats | w warnings | e errors');
|
|
2389
|
-
const previewHint = ui.view === 'runs'
|
|
2390
|
-
? (previewOpen
|
|
2391
|
-
? ` | tab ${paneFocus === 'preview' ? 'main' : 'preview'} | ↑/↓ ${paneFocus === 'preview' ? 'scroll' : 'navigate'} | v close | vv fullscreen`
|
|
2392
|
-
: ' | ↑/↓ navigate | enter expand | v preview | vv fullscreen | o open')
|
|
2393
|
-
: '';
|
|
2394
|
-
const helpText = `q quit | r refresh | h/? help | 1 runs | 2 overview | 3 health | g/G section${sectionHint}${previewHint}${flowSwitchHint}`;
|
|
2395
2778
|
|
|
2396
2779
|
const renderPanel = (panel, index, width, isFocused) => React.createElement(SectionPanel, {
|
|
2397
2780
|
key: panel.key,
|
|
@@ -2406,7 +2789,7 @@ function createDashboardApp(deps) {
|
|
|
2406
2789
|
});
|
|
2407
2790
|
|
|
2408
2791
|
let contentNode;
|
|
2409
|
-
if (splitPreviewLayout &&
|
|
2792
|
+
if (splitPreviewLayout && !overlayPreviewOpen) {
|
|
2410
2793
|
const previewPanel = {
|
|
2411
2794
|
key: 'preview-split',
|
|
2412
2795
|
title: `Preview: ${effectivePreviewTarget?.label || 'unknown'}`,
|
|
@@ -2455,9 +2838,11 @@ function createDashboardApp(deps) {
|
|
|
2455
2838
|
panel,
|
|
2456
2839
|
index,
|
|
2457
2840
|
fullWidth,
|
|
2458
|
-
|
|
2841
|
+
ui.showHelp
|
|
2842
|
+
? true
|
|
2843
|
+
: ((panel.key === 'preview' || panel.key === 'preview-overlay')
|
|
2459
2844
|
? paneFocus === 'preview'
|
|
2460
|
-
: (paneFocus === 'main' && panel.key === focusedSection)
|
|
2845
|
+
: (paneFocus === 'main' && panel.key === focusedSection))
|
|
2461
2846
|
));
|
|
2462
2847
|
}
|
|
2463
2848
|
|
|
@@ -2466,11 +2851,11 @@ function createDashboardApp(deps) {
|
|
|
2466
2851
|
{ flexDirection: 'column', width: fullWidth },
|
|
2467
2852
|
React.createElement(Text, { color: 'cyan' }, buildHeaderLine(snapshot, activeFlow, watchEnabled, watchStatus, lastRefreshAt, ui.view, fullWidth)),
|
|
2468
2853
|
React.createElement(FlowBar, { activeFlow, width: fullWidth, flowIds: availableFlowIds }),
|
|
2469
|
-
React.createElement(TabsBar, { view: ui.view, width: fullWidth, icons }),
|
|
2854
|
+
React.createElement(TabsBar, { view: ui.view, width: fullWidth, icons, flow: activeFlow }),
|
|
2470
2855
|
showErrorInline
|
|
2471
2856
|
? React.createElement(Text, { color: 'red' }, truncate(buildErrorLines(error, fullWidth)[0] || 'Error', fullWidth))
|
|
2472
2857
|
: null,
|
|
2473
|
-
|
|
2858
|
+
showGlobalErrorPanel
|
|
2474
2859
|
? React.createElement(SectionPanel, {
|
|
2475
2860
|
title: 'Errors',
|
|
2476
2861
|
lines: buildErrorLines(error, Math.max(18, fullWidth - 4)),
|
|
@@ -2486,8 +2871,8 @@ function createDashboardApp(deps) {
|
|
|
2486
2871
|
statusLine !== ''
|
|
2487
2872
|
? React.createElement(Text, { color: 'yellow' }, truncate(statusLine, fullWidth))
|
|
2488
2873
|
: null,
|
|
2489
|
-
|
|
2490
|
-
? React.createElement(Text, { color: 'gray' }, truncate(
|
|
2874
|
+
showFooterHelpLine
|
|
2875
|
+
? React.createElement(Text, { color: 'gray' }, truncate(quickHelpText, fullWidth))
|
|
2491
2876
|
: null
|
|
2492
2877
|
);
|
|
2493
2878
|
}
|
|
@@ -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": {
|