@happy-nut/monacori 0.1.23 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-main.js +16 -28
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +3 -0
- package/dist/diff.js +9 -1
- package/dist/i18n.js +12 -6
- package/dist/render.js +3 -1
- package/dist/self-update.d.ts +8 -0
- package/dist/self-update.js +18 -0
- package/dist/viewer.client.js +573 -25
- package/dist/viewer.client.min.js +1 -1
- package/dist/viewer.css +16 -9
- package/package.json +1 -1
- package/scripts/patch-electron-name.mjs +76 -26
package/dist/viewer.client.js
CHANGED
|
@@ -918,10 +918,18 @@ function allQuickItems() {
|
|
|
918
918
|
}));
|
|
919
919
|
}
|
|
920
920
|
|
|
921
|
+
// The agent's plan file, pinned to the top of Recent so a freshly-written plan is one ⌘E away — loadRecent
|
|
922
|
+
// only tracks files you've opened, so a brand-new plan would otherwise be absent until first opened.
|
|
923
|
+
var PLAN_PATH = '.monacori/plan.md';
|
|
924
|
+
function planQuickItem() {
|
|
925
|
+
var f = sourceByPath.get(PLAN_PATH);
|
|
926
|
+
if (!f) return null;
|
|
927
|
+
return { path: f.path, name: baseName(f.path), detail: 'plan', kind: 'source', recent: true, recentRank: -1 };
|
|
928
|
+
}
|
|
921
929
|
function recentItems() {
|
|
922
930
|
const all = allQuickItems();
|
|
923
931
|
const byPath = new Map(all.map((item) => [item.path, item]));
|
|
924
|
-
|
|
932
|
+
const items = loadRecent()
|
|
925
933
|
.map((item) => byPath.get(item.path) || {
|
|
926
934
|
path: item.path,
|
|
927
935
|
name: baseName(item.path),
|
|
@@ -931,6 +939,9 @@ function recentItems() {
|
|
|
931
939
|
recentRank: 0,
|
|
932
940
|
})
|
|
933
941
|
.map((item, index) => ({ ...item, recent: true, recentRank: index }));
|
|
942
|
+
const plan = planQuickItem();
|
|
943
|
+
if (plan) return [plan].concat(items.filter((it) => it.path !== plan.path));
|
|
944
|
+
return items;
|
|
934
945
|
}
|
|
935
946
|
|
|
936
947
|
function scoreQuickItem(item, query) {
|
|
@@ -1184,11 +1195,22 @@ document.addEventListener('keydown', (event) => {
|
|
|
1184
1195
|
toggleHistory();
|
|
1185
1196
|
return;
|
|
1186
1197
|
}
|
|
1198
|
+
if (typeof isHistoryOpen === 'function' && isHistoryOpen() && typeof handleHistoryKey === 'function' && handleHistoryKey(event)) return;
|
|
1187
1199
|
|
|
1188
1200
|
// Settings overlay (or a focused merged/memo dock) captures keys: stand down the rest of the global
|
|
1189
1201
|
// shortcuts (Cmd+1, F7, Cmd+[/], Cmd+B, …). Each has its own Esc + editing handlers.
|
|
1190
1202
|
if (isFloatingModalOpen()) return;
|
|
1191
1203
|
|
|
1204
|
+
// Cmd/Ctrl+A in the diff/source view selects ONLY that view's content (the browser default reached into
|
|
1205
|
+
// the sidebar + terminal). In an editable field, let the default select-within-field stand.
|
|
1206
|
+
if ((event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey && (event.key === 'a' || event.key === 'A')) {
|
|
1207
|
+
var aae = document.activeElement;
|
|
1208
|
+
if (!(aae && (aae.tagName === 'INPUT' || aae.tagName === 'TEXTAREA' || aae.tagName === 'SELECT')) && selectAllInView()) {
|
|
1209
|
+
event.preventDefault();
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1192
1214
|
if ((event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey && event.key === '1') {
|
|
1193
1215
|
event.preventDefault();
|
|
1194
1216
|
// Coming from the diff: open the file you were viewing as source so Cmd+1 lands ON it (not a stale/blank
|
|
@@ -1636,7 +1658,6 @@ window.addEventListener('beforeunload', saveUiState);
|
|
|
1636
1658
|
});
|
|
1637
1659
|
ensureDiffCursor();
|
|
1638
1660
|
})();
|
|
1639
|
-
|
|
1640
1661
|
// ===== Side-by-side diff caret (keyboard navigation across the old/new panes) =====
|
|
1641
1662
|
function isDiffViewVisible() {
|
|
1642
1663
|
var d = document.getElementById('diff-view');
|
|
@@ -2078,6 +2099,19 @@ function commentsAt(path, line) {
|
|
|
2078
2099
|
function commentKindLabel(kind) {
|
|
2079
2100
|
return kind === 'q' ? t('comment.kind.q') : t('comment.kind.c');
|
|
2080
2101
|
}
|
|
2102
|
+
// Monochrome inline icons for the two comment kinds: a help-circle for questions, a pencil for change
|
|
2103
|
+
// requests (the pencil path matches the activity-rail "c" button). No emoji, no color — stroke=currentColor
|
|
2104
|
+
// so the kind pill stays monotone (.mc-kind in viewer.css); the icon, not the color, distinguishes q vs c.
|
|
2105
|
+
function commentKindIcon(kind) {
|
|
2106
|
+
var path = kind === 'q'
|
|
2107
|
+
? '<circle cx="12" cy="12" r="9"/><path d="M9.4 9.3a2.7 2.7 0 0 1 5.2 1c0 1.8-2.6 2.4-2.6 2.4"/><line x1="12" y1="16.7" x2="12.01" y2="16.7"/>'
|
|
2108
|
+
: '<path d="M14.5 5.5l4 4"/><path d="M4.5 19.5l1-4 10-10 3 3-10 10z"/>';
|
|
2109
|
+
return '<svg class="mc-kind-ic" viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + path + '</svg>';
|
|
2110
|
+
}
|
|
2111
|
+
// Full inner HTML for a .mc-kind pill: monochrome icon + the (localized) label.
|
|
2112
|
+
function commentKindHtml(kind) {
|
|
2113
|
+
return commentKindIcon(kind) + '<span class="mc-kind-text">' + escapeHtml(commentKindLabel(kind)) + '</span>';
|
|
2114
|
+
}
|
|
2081
2115
|
function relevantLines(path) {
|
|
2082
2116
|
var set = {};
|
|
2083
2117
|
reviewComments.forEach(function (c) { if (c.path === path) set[c.line] = true; });
|
|
@@ -2181,14 +2215,14 @@ function threadHtml(path, line) {
|
|
|
2181
2215
|
commentsAt(path, line).forEach(function (c) {
|
|
2182
2216
|
if (composerState && composerState.editSeq === c.seq) return; // being edited -> rendered as the composer below
|
|
2183
2217
|
html += '<div class="mc-card mc-' + c.kind + '">'
|
|
2184
|
-
+ '<div class="mc-card-head"><span class="mc-kind">' +
|
|
2218
|
+
+ '<div class="mc-card-head"><span class="mc-kind">' + commentKindHtml(c.kind) + '</span>'
|
|
2185
2219
|
+ '<button type="button" class="mc-del" data-seq="' + c.seq + '" title="' + escapeHtml(t('composer.delete')) + '">×</button></div>'
|
|
2186
2220
|
+ '<div class="mc-card-body">' + escapeHtml(c.text) + '</div></div>';
|
|
2187
2221
|
});
|
|
2188
2222
|
if (composerState && composerState.path === path && composerState.line === line) {
|
|
2189
2223
|
var ph = composerState.kind === 'q' ? t('composer.question') : t('composer.changeRequest');
|
|
2190
2224
|
html += '<div class="mc-card mc-' + composerState.kind + ' mc-composer">'
|
|
2191
|
-
+ '<div class="mc-card-head"><span class="mc-kind">' +
|
|
2225
|
+
+ '<div class="mc-card-head"><span class="mc-kind">' + commentKindHtml(composerState.kind) + '</span><span class="mc-target" title="' + escapeHtml(composerState.path || '') + '">' + escapeHtml(composerTargetLabel(composerState)) + '</span></div>'
|
|
2192
2226
|
+ '<textarea class="mc-input" rows="3" placeholder="' + escapeHtml(ph) + '">' + escapeHtml(composerState.editText || '') + '</textarea>'
|
|
2193
2227
|
+ '<div class="mc-actions"><button type="button" class="mc-btn mc-save">' + escapeHtml(t('composer.save')) + '</button>'
|
|
2194
2228
|
+ '<button type="button" class="mc-btn mc-ghost mc-cancel">' + escapeHtml(t('composer.cancel')) + '</button>'
|
|
@@ -2380,7 +2414,7 @@ function saveComposer(ta) {
|
|
|
2380
2414
|
// Settings → Merge prompts (stored per browser in localStorage); buildMergedText + the textarea
|
|
2381
2415
|
// placeholders fall back to these when the stored value is empty.
|
|
2382
2416
|
function defaultMergePrompt(kind) {
|
|
2383
|
-
return t(kind === 'q' ? 'mergePrompt.default.q' : 'mergePrompt.default.c');
|
|
2417
|
+
return t(kind === 'q' ? 'mergePrompt.default.q' : kind === 'plan' ? 'plan.contract' : 'mergePrompt.default.c');
|
|
2384
2418
|
}
|
|
2385
2419
|
var mergePromptsKey = 'monacori-merge-prompts';
|
|
2386
2420
|
function loadMergePrompts() {
|
|
@@ -2501,6 +2535,10 @@ function buildMergedText(kind) {
|
|
|
2501
2535
|
var items = reviewComments.filter(function (c) { return c.kind === kind; });
|
|
2502
2536
|
var nl = String.fromCharCode(10);
|
|
2503
2537
|
var lines = [];
|
|
2538
|
+
// Change requests are task instructions, so they lead with the plan contract: plan first, decompose into
|
|
2539
|
+
// verifiable steps, write the plan to .monacori/plan.md (editable in Settings → Merge prompts). Questions
|
|
2540
|
+
// are read-only clarifications, so they skip it.
|
|
2541
|
+
if (kind === 'c') { lines.push(mergePromptFor('plan')); lines.push(''); }
|
|
2504
2542
|
// Per-kind agent contract heading (editable in Settings → Merge prompts; default otherwise).
|
|
2505
2543
|
lines.push(mergePromptFor(kind));
|
|
2506
2544
|
lines.push('');
|
|
@@ -2741,9 +2779,13 @@ function openMemoView() {
|
|
|
2741
2779
|
sendBtn.setAttribute('data-i18n', 'merged.sendToTerminal');
|
|
2742
2780
|
sendBtn.textContent = t('merged.sendToTerminal');
|
|
2743
2781
|
sendBtn.addEventListener('click', function () {
|
|
2782
|
+
// The memo is a task instruction, so it leads with the plan contract (plan first, decompose into
|
|
2783
|
+
// verifiable steps, write the plan to .monacori/plan.md) — same prefix change requests get. Empty memo
|
|
2784
|
+
// sends nothing extra.
|
|
2744
2785
|
var text = area.value;
|
|
2786
|
+
var planned = text.trim() ? mergePromptFor('plan') + '\n\n' + text : text;
|
|
2745
2787
|
dock.close();
|
|
2746
|
-
window.__monacoriTerminal.enterSendMode(
|
|
2788
|
+
window.__monacoriTerminal.enterSendMode(planned);
|
|
2747
2789
|
});
|
|
2748
2790
|
dock.bar.insertBefore(sendBtn, dock.bar.querySelector('.dock-max'));
|
|
2749
2791
|
}
|
|
@@ -2867,6 +2909,10 @@ refreshComments();
|
|
|
2867
2909
|
// Exception: keep focus for clipboard/selection combos (Cmd+C/V/X/A) so the terminal's own copy &
|
|
2868
2910
|
// paste keep working — blurring on Cmd+V drops the textarea focus the paste event needs.
|
|
2869
2911
|
term.attachCustomKeyEventHandler(function (e) {
|
|
2912
|
+
// F7 / Shift+F7 are diff prev/next-change nav. Don't let the terminal eat them (it would send an
|
|
2913
|
+
// escape sequence to the shell); return false so xterm ignores the key and it bubbles to the document
|
|
2914
|
+
// handler. We DON'T blur — the diff caret is a JS cursor, so nav runs while the terminal keeps focus.
|
|
2915
|
+
if (e.type === 'keydown' && e.key === 'F7' && !e.metaKey && !e.ctrlKey && !e.altKey) return false;
|
|
2870
2916
|
if (e.type === 'keydown' && e.metaKey) {
|
|
2871
2917
|
var k = (e.key || '').toLowerCase();
|
|
2872
2918
|
// The bare modifier press (Cmd goes down BEFORE the letter on macOS) must not blur — blurring
|
|
@@ -2879,6 +2925,10 @@ refreshComments();
|
|
|
2879
2925
|
// copy misses xterm's own selection, so Cmd+C silently did nothing. No selection -> fall through.
|
|
2880
2926
|
if (e.code === 'KeyC' && term.hasSelection && term.hasSelection()) { copyToClipboard(term.getSelection()); return false; }
|
|
2881
2927
|
if (e.code === 'KeyC' || e.code === 'KeyV' || e.code === 'KeyX' || e.code === 'KeyA') return true;
|
|
2928
|
+
// Cmd/Ctrl+W is the close-pane menu accelerator. onCloseTab closes the FOCUSED pane only if the
|
|
2929
|
+
// terminal still has focus — blurring here first made hasFocus() false, so the focused split pane
|
|
2930
|
+
// never closed. Release the key WITHOUT blurring so focus stays and onCloseTab can close it.
|
|
2931
|
+
if (e.code === 'KeyW') return false;
|
|
2882
2932
|
try { term.blur(); } catch (x) {}
|
|
2883
2933
|
return false;
|
|
2884
2934
|
}
|
|
@@ -3193,6 +3243,7 @@ if (window.monacoriMenu && typeof window.monacoriMenu.onCloseTab === 'function')
|
|
|
3193
3243
|
var gearBtn = document.getElementById('app-info-btn');
|
|
3194
3244
|
var flag = document.getElementById('app-update-flag');
|
|
3195
3245
|
var updateBtn = document.getElementById('app-info-update');
|
|
3246
|
+
var pta = document.getElementById('settings-prompt-plan');
|
|
3196
3247
|
var qta = document.getElementById('settings-prompt-q');
|
|
3197
3248
|
var cta = document.getElementById('settings-prompt-c');
|
|
3198
3249
|
var resetBtn = document.getElementById('settings-reset');
|
|
@@ -3205,6 +3256,7 @@ if (window.monacoriMenu && typeof window.monacoriMenu.onCloseTab === 'function')
|
|
|
3205
3256
|
}
|
|
3206
3257
|
function fill() {
|
|
3207
3258
|
var s = loadMergePrompts();
|
|
3259
|
+
if (pta) { pta.value = typeof s.plan === 'string' ? s.plan : ''; pta.placeholder = defaultMergePrompt('plan'); }
|
|
3208
3260
|
if (qta) { qta.value = typeof s.q === 'string' ? s.q : ''; qta.placeholder = defaultMergePrompt('q'); }
|
|
3209
3261
|
if (cta) { cta.value = typeof s.c === 'string' ? s.c : ''; cta.placeholder = defaultMergePrompt('c'); }
|
|
3210
3262
|
}
|
|
@@ -3240,9 +3292,10 @@ if (window.monacoriMenu && typeof window.monacoriMenu.onCloseTab === 'function')
|
|
|
3240
3292
|
}).catch(function () { updateBtn.disabled = false; if (status) status.textContent = t('settings.updateFailed'); });
|
|
3241
3293
|
});
|
|
3242
3294
|
}
|
|
3295
|
+
if (pta) pta.addEventListener('input', function () { saveMergePrompt('plan', pta.value); flash(); });
|
|
3243
3296
|
if (qta) qta.addEventListener('input', function () { saveMergePrompt('q', qta.value); flash(); });
|
|
3244
3297
|
if (cta) cta.addEventListener('input', function () { saveMergePrompt('c', cta.value); flash(); });
|
|
3245
|
-
if (resetBtn) resetBtn.addEventListener('click', function () { saveMergePrompt('q', ''); saveMergePrompt('c', ''); fill(); flash(); });
|
|
3298
|
+
if (resetBtn) resetBtn.addEventListener('click', function () { saveMergePrompt('plan', ''); saveMergePrompt('q', ''); saveMergePrompt('c', ''); fill(); flash(); });
|
|
3246
3299
|
// Terminal-bell notification toggle (default ON — persistRead returns undefined when never set).
|
|
3247
3300
|
var bellCb = document.getElementById('set-bell-notify');
|
|
3248
3301
|
if (bellCb) {
|
|
@@ -3383,15 +3436,21 @@ function restoreUiState() {
|
|
|
3383
3436
|
}
|
|
3384
3437
|
return true;
|
|
3385
3438
|
}
|
|
3386
|
-
|
|
3387
|
-
|
|
3439
|
+
// Source view. Open the saved file — or fall back to the first restored tab when that file is gone
|
|
3440
|
+
// (filtered out above) or wasn't recorded. Otherwise we'd render the tab bar but leave the body on its
|
|
3441
|
+
// "select a file" placeholder, which looks broken (a tab is clearly open). No openable tab → drop the
|
|
3442
|
+
// stale tabs and let the init fallback pick a sensible default.
|
|
3443
|
+
var openPath = (state.sourcePath && sourceByPath.has(state.sourcePath)) ? state.sourcePath : (sourceTabs[0] || '');
|
|
3444
|
+
if (openPath) {
|
|
3445
|
+
openSourceFile(openPath);
|
|
3388
3446
|
// Restore the exact source caret/scroll (openSourceFile alone resets it to the top).
|
|
3389
|
-
if (state.viewerCursor && state.viewerCursor.path ===
|
|
3447
|
+
if (state.viewerCursor && state.viewerCursor.path === openPath) {
|
|
3390
3448
|
var vc = state.viewerCursor;
|
|
3391
|
-
setTimeout(function () { try { setSourceCursor(
|
|
3449
|
+
setTimeout(function () { try { setSourceCursor(openPath, vc.lineIndex, vc.column, true, -1); } catch (e) {} }, 60);
|
|
3392
3450
|
}
|
|
3393
3451
|
return true;
|
|
3394
3452
|
}
|
|
3453
|
+
sourceTabs = [];
|
|
3395
3454
|
} catch {
|
|
3396
3455
|
sessionStorage.removeItem(uiStateKey);
|
|
3397
3456
|
}
|
|
@@ -3412,6 +3471,45 @@ function flushPendingDiffUpdate() {
|
|
|
3412
3471
|
pendingDiffUpdate = null;
|
|
3413
3472
|
try { applyDiffUpdate(u); } catch (e) {}
|
|
3414
3473
|
}
|
|
3474
|
+
// Flicker-saver for the live-watch refresh. The default path replaces the WHOLE diff DOM
|
|
3475
|
+
// (container.innerHTML = …), which re-renders the file you're looking at even when only an OFF-SCREEN file
|
|
3476
|
+
// changed. When the file set AND order are identical, reconcile per-file instead: keep every unchanged
|
|
3477
|
+
// wrapper's DOM node untouched (no flicker — including the visible one) and swap only the changed wrappers,
|
|
3478
|
+
// which are off-screen so the swap is invisible. Returns false (caller does the full innerHTML swap) for the
|
|
3479
|
+
// risky cases — files added/removed/reordered — so that proven path still handles index-shift correctly.
|
|
3480
|
+
function reconcileDiffWrappers(container, newDiffHtml, oldSigByPath, newSigByPath) {
|
|
3481
|
+
var oldW = Array.prototype.slice.call(container.querySelectorAll('.d2h-file-wrapper'));
|
|
3482
|
+
if (!oldW.length) return false;
|
|
3483
|
+
var tmp = document.createElement('div');
|
|
3484
|
+
tmp.innerHTML = newDiffHtml;
|
|
3485
|
+
var newW = Array.prototype.slice.call(tmp.querySelectorAll('.d2h-file-wrapper'));
|
|
3486
|
+
if (newW.length !== oldW.length) return false; // add/remove → full swap (global hunk indices shift)
|
|
3487
|
+
for (var i = 0; i < oldW.length; i++) {
|
|
3488
|
+
if (diffWrapperPathKey(oldW[i]) !== diffWrapperPathKey(newW[i])) return false; // reordered → full swap
|
|
3489
|
+
}
|
|
3490
|
+
for (var j = 0; j < oldW.length; j++) {
|
|
3491
|
+
var ow = oldW[j], nw = newW[j], p = diffWrapperPathKey(ow);
|
|
3492
|
+
if ((oldSigByPath.get(p) || '') !== (newSigByPath.get(p) || '')) {
|
|
3493
|
+
// Changed file (off-screen): drop in the fresh shell and clear its cached body so it refetches the
|
|
3494
|
+
// new content when it next scrolls into view. Replacing an off-screen node is invisible.
|
|
3495
|
+
var nidx = (nw.id || '').replace('file-', '');
|
|
3496
|
+
delete bodyCache[nidx];
|
|
3497
|
+
delete bodyPromise[nidx];
|
|
3498
|
+
ow.parentNode.replaceChild(nw, ow);
|
|
3499
|
+
} else {
|
|
3500
|
+
// Unchanged file: keep its DOM node (no flicker). An earlier file's changed hunk count can shift the
|
|
3501
|
+
// global numbering, so sync the index attrs and renumber a materialized body's hunk ids (id/class
|
|
3502
|
+
// changes only — invisible, no flicker).
|
|
3503
|
+
var baseChanged = ow.getAttribute('data-first-hunk') !== nw.getAttribute('data-first-hunk');
|
|
3504
|
+
ow.id = nw.id;
|
|
3505
|
+
if (nw.hasAttribute('data-first-hunk')) ow.setAttribute('data-first-hunk', nw.getAttribute('data-first-hunk'));
|
|
3506
|
+
if (nw.hasAttribute('data-hunk-count')) ow.setAttribute('data-hunk-count', nw.getAttribute('data-hunk-count'));
|
|
3507
|
+
var body = ow.querySelector('.d2h-files-diff');
|
|
3508
|
+
if (baseChanged && body && !body.hasAttribute('data-lazy')) markWrapperHunks(ow);
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
return true;
|
|
3512
|
+
}
|
|
3415
3513
|
function applyDiffUpdate(u) {
|
|
3416
3514
|
if (!u || !u.signature || u.signature === currentSignature) return false; // unchanged — nothing to do
|
|
3417
3515
|
if (composerState) { pendingDiffUpdate = u; return false; } // composing a comment — hold the refresh until close/save
|
|
@@ -3431,11 +3529,19 @@ function applyDiffUpdate(u) {
|
|
|
3431
3529
|
// open file's signature BEFORE fileSignatureByPath is rebuilt below.
|
|
3432
3530
|
var prevOpenSig = openPath ? (fileSignatureByPath.get(openPath) || '') : '';
|
|
3433
3531
|
|
|
3434
|
-
//
|
|
3435
|
-
//
|
|
3436
|
-
|
|
3532
|
+
// Fast-path: when the file set + order are unchanged, reconcile per-file so an off-screen change never
|
|
3533
|
+
// flickers the file you're viewing. Falls back to the full swap (below) for add/remove/reorder or eager.
|
|
3534
|
+
var newSigByPath = new Map((u.fileStates || []).map(function (f) { return [f.path, f.signature]; }));
|
|
3535
|
+
var fastPath = false;
|
|
3536
|
+
if (REVIEW_LAZY && container && u.diffContainer) {
|
|
3537
|
+
fastPath = reconcileDiffWrappers(container, u.diffContainer, fileSignatureByPath, newSigByPath);
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
// Full-swap path only: snapshot already-materialized bodies (keyed by path + signature) BEFORE the swap so
|
|
3541
|
+
// UNCHANGED files re-fill synchronously afterwards — otherwise the swap blanks every wrapper into an empty
|
|
3542
|
+
// lazy shell until its body reloads over IPC (the "flicker"). The fast-path keeps those nodes, so skip it.
|
|
3437
3543
|
var prevBodies = {};
|
|
3438
|
-
if (REVIEW_LAZY && container) {
|
|
3544
|
+
if (!fastPath && REVIEW_LAZY && container) {
|
|
3439
3545
|
container.querySelectorAll('.d2h-file-wrapper').forEach(function (w) {
|
|
3440
3546
|
var b = w.querySelector('.d2h-files-diff');
|
|
3441
3547
|
if (!b || b.hasAttribute('data-lazy')) return; // only bodies that are actually materialized
|
|
@@ -3444,8 +3550,9 @@ function applyDiffUpdate(u) {
|
|
|
3444
3550
|
});
|
|
3445
3551
|
}
|
|
3446
3552
|
|
|
3447
|
-
// 1) Replace the visible regions straight from the payload (no full-HTML parse)
|
|
3448
|
-
|
|
3553
|
+
// 1) Replace the visible regions straight from the payload (no full-HTML parse) — unless the fast-path
|
|
3554
|
+
// already reconciled the diff DOM in place.
|
|
3555
|
+
if (container && !fastPath) container.innerHTML = u.diffContainer || '';
|
|
3449
3556
|
var changesPanel = document.getElementById('changes-panel');
|
|
3450
3557
|
if (changesPanel) changesPanel.innerHTML = u.changesPanel || '';
|
|
3451
3558
|
// Files tree: keep the inert island (lazy, not yet opened) in sync, and refresh the live panel when it's
|
|
@@ -3507,7 +3614,7 @@ function applyDiffUpdate(u) {
|
|
|
3507
3614
|
// flicker). Runs BEFORE setupLazyDiff so the IntersectionObserver sees them already materialized and never
|
|
3508
3615
|
// re-fetches them. The fresh wrapper carries the correct data-first-hunk + file index, so materializeBody
|
|
3509
3616
|
// numbers hunks exactly as a normal lazy load would. Changed/new files stay shells and lazy-load as usual.
|
|
3510
|
-
if (REVIEW_LAZY && container) {
|
|
3617
|
+
if (!fastPath && REVIEW_LAZY && container) {
|
|
3511
3618
|
container.querySelectorAll('.d2h-file-wrapper').forEach(function (w) {
|
|
3512
3619
|
var p = diffWrapperPathKey(w);
|
|
3513
3620
|
var prev = p ? prevBodies[p] : null;
|
|
@@ -3894,6 +4001,26 @@ function isSourceViewerVisible() {
|
|
|
3894
4001
|
return Boolean(viewer && !viewer.classList.contains('hidden'));
|
|
3895
4002
|
}
|
|
3896
4003
|
|
|
4004
|
+
// Cmd/Ctrl+A scoped to the current view: select the source body, or the active diff file's content —
|
|
4005
|
+
// NOT the whole page (the browser default reached into the sidebar/terminal). Returns false if there's
|
|
4006
|
+
// no view target so the caller can fall back to the default.
|
|
4007
|
+
function selectAllInView() {
|
|
4008
|
+
var target = null;
|
|
4009
|
+
if (isSourceViewerVisible()) target = document.getElementById('source-body');
|
|
4010
|
+
else if (typeof isDiffViewVisible === 'function' && isDiffViewVisible()) {
|
|
4011
|
+
target = document.querySelector('#diff2html-container .d2h-file-wrapper:not(.df-inactive)') || document.getElementById('diff2html-container');
|
|
4012
|
+
}
|
|
4013
|
+
if (!target) return false;
|
|
4014
|
+
try {
|
|
4015
|
+
var sel = window.getSelection();
|
|
4016
|
+
var range = document.createRange();
|
|
4017
|
+
range.selectNodeContents(target);
|
|
4018
|
+
sel.removeAllRanges();
|
|
4019
|
+
sel.addRange(range);
|
|
4020
|
+
} catch (e) { return false; }
|
|
4021
|
+
return true;
|
|
4022
|
+
}
|
|
4023
|
+
|
|
3897
4024
|
function openDiffFileAtCaret() {
|
|
3898
4025
|
if (diffCursor && isDiffViewVisible()) {
|
|
3899
4026
|
const dwrap = diffWrapperByPath(diffCursor.path);
|
|
@@ -5221,6 +5348,8 @@ var historyGraph = [];
|
|
|
5221
5348
|
var historyMaxLane = 0;
|
|
5222
5349
|
var historyActiveSha = '';
|
|
5223
5350
|
var historyLoading = false;
|
|
5351
|
+
var historyFocus = 'commits'; // commits | files | diff
|
|
5352
|
+
var historyDiffState = null;
|
|
5224
5353
|
|
|
5225
5354
|
// Lane layout. Walks commits newest-first, tracking open edges (lanes) by the hash each expects next.
|
|
5226
5355
|
// Returns per-row { hash, myLane, color, topEdges, bottomEdges } using LANE INDICES + COLOR INDICES (px-free,
|
|
@@ -5334,6 +5463,33 @@ function renderHistoryList() {
|
|
|
5334
5463
|
}).join('');
|
|
5335
5464
|
}
|
|
5336
5465
|
|
|
5466
|
+
function historyVisibleRows() {
|
|
5467
|
+
var list = document.getElementById('history-list');
|
|
5468
|
+
return list ? Array.prototype.slice.call(list.querySelectorAll('.hrow')).filter(function (r) { return !r.classList.contains('hidden'); }) : [];
|
|
5469
|
+
}
|
|
5470
|
+
function selectHistoryCommit(sha, shouldScroll) {
|
|
5471
|
+
if (!sha) return;
|
|
5472
|
+
historyActiveSha = sha;
|
|
5473
|
+
var list = document.getElementById('history-list');
|
|
5474
|
+
if (!list) return;
|
|
5475
|
+
var active = null;
|
|
5476
|
+
list.querySelectorAll('.hrow').forEach(function (r) {
|
|
5477
|
+
var on = r.dataset.sha === sha;
|
|
5478
|
+
r.classList.toggle('active', on);
|
|
5479
|
+
if (on) active = r;
|
|
5480
|
+
});
|
|
5481
|
+
if (shouldScroll !== false && active && active.scrollIntoView) active.scrollIntoView({ block: 'nearest' });
|
|
5482
|
+
}
|
|
5483
|
+
function moveHistoryCommit(delta) {
|
|
5484
|
+
var rows = historyVisibleRows();
|
|
5485
|
+
if (!rows.length) return;
|
|
5486
|
+
var idx = rows.findIndex(function (r) { return r.dataset.sha === historyActiveSha; });
|
|
5487
|
+
if (idx < 0) idx = delta > 0 ? -1 : 0;
|
|
5488
|
+
idx = Math.max(0, Math.min(rows.length - 1, idx + delta));
|
|
5489
|
+
historyFocus = 'commits';
|
|
5490
|
+
selectHistoryCommit(rows[idx].dataset.sha, true);
|
|
5491
|
+
}
|
|
5492
|
+
|
|
5337
5493
|
// Text filter (subject / author). The graph only reads right on the full contiguous history, so filtering
|
|
5338
5494
|
// hides the graph column (IntelliJ does the same) and just shows matching rows.
|
|
5339
5495
|
function applyHistoryFilter() {
|
|
@@ -5348,13 +5504,15 @@ function applyHistoryFilter() {
|
|
|
5348
5504
|
var hit = !q || (c.subject + '\n' + c.author + '\n' + c.hash).toLowerCase().indexOf(q) !== -1;
|
|
5349
5505
|
rows[i].classList.toggle('hidden', !hit);
|
|
5350
5506
|
}
|
|
5507
|
+
var visible = historyVisibleRows();
|
|
5508
|
+
if (visible.length && !visible.some(function (r) { return r.dataset.sha === historyActiveSha; })) {
|
|
5509
|
+
selectHistoryCommit(visible[0].dataset.sha, false);
|
|
5510
|
+
}
|
|
5351
5511
|
}
|
|
5352
5512
|
|
|
5353
5513
|
function openHistoryCommit(sha) {
|
|
5354
5514
|
if (!sha || !window.monacoriGit) return;
|
|
5355
|
-
|
|
5356
|
-
var list = document.getElementById('history-list');
|
|
5357
|
-
if (list) list.querySelectorAll('.hrow').forEach(function (r) { r.classList.toggle('active', r.dataset.sha === sha); });
|
|
5515
|
+
selectHistoryCommit(sha, true);
|
|
5358
5516
|
var detail = document.getElementById('history-detail');
|
|
5359
5517
|
if (detail) detail.innerHTML = '<div class="quick-open-empty">' + escapeHtml(t('history.loading')) + '</div>';
|
|
5360
5518
|
Promise.resolve(window.monacoriGit.commitDiff(sha)).then(function (d) {
|
|
@@ -5373,9 +5531,335 @@ function renderHistoryDetail(d) {
|
|
|
5373
5531
|
+ '<span class="hd-date">' + escapeHtml(historyShortDate(d.date)) + '</span>'
|
|
5374
5532
|
+ historyRefBadges(d.refs) + '</div></div>';
|
|
5375
5533
|
var body = (d.diffHtml && d.diffHtml.trim())
|
|
5376
|
-
? '<div class="history-diff diff2html-container">' + d.diffHtml + '</div>'
|
|
5534
|
+
? '<div class="history-workspace"><aside id="history-files" class="history-files"></aside><div id="history-diff-container" class="history-diff diff2html-container" tabindex="0" aria-readonly="true">' + d.diffHtml + '</div></div>'
|
|
5377
5535
|
: '<div class="quick-open-empty">' + escapeHtml(t(d.isMerge ? 'history.merge' : 'history.noDiff')) + '</div>';
|
|
5378
5536
|
detail.innerHTML = head + body;
|
|
5537
|
+
setupHistoryDiffWorkspace(d.hash || historyActiveSha);
|
|
5538
|
+
}
|
|
5539
|
+
|
|
5540
|
+
function historyWrapperPathKey(w) {
|
|
5541
|
+
return (w.dataset && w.dataset.path) || ((w.querySelector('.d2h-file-name') || {}).textContent || '').trim();
|
|
5542
|
+
}
|
|
5543
|
+
function historyWrapperByPath(path) {
|
|
5544
|
+
if (!historyDiffState || !path) return null;
|
|
5545
|
+
for (var i = 0; i < historyDiffState.wrappers.length; i++) {
|
|
5546
|
+
if (historyWrapperPathKey(historyDiffState.wrappers[i]) === path) return historyDiffState.wrappers[i];
|
|
5547
|
+
}
|
|
5548
|
+
return null;
|
|
5549
|
+
}
|
|
5550
|
+
function historySideTables(wrapper) {
|
|
5551
|
+
var sides = wrapper ? wrapper.querySelectorAll('.d2h-file-side-diff') : [];
|
|
5552
|
+
return { left: sides[0] || null, right: sides[sides.length - 1] || null };
|
|
5553
|
+
}
|
|
5554
|
+
function historySideTable(wrapper, side) {
|
|
5555
|
+
var t = historySideTables(wrapper);
|
|
5556
|
+
return side === 'old' ? t.left : t.right;
|
|
5557
|
+
}
|
|
5558
|
+
function historyRowsOf(sideTable) {
|
|
5559
|
+
return diffRowsOf(sideTable);
|
|
5560
|
+
}
|
|
5561
|
+
function historyRowAt(wrapper, side, rowIndex) {
|
|
5562
|
+
return historyRowsOf(historySideTable(wrapper, side))[rowIndex] || null;
|
|
5563
|
+
}
|
|
5564
|
+
function historyDiffRowInfoFromNode(node) {
|
|
5565
|
+
var el = node ? (node.nodeType === 1 ? node : node.parentElement) : null;
|
|
5566
|
+
if (!el || !el.closest) return null;
|
|
5567
|
+
var wrapper = el.closest('.d2h-file-wrapper');
|
|
5568
|
+
var sideEl = el.closest('.d2h-file-side-diff');
|
|
5569
|
+
var row = el.closest('tr');
|
|
5570
|
+
if (!wrapper || !sideEl || !row || !isDiffCodeRow(row)) return null;
|
|
5571
|
+
var path = historyWrapperPathKey(wrapper);
|
|
5572
|
+
var t = historySideTables(wrapper);
|
|
5573
|
+
var rowIndex = historyRowsOf(sideEl).indexOf(row);
|
|
5574
|
+
if (!path || rowIndex < 0) return null;
|
|
5575
|
+
return { path: path, side: sideEl === t.left ? 'old' : 'new', rowIndex: rowIndex };
|
|
5576
|
+
}
|
|
5577
|
+
function historyFirstDiffCodeRow(wrapper, side) {
|
|
5578
|
+
var rows = historyRowsOf(historySideTable(wrapper, side));
|
|
5579
|
+
for (var i = 0; i < rows.length; i++) if (isDiffCodeRow(rows[i])) return i;
|
|
5580
|
+
return -1;
|
|
5581
|
+
}
|
|
5582
|
+
function historyFirstCodeRowOfHunk(hunkRow) {
|
|
5583
|
+
var row = hunkRow.nextElementSibling;
|
|
5584
|
+
var firstRow = null;
|
|
5585
|
+
while (row && !row.classList.contains('history-hunk') && !row.classList.contains('history-hunk-peer')) {
|
|
5586
|
+
if (row.querySelector && row.querySelector('.d2h-code-side-line')) {
|
|
5587
|
+
if (!firstRow) firstRow = row;
|
|
5588
|
+
if (isChangeCodeRow(row)) return row;
|
|
5589
|
+
}
|
|
5590
|
+
row = row.nextElementSibling;
|
|
5591
|
+
}
|
|
5592
|
+
return firstRow || hunkRow;
|
|
5593
|
+
}
|
|
5594
|
+
function historyFirstChangeRowForCaret(hunkRow) {
|
|
5595
|
+
var wrapper = hunkRow.closest('.d2h-file-wrapper');
|
|
5596
|
+
var sides = wrapper ? wrapper.querySelectorAll('.d2h-file-side-diff') : [];
|
|
5597
|
+
var hunkSideEl = hunkRow.closest('.d2h-file-side-diff');
|
|
5598
|
+
if (sides.length >= 2 && hunkSideEl) {
|
|
5599
|
+
var hunkRows = Array.prototype.slice.call(hunkSideEl.querySelectorAll('tr'));
|
|
5600
|
+
var otherEl = hunkSideEl === sides[0] ? sides[1] : sides[0];
|
|
5601
|
+
var otherRows = Array.prototype.slice.call(otherEl.querySelectorAll('tr'));
|
|
5602
|
+
var fallbackOld = null;
|
|
5603
|
+
for (var i = hunkRows.indexOf(hunkRow) + 1; i < hunkRows.length; i++) {
|
|
5604
|
+
var hr = hunkRows[i];
|
|
5605
|
+
if (hr.classList.contains('history-hunk') || hr.classList.contains('history-hunk-peer')) break;
|
|
5606
|
+
if (isChangeCodeRow(otherRows[i])) return otherRows[i];
|
|
5607
|
+
if (fallbackOld === null && isChangeCodeRow(hr)) fallbackOld = hr;
|
|
5608
|
+
}
|
|
5609
|
+
if (fallbackOld) return fallbackOld;
|
|
5610
|
+
}
|
|
5611
|
+
return historyFirstCodeRowOfHunk(hunkRow);
|
|
5612
|
+
}
|
|
5613
|
+
function setupHistoryDiffWorkspace(sha) {
|
|
5614
|
+
var container = document.getElementById('history-diff-container');
|
|
5615
|
+
var filesEl = document.getElementById('history-files');
|
|
5616
|
+
if (!container || !filesEl) { historyDiffState = null; return; }
|
|
5617
|
+
container.querySelectorAll('.d2h-code-side-linenumber, .d2h-code-linenumber, .d2h-code-line-prefix').forEach(function (el) { el.setAttribute('contenteditable', 'false'); });
|
|
5618
|
+
var wrappers = Array.prototype.slice.call(container.querySelectorAll('.d2h-file-wrapper'));
|
|
5619
|
+
var files = [], hunks = [];
|
|
5620
|
+
var hunkIndex = 0;
|
|
5621
|
+
wrappers.forEach(function (wrapper, fileIndex) {
|
|
5622
|
+
var path = historyWrapperPathKey(wrapper);
|
|
5623
|
+
if (path) wrapper.dataset.path = path;
|
|
5624
|
+
wrapper.dataset.historyFileIndex = String(fileIndex);
|
|
5625
|
+
var first = hunkIndex;
|
|
5626
|
+
var headerToIndex = new Map();
|
|
5627
|
+
Array.prototype.forEach.call(wrapper.querySelectorAll('tr'), function (row) {
|
|
5628
|
+
var header = (row.textContent || '').trim();
|
|
5629
|
+
if (header.indexOf('@@') !== 0) return;
|
|
5630
|
+
var idx = headerToIndex.get(header);
|
|
5631
|
+
if (idx === undefined) {
|
|
5632
|
+
idx = hunkIndex++;
|
|
5633
|
+
headerToIndex.set(header, idx);
|
|
5634
|
+
row.classList.add('history-hunk');
|
|
5635
|
+
hunks[idx] = { path: path, row: row };
|
|
5636
|
+
} else {
|
|
5637
|
+
row.classList.add('history-hunk-peer');
|
|
5638
|
+
}
|
|
5639
|
+
row.dataset.historyHunkIndex = String(idx);
|
|
5640
|
+
row.dataset.historyFile = path;
|
|
5641
|
+
});
|
|
5642
|
+
files.push({ path: path, hunk: first, count: hunkIndex - first });
|
|
5643
|
+
});
|
|
5644
|
+
historyDiffState = { sha: sha, container: container, filesEl: filesEl, wrappers: wrappers, files: files, hunks: hunks, currentHunk: -1, cursor: null, fileFocusIndex: 0 };
|
|
5645
|
+
filesEl.innerHTML = files.map(function (file, i) {
|
|
5646
|
+
var slash = file.path.lastIndexOf('/');
|
|
5647
|
+
var name = slash >= 0 ? file.path.slice(slash + 1) : file.path;
|
|
5648
|
+
var dir = slash >= 0 ? file.path.slice(0, slash) : '';
|
|
5649
|
+
return '<button type="button" class="file-link history-file" data-index="' + i + '" data-file="' + escapeHtml(file.path) + '" data-hunk="' + file.hunk + '">'
|
|
5650
|
+
+ '<span class="status status-modified">M</span><span class="change-name"><span class="path" title="' + escapeHtml(file.path) + '">' + escapeHtml(name) + '</span>'
|
|
5651
|
+
+ (dir ? '<span class="change-dir">' + escapeHtml(dir) + '</span>' : '') + '</span></button>';
|
|
5652
|
+
}).join('');
|
|
5653
|
+
container.addEventListener('click', function (event) {
|
|
5654
|
+
var info = historyDiffRowInfoFromNode(event.target);
|
|
5655
|
+
if (info && info.path) {
|
|
5656
|
+
historyFocus = 'diff';
|
|
5657
|
+
historySetDiffCursor(info.path, info.side, info.rowIndex, 0, false);
|
|
5658
|
+
}
|
|
5659
|
+
});
|
|
5660
|
+
if (files[0]) historyShowFile(files[0].path, files[0].hunk, false);
|
|
5661
|
+
focusHistoryDiff();
|
|
5662
|
+
}
|
|
5663
|
+
function historySetFileFocus(index) {
|
|
5664
|
+
if (!historyDiffState || !historyDiffState.files.length) return;
|
|
5665
|
+
var max = historyDiffState.files.length - 1;
|
|
5666
|
+
historyDiffState.fileFocusIndex = Math.max(0, Math.min(max, index));
|
|
5667
|
+
historyFocus = 'files';
|
|
5668
|
+
var btns = historyDiffState.filesEl.querySelectorAll('.history-file');
|
|
5669
|
+
btns.forEach(function (b, i) {
|
|
5670
|
+
b.classList.toggle('tree-focus', i === historyDiffState.fileFocusIndex);
|
|
5671
|
+
if (i === historyDiffState.fileFocusIndex && b.scrollIntoView) b.scrollIntoView({ block: 'nearest' });
|
|
5672
|
+
});
|
|
5673
|
+
}
|
|
5674
|
+
function focusHistoryFiles() {
|
|
5675
|
+
if (!historyDiffState) return;
|
|
5676
|
+
var active = historyDiffState.files.findIndex(function (f) { return f.path === historyCurrentFile(); });
|
|
5677
|
+
historySetFileFocus(active >= 0 ? active : 0);
|
|
5678
|
+
}
|
|
5679
|
+
function focusHistoryDiff() {
|
|
5680
|
+
if (!historyDiffState) return;
|
|
5681
|
+
historyFocus = 'diff';
|
|
5682
|
+
historyDiffState.filesEl.querySelectorAll('.history-file').forEach(function (b) { b.classList.remove('tree-focus'); });
|
|
5683
|
+
try { historyDiffState.container.focus(); } catch (e) {}
|
|
5684
|
+
}
|
|
5685
|
+
function historyCurrentFile() {
|
|
5686
|
+
if (!historyDiffState) return '';
|
|
5687
|
+
var active = historyDiffState.filesEl.querySelector('.history-file.active');
|
|
5688
|
+
return active ? active.dataset.file || '' : '';
|
|
5689
|
+
}
|
|
5690
|
+
function historyShowFile(path, hunkIndex, shouldScroll) {
|
|
5691
|
+
if (!historyDiffState || !path) return;
|
|
5692
|
+
historyDiffState.wrappers.forEach(function (wrapper) {
|
|
5693
|
+
wrapper.classList.toggle('df-inactive', historyWrapperPathKey(wrapper) !== path);
|
|
5694
|
+
});
|
|
5695
|
+
historyDiffState.filesEl.querySelectorAll('.history-file').forEach(function (button, i) {
|
|
5696
|
+
var on = button.dataset.file === path;
|
|
5697
|
+
button.classList.toggle('active', on);
|
|
5698
|
+
if (on) historyDiffState.fileFocusIndex = i;
|
|
5699
|
+
});
|
|
5700
|
+
var file = historyDiffState.files.find(function (f) { return f.path === path; });
|
|
5701
|
+
var target = typeof hunkIndex === 'number' && hunkIndex >= 0 ? hunkIndex : (file ? file.hunk : -1);
|
|
5702
|
+
if (target >= 0 && historyDiffState.hunks[target]) historySetActiveHunk(target, shouldScroll !== false);
|
|
5703
|
+
else historyEnsureDiffCursor(path, shouldScroll !== false);
|
|
5704
|
+
}
|
|
5705
|
+
function historyHunkPathAt(index) {
|
|
5706
|
+
return historyDiffState && historyDiffState.hunks[index] ? historyDiffState.hunks[index].path : '';
|
|
5707
|
+
}
|
|
5708
|
+
function historySetActiveHunk(index, shouldScroll) {
|
|
5709
|
+
if (!historyDiffState || !historyDiffState.hunks.length) return;
|
|
5710
|
+
var len = historyDiffState.hunks.length;
|
|
5711
|
+
var idx = ((index % len) + len) % len;
|
|
5712
|
+
var h = historyDiffState.hunks[idx];
|
|
5713
|
+
if (!h || !h.path) return;
|
|
5714
|
+
historyDiffState.currentHunk = idx;
|
|
5715
|
+
historyDiffState.wrappers.forEach(function (wrapper) {
|
|
5716
|
+
wrapper.classList.toggle('df-inactive', historyWrapperPathKey(wrapper) !== h.path);
|
|
5717
|
+
});
|
|
5718
|
+
historyDiffState.filesEl.querySelectorAll('.history-file').forEach(function (button, i) {
|
|
5719
|
+
var on = button.dataset.file === h.path;
|
|
5720
|
+
button.classList.toggle('active', on);
|
|
5721
|
+
if (on) historyDiffState.fileFocusIndex = i;
|
|
5722
|
+
});
|
|
5723
|
+
historyDiffState.container.querySelectorAll('.history-hunk.active, .history-hunk-peer.active').forEach(function (row) { row.classList.remove('active'); });
|
|
5724
|
+
historyDiffState.container.querySelectorAll('[data-history-hunk-index="' + idx + '"]').forEach(function (row) { row.classList.add('active'); });
|
|
5725
|
+
var targetRow = historyFirstChangeRowForCaret(h.row);
|
|
5726
|
+
if (targetRow) {
|
|
5727
|
+
var info = historyDiffRowInfoFromNode(targetRow);
|
|
5728
|
+
if (info) historySetDiffCursor(info.path, info.side, info.rowIndex, 0, shouldScroll);
|
|
5729
|
+
}
|
|
5730
|
+
}
|
|
5731
|
+
function historyEnsureDiffCursor(path, reveal) {
|
|
5732
|
+
var wrapper = historyWrapperByPath(path);
|
|
5733
|
+
var ri = historyFirstDiffCodeRow(wrapper, 'new');
|
|
5734
|
+
if (ri >= 0) historySetDiffCursor(path, 'new', ri, 0, reveal);
|
|
5735
|
+
}
|
|
5736
|
+
function historyClearDiffCaret() {
|
|
5737
|
+
if (!historyDiffState) return;
|
|
5738
|
+
historyDiffState.container.querySelectorAll('.mc-diff-cursor-row').forEach(function (r) { r.classList.remove('mc-diff-cursor-row'); });
|
|
5739
|
+
historyDiffState.container.querySelectorAll('.code-cursor').forEach(function (s) { var p = s.parentNode; if (p) { p.removeChild(s); if (p.normalize) p.normalize(); } });
|
|
5740
|
+
}
|
|
5741
|
+
function historyRenderDiffCaret() {
|
|
5742
|
+
historyClearDiffCaret();
|
|
5743
|
+
if (!historyDiffState || !historyDiffState.cursor) return;
|
|
5744
|
+
var c = historyDiffState.cursor;
|
|
5745
|
+
var wrapper = historyWrapperByPath(c.path);
|
|
5746
|
+
var row = wrapper ? historyRowAt(wrapper, c.side, c.rowIndex) : null;
|
|
5747
|
+
if (!row) return;
|
|
5748
|
+
row.classList.add('mc-diff-cursor-row');
|
|
5749
|
+
var ctn = diffCellCtn(row);
|
|
5750
|
+
if (!ctn) return;
|
|
5751
|
+
if ((ctn.textContent || '').length === 0) {
|
|
5752
|
+
var emptySpan = document.createElement('span');
|
|
5753
|
+
emptySpan.className = 'code-cursor';
|
|
5754
|
+
emptySpan.setAttribute('aria-hidden', 'true');
|
|
5755
|
+
emptySpan.style.position = 'absolute';
|
|
5756
|
+
ctn.appendChild(emptySpan);
|
|
5757
|
+
return;
|
|
5758
|
+
}
|
|
5759
|
+
var pos = diffCaretDomPosition(ctn, c.column);
|
|
5760
|
+
if (!pos) return;
|
|
5761
|
+
var span = document.createElement('span');
|
|
5762
|
+
span.className = 'code-cursor';
|
|
5763
|
+
span.setAttribute('aria-hidden', 'true');
|
|
5764
|
+
try {
|
|
5765
|
+
var off = pos.node.nodeType === 3 ? Math.min(pos.offset, (pos.node.textContent || '').length) : pos.offset;
|
|
5766
|
+
var range = document.createRange();
|
|
5767
|
+
range.setStart(pos.node, off);
|
|
5768
|
+
range.collapse(true);
|
|
5769
|
+
range.insertNode(span);
|
|
5770
|
+
} catch (e) {}
|
|
5771
|
+
}
|
|
5772
|
+
function historySetDiffCursor(path, side, rowIndex, column, reveal) {
|
|
5773
|
+
if (!historyDiffState) return;
|
|
5774
|
+
var wrapper = historyWrapperByPath(path);
|
|
5775
|
+
if (!wrapper) return;
|
|
5776
|
+
var rows = historyRowsOf(historySideTable(wrapper, side));
|
|
5777
|
+
if (!rows.length) return;
|
|
5778
|
+
var ri = Math.max(0, Math.min(rowIndex, rows.length - 1));
|
|
5779
|
+
var col = Math.max(0, Math.min(column, diffLineText(rows[ri]).length));
|
|
5780
|
+
historyDiffState.cursor = { path: path, side: side, rowIndex: ri, column: col };
|
|
5781
|
+
historyRenderDiffCaret();
|
|
5782
|
+
if (reveal) scrolloffReveal(rows[ri], historyDiffState.container, 0.15);
|
|
5783
|
+
}
|
|
5784
|
+
function historyMoveDiffCursor(dLine, dColumn) {
|
|
5785
|
+
if (!historyDiffState || !historyDiffState.cursor) return;
|
|
5786
|
+
var c = historyDiffState.cursor;
|
|
5787
|
+
var wrapper = historyWrapperByPath(c.path);
|
|
5788
|
+
if (!wrapper) return;
|
|
5789
|
+
var rows = historyRowsOf(historySideTable(wrapper, c.side));
|
|
5790
|
+
var ri = c.rowIndex, col = c.column;
|
|
5791
|
+
var text = diffLineText(rows[ri]);
|
|
5792
|
+
if (dColumn < 0) {
|
|
5793
|
+
if (col > 0) col -= 1;
|
|
5794
|
+
else { var p = ri - 1; while (p >= 0 && !isDiffCodeRow(rows[p])) p -= 1; if (p >= 0) { ri = p; col = diffLineText(rows[p]).length; } }
|
|
5795
|
+
} else if (dColumn > 0) {
|
|
5796
|
+
if (col < text.length) col += 1;
|
|
5797
|
+
else { var n = ri + 1; while (n < rows.length && !isDiffCodeRow(rows[n])) n += 1; if (n < rows.length) { ri = n; col = 0; } }
|
|
5798
|
+
}
|
|
5799
|
+
if (dLine !== 0) {
|
|
5800
|
+
var step = dLine > 0 ? 1 : -1;
|
|
5801
|
+
var cand = ri + step;
|
|
5802
|
+
while (cand >= 0 && cand < rows.length && !isDiffCodeRow(rows[cand])) cand += step;
|
|
5803
|
+
if (cand >= 0 && cand < rows.length) { ri = cand; col = Math.min(col, diffLineText(rows[ri]).length); }
|
|
5804
|
+
}
|
|
5805
|
+
historySetDiffCursor(c.path, c.side, ri, col, true);
|
|
5806
|
+
}
|
|
5807
|
+
function historyNextHunk(delta) {
|
|
5808
|
+
if (!historyDiffState || !historyDiffState.hunks.length) return;
|
|
5809
|
+
var base = historyDiffState.currentHunk >= 0 ? historyDiffState.currentHunk : 0;
|
|
5810
|
+
historySetActiveHunk(base + delta, true);
|
|
5811
|
+
focusHistoryDiff();
|
|
5812
|
+
}
|
|
5813
|
+
function historySelectAllDiff() {
|
|
5814
|
+
if (!historyDiffState) return;
|
|
5815
|
+
var sel = window.getSelection();
|
|
5816
|
+
if (!sel) return;
|
|
5817
|
+
var range = document.createRange();
|
|
5818
|
+
range.selectNodeContents(historyDiffState.container);
|
|
5819
|
+
sel.removeAllRanges();
|
|
5820
|
+
sel.addRange(range);
|
|
5821
|
+
}
|
|
5822
|
+
function handleHistoryDiffKey(event) {
|
|
5823
|
+
if (!historyDiffState || !historyDiffState.cursor) return false;
|
|
5824
|
+
if (event.key === 'Tab' && event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey) {
|
|
5825
|
+
var tc = historyDiffState.cursor;
|
|
5826
|
+
var tw = historyWrapperByPath(tc.path);
|
|
5827
|
+
var otherSide = tc.side === 'new' ? 'old' : 'new';
|
|
5828
|
+
var otherRow = tw ? historyRowAt(tw, otherSide, tc.rowIndex) : null;
|
|
5829
|
+
if (isDiffCodeRow(otherRow)) historySetDiffCursor(tc.path, otherSide, tc.rowIndex, Math.min(tc.column, diffLineText(otherRow).length), true);
|
|
5830
|
+
return true;
|
|
5831
|
+
}
|
|
5832
|
+
if (!event.metaKey && !event.ctrlKey && !event.altKey) {
|
|
5833
|
+
if (event.key === 'ArrowDown') { historyMoveDiffCursor(1, 0); return true; }
|
|
5834
|
+
if (event.key === 'ArrowUp') { historyMoveDiffCursor(-1, 0); return true; }
|
|
5835
|
+
if (event.key === 'ArrowLeft') { historyMoveDiffCursor(0, -1); return true; }
|
|
5836
|
+
if (event.key === 'ArrowRight') { historyMoveDiffCursor(0, 1); return true; }
|
|
5837
|
+
}
|
|
5838
|
+
if (event.altKey && !event.metaKey && !event.ctrlKey && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
|
|
5839
|
+
var wc = historyDiffState.cursor;
|
|
5840
|
+
var ww = historyWrapperByPath(wc.path);
|
|
5841
|
+
var wr = ww ? historyRowAt(ww, wc.side, wc.rowIndex) : null;
|
|
5842
|
+
var nextCol = typeof nextWordBoundary === 'function'
|
|
5843
|
+
? nextWordBoundary(diffLineText(wr), wc.column, event.key === 'ArrowRight' ? 1 : -1)
|
|
5844
|
+
: wc.column;
|
|
5845
|
+
historySetDiffCursor(wc.path, wc.side, wc.rowIndex, nextCol, true);
|
|
5846
|
+
return true;
|
|
5847
|
+
}
|
|
5848
|
+
if ((event.metaKey || event.ctrlKey) && !event.altKey && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
|
|
5849
|
+
var c = historyDiffState.cursor;
|
|
5850
|
+
var wrapper = historyWrapperByPath(c.path);
|
|
5851
|
+
var row = wrapper ? historyRowAt(wrapper, c.side, c.rowIndex) : null;
|
|
5852
|
+
var len = diffLineText(row).length;
|
|
5853
|
+
if (event.key === 'ArrowLeft') {
|
|
5854
|
+
if (c.column > 0) historySetDiffCursor(c.path, c.side, c.rowIndex, 0, true);
|
|
5855
|
+
else if (c.side === 'new') { var oldRow = historyRowAt(wrapper, 'old', c.rowIndex); if (isDiffCodeRow(oldRow)) historySetDiffCursor(c.path, 'old', c.rowIndex, diffLineText(oldRow).length, true); }
|
|
5856
|
+
} else {
|
|
5857
|
+
if (c.column < len) historySetDiffCursor(c.path, c.side, c.rowIndex, len, true);
|
|
5858
|
+
else if (c.side === 'old') { var newRow = historyRowAt(wrapper, 'new', c.rowIndex); if (isDiffCodeRow(newRow)) historySetDiffCursor(c.path, 'new', c.rowIndex, 0, true); }
|
|
5859
|
+
}
|
|
5860
|
+
return true;
|
|
5861
|
+
}
|
|
5862
|
+
return false;
|
|
5379
5863
|
}
|
|
5380
5864
|
|
|
5381
5865
|
function isHistoryOpen() {
|
|
@@ -5385,6 +5869,7 @@ function isHistoryOpen() {
|
|
|
5385
5869
|
function closeHistory() {
|
|
5386
5870
|
var v = document.getElementById('history-view');
|
|
5387
5871
|
if (v) v.classList.add('hidden');
|
|
5872
|
+
historyFocus = 'commits';
|
|
5388
5873
|
if (typeof syncRail === 'function') syncRail();
|
|
5389
5874
|
}
|
|
5390
5875
|
function openHistory() {
|
|
@@ -5397,6 +5882,8 @@ function openHistory() {
|
|
|
5397
5882
|
if (search) { search.value = ''; }
|
|
5398
5883
|
applyHistoryFilter();
|
|
5399
5884
|
historyLoading = true;
|
|
5885
|
+
historyFocus = 'commits';
|
|
5886
|
+
historyDiffState = null;
|
|
5400
5887
|
renderHistoryList();
|
|
5401
5888
|
Promise.resolve(window.monacoriGit.log({ limit: 300 })).then(function (commits) {
|
|
5402
5889
|
historyLoading = false;
|
|
@@ -5406,13 +5893,64 @@ function openHistory() {
|
|
|
5406
5893
|
renderHistoryList();
|
|
5407
5894
|
var detail = document.getElementById('history-detail');
|
|
5408
5895
|
if (detail) detail.innerHTML = '<div class="quick-open-empty">' + escapeHtml(t('history.selectCommit')) + '</div>';
|
|
5409
|
-
if (historyCommits[0])
|
|
5410
|
-
|
|
5896
|
+
if (historyCommits[0]) selectHistoryCommit(historyCommits[0].hash, false);
|
|
5897
|
+
setTimeout(function () { try { v.focus(); } catch (e) {} }, 0);
|
|
5411
5898
|
}, function () { historyLoading = false; renderHistoryList(); });
|
|
5412
5899
|
}
|
|
5413
5900
|
function toggleHistory() { if (isHistoryOpen()) closeHistory(); else openHistory(); }
|
|
5414
5901
|
if (typeof window !== 'undefined') window.__monacoriHistory = { open: openHistory, close: closeHistory, toggle: toggleHistory, isOpen: isHistoryOpen };
|
|
5415
5902
|
|
|
5903
|
+
function handleHistoryKey(e) {
|
|
5904
|
+
if (!isHistoryOpen()) return false;
|
|
5905
|
+
var ae = document.activeElement;
|
|
5906
|
+
var inSearch = ae && ae.id === 'history-search';
|
|
5907
|
+
if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey && (e.code === 'Digit9' || e.key === '9')) {
|
|
5908
|
+
e.preventDefault(); e.stopPropagation(); closeHistory(); return true;
|
|
5909
|
+
}
|
|
5910
|
+
if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); closeHistory(); return true; }
|
|
5911
|
+
if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey && e.key === '0') {
|
|
5912
|
+
if (historyDiffState) { e.preventDefault(); e.stopPropagation(); focusHistoryFiles(); return true; }
|
|
5913
|
+
}
|
|
5914
|
+
if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey && (e.key === 'a' || e.key === 'A') && historyFocus === 'diff') {
|
|
5915
|
+
e.preventDefault(); e.stopPropagation(); historySelectAllDiff(); return true;
|
|
5916
|
+
}
|
|
5917
|
+
if (e.key === 'PageDown' || e.key === 'PageUp') {
|
|
5918
|
+
var scroller = historyFocus === 'diff' && historyDiffState ? historyDiffState.container : document.getElementById('history-list');
|
|
5919
|
+
if (scroller) { e.preventDefault(); e.stopPropagation(); scroller.scrollTop += (e.key === 'PageDown' ? 0.9 : -0.9) * scroller.clientHeight; return true; }
|
|
5920
|
+
}
|
|
5921
|
+
if (e.key === 'F7' && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
|
5922
|
+
e.preventDefault(); e.stopPropagation(); historyNextHunk(e.shiftKey ? -1 : 1); return true;
|
|
5923
|
+
}
|
|
5924
|
+
if (!e.metaKey && !e.ctrlKey && !e.altKey && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
|
|
5925
|
+
e.preventDefault(); e.stopPropagation();
|
|
5926
|
+
var delta = e.key === 'ArrowDown' ? 1 : -1;
|
|
5927
|
+
if (historyFocus === 'files') historySetFileFocus((historyDiffState ? historyDiffState.fileFocusIndex : 0) + delta);
|
|
5928
|
+
else if (historyFocus === 'diff' && historyDiffState) historyMoveDiffCursor(delta, 0);
|
|
5929
|
+
else moveHistoryCommit(delta);
|
|
5930
|
+
return true;
|
|
5931
|
+
}
|
|
5932
|
+
if (!e.metaKey && !e.ctrlKey && !e.altKey && (e.key === 'ArrowLeft' || e.key === 'ArrowRight') && historyFocus === 'diff') {
|
|
5933
|
+
e.preventDefault(); e.stopPropagation(); historyMoveDiffCursor(0, e.key === 'ArrowRight' ? 1 : -1); return true;
|
|
5934
|
+
}
|
|
5935
|
+
if (!e.metaKey && !e.ctrlKey && !e.altKey && e.key === 'Enter') {
|
|
5936
|
+
e.preventDefault(); e.stopPropagation();
|
|
5937
|
+
if (historyFocus === 'diff') {
|
|
5938
|
+
return true;
|
|
5939
|
+
} else if (historyFocus === 'files' && historyDiffState) {
|
|
5940
|
+
var file = historyDiffState.files[historyDiffState.fileFocusIndex];
|
|
5941
|
+
if (file) historyShowFile(file.path, file.hunk, true);
|
|
5942
|
+
focusHistoryDiff();
|
|
5943
|
+
} else {
|
|
5944
|
+
var rows = historyVisibleRows();
|
|
5945
|
+
var row = rows.find(function (r) { return r.dataset.sha === historyActiveSha; }) || rows[0];
|
|
5946
|
+
if (row) openHistoryCommit(row.dataset.sha);
|
|
5947
|
+
}
|
|
5948
|
+
return true;
|
|
5949
|
+
}
|
|
5950
|
+
if (historyFocus === 'diff' && handleHistoryDiffKey(e)) { e.preventDefault(); e.stopPropagation(); return true; }
|
|
5951
|
+
return !inSearch && historyFocus !== 'commits';
|
|
5952
|
+
}
|
|
5953
|
+
|
|
5416
5954
|
(function wireHistory() {
|
|
5417
5955
|
var list = document.getElementById('history-list');
|
|
5418
5956
|
if (list) list.addEventListener('click', function (e) {
|
|
@@ -5424,8 +5962,18 @@ if (typeof window !== 'undefined') window.__monacoriHistory = { open: openHistor
|
|
|
5424
5962
|
var closeBtn = document.getElementById('history-close');
|
|
5425
5963
|
if (closeBtn) closeBtn.addEventListener('click', closeHistory);
|
|
5426
5964
|
var view = document.getElementById('history-view');
|
|
5965
|
+
if (view) view.setAttribute('tabindex', '-1');
|
|
5427
5966
|
if (view) view.addEventListener('keydown', function (e) {
|
|
5428
|
-
|
|
5967
|
+
handleHistoryKey(e);
|
|
5968
|
+
});
|
|
5969
|
+
var detail = document.getElementById('history-detail');
|
|
5970
|
+
if (detail) detail.addEventListener('click', function (e) {
|
|
5971
|
+
var file = e.target.closest && e.target.closest('.history-file[data-file]');
|
|
5972
|
+
if (file && historyDiffState) {
|
|
5973
|
+
e.preventDefault();
|
|
5974
|
+
historyShowFile(file.dataset.file, Number(file.dataset.hunk), true);
|
|
5975
|
+
focusHistoryDiff();
|
|
5976
|
+
}
|
|
5429
5977
|
});
|
|
5430
5978
|
})();
|
|
5431
5979
|
// ===== Go-to-line (Cmd/Ctrl+L), copy caret location (Cmd/Ctrl+K), and the sidebar row action menu. =====
|