specsmd 0.1.55 → 0.1.57
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/git/changes.js +33 -1
- package/lib/dashboard/tui/app.js +116 -162
- package/package.json +1 -1
|
@@ -324,7 +324,39 @@ function loadGitDiffPreview(changeEntry) {
|
|
|
324
324
|
return result.stdout;
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
function loadGitCommitPreview(changeEntry) {
|
|
328
|
+
const repoRoot = typeof changeEntry?.repoRoot === 'string'
|
|
329
|
+
? changeEntry.repoRoot
|
|
330
|
+
: (typeof changeEntry?.workspacePath === 'string' ? findGitRoot(changeEntry.workspacePath) : null);
|
|
331
|
+
const commitHash = typeof changeEntry?.commitHash === 'string'
|
|
332
|
+
? changeEntry.commitHash.trim()
|
|
333
|
+
: '';
|
|
334
|
+
|
|
335
|
+
if (!repoRoot) {
|
|
336
|
+
return '[git] repository is unavailable for commit preview.';
|
|
337
|
+
}
|
|
338
|
+
if (commitHash === '') {
|
|
339
|
+
return '[git] no commit selected.';
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const result = runGit(
|
|
343
|
+
['-c', 'color.ui=false', '--no-pager', 'show', '--patch', '--stat', '--no-ext-diff', commitHash],
|
|
344
|
+
repoRoot
|
|
345
|
+
);
|
|
346
|
+
if (!result.ok) {
|
|
347
|
+
return `[git] unable to load commit diff: ${result.error}`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const output = result.stdout.trim();
|
|
351
|
+
if (output === '') {
|
|
352
|
+
return '[git] no commit output for this selection.';
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return result.stdout;
|
|
356
|
+
}
|
|
357
|
+
|
|
327
358
|
module.exports = {
|
|
328
359
|
listGitChanges,
|
|
329
|
-
loadGitDiffPreview
|
|
360
|
+
loadGitDiffPreview,
|
|
361
|
+
loadGitCommitPreview
|
|
330
362
|
};
|
package/lib/dashboard/tui/app.js
CHANGED
|
@@ -5,7 +5,6 @@ const stringWidthModule = require('string-width');
|
|
|
5
5
|
const sliceAnsiModule = require('slice-ansi');
|
|
6
6
|
const { createWatchRuntime } = require('../runtime/watch-runtime');
|
|
7
7
|
const { createInitialUIState } = require('./store');
|
|
8
|
-
const { loadGitDiffPreview } = require('../git/changes');
|
|
9
8
|
|
|
10
9
|
const stringWidth = typeof stringWidthModule === 'function'
|
|
11
10
|
? stringWidthModule
|
|
@@ -13,6 +12,10 @@ const stringWidth = typeof stringWidthModule === 'function'
|
|
|
13
12
|
const sliceAnsi = typeof sliceAnsiModule === 'function'
|
|
14
13
|
? sliceAnsiModule
|
|
15
14
|
: sliceAnsiModule.default;
|
|
15
|
+
const {
|
|
16
|
+
loadGitDiffPreview,
|
|
17
|
+
loadGitCommitPreview
|
|
18
|
+
} = require('../git/changes');
|
|
16
19
|
|
|
17
20
|
function toDashboardError(error, defaultCode = 'DASHBOARD_ERROR') {
|
|
18
21
|
if (!error) {
|
|
@@ -1250,135 +1253,51 @@ function buildGitStatusPanelLines(snapshot) {
|
|
|
1250
1253
|
];
|
|
1251
1254
|
}
|
|
1252
1255
|
|
|
1253
|
-
function
|
|
1256
|
+
function buildGitCommitRows(snapshot) {
|
|
1254
1257
|
const git = getGitChangesSnapshot(snapshot);
|
|
1255
1258
|
if (!git.available) {
|
|
1256
1259
|
return [{
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
const branchLines = readGitCommandLines(git.rootPath, [
|
|
1264
|
-
'-c',
|
|
1265
|
-
'color.ui=false',
|
|
1266
|
-
'for-each-ref',
|
|
1267
|
-
'--sort=-committerdate',
|
|
1268
|
-
'--format=%(if)%(HEAD)%(then)*%(else) %(end) %(refname:short)',
|
|
1269
|
-
'refs/heads'
|
|
1270
|
-
], { limit: 8 });
|
|
1271
|
-
|
|
1272
|
-
const lines = [{
|
|
1273
|
-
text: 'Local branches · Remotes · Tags',
|
|
1274
|
-
color: 'cyan',
|
|
1275
|
-
bold: true
|
|
1276
|
-
}];
|
|
1277
|
-
|
|
1278
|
-
if (branchLines.length === 0) {
|
|
1279
|
-
lines.push({
|
|
1280
|
-
text: 'No local branches found',
|
|
1281
|
-
color: 'gray',
|
|
1282
|
-
bold: false
|
|
1283
|
-
});
|
|
1284
|
-
return lines;
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
for (const line of branchLines) {
|
|
1288
|
-
const isCurrent = line.startsWith('*');
|
|
1289
|
-
lines.push({
|
|
1290
|
-
text: line,
|
|
1291
|
-
color: isCurrent ? 'green' : 'gray',
|
|
1292
|
-
bold: isCurrent
|
|
1293
|
-
});
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
return lines;
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
function buildGitCommitsPanelLines(snapshot) {
|
|
1300
|
-
const git = getGitChangesSnapshot(snapshot);
|
|
1301
|
-
if (!git.available) {
|
|
1302
|
-
return [{
|
|
1303
|
-
text: 'No commit log (git unavailable)',
|
|
1304
|
-
color: 'gray',
|
|
1305
|
-
bold: false
|
|
1260
|
+
kind: 'info',
|
|
1261
|
+
key: 'git:commits:unavailable',
|
|
1262
|
+
label: 'No commit history (git unavailable)',
|
|
1263
|
+
selectable: false
|
|
1306
1264
|
}];
|
|
1307
1265
|
}
|
|
1308
1266
|
|
|
1309
|
-
const
|
|
1267
|
+
const commitLines = readGitCommandLines(git.rootPath, [
|
|
1310
1268
|
'-c',
|
|
1311
1269
|
'color.ui=false',
|
|
1312
|
-
'
|
|
1270
|
+
'log',
|
|
1313
1271
|
'--date=relative',
|
|
1314
|
-
'--pretty=format:%h %
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
const lines = [{
|
|
1318
|
-
text: 'Commits · Reflog',
|
|
1319
|
-
color: 'cyan',
|
|
1320
|
-
bold: true
|
|
1321
|
-
}];
|
|
1322
|
-
|
|
1323
|
-
if (reflogLines.length === 0) {
|
|
1324
|
-
lines.push({
|
|
1325
|
-
text: 'No reflog entries',
|
|
1326
|
-
color: 'gray',
|
|
1327
|
-
bold: false
|
|
1328
|
-
});
|
|
1329
|
-
return lines;
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
lines.push(...reflogLines.map((line) => ({
|
|
1333
|
-
text: line,
|
|
1334
|
-
color: 'gray',
|
|
1335
|
-
bold: false
|
|
1336
|
-
})));
|
|
1337
|
-
return lines;
|
|
1338
|
-
}
|
|
1272
|
+
'--pretty=format:%h %s',
|
|
1273
|
+
'--max-count=30'
|
|
1274
|
+
], { limit: 30 });
|
|
1339
1275
|
|
|
1340
|
-
|
|
1341
|
-
const git = getGitChangesSnapshot(snapshot);
|
|
1342
|
-
if (!git.available) {
|
|
1276
|
+
if (commitLines.length === 0) {
|
|
1343
1277
|
return [{
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1278
|
+
kind: 'info',
|
|
1279
|
+
key: 'git:commits:empty',
|
|
1280
|
+
label: 'No commits found',
|
|
1281
|
+
selectable: false
|
|
1347
1282
|
}];
|
|
1348
1283
|
}
|
|
1349
1284
|
|
|
1350
|
-
|
|
1351
|
-
'
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
'--pretty=format:%gd %s'
|
|
1356
|
-
], {
|
|
1357
|
-
acceptedStatuses: [0],
|
|
1358
|
-
limit: 4
|
|
1359
|
-
});
|
|
1360
|
-
|
|
1361
|
-
const lines = [{
|
|
1362
|
-
text: 'Stash',
|
|
1363
|
-
color: 'cyan',
|
|
1364
|
-
bold: true
|
|
1365
|
-
}];
|
|
1366
|
-
|
|
1367
|
-
if (stashLines.length === 0) {
|
|
1368
|
-
lines.push({
|
|
1369
|
-
text: 'No stashes',
|
|
1370
|
-
color: 'gray',
|
|
1371
|
-
bold: false
|
|
1372
|
-
});
|
|
1373
|
-
return lines;
|
|
1374
|
-
}
|
|
1285
|
+
return commitLines.map((line, index) => {
|
|
1286
|
+
const firstSpace = line.indexOf(' ');
|
|
1287
|
+
const commitHash = firstSpace > 0 ? line.slice(0, firstSpace) : '';
|
|
1288
|
+
const message = firstSpace > 0 ? line.slice(firstSpace + 1) : line;
|
|
1289
|
+
const label = commitHash ? `${commitHash} ${message}` : message;
|
|
1375
1290
|
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1291
|
+
return {
|
|
1292
|
+
kind: 'git-commit',
|
|
1293
|
+
key: `git:commit:${commitHash || index}:${index}`,
|
|
1294
|
+
label,
|
|
1295
|
+
commitHash,
|
|
1296
|
+
repoRoot: git.rootPath,
|
|
1297
|
+
previewType: 'git-commit-diff',
|
|
1298
|
+
selectable: true
|
|
1299
|
+
};
|
|
1300
|
+
});
|
|
1382
1301
|
}
|
|
1383
1302
|
|
|
1384
1303
|
function getDashboardWorktreeMeta(snapshot) {
|
|
@@ -1558,7 +1477,7 @@ function getSectionOrderForView(view, options = {}) {
|
|
|
1558
1477
|
return ['standards', 'stats', 'warnings', 'error-details'];
|
|
1559
1478
|
}
|
|
1560
1479
|
if (view === 'git') {
|
|
1561
|
-
return ['git-status', 'git-changes', 'git-
|
|
1480
|
+
return ['git-status', 'git-changes', 'git-commits', 'git-diff'];
|
|
1562
1481
|
}
|
|
1563
1482
|
const sections = [];
|
|
1564
1483
|
if (includeWorktrees) {
|
|
@@ -2578,7 +2497,7 @@ function buildInteractiveRowsLines(rows, selectedIndex, icons, width, isFocusedS
|
|
|
2578
2497
|
};
|
|
2579
2498
|
}
|
|
2580
2499
|
|
|
2581
|
-
if (row.kind === 'file' || row.kind === 'git-file') {
|
|
2500
|
+
if (row.kind === 'file' || row.kind === 'git-file' || row.kind === 'git-commit') {
|
|
2582
2501
|
const scope = row.scope ? `[${formatScope(row.scope)}] ` : '';
|
|
2583
2502
|
return {
|
|
2584
2503
|
text: truncate(`${cursor} ${icons.runFile} ${scope}${row.label}`, width),
|
|
@@ -2615,7 +2534,26 @@ function getSelectedRow(rows, selectedIndex) {
|
|
|
2615
2534
|
}
|
|
2616
2535
|
|
|
2617
2536
|
function rowToFileEntry(row) {
|
|
2618
|
-
if (!row
|
|
2537
|
+
if (!row) {
|
|
2538
|
+
return null;
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
if (row.kind === 'git-commit') {
|
|
2542
|
+
const commitHash = typeof row.commitHash === 'string' ? row.commitHash : '';
|
|
2543
|
+
if (commitHash === '') {
|
|
2544
|
+
return null;
|
|
2545
|
+
}
|
|
2546
|
+
return {
|
|
2547
|
+
label: row.label || commitHash,
|
|
2548
|
+
path: commitHash,
|
|
2549
|
+
scope: 'commit',
|
|
2550
|
+
previewType: row.previewType || 'git-commit-diff',
|
|
2551
|
+
repoRoot: row.repoRoot,
|
|
2552
|
+
commitHash
|
|
2553
|
+
};
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
if ((row.kind !== 'file' && row.kind !== 'git-file') || typeof row.path !== 'string') {
|
|
2619
2557
|
return null;
|
|
2620
2558
|
}
|
|
2621
2559
|
return {
|
|
@@ -2752,7 +2690,7 @@ function buildQuickHelpText(view, options = {}) {
|
|
|
2752
2690
|
}
|
|
2753
2691
|
parts.push('a current', 'f files');
|
|
2754
2692
|
} else if (view === 'git') {
|
|
2755
|
-
parts.push('6 status', '7 files', '8
|
|
2693
|
+
parts.push('6 status', '7 files', '8 commits', '- diff');
|
|
2756
2694
|
}
|
|
2757
2695
|
parts.push(`tab1 ${activeLabel}`);
|
|
2758
2696
|
|
|
@@ -2784,7 +2722,7 @@ function buildGitCommandStrip(view, options = {}) {
|
|
|
2784
2722
|
} else if (view === 'health') {
|
|
2785
2723
|
parts.push('s standards', 't stats', 'w warnings');
|
|
2786
2724
|
} else if (view === 'git') {
|
|
2787
|
-
parts.push('6 status', '7 files', '8
|
|
2725
|
+
parts.push('6 status', '7 files', '8 commits', '- diff', 'space preview');
|
|
2788
2726
|
}
|
|
2789
2727
|
|
|
2790
2728
|
if (previewOpen) {
|
|
@@ -2873,8 +2811,9 @@ function buildHelpOverlayLines(options = {}) {
|
|
|
2873
2811
|
`s standards | t stats | w warnings${showErrorSection ? ' | e errors' : ''}`,
|
|
2874
2812
|
{ text: '', color: undefined, bold: false },
|
|
2875
2813
|
{ text: 'Tab 5 Git Changes', color: 'yellow', bold: true },
|
|
2876
|
-
'select changed files and preview diffs',
|
|
2877
|
-
'
|
|
2814
|
+
'7 files: select changed files and preview per-file diffs',
|
|
2815
|
+
'8 commits: select a commit to preview the full commit diff',
|
|
2816
|
+
'6 status | 7 files | 8 commits | - diff',
|
|
2878
2817
|
{ text: '', color: undefined, bold: false },
|
|
2879
2818
|
{ text: `Current view: ${String(view || 'runs').toUpperCase()}`, color: 'gray', bold: false }
|
|
2880
2819
|
);
|
|
@@ -2987,6 +2926,12 @@ function colorizeMarkdownLine(line, inCodeBlock) {
|
|
|
2987
2926
|
};
|
|
2988
2927
|
}
|
|
2989
2928
|
|
|
2929
|
+
function sanitizeRenderLine(value) {
|
|
2930
|
+
const raw = String(value ?? '');
|
|
2931
|
+
const withoutAnsi = raw.replace(/\u001B\[[0-9;?]*[ -/]*[@-~]/g, '');
|
|
2932
|
+
return withoutAnsi.replace(/[\u0000-\u0008\u000B-\u001A\u001C-\u001F\u007F]/g, '');
|
|
2933
|
+
}
|
|
2934
|
+
|
|
2990
2935
|
function buildPreviewLines(fileEntry, width, scrollOffset, options = {}) {
|
|
2991
2936
|
const fullDocument = options?.fullDocument === true;
|
|
2992
2937
|
|
|
@@ -2994,10 +2939,14 @@ function buildPreviewLines(fileEntry, width, scrollOffset, options = {}) {
|
|
|
2994
2939
|
return [{ text: truncate('No file selected', width), color: 'gray', bold: false }];
|
|
2995
2940
|
}
|
|
2996
2941
|
|
|
2997
|
-
const
|
|
2942
|
+
const isGitFilePreview = fileEntry.previewType === 'git-diff';
|
|
2943
|
+
const isGitCommitPreview = fileEntry.previewType === 'git-commit-diff';
|
|
2944
|
+
const isGitPreview = isGitFilePreview || isGitCommitPreview;
|
|
2998
2945
|
let rawLines = [];
|
|
2999
2946
|
if (isGitPreview) {
|
|
3000
|
-
const diffText =
|
|
2947
|
+
const diffText = isGitCommitPreview
|
|
2948
|
+
? loadGitCommitPreview(fileEntry)
|
|
2949
|
+
: loadGitDiffPreview(fileEntry);
|
|
3001
2950
|
rawLines = String(diffText || '').split(/\r?\n/);
|
|
3002
2951
|
} else {
|
|
3003
2952
|
let content;
|
|
@@ -3014,12 +2963,18 @@ function buildPreviewLines(fileEntry, width, scrollOffset, options = {}) {
|
|
|
3014
2963
|
}
|
|
3015
2964
|
|
|
3016
2965
|
const headLine = {
|
|
3017
|
-
text: truncate(
|
|
2966
|
+
text: truncate(
|
|
2967
|
+
isGitCommitPreview
|
|
2968
|
+
? `commit: ${fileEntry.commitHash || fileEntry.path}`
|
|
2969
|
+
: `${isGitPreview ? 'diff' : 'file'}: ${fileEntry.path}`,
|
|
2970
|
+
width
|
|
2971
|
+
),
|
|
3018
2972
|
color: 'cyan',
|
|
3019
2973
|
bold: true
|
|
3020
2974
|
};
|
|
3021
2975
|
|
|
3022
|
-
const
|
|
2976
|
+
const normalizedLines = rawLines.map((line) => sanitizeRenderLine(line));
|
|
2977
|
+
const cappedLines = fullDocument ? normalizedLines : normalizedLines.slice(0, 300);
|
|
3023
2978
|
const hiddenLineCount = fullDocument ? 0 : Math.max(0, rawLines.length - cappedLines.length);
|
|
3024
2979
|
let inCodeBlock = false;
|
|
3025
2980
|
|
|
@@ -3331,7 +3286,7 @@ function createDashboardApp(deps) {
|
|
|
3331
3286
|
intents: 'intent-status',
|
|
3332
3287
|
completed: 'completed-runs',
|
|
3333
3288
|
health: 'standards',
|
|
3334
|
-
git: 'git-
|
|
3289
|
+
git: 'git-status'
|
|
3335
3290
|
});
|
|
3336
3291
|
const [selectionBySection, setSelectionBySection] = useState({
|
|
3337
3292
|
worktrees: 0,
|
|
@@ -3344,7 +3299,8 @@ function createDashboardApp(deps) {
|
|
|
3344
3299
|
stats: 0,
|
|
3345
3300
|
warnings: 0,
|
|
3346
3301
|
'error-details': 0,
|
|
3347
|
-
'git-changes': 0
|
|
3302
|
+
'git-changes': 0,
|
|
3303
|
+
'git-commits': 0
|
|
3348
3304
|
});
|
|
3349
3305
|
const [expandedGroups, setExpandedGroups] = useState({});
|
|
3350
3306
|
const [previewTarget, setPreviewTarget] = useState(null);
|
|
@@ -3508,6 +3464,9 @@ function createDashboardApp(deps) {
|
|
|
3508
3464
|
);
|
|
3509
3465
|
})()
|
|
3510
3466
|
: toLoadingRows('Loading git changes...', 'git-loading');
|
|
3467
|
+
const gitCommitRows = shouldHydrateSecondaryTabs
|
|
3468
|
+
? buildGitCommitRows(snapshot)
|
|
3469
|
+
: toLoadingRows('Loading commit history...', 'git-commits-loading');
|
|
3511
3470
|
|
|
3512
3471
|
const rowsBySection = {
|
|
3513
3472
|
worktrees: worktreeRows,
|
|
@@ -3520,7 +3479,8 @@ function createDashboardApp(deps) {
|
|
|
3520
3479
|
stats: statsRows,
|
|
3521
3480
|
warnings: warningsRows,
|
|
3522
3481
|
'error-details': errorDetailsRows,
|
|
3523
|
-
'git-changes': gitRows
|
|
3482
|
+
'git-changes': gitRows,
|
|
3483
|
+
'git-commits': gitCommitRows
|
|
3524
3484
|
};
|
|
3525
3485
|
const worktreeItems = getWorktreeItems(snapshot);
|
|
3526
3486
|
const selectedWorktree = getSelectedWorktree(snapshot);
|
|
@@ -3543,6 +3503,8 @@ function createDashboardApp(deps) {
|
|
|
3543
3503
|
const selectedFocusedFile = rowToFileEntry(selectedFocusedRow);
|
|
3544
3504
|
const selectedGitRow = getSelectedRow(gitRows, selectionBySection['git-changes'] || 0);
|
|
3545
3505
|
const selectedGitFile = rowToFileEntry(selectedGitRow);
|
|
3506
|
+
const selectedGitCommitRow = getSelectedRow(gitCommitRows, selectionBySection['git-commits'] || 0);
|
|
3507
|
+
const selectedGitCommit = rowToFileEntry(selectedGitCommitRow);
|
|
3546
3508
|
const firstGitFile = firstFileEntryFromRows(gitRows);
|
|
3547
3509
|
|
|
3548
3510
|
const refresh = useCallback(async (overrideSelectedWorktreeId = null) => {
|
|
@@ -3742,14 +3704,15 @@ function createDashboardApp(deps) {
|
|
|
3742
3704
|
stats: 0,
|
|
3743
3705
|
warnings: 0,
|
|
3744
3706
|
'error-details': 0,
|
|
3745
|
-
'git-changes': 0
|
|
3707
|
+
'git-changes': 0,
|
|
3708
|
+
'git-commits': 0
|
|
3746
3709
|
});
|
|
3747
3710
|
setSectionFocus({
|
|
3748
3711
|
runs: 'current-run',
|
|
3749
3712
|
intents: 'intent-status',
|
|
3750
3713
|
completed: 'completed-runs',
|
|
3751
3714
|
health: 'standards',
|
|
3752
|
-
git: 'git-
|
|
3715
|
+
git: 'git-status'
|
|
3753
3716
|
});
|
|
3754
3717
|
setOverviewIntentFilter('next');
|
|
3755
3718
|
setExpandedGroups({});
|
|
@@ -3785,14 +3748,15 @@ function createDashboardApp(deps) {
|
|
|
3785
3748
|
stats: 0,
|
|
3786
3749
|
warnings: 0,
|
|
3787
3750
|
'error-details': 0,
|
|
3788
|
-
'git-changes': 0
|
|
3751
|
+
'git-changes': 0,
|
|
3752
|
+
'git-commits': 0
|
|
3789
3753
|
});
|
|
3790
3754
|
setSectionFocus({
|
|
3791
3755
|
runs: 'current-run',
|
|
3792
3756
|
intents: 'intent-status',
|
|
3793
3757
|
completed: 'completed-runs',
|
|
3794
3758
|
health: 'standards',
|
|
3795
|
-
git: 'git-
|
|
3759
|
+
git: 'git-status'
|
|
3796
3760
|
});
|
|
3797
3761
|
setOverviewIntentFilter('next');
|
|
3798
3762
|
setExpandedGroups({});
|
|
@@ -3908,20 +3872,10 @@ function createDashboardApp(deps) {
|
|
|
3908
3872
|
return;
|
|
3909
3873
|
}
|
|
3910
3874
|
if (input === '8') {
|
|
3911
|
-
setSectionFocus((previous) => ({ ...previous, git: 'git-branches' }));
|
|
3912
|
-
setPaneFocus('main');
|
|
3913
|
-
return;
|
|
3914
|
-
}
|
|
3915
|
-
if (input === '9') {
|
|
3916
3875
|
setSectionFocus((previous) => ({ ...previous, git: 'git-commits' }));
|
|
3917
3876
|
setPaneFocus('main');
|
|
3918
3877
|
return;
|
|
3919
3878
|
}
|
|
3920
|
-
if (input === '0') {
|
|
3921
|
-
setSectionFocus((previous) => ({ ...previous, git: 'git-stash' }));
|
|
3922
|
-
setPaneFocus('main');
|
|
3923
|
-
return;
|
|
3924
|
-
}
|
|
3925
3879
|
if (input === '-') {
|
|
3926
3880
|
setSectionFocus((previous) => ({ ...previous, git: 'git-diff' }));
|
|
3927
3881
|
setPaneFocus('main');
|
|
@@ -4038,6 +3992,10 @@ function createDashboardApp(deps) {
|
|
|
4038
3992
|
|
|
4039
3993
|
if (input === 'o') {
|
|
4040
3994
|
const target = selectedFocusedFile || previewTarget;
|
|
3995
|
+
if (target?.previewType === 'git-commit-diff') {
|
|
3996
|
+
setStatusLine('Commit entries cannot be opened as files.');
|
|
3997
|
+
return;
|
|
3998
|
+
}
|
|
4041
3999
|
const result = openFileWithDefaultApp(target?.path);
|
|
4042
4000
|
setStatusLine(result.message);
|
|
4043
4001
|
}
|
|
@@ -4119,6 +4077,13 @@ function createDashboardApp(deps) {
|
|
|
4119
4077
|
setPreviewScroll(0);
|
|
4120
4078
|
}, [previewOpen, overlayPreviewOpen, paneFocus, selectedFocusedFile?.path, previewTarget?.path]);
|
|
4121
4079
|
|
|
4080
|
+
useEffect(() => {
|
|
4081
|
+
if (ui.view !== 'git') {
|
|
4082
|
+
return;
|
|
4083
|
+
}
|
|
4084
|
+
setPreviewScroll(0);
|
|
4085
|
+
}, [ui.view, focusedSection, selectedGitFile?.path, selectedGitCommit?.commitHash]);
|
|
4086
|
+
|
|
4122
4087
|
useEffect(() => {
|
|
4123
4088
|
if (statusLine === '') {
|
|
4124
4089
|
return undefined;
|
|
@@ -4282,16 +4247,17 @@ function createDashboardApp(deps) {
|
|
|
4282
4247
|
fullDocument: overlayPreviewOpen
|
|
4283
4248
|
})
|
|
4284
4249
|
: [];
|
|
4285
|
-
const gitInlineDiffTarget =
|
|
4250
|
+
const gitInlineDiffTarget = (
|
|
4251
|
+
focusedSection === 'git-commits'
|
|
4252
|
+
? (selectedGitCommit || selectedGitFile || firstGitFile)
|
|
4253
|
+
: (selectedGitFile || firstGitFile)
|
|
4254
|
+
) || null;
|
|
4286
4255
|
const gitInlineDiffLines = ui.view === 'git'
|
|
4287
4256
|
? buildPreviewLines(gitInlineDiffTarget, compactWidth, previewOpen && paneFocus === 'preview' ? previewScroll : 0, {
|
|
4288
4257
|
fullDocument: false
|
|
4289
4258
|
})
|
|
4290
4259
|
: [];
|
|
4291
4260
|
const gitStatusPanelLines = ui.view === 'git' ? buildGitStatusPanelLines(snapshot) : [];
|
|
4292
|
-
const gitBranchesPanelLines = ui.view === 'git' ? buildGitBranchesPanelLines(snapshot) : [];
|
|
4293
|
-
const gitCommitsPanelLines = ui.view === 'git' ? buildGitCommitsPanelLines(snapshot) : [];
|
|
4294
|
-
const gitStashPanelLines = ui.view === 'git' ? buildGitStashPanelLines(snapshot) : [];
|
|
4295
4261
|
|
|
4296
4262
|
const shortcutsOverlayLines = buildHelpOverlayLines({
|
|
4297
4263
|
view: ui.view,
|
|
@@ -4411,27 +4377,15 @@ function createDashboardApp(deps) {
|
|
|
4411
4377
|
lines: sectionLines['git-changes'],
|
|
4412
4378
|
borderColor: 'yellow'
|
|
4413
4379
|
},
|
|
4414
|
-
{
|
|
4415
|
-
key: 'git-branches',
|
|
4416
|
-
title: '[8]-Local branches',
|
|
4417
|
-
lines: gitBranchesPanelLines,
|
|
4418
|
-
borderColor: 'magenta'
|
|
4419
|
-
},
|
|
4420
4380
|
{
|
|
4421
4381
|
key: 'git-commits',
|
|
4422
|
-
title: '[
|
|
4423
|
-
lines:
|
|
4382
|
+
title: '[8]-Commits',
|
|
4383
|
+
lines: sectionLines['git-commits'],
|
|
4424
4384
|
borderColor: 'cyan'
|
|
4425
4385
|
},
|
|
4426
|
-
{
|
|
4427
|
-
key: 'git-stash',
|
|
4428
|
-
title: '[0]-Stash',
|
|
4429
|
-
lines: gitStashPanelLines,
|
|
4430
|
-
borderColor: 'blue'
|
|
4431
|
-
},
|
|
4432
4386
|
{
|
|
4433
4387
|
key: 'git-diff',
|
|
4434
|
-
title: '[-]-Unstaged changes',
|
|
4388
|
+
title: focusedSection === 'git-commits' ? '[-]-Selected commit diff' : '[-]-Unstaged changes',
|
|
4435
4389
|
lines: gitInlineDiffLines,
|
|
4436
4390
|
borderColor: 'yellow'
|
|
4437
4391
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specsmd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.57",
|
|
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": {
|