@happy-nut/monacori 0.1.22 → 0.1.23
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 +57 -1
- package/dist/git-log.d.ts +23 -0
- package/dist/git-log.js +60 -0
- package/dist/i18n.js +34 -0
- package/dist/preload.cjs +8 -0
- package/dist/render.js +20 -0
- package/dist/viewer.client.js +360 -0
- package/dist/viewer.client.min.js +1 -1
- package/dist/viewer.css +62 -7
- package/package.json +1 -1
package/dist/viewer.client.js
CHANGED
|
@@ -1100,6 +1100,11 @@ function handleTreeKey(event) {
|
|
|
1100
1100
|
if (event.key === 'ArrowUp') { event.preventDefault(); focusTree(treeFocusIndex - 1); return true; }
|
|
1101
1101
|
if (event.key === 'PageDown') { event.preventDefault(); focusTree(treeFocusIndex + treePageSize()); return true; }
|
|
1102
1102
|
if (event.key === 'PageUp') { event.preventDefault(); focusTree(treeFocusIndex - treePageSize()); return true; }
|
|
1103
|
+
if (event.key === 'Enter' && event.altKey) {
|
|
1104
|
+
event.preventDefault();
|
|
1105
|
+
if (row && typeof openTreeRowMenu === 'function') openTreeRowMenu(row); // copy path / Finder / terminal
|
|
1106
|
+
return true;
|
|
1107
|
+
}
|
|
1103
1108
|
if (event.key === 'Enter') {
|
|
1104
1109
|
event.preventDefault();
|
|
1105
1110
|
if (row && row.classList.contains('file-link')) { row.click(); clearTreeFocus(); }
|
|
@@ -1137,6 +1142,9 @@ function handleTreeKey(event) {
|
|
|
1137
1142
|
function isFloatingModalOpen() {
|
|
1138
1143
|
var sm = document.getElementById('settings-modal');
|
|
1139
1144
|
if (sm && !sm.classList.contains('hidden')) return true;
|
|
1145
|
+
var hv = document.getElementById('history-view');
|
|
1146
|
+
if (hv && !hv.classList.contains('hidden')) return true; // history overlay owns the keys (Esc/filter/click)
|
|
1147
|
+
if (document.getElementById('goto-line')) return true; // go-to-line prompt owns the keys until Enter/Esc
|
|
1140
1148
|
// The merged/memo panels are now docked (inline), not overlays — but while one OWNS focus we still stand
|
|
1141
1149
|
// down the global nav shortcuts so typing / ▲▼ inside it isn't hijacked. Focus elsewhere -> shortcuts run.
|
|
1142
1150
|
return isDockFocused();
|
|
@@ -1170,6 +1178,12 @@ document.addEventListener('keydown', (event) => {
|
|
|
1170
1178
|
openMemoView();
|
|
1171
1179
|
return;
|
|
1172
1180
|
}
|
|
1181
|
+
// Cmd/Ctrl+9 toggles the git history view (above the focus guard so a 2nd press closes it from inside).
|
|
1182
|
+
if (!settingsUp && (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey && (event.code === 'Digit9' || event.key === '9') && typeof toggleHistory === 'function') {
|
|
1183
|
+
event.preventDefault();
|
|
1184
|
+
toggleHistory();
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1173
1187
|
|
|
1174
1188
|
// Settings overlay (or a focused merged/memo dock) captures keys: stand down the rest of the global
|
|
1175
1189
|
// shortcuts (Cmd+1, F7, Cmd+[/], Cmd+B, …). Each has its own Esc + editing handlers.
|
|
@@ -1190,6 +1204,24 @@ document.addEventListener('keydown', (event) => {
|
|
|
1190
1204
|
focusOpenFileInTree();
|
|
1191
1205
|
return;
|
|
1192
1206
|
}
|
|
1207
|
+
// Cmd/Ctrl+L = go to line (numeric prompt); Cmd/Ctrl+K = copy the caret's file:line. Skip when an
|
|
1208
|
+
// editable field owns focus (a comment composer textarea) so we don't hijack the user's typing.
|
|
1209
|
+
if ((event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey && (event.key === 'l' || event.key === 'L')) {
|
|
1210
|
+
var lkae = document.activeElement;
|
|
1211
|
+
if (!(lkae && (lkae.tagName === 'INPUT' || lkae.tagName === 'TEXTAREA' || lkae.tagName === 'SELECT'))) {
|
|
1212
|
+
event.preventDefault();
|
|
1213
|
+
openGotoLine();
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
if ((event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey && (event.key === 'k' || event.key === 'K')) {
|
|
1218
|
+
var kkae = document.activeElement;
|
|
1219
|
+
if (!(kkae && (kkae.tagName === 'INPUT' || kkae.tagName === 'TEXTAREA' || kkae.tagName === 'SELECT'))) {
|
|
1220
|
+
event.preventDefault();
|
|
1221
|
+
copyCaretLocation();
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1193
1225
|
if ((event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey && event.key === '0') {
|
|
1194
1226
|
event.preventDefault();
|
|
1195
1227
|
setTab('changes');
|
|
@@ -1483,6 +1515,7 @@ document.querySelector('.activity-rail')?.addEventListener('click', (event) => {
|
|
|
1483
1515
|
else if (view === 'files') { setTab('files'); }
|
|
1484
1516
|
else if (view === 'q' || view === 'c') { toggleMergedRail(view); }
|
|
1485
1517
|
else if (view === 'memo') { openMemoView(); } // openMemoView already toggles
|
|
1518
|
+
else if (view === 'history') { toggleHistory(); }
|
|
1486
1519
|
syncRail();
|
|
1487
1520
|
});
|
|
1488
1521
|
|
|
@@ -3266,6 +3299,8 @@ function syncRail() {
|
|
|
3266
3299
|
setOn('q', !!(merged && merged.dataset.kind === 'q'));
|
|
3267
3300
|
setOn('c', !!(merged && merged.dataset.kind === 'c'));
|
|
3268
3301
|
setOn('memo', !!document.getElementById('mc-memo-panel'));
|
|
3302
|
+
var hv = document.getElementById('history-view');
|
|
3303
|
+
setOn('history', !!(hv && !hv.classList.contains('hidden')));
|
|
3269
3304
|
}
|
|
3270
3305
|
// Rail click for the merged views toggles: a 2nd click on the open kind closes it (memo already toggles).
|
|
3271
3306
|
function toggleMergedRail(kind) {
|
|
@@ -5175,3 +5210,328 @@ function formatBytes(bytes) {
|
|
|
5175
5210
|
if (kib < 1024) return kib.toFixed(1) + ' KiB';
|
|
5176
5211
|
return (kib / 1024).toFixed(1) + ' MiB';
|
|
5177
5212
|
}
|
|
5213
|
+
// ===== Git history view (Cmd+9): commit list with graph lanes + per-commit diff. =====
|
|
5214
|
+
// Data comes from the main process (window.monacoriGit.log / .commitDiff); the lane layout is computed
|
|
5215
|
+
// here from each commit's parents. Read-only — the per-commit diff is static diff2html HTML.
|
|
5216
|
+
|
|
5217
|
+
var HISTORY_LANE_W = 14, HISTORY_DOT_R = 3.5, HISTORY_ROW_H = 24;
|
|
5218
|
+
var HISTORY_COLORS = ['#6c9fd4', '#7faf6b', '#d4a857', '#c77dd4', '#d36c6c', '#5bb6b6', '#b0884f', '#8d8df0'];
|
|
5219
|
+
var historyCommits = [];
|
|
5220
|
+
var historyGraph = [];
|
|
5221
|
+
var historyMaxLane = 0;
|
|
5222
|
+
var historyActiveSha = '';
|
|
5223
|
+
var historyLoading = false;
|
|
5224
|
+
|
|
5225
|
+
// Lane layout. Walks commits newest-first, tracking open edges (lanes) by the hash each expects next.
|
|
5226
|
+
// Returns per-row { hash, myLane, color, topEdges, bottomEdges } using LANE INDICES + COLOR INDICES (px-free,
|
|
5227
|
+
// so it's unit-testable). First parent inherits the commit's color so a branch keeps one hue down its line.
|
|
5228
|
+
function computeHistoryGraph(commits) {
|
|
5229
|
+
var lanes = []; // lane index -> hash the lane is waiting to reach (open edge from above)
|
|
5230
|
+
var colorOf = {}; // hash -> color index
|
|
5231
|
+
var next = 0;
|
|
5232
|
+
function colorFor(h) { if (colorOf[h] == null) colorOf[h] = next++; return colorOf[h]; }
|
|
5233
|
+
function freeLane() { for (var i = 0; i < lanes.length; i++) if (lanes[i] == null) return i; lanes.push(null); return lanes.length - 1; }
|
|
5234
|
+
var rows = [];
|
|
5235
|
+
var maxLane = 0;
|
|
5236
|
+
for (var ci = 0; ci < commits.length; ci++) {
|
|
5237
|
+
var c = commits[ci];
|
|
5238
|
+
var incoming = lanes.slice();
|
|
5239
|
+
var myLane = lanes.indexOf(c.hash);
|
|
5240
|
+
if (myLane === -1) myLane = freeLane();
|
|
5241
|
+
var myColor = colorFor(c.hash);
|
|
5242
|
+
lanes[myLane] = c.hash;
|
|
5243
|
+
for (var i = 0; i < lanes.length; i++) if (i !== myLane && lanes[i] === c.hash) lanes[i] = null; // merge other edges in
|
|
5244
|
+
var parents = c.parents || [];
|
|
5245
|
+
var parentLanes = {};
|
|
5246
|
+
if (parents.length === 0) {
|
|
5247
|
+
lanes[myLane] = null; // root commit — the lane ends here
|
|
5248
|
+
} else {
|
|
5249
|
+
lanes[myLane] = parents[0];
|
|
5250
|
+
if (colorOf[parents[0]] == null) colorOf[parents[0]] = myColor; // first parent keeps the hue
|
|
5251
|
+
parentLanes[myLane] = true;
|
|
5252
|
+
for (var p = 1; p < parents.length; p++) {
|
|
5253
|
+
var ex = lanes.indexOf(parents[p]);
|
|
5254
|
+
var l = ex !== -1 ? ex : freeLane();
|
|
5255
|
+
lanes[l] = parents[p];
|
|
5256
|
+
colorFor(parents[p]);
|
|
5257
|
+
parentLanes[l] = true;
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
var outgoing = lanes.slice();
|
|
5261
|
+
var topEdges = [];
|
|
5262
|
+
for (var a = 0; a < incoming.length; a++) {
|
|
5263
|
+
if (incoming[a] == null) continue;
|
|
5264
|
+
topEdges.push({ from: a, to: incoming[a] === c.hash ? myLane : a, color: colorOf[incoming[a]] });
|
|
5265
|
+
}
|
|
5266
|
+
var bottomEdges = [];
|
|
5267
|
+
for (var b = 0; b < outgoing.length; b++) {
|
|
5268
|
+
if (outgoing[b] == null) continue;
|
|
5269
|
+
bottomEdges.push({ from: parentLanes[b] ? myLane : b, to: b, color: colorOf[outgoing[b]] });
|
|
5270
|
+
}
|
|
5271
|
+
for (var m = 0; m < Math.max(incoming.length, outgoing.length); m++) {
|
|
5272
|
+
if (incoming[m] != null || outgoing[m] != null) maxLane = Math.max(maxLane, m);
|
|
5273
|
+
}
|
|
5274
|
+
maxLane = Math.max(maxLane, myLane);
|
|
5275
|
+
rows.push({ hash: c.hash, myLane: myLane, color: myColor, topEdges: topEdges, bottomEdges: bottomEdges });
|
|
5276
|
+
}
|
|
5277
|
+
rows.maxLane = maxLane;
|
|
5278
|
+
return rows;
|
|
5279
|
+
}
|
|
5280
|
+
if (typeof window !== 'undefined') window.computeHistoryGraph = computeHistoryGraph; // exposed for tests
|
|
5281
|
+
|
|
5282
|
+
function historyLaneX(l) { return 9 + l * HISTORY_LANE_W; }
|
|
5283
|
+
function historyColor(i) { return HISTORY_COLORS[i % HISTORY_COLORS.length]; }
|
|
5284
|
+
function historyRowSvg(row) {
|
|
5285
|
+
var w = historyLaneX(historyMaxLane) + 9, h = HISTORY_ROW_H, mid = h / 2;
|
|
5286
|
+
var s = '<svg class="hgraph" width="' + w + '" height="' + h + '" viewBox="0 0 ' + w + ' ' + h + '" aria-hidden="true">';
|
|
5287
|
+
var edge = function (e, y1, y2) {
|
|
5288
|
+
var x1 = historyLaneX(e.from), x2 = historyLaneX(e.to);
|
|
5289
|
+
var c1 = (y1 + y2) / 2;
|
|
5290
|
+
return '<path d="M' + x1 + ' ' + y1 + ' C ' + x1 + ' ' + c1 + ', ' + x2 + ' ' + c1 + ', ' + x2 + ' ' + y2 + '" stroke="' + historyColor(e.color) + '" fill="none" stroke-width="1.6"/>';
|
|
5291
|
+
};
|
|
5292
|
+
row.topEdges.forEach(function (e) { s += edge(e, 0, mid); });
|
|
5293
|
+
row.bottomEdges.forEach(function (e) { s += edge(e, mid, h); });
|
|
5294
|
+
s += '<circle cx="' + historyLaneX(row.myLane) + '" cy="' + mid + '" r="' + HISTORY_DOT_R + '" fill="' + historyColor(row.color) + '"/></svg>';
|
|
5295
|
+
return s;
|
|
5296
|
+
}
|
|
5297
|
+
|
|
5298
|
+
// "HEAD -> main, origin/main, tag: v1" -> small badges (HEAD/branch/tag styled distinctly).
|
|
5299
|
+
function historyRefBadges(refs) {
|
|
5300
|
+
if (!refs || !refs.trim()) return '';
|
|
5301
|
+
return refs.split(',').map(function (r) {
|
|
5302
|
+
r = r.trim();
|
|
5303
|
+
if (!r) return '';
|
|
5304
|
+
var cls = 'href-branch', label = r;
|
|
5305
|
+
if (r.indexOf('tag:') === 0) { cls = 'href-tag'; label = r.replace('tag:', '').trim(); }
|
|
5306
|
+
else if (r.indexOf('HEAD') === 0) { cls = 'href-head'; }
|
|
5307
|
+
else if (r.indexOf('origin/') === 0 || r.indexOf('/') !== -1) { cls = 'href-remote'; }
|
|
5308
|
+
return '<span class="href ' + cls + '">' + escapeHtml(label) + '</span>';
|
|
5309
|
+
}).join('');
|
|
5310
|
+
}
|
|
5311
|
+
|
|
5312
|
+
function historyShortDate(iso) {
|
|
5313
|
+
if (!iso) return '';
|
|
5314
|
+
// 2026-06-20T21:03:11+09:00 -> "2026-06-20 21:03"
|
|
5315
|
+
var m = String(iso).match(/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})/);
|
|
5316
|
+
return m ? m[1] + ' ' + m[2] : String(iso).slice(0, 16);
|
|
5317
|
+
}
|
|
5318
|
+
|
|
5319
|
+
function renderHistoryList() {
|
|
5320
|
+
var list = document.getElementById('history-list');
|
|
5321
|
+
if (!list) return;
|
|
5322
|
+
if (!historyCommits.length) {
|
|
5323
|
+
list.innerHTML = '<div class="quick-open-empty">' + escapeHtml(t(historyLoading ? 'history.loading' : 'history.empty')) + '</div>';
|
|
5324
|
+
return;
|
|
5325
|
+
}
|
|
5326
|
+
list.style.setProperty('--hgraph-w', (historyLaneX(historyMaxLane) + 9) + 'px');
|
|
5327
|
+
list.innerHTML = historyCommits.map(function (c, i) {
|
|
5328
|
+
return '<button type="button" class="hrow' + (c.hash === historyActiveSha ? ' active' : '') + '" data-sha="' + escapeHtml(c.hash) + '">'
|
|
5329
|
+
+ '<span class="hgraph-cell">' + historyRowSvg(historyGraph[i]) + '</span>'
|
|
5330
|
+
+ '<span class="hmsg">' + historyRefBadges(c.refs) + escapeHtml(c.subject) + '</span>'
|
|
5331
|
+
+ '<span class="hauthor">' + escapeHtml(c.author) + '</span>'
|
|
5332
|
+
+ '<span class="hdate">' + escapeHtml(historyShortDate(c.date)) + '</span>'
|
|
5333
|
+
+ '</button>';
|
|
5334
|
+
}).join('');
|
|
5335
|
+
}
|
|
5336
|
+
|
|
5337
|
+
// Text filter (subject / author). The graph only reads right on the full contiguous history, so filtering
|
|
5338
|
+
// hides the graph column (IntelliJ does the same) and just shows matching rows.
|
|
5339
|
+
function applyHistoryFilter() {
|
|
5340
|
+
var input = document.getElementById('history-search');
|
|
5341
|
+
var list = document.getElementById('history-list');
|
|
5342
|
+
if (!list) return;
|
|
5343
|
+
var q = (input && input.value || '').trim().toLowerCase();
|
|
5344
|
+
list.classList.toggle('filtering', q.length > 0);
|
|
5345
|
+
var rows = list.querySelectorAll('.hrow');
|
|
5346
|
+
for (var i = 0; i < rows.length; i++) {
|
|
5347
|
+
var c = historyCommits[i];
|
|
5348
|
+
var hit = !q || (c.subject + '\n' + c.author + '\n' + c.hash).toLowerCase().indexOf(q) !== -1;
|
|
5349
|
+
rows[i].classList.toggle('hidden', !hit);
|
|
5350
|
+
}
|
|
5351
|
+
}
|
|
5352
|
+
|
|
5353
|
+
function openHistoryCommit(sha) {
|
|
5354
|
+
if (!sha || !window.monacoriGit) return;
|
|
5355
|
+
historyActiveSha = sha;
|
|
5356
|
+
var list = document.getElementById('history-list');
|
|
5357
|
+
if (list) list.querySelectorAll('.hrow').forEach(function (r) { r.classList.toggle('active', r.dataset.sha === sha); });
|
|
5358
|
+
var detail = document.getElementById('history-detail');
|
|
5359
|
+
if (detail) detail.innerHTML = '<div class="quick-open-empty">' + escapeHtml(t('history.loading')) + '</div>';
|
|
5360
|
+
Promise.resolve(window.monacoriGit.commitDiff(sha)).then(function (d) {
|
|
5361
|
+
if (!d || historyActiveSha !== sha) return; // selection moved on while loading
|
|
5362
|
+
renderHistoryDetail(d);
|
|
5363
|
+
}, function () {});
|
|
5364
|
+
}
|
|
5365
|
+
|
|
5366
|
+
function renderHistoryDetail(d) {
|
|
5367
|
+
var detail = document.getElementById('history-detail');
|
|
5368
|
+
if (!detail) return;
|
|
5369
|
+
var head = '<div class="history-detail-head">'
|
|
5370
|
+
+ '<div class="hd-msg">' + escapeHtml(d.message || '').replace(/\n/g, '<br>') + '</div>'
|
|
5371
|
+
+ '<div class="hd-meta"><span class="hd-hash">' + escapeHtml((d.hash || '').slice(0, 10)) + '</span>'
|
|
5372
|
+
+ '<span class="hd-author">' + escapeHtml(d.author) + (d.email ? ' <' + escapeHtml(d.email) + '>' : '') + '</span>'
|
|
5373
|
+
+ '<span class="hd-date">' + escapeHtml(historyShortDate(d.date)) + '</span>'
|
|
5374
|
+
+ historyRefBadges(d.refs) + '</div></div>';
|
|
5375
|
+
var body = (d.diffHtml && d.diffHtml.trim())
|
|
5376
|
+
? '<div class="history-diff diff2html-container">' + d.diffHtml + '</div>'
|
|
5377
|
+
: '<div class="quick-open-empty">' + escapeHtml(t(d.isMerge ? 'history.merge' : 'history.noDiff')) + '</div>';
|
|
5378
|
+
detail.innerHTML = head + body;
|
|
5379
|
+
}
|
|
5380
|
+
|
|
5381
|
+
function isHistoryOpen() {
|
|
5382
|
+
var v = document.getElementById('history-view');
|
|
5383
|
+
return !!(v && !v.classList.contains('hidden'));
|
|
5384
|
+
}
|
|
5385
|
+
function closeHistory() {
|
|
5386
|
+
var v = document.getElementById('history-view');
|
|
5387
|
+
if (v) v.classList.add('hidden');
|
|
5388
|
+
if (typeof syncRail === 'function') syncRail();
|
|
5389
|
+
}
|
|
5390
|
+
function openHistory() {
|
|
5391
|
+
var v = document.getElementById('history-view');
|
|
5392
|
+
if (!v) return;
|
|
5393
|
+
if (!window.monacoriGit) return; // browser/serve mode: no git bridge
|
|
5394
|
+
v.classList.remove('hidden');
|
|
5395
|
+
if (typeof syncRail === 'function') syncRail();
|
|
5396
|
+
var search = document.getElementById('history-search');
|
|
5397
|
+
if (search) { search.value = ''; }
|
|
5398
|
+
applyHistoryFilter();
|
|
5399
|
+
historyLoading = true;
|
|
5400
|
+
renderHistoryList();
|
|
5401
|
+
Promise.resolve(window.monacoriGit.log({ limit: 300 })).then(function (commits) {
|
|
5402
|
+
historyLoading = false;
|
|
5403
|
+
historyCommits = Array.isArray(commits) ? commits : [];
|
|
5404
|
+
historyGraph = computeHistoryGraph(historyCommits);
|
|
5405
|
+
historyMaxLane = historyGraph.maxLane || 0;
|
|
5406
|
+
renderHistoryList();
|
|
5407
|
+
var detail = document.getElementById('history-detail');
|
|
5408
|
+
if (detail) detail.innerHTML = '<div class="quick-open-empty">' + escapeHtml(t('history.selectCommit')) + '</div>';
|
|
5409
|
+
if (historyCommits[0]) openHistoryCommit(historyCommits[0].hash); // preview the newest commit
|
|
5410
|
+
if (search) setTimeout(function () { try { search.focus(); } catch (e) {} }, 0);
|
|
5411
|
+
}, function () { historyLoading = false; renderHistoryList(); });
|
|
5412
|
+
}
|
|
5413
|
+
function toggleHistory() { if (isHistoryOpen()) closeHistory(); else openHistory(); }
|
|
5414
|
+
if (typeof window !== 'undefined') window.__monacoriHistory = { open: openHistory, close: closeHistory, toggle: toggleHistory, isOpen: isHistoryOpen };
|
|
5415
|
+
|
|
5416
|
+
(function wireHistory() {
|
|
5417
|
+
var list = document.getElementById('history-list');
|
|
5418
|
+
if (list) list.addEventListener('click', function (e) {
|
|
5419
|
+
var row = e.target.closest && e.target.closest('.hrow[data-sha]');
|
|
5420
|
+
if (row) openHistoryCommit(row.dataset.sha);
|
|
5421
|
+
});
|
|
5422
|
+
var search = document.getElementById('history-search');
|
|
5423
|
+
if (search) search.addEventListener('input', applyHistoryFilter);
|
|
5424
|
+
var closeBtn = document.getElementById('history-close');
|
|
5425
|
+
if (closeBtn) closeBtn.addEventListener('click', closeHistory);
|
|
5426
|
+
var view = document.getElementById('history-view');
|
|
5427
|
+
if (view) view.addEventListener('keydown', function (e) {
|
|
5428
|
+
if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); closeHistory(); }
|
|
5429
|
+
});
|
|
5430
|
+
})();
|
|
5431
|
+
// ===== Go-to-line (Cmd/Ctrl+L), copy caret location (Cmd/Ctrl+K), and the sidebar row action menu. =====
|
|
5432
|
+
|
|
5433
|
+
// Programmatic clipboard write. Electron's bridge is reliable on file://; navigator.clipboard is the fallback.
|
|
5434
|
+
function copyTextToClipboard(text) {
|
|
5435
|
+
try { if (window.monacoriClipboard && typeof window.monacoriClipboard.write === 'function') { window.monacoriClipboard.write(text); return true; } } catch (e) {}
|
|
5436
|
+
try { if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text); return true; } } catch (e) {}
|
|
5437
|
+
return false;
|
|
5438
|
+
}
|
|
5439
|
+
|
|
5440
|
+
// "path:line" for the current caret — source view (the painted file) or the diff caret. '' if neither.
|
|
5441
|
+
function caretLocation() {
|
|
5442
|
+
if (typeof isSourceViewerVisible === 'function' && isSourceViewerVisible()) {
|
|
5443
|
+
var sv = document.getElementById('source-viewer');
|
|
5444
|
+
var p = (sv && sv.dataset.openPath) || '';
|
|
5445
|
+
if (p && typeof viewerCursor !== 'undefined' && viewerCursor && viewerCursor.path === p) return p + ':' + (viewerCursor.lineIndex + 1);
|
|
5446
|
+
if (p) return p;
|
|
5447
|
+
}
|
|
5448
|
+
if (typeof isDiffViewVisible === 'function' && isDiffViewVisible() && typeof diffCursor !== 'undefined' && diffCursor) {
|
|
5449
|
+
var wrap = diffWrapperByPath(diffCursor.path);
|
|
5450
|
+
var row = wrap ? diffRowAt(wrap, diffCursor.side, diffCursor.rowIndex) : null;
|
|
5451
|
+
var ln = row ? diffLineNumber(row) : null;
|
|
5452
|
+
return diffCursor.path + (ln ? ':' + ln : '');
|
|
5453
|
+
}
|
|
5454
|
+
return '';
|
|
5455
|
+
}
|
|
5456
|
+
|
|
5457
|
+
// Cmd/Ctrl+K — copy the caret's file:line to the clipboard.
|
|
5458
|
+
function copyCaretLocation() {
|
|
5459
|
+
var loc = caretLocation();
|
|
5460
|
+
if (!loc) return;
|
|
5461
|
+
if (copyTextToClipboard(loc) && typeof showToast === 'function') showToast(t('goto.copied') + ' ' + loc);
|
|
5462
|
+
}
|
|
5463
|
+
|
|
5464
|
+
// Diff view: place the caret on the row whose (new, then old) line number matches n, in the active file.
|
|
5465
|
+
function gotoDiffLine(n) {
|
|
5466
|
+
var path = (typeof diffCursor !== 'undefined' && diffCursor && diffCursor.path) || '';
|
|
5467
|
+
if (!path && typeof diffActiveWrapper === 'function') {
|
|
5468
|
+
var w = diffActiveWrapper();
|
|
5469
|
+
var nm = w && w.querySelector('.d2h-file-name');
|
|
5470
|
+
if (nm && nm.textContent) path = nm.textContent.trim();
|
|
5471
|
+
}
|
|
5472
|
+
var wrap = path && diffWrapperByPath(path);
|
|
5473
|
+
if (!wrap) return;
|
|
5474
|
+
var sides = [(diffCursor && diffCursor.side) || 'new', 'new', 'old'];
|
|
5475
|
+
for (var s = 0; s < sides.length; s++) {
|
|
5476
|
+
var rows = diffRowsOf(diffSideTable(wrap, sides[s]));
|
|
5477
|
+
for (var i = 0; i < rows.length; i++) {
|
|
5478
|
+
if (diffLineNumber(rows[i]) === n) { setDiffCursor(path, sides[s], i, 0, true); return; }
|
|
5479
|
+
}
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
|
|
5483
|
+
function gotoLineJump(n) {
|
|
5484
|
+
if (!(n >= 1)) return;
|
|
5485
|
+
if (typeof isSourceViewerVisible === 'function' && isSourceViewerVisible()) {
|
|
5486
|
+
var sv = document.getElementById('source-viewer');
|
|
5487
|
+
var p = (sv && sv.dataset.openPath) || '';
|
|
5488
|
+
var f = p && sourceByPath.get(p);
|
|
5489
|
+
if (f && f.embedded && typeof f.content === 'string') {
|
|
5490
|
+
var max = f.content.split(/\r?\n/).length;
|
|
5491
|
+
setSourceCursor(p, Math.max(0, Math.min(max - 1, n - 1)), 0, true, -1);
|
|
5492
|
+
return;
|
|
5493
|
+
}
|
|
5494
|
+
}
|
|
5495
|
+
if (typeof isDiffViewVisible === 'function' && isDiffViewVisible()) gotoDiffLine(n);
|
|
5496
|
+
}
|
|
5497
|
+
|
|
5498
|
+
// Cmd/Ctrl+L — a small numeric prompt; Enter jumps, Esc closes.
|
|
5499
|
+
function openGotoLine() {
|
|
5500
|
+
if (!((typeof isSourceViewerVisible === 'function' && isSourceViewerVisible()) || (typeof isDiffViewVisible === 'function' && isDiffViewVisible()))) return;
|
|
5501
|
+
var prior = document.getElementById('goto-line');
|
|
5502
|
+
if (prior) prior.remove();
|
|
5503
|
+
var box = document.createElement('div');
|
|
5504
|
+
box.id = 'goto-line';
|
|
5505
|
+
box.className = 'goto-line';
|
|
5506
|
+
var input = document.createElement('input');
|
|
5507
|
+
input.type = 'text';
|
|
5508
|
+
input.inputMode = 'numeric';
|
|
5509
|
+
input.className = 'goto-line-input';
|
|
5510
|
+
input.placeholder = t('goto.placeholder');
|
|
5511
|
+
box.appendChild(input);
|
|
5512
|
+
document.body.appendChild(box);
|
|
5513
|
+
function close() { box.remove(); document.removeEventListener('keydown', onKey, true); }
|
|
5514
|
+
function onKey(e) {
|
|
5515
|
+
if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); close(); }
|
|
5516
|
+
else if (e.key === 'Enter') { e.preventDefault(); e.stopPropagation(); var n = parseInt(input.value, 10); close(); if (n >= 1) gotoLineJump(n); }
|
|
5517
|
+
}
|
|
5518
|
+
// Capture phase so Enter/Esc are handled here before the global keymap (which is on bubble).
|
|
5519
|
+
document.addEventListener('keydown', onKey, true);
|
|
5520
|
+
setTimeout(function () { try { input.focus(); } catch (e) {} }, 0);
|
|
5521
|
+
}
|
|
5522
|
+
|
|
5523
|
+
// Sidebar Opt+Enter: actions for a focused file row (copy path / reveal in Finder / open terminal here).
|
|
5524
|
+
function openTreeRowMenu(row) {
|
|
5525
|
+
if (!row) return;
|
|
5526
|
+
var path = row.dataset.sourceFile || row.dataset.file || '';
|
|
5527
|
+
if (!path) return;
|
|
5528
|
+
var r = row.getBoundingClientRect();
|
|
5529
|
+
var items = [
|
|
5530
|
+
{ label: t('menu.copyPath'), onSelect: function () { if (copyTextToClipboard(path) && typeof showToast === 'function') showToast(t('goto.copied') + ' ' + path); } },
|
|
5531
|
+
];
|
|
5532
|
+
if (window.monacoriApp && typeof window.monacoriApp.revealInFinder === 'function') {
|
|
5533
|
+
items.push({ label: t('menu.revealFinder'), onSelect: function () { try { window.monacoriApp.revealInFinder(path); } catch (e) {} } });
|
|
5534
|
+
items.push({ label: t('menu.openTerminal'), onSelect: function () { try { window.monacoriApp.openTerminalAt(path); } catch (e) {} } });
|
|
5535
|
+
}
|
|
5536
|
+
showCustomDropdown(Math.round(r.left + 14), Math.round(r.bottom + 2), items, Math.round(r.top));
|
|
5537
|
+
}
|