@youtyan/code-viewer 0.1.12 → 0.1.13
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/package.json +1 -1
- package/web/app.js +720 -62
- package/web/index.html +1 -0
- package/web/style.css +189 -0
- package/web-src/routes.ts +16 -1
package/web/app.js
CHANGED
|
@@ -131,6 +131,80 @@
|
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
// web-src/focus-scope.ts
|
|
135
|
+
function isEditableKeyTarget(target) {
|
|
136
|
+
if (!target)
|
|
137
|
+
return false;
|
|
138
|
+
const tag = target.tagName;
|
|
139
|
+
return tag === "INPUT" || tag === "TEXTAREA" || target.closest('[contenteditable="true"]') != null;
|
|
140
|
+
}
|
|
141
|
+
function keymapScope(target) {
|
|
142
|
+
if (target?.closest("#content"))
|
|
143
|
+
return "main";
|
|
144
|
+
if (target?.closest("#sidebar"))
|
|
145
|
+
return "sidebar";
|
|
146
|
+
return "global";
|
|
147
|
+
}
|
|
148
|
+
function prepareKeyboardPanels(doc = document) {
|
|
149
|
+
const sidebar = doc.querySelector("#sidebar");
|
|
150
|
+
const content = doc.querySelector("#content");
|
|
151
|
+
if (sidebar)
|
|
152
|
+
sidebar.tabIndex = -1;
|
|
153
|
+
if (content)
|
|
154
|
+
content.tabIndex = -1;
|
|
155
|
+
}
|
|
156
|
+
function getPanelFocusScope(doc = document) {
|
|
157
|
+
const scope = doc.body?.dataset.focusScope;
|
|
158
|
+
return scope === "sidebar" || scope === "main" ? scope : null;
|
|
159
|
+
}
|
|
160
|
+
function setPanelFocusScope(scope, doc = document) {
|
|
161
|
+
if (!doc.body)
|
|
162
|
+
return;
|
|
163
|
+
if (scope)
|
|
164
|
+
doc.body.dataset.focusScope = scope;
|
|
165
|
+
else
|
|
166
|
+
delete doc.body.dataset.focusScope;
|
|
167
|
+
}
|
|
168
|
+
function restorePanelFocusScope(scope, doc = document) {
|
|
169
|
+
if (scope === "sidebar")
|
|
170
|
+
focusSidebarPanel(doc);
|
|
171
|
+
else if (scope === "main")
|
|
172
|
+
focusMainPanel(doc);
|
|
173
|
+
else
|
|
174
|
+
setPanelFocusScope(null, doc);
|
|
175
|
+
}
|
|
176
|
+
function focusSidebarPanel(doc = document) {
|
|
177
|
+
const active = doc.querySelector("#filelist li.active[data-path], #filelist .tree-dir.active[data-dirpath]");
|
|
178
|
+
const sidebar = doc.querySelector("#sidebar");
|
|
179
|
+
(active || sidebar)?.focus({ preventScroll: true });
|
|
180
|
+
setPanelFocusScope("sidebar", doc);
|
|
181
|
+
}
|
|
182
|
+
function focusMainPanel(doc = document) {
|
|
183
|
+
doc.querySelector("#content")?.focus({ preventScroll: true });
|
|
184
|
+
setPanelFocusScope("main", doc);
|
|
185
|
+
}
|
|
186
|
+
function findMainScrollTarget(doc = document) {
|
|
187
|
+
const active = doc.activeElement;
|
|
188
|
+
const activeScroller = active?.closest("#content .gdp-source-virtual-scroller");
|
|
189
|
+
if (activeScroller && activeScroller.offsetParent !== null)
|
|
190
|
+
return activeScroller;
|
|
191
|
+
const sourceScroller = doc.querySelector("#content .gdp-source-virtual-scroller");
|
|
192
|
+
if (sourceScroller && sourceScroller.offsetParent !== null)
|
|
193
|
+
return sourceScroller;
|
|
194
|
+
const content = doc.querySelector("#content");
|
|
195
|
+
if (!content || content.offsetParent === null)
|
|
196
|
+
return null;
|
|
197
|
+
const isScrollable = (item) => {
|
|
198
|
+
if (item.offsetParent === null)
|
|
199
|
+
return false;
|
|
200
|
+
const style = doc.defaultView?.getComputedStyle(item);
|
|
201
|
+
return !!style && /(auto|scroll)/.test(style.overflowY) && item.scrollHeight > item.clientHeight;
|
|
202
|
+
};
|
|
203
|
+
const preferred = Array.from(content.querySelectorAll(".gdp-source-viewer, .gdp-markdown-layout, .gdp-markdown-preview, .d2h-files-diff, .d2h-file-diff"));
|
|
204
|
+
const scrollable = preferred.find(isScrollable) || (isScrollable(content) ? content : null) || Array.from(content.querySelectorAll("*")).find(isScrollable);
|
|
205
|
+
return scrollable || doc.scrollingElement;
|
|
206
|
+
}
|
|
207
|
+
|
|
134
208
|
// web-src/fuzzy-search.ts
|
|
135
209
|
function basenameStart(path) {
|
|
136
210
|
const slash = path.lastIndexOf("/");
|
|
@@ -276,6 +350,77 @@
|
|
|
276
350
|
return rankFuzzyPaths(query, items).map((item) => ({ ...item, mode: "fuzzy" }));
|
|
277
351
|
}
|
|
278
352
|
|
|
353
|
+
// web-src/keymap.ts
|
|
354
|
+
var DEFAULT_KEY_BINDINGS = [
|
|
355
|
+
{ action: "open-file-palette", key: "k", ctrl: true, allowEditable: true, allowPaletteOpen: true },
|
|
356
|
+
{ action: "open-file-palette", key: "k", meta: true, allowEditable: true, allowPaletteOpen: true },
|
|
357
|
+
{ action: "open-grep-palette", key: "g", ctrl: true, allowEditable: true, allowPaletteOpen: true },
|
|
358
|
+
{ action: "open-grep-palette", key: "g", meta: true, allowEditable: true, allowPaletteOpen: true },
|
|
359
|
+
{ action: "focus-file-filter", key: "/" },
|
|
360
|
+
{ action: "focus-sidebar", key: "h", ctrl: true },
|
|
361
|
+
{ action: "focus-main", key: "l", ctrl: true },
|
|
362
|
+
{ action: "cancel-source-load", key: "escape", requires: { lightboxClosed: true } },
|
|
363
|
+
{ action: "open-sidebar-item", key: "enter", scope: "sidebar" },
|
|
364
|
+
{ action: "open-sidebar-item", key: "enter", scope: "global" },
|
|
365
|
+
{ action: "sidebar-next", key: "j", scope: "sidebar" },
|
|
366
|
+
{ action: "sidebar-next", key: "j", scope: "global" },
|
|
367
|
+
{ action: "sidebar-previous", key: "k", scope: "sidebar" },
|
|
368
|
+
{ action: "sidebar-previous", key: "k", scope: "global" },
|
|
369
|
+
{ action: "sidebar-page-down", key: "d", scope: "sidebar", ctrl: true },
|
|
370
|
+
{ action: "sidebar-page-down", key: "d", scope: "global", ctrl: true },
|
|
371
|
+
{ action: "sidebar-page-up", key: "u", scope: "sidebar", ctrl: true },
|
|
372
|
+
{ action: "sidebar-page-up", key: "u", scope: "global", ctrl: true },
|
|
373
|
+
{ action: "sidebar-expand", key: "l", scope: "sidebar" },
|
|
374
|
+
{ action: "sidebar-expand", key: "l", scope: "global" },
|
|
375
|
+
{ action: "sidebar-collapse", key: "h", scope: "sidebar" },
|
|
376
|
+
{ action: "sidebar-collapse", key: "h", scope: "global" },
|
|
377
|
+
{ action: "scroll-main-down", key: "j", scope: "main" },
|
|
378
|
+
{ action: "scroll-main-up", key: "k", scope: "main" },
|
|
379
|
+
{ action: "scroll-main-page-down", key: "d", scope: "main", ctrl: true },
|
|
380
|
+
{ action: "scroll-main-page-up", key: "u", scope: "main", ctrl: true },
|
|
381
|
+
{ action: "tab-preview", key: "p", scope: "main", pendingG: true },
|
|
382
|
+
{ action: "tab-code", key: "c", scope: "main", pendingG: true },
|
|
383
|
+
{ action: "goto-top", key: "g", pendingG: true },
|
|
384
|
+
{ action: "goto-bottom", key: "g", shift: true, pendingG: true },
|
|
385
|
+
{ action: "goto-bottom", key: "g", shift: true },
|
|
386
|
+
{ action: "start-g-sequence", key: "g", scope: "sidebar" },
|
|
387
|
+
{ action: "start-g-sequence", key: "g", scope: "main" },
|
|
388
|
+
{ action: "layout-unified", key: "u" },
|
|
389
|
+
{ action: "layout-split", key: "s" },
|
|
390
|
+
{ action: "toggle-theme", key: "t" }
|
|
391
|
+
];
|
|
392
|
+
function resolveKeymapAction(event, context) {
|
|
393
|
+
const key = event.key.toLowerCase();
|
|
394
|
+
if (context.composing)
|
|
395
|
+
return null;
|
|
396
|
+
for (const binding of DEFAULT_KEY_BINDINGS) {
|
|
397
|
+
if (binding.key !== key)
|
|
398
|
+
continue;
|
|
399
|
+
if (binding.requires?.lightboxClosed && context.lightboxOpen)
|
|
400
|
+
continue;
|
|
401
|
+
if (binding.scope && binding.scope !== context.scope)
|
|
402
|
+
continue;
|
|
403
|
+
if (!!binding.pendingG !== !!context.pendingG)
|
|
404
|
+
continue;
|
|
405
|
+
if (context.paletteOpen && !binding.allowPaletteOpen)
|
|
406
|
+
continue;
|
|
407
|
+
if (context.editable && !binding.allowEditable)
|
|
408
|
+
continue;
|
|
409
|
+
if (!!binding.ctrl !== !!event.ctrlKey)
|
|
410
|
+
continue;
|
|
411
|
+
if (!!binding.meta !== !!event.metaKey)
|
|
412
|
+
continue;
|
|
413
|
+
if (!!binding.alt !== !!event.altKey)
|
|
414
|
+
continue;
|
|
415
|
+
if (!!binding.shift !== !!event.shiftKey)
|
|
416
|
+
continue;
|
|
417
|
+
if (!binding.ctrl && !binding.meta && !binding.alt && !binding.shift && (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey))
|
|
418
|
+
continue;
|
|
419
|
+
return binding.action;
|
|
420
|
+
}
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
|
|
279
424
|
// web-src/search-palette.ts
|
|
280
425
|
var PALETTE_RESULT_LIMIT = 50;
|
|
281
426
|
function limitPaletteResults(items) {
|
|
@@ -369,6 +514,13 @@
|
|
|
369
514
|
return { screen: "unknown", reason: "missing-path", rawPathname: pathname, rawSearch: search, range };
|
|
370
515
|
return { screen: "file", path, ref, range, view: target ? "blob" : "detail", ...line ? { line } : {} };
|
|
371
516
|
}
|
|
517
|
+
case "/help":
|
|
518
|
+
return {
|
|
519
|
+
screen: "help",
|
|
520
|
+
range,
|
|
521
|
+
lang: params.get("lang") || "en",
|
|
522
|
+
section: params.get("section") || "keybindings"
|
|
523
|
+
};
|
|
372
524
|
default:
|
|
373
525
|
return { screen: "unknown", reason: "unknown-pathname", rawPathname: pathname, rawSearch: search, range };
|
|
374
526
|
}
|
|
@@ -391,6 +543,15 @@
|
|
|
391
543
|
return "/file?path=" + encodeURIComponent(route.path) + "&ref=" + encodeURIComponent(route.ref || "worktree") + "&from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree") + (route.line ? "&line=" + encodeURIComponent(formatLineTarget(route.line)) : "");
|
|
392
544
|
case "diff":
|
|
393
545
|
return "/todif?from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree") + (route.path ? "&path=" + encodeURIComponent(route.path) : "") + (route.line ? "&line=" + encodeURIComponent(formatLineTarget(route.line)) : "");
|
|
546
|
+
case "help": {
|
|
547
|
+
const params = new URLSearchParams;
|
|
548
|
+
if (route.lang && route.lang !== "en")
|
|
549
|
+
params.set("lang", route.lang);
|
|
550
|
+
if (route.section && route.section !== "keybindings")
|
|
551
|
+
params.set("section", route.section);
|
|
552
|
+
const qs = params.toString();
|
|
553
|
+
return "/help" + (qs ? "?" + qs : "");
|
|
554
|
+
}
|
|
394
555
|
case "unknown":
|
|
395
556
|
return "/todif?from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree");
|
|
396
557
|
default:
|
|
@@ -6184,7 +6345,29 @@
|
|
|
6184
6345
|
return markdown;
|
|
6185
6346
|
}
|
|
6186
6347
|
function renderMarkdownHtml(textValue, target, highlighter, signal) {
|
|
6187
|
-
|
|
6348
|
+
const md = createMarkdownIt(target, highlighter, signal);
|
|
6349
|
+
const frontmatter = splitYamlFrontmatter(textValue);
|
|
6350
|
+
if (!frontmatter)
|
|
6351
|
+
return md.render(textValue);
|
|
6352
|
+
return '<div class="gdp-markdown-frontmatter" data-gdp-frontmatter="yaml">' + md.render("```yaml\n" + frontmatter.yaml + "\n```\n") + "</div>" + md.render(frontmatter.body);
|
|
6353
|
+
}
|
|
6354
|
+
function splitYamlFrontmatter(textValue) {
|
|
6355
|
+
if (!textValue.startsWith(`---
|
|
6356
|
+
`) && !textValue.startsWith(`---\r
|
|
6357
|
+
`))
|
|
6358
|
+
return null;
|
|
6359
|
+
const newline2 = textValue.startsWith(`---\r
|
|
6360
|
+
`) ? `\r
|
|
6361
|
+
` : `
|
|
6362
|
+
`;
|
|
6363
|
+
const start = 3 + newline2.length;
|
|
6364
|
+
const closing = textValue.indexOf(newline2 + "---" + newline2, start);
|
|
6365
|
+
if (closing < 0)
|
|
6366
|
+
return null;
|
|
6367
|
+
return {
|
|
6368
|
+
yaml: textValue.slice(start, closing),
|
|
6369
|
+
body: textValue.slice(closing + newline2.length + 3 + newline2.length)
|
|
6370
|
+
};
|
|
6188
6371
|
}
|
|
6189
6372
|
async function loadMarkdownHighlighter() {
|
|
6190
6373
|
if (!shikiPromise) {
|
|
@@ -6581,6 +6764,177 @@
|
|
|
6581
6764
|
let REPO_SIDEBAR_REF = null;
|
|
6582
6765
|
let REPO_SIDEBAR_LOAD_REF = null;
|
|
6583
6766
|
let REPO_SIDEBAR_LOAD = null;
|
|
6767
|
+
let PENDING_G_SCOPE = null;
|
|
6768
|
+
let PENDING_G_UNTIL = 0;
|
|
6769
|
+
let SOURCE_CURSOR = null;
|
|
6770
|
+
const SOURCE_CURSOR_TOTALS = new Map;
|
|
6771
|
+
const HELP_LANGUAGES = ["en", "ja"];
|
|
6772
|
+
const HELP_SECTIONS = ["keybindings"];
|
|
6773
|
+
const HELP_CONTENT = {
|
|
6774
|
+
en: {
|
|
6775
|
+
languageLabel: "Language",
|
|
6776
|
+
title: "Help",
|
|
6777
|
+
sections: {
|
|
6778
|
+
keybindings: {
|
|
6779
|
+
nav: "Keybindings",
|
|
6780
|
+
title: "Keyboard Shortcuts",
|
|
6781
|
+
intro: "Use these shortcuts to move between panels and navigate files without leaving the keyboard.",
|
|
6782
|
+
groups: [
|
|
6783
|
+
{ title: "Global", rows: [["Ctrl+K", "Open file palette"], ["Ctrl+G", "Open grep palette"], ["/", "Focus file filter"], ["t", "Toggle theme"]] },
|
|
6784
|
+
{ title: "Panels", rows: [["Ctrl+H", "Focus sidebar"], ["Ctrl+L", "Focus main panel"]] },
|
|
6785
|
+
{ title: "Sidebar", rows: [["j / k", "Move selection down / up"], ["Ctrl+D / Ctrl+U", "Move selection by half a page"], ["gg / Shift+G", "Move to top / bottom"], ["Enter", "Open selected item"], ["h / l", "Collapse / expand directory"]] },
|
|
6786
|
+
{ title: "Main Panel", rows: [["j / k", "Move code cursor down / up"], ["Ctrl+D / Ctrl+U", "Move code cursor by half a page"], ["gg / Shift+G", "Move code cursor to top / bottom"], ["gp / gc", "Switch to Preview / Code tab"]] }
|
|
6787
|
+
]
|
|
6788
|
+
}
|
|
6789
|
+
}
|
|
6790
|
+
},
|
|
6791
|
+
ja: {
|
|
6792
|
+
languageLabel: "言語",
|
|
6793
|
+
title: "ヘルプ",
|
|
6794
|
+
sections: {
|
|
6795
|
+
keybindings: {
|
|
6796
|
+
nav: "キーバインド",
|
|
6797
|
+
title: "キーバインド",
|
|
6798
|
+
intro: "キーボードだけでパネル移動、ファイル選択、スクロールを行うためのショートカットです。",
|
|
6799
|
+
groups: [
|
|
6800
|
+
{ title: "グローバル", rows: [["Ctrl+K", "ファイルパレットを開く"], ["Ctrl+G", "grep パレットを開く"], ["/", "ファイルフィルターへフォーカス"], ["t", "テーマ切り替え"]] },
|
|
6801
|
+
{ title: "パネル", rows: [["Ctrl+H", "サイドバーへフォーカス"], ["Ctrl+L", "メインパネルへフォーカス"]] },
|
|
6802
|
+
{ title: "サイドバー", rows: [["j / k", "選択を下 / 上へ移動"], ["Ctrl+D / Ctrl+U", "半ページ分選択を移動"], ["gg / Shift+G", "先頭 / 末尾へ移動"], ["Enter", "選択項目を開く"], ["h / l", "ディレクトリを閉じる / 開く"]] },
|
|
6803
|
+
{ title: "メインパネル", rows: [["j / k", "コードカーソルを下 / 上へ移動"], ["Ctrl+D / Ctrl+U", "コードカーソルを半ページ分移動"], ["gg / Shift+G", "コードカーソルを先頭 / 末尾へ移動"], ["gp / gc", "Preview / Code タブへ切り替え"]] }
|
|
6804
|
+
]
|
|
6805
|
+
}
|
|
6806
|
+
}
|
|
6807
|
+
}
|
|
6808
|
+
};
|
|
6809
|
+
function sourceLineScrollAmount() {
|
|
6810
|
+
const virtualRow = Array.from(document.querySelectorAll("#content .gdp-source-virtual-row")).find((item) => item.offsetParent !== null);
|
|
6811
|
+
if (virtualRow)
|
|
6812
|
+
return virtualRow.getBoundingClientRect().height || VIRTUAL_SOURCE_ROW_HEIGHT;
|
|
6813
|
+
const sourceRow = Array.from(document.querySelectorAll("#content .gdp-source-table tr")).find((item) => item.offsetParent !== null);
|
|
6814
|
+
if (sourceRow)
|
|
6815
|
+
return sourceRow.getBoundingClientRect().height || 20;
|
|
6816
|
+
const preview = document.querySelector("#content .gdp-markdown-preview:not([hidden])");
|
|
6817
|
+
const lineHeight = Number.parseFloat(getComputedStyle(preview || document.body).lineHeight);
|
|
6818
|
+
return Number.isFinite(lineHeight) && lineHeight > 0 ? lineHeight : 20;
|
|
6819
|
+
}
|
|
6820
|
+
function hasVisibleSourceCodeSurface() {
|
|
6821
|
+
return Array.from(document.querySelectorAll("#content .gdp-source-virtual-scroller, #content .gdp-source-table")).some((item) => item.offsetParent !== null);
|
|
6822
|
+
}
|
|
6823
|
+
function sourceCursorKey(target) {
|
|
6824
|
+
return target.ref + "\x00" + target.path;
|
|
6825
|
+
}
|
|
6826
|
+
function sourceCursorMatches(target, line) {
|
|
6827
|
+
return !!SOURCE_CURSOR && sourceTargetsEqual(SOURCE_CURSOR.target, target) && SOURCE_CURSOR.line === line;
|
|
6828
|
+
}
|
|
6829
|
+
function syncSourceCursorRows(target) {
|
|
6830
|
+
document.querySelectorAll("#content [data-line]").forEach((row) => {
|
|
6831
|
+
const line = Number(row.dataset.line || "0");
|
|
6832
|
+
row.classList.toggle("gdp-source-cursor", sourceCursorMatches(target, line));
|
|
6833
|
+
});
|
|
6834
|
+
}
|
|
6835
|
+
function visibleSourceLineFallback() {
|
|
6836
|
+
const scroller = findMainScrollTarget();
|
|
6837
|
+
if (scroller)
|
|
6838
|
+
return Math.max(1, Math.floor(scroller.scrollTop / VIRTUAL_SOURCE_ROW_HEIGHT) + 1);
|
|
6839
|
+
const rows = $$("#content .gdp-source-table tr[data-line]");
|
|
6840
|
+
const contentTop = document.querySelector("#content")?.getBoundingClientRect().top ?? 0;
|
|
6841
|
+
const row = rows.find((item) => item.getBoundingClientRect().bottom >= Math.max(0, contentTop));
|
|
6842
|
+
return Math.max(1, Number(row?.dataset.line || "1"));
|
|
6843
|
+
}
|
|
6844
|
+
function ensureSourceCursor(target) {
|
|
6845
|
+
if (SOURCE_CURSOR && sourceTargetsEqual(SOURCE_CURSOR.target, target))
|
|
6846
|
+
return SOURCE_CURSOR;
|
|
6847
|
+
const routeLine = lineTargetStart(currentSourceLineTarget(target));
|
|
6848
|
+
SOURCE_CURSOR = { target, line: routeLine || visibleSourceLineFallback() };
|
|
6849
|
+
syncSourceCursorRows(target);
|
|
6850
|
+
return SOURCE_CURSOR;
|
|
6851
|
+
}
|
|
6852
|
+
function resetSourceCursorForTarget(target, totalLines) {
|
|
6853
|
+
const routeLine = lineTargetStart(currentSourceLineTarget(target));
|
|
6854
|
+
SOURCE_CURSOR = { target, line: Math.max(1, Math.min(totalLines, routeLine || 1)) };
|
|
6855
|
+
}
|
|
6856
|
+
function scrollSourceCursorIntoView(cursor, edge = "nearest") {
|
|
6857
|
+
const scroller = findMainScrollTarget();
|
|
6858
|
+
if (scroller) {
|
|
6859
|
+
const top = (cursor.line - 1) * VIRTUAL_SOURCE_ROW_HEIGHT;
|
|
6860
|
+
const bottom = top + VIRTUAL_SOURCE_ROW_HEIGHT;
|
|
6861
|
+
const before = scroller.scrollTop;
|
|
6862
|
+
if (edge === "center")
|
|
6863
|
+
scroller.scrollTop = Math.max(0, top - Math.round(scroller.clientHeight / 2));
|
|
6864
|
+
else if (top < scroller.scrollTop)
|
|
6865
|
+
scroller.scrollTop = top;
|
|
6866
|
+
else if (bottom > scroller.scrollTop + scroller.clientHeight)
|
|
6867
|
+
scroller.scrollTop = bottom - scroller.clientHeight;
|
|
6868
|
+
if (scroller.scrollTop !== before)
|
|
6869
|
+
scroller.dispatchEvent(new Event("scroll"));
|
|
6870
|
+
scroller.__gdpRenderVirtualSource?.();
|
|
6871
|
+
syncSourceCursorRows(cursor.target);
|
|
6872
|
+
return;
|
|
6873
|
+
}
|
|
6874
|
+
document.querySelector('#content [data-line="' + cursor.line + '"]')?.scrollIntoView({ block: edge });
|
|
6875
|
+
}
|
|
6876
|
+
function moveSourceCursor(direction, unit, edge) {
|
|
6877
|
+
if (!hasVisibleSourceCodeSurface())
|
|
6878
|
+
return false;
|
|
6879
|
+
const target = sourceTargetFromRoute();
|
|
6880
|
+
if (!target)
|
|
6881
|
+
return false;
|
|
6882
|
+
const total = SOURCE_CURSOR_TOTALS.get(sourceCursorKey(target));
|
|
6883
|
+
if (!total)
|
|
6884
|
+
return false;
|
|
6885
|
+
const cursor = ensureSourceCursor(target);
|
|
6886
|
+
if (unit === "edge") {
|
|
6887
|
+
cursor.line = edge === "bottom" ? total : 1;
|
|
6888
|
+
syncSourceCursorRows(target);
|
|
6889
|
+
scrollSourceCursorIntoView(cursor, "center");
|
|
6890
|
+
return true;
|
|
6891
|
+
}
|
|
6892
|
+
const pageRows = Math.max(1, Math.floor((findMainScrollTarget()?.clientHeight || window.innerHeight) * 0.55 / (sourceLineScrollAmount() || VIRTUAL_SOURCE_ROW_HEIGHT)));
|
|
6893
|
+
const delta = unit === "page" ? pageRows : 1;
|
|
6894
|
+
cursor.line = Math.max(1, Math.min(total, cursor.line + direction * delta));
|
|
6895
|
+
syncSourceCursorRows(target);
|
|
6896
|
+
scrollSourceCursorIntoView(cursor);
|
|
6897
|
+
return true;
|
|
6898
|
+
}
|
|
6899
|
+
function scrollMainPanel(direction, repeated = false, unit = "line") {
|
|
6900
|
+
if (moveSourceCursor(direction, unit))
|
|
6901
|
+
return;
|
|
6902
|
+
const target = findMainScrollTarget();
|
|
6903
|
+
const viewportHeight = target?.clientHeight || document.scrollingElement?.clientHeight || window.innerHeight;
|
|
6904
|
+
const top = direction * (unit === "line" ? Math.round(sourceLineScrollAmount() || 32) : Math.round(viewportHeight * 0.55));
|
|
6905
|
+
const behavior = repeated ? "auto" : "smooth";
|
|
6906
|
+
if (target)
|
|
6907
|
+
target.scrollBy({ top, behavior });
|
|
6908
|
+
else
|
|
6909
|
+
window.scrollBy({ top, behavior });
|
|
6910
|
+
}
|
|
6911
|
+
function scrollMainToEdge(edge) {
|
|
6912
|
+
if (moveSourceCursor(edge === "bottom" ? 1 : -1, "edge", edge))
|
|
6913
|
+
return;
|
|
6914
|
+
const target = findMainScrollTarget();
|
|
6915
|
+
if (target) {
|
|
6916
|
+
target.scrollTo({ top: edge === "top" ? 0 : target.scrollHeight, behavior: "auto" });
|
|
6917
|
+
return;
|
|
6918
|
+
}
|
|
6919
|
+
const top = edge === "top" ? 0 : Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
|
|
6920
|
+
window.scrollTo({ top, behavior: "auto" });
|
|
6921
|
+
}
|
|
6922
|
+
function switchSourceTab(tab) {
|
|
6923
|
+
const tabs = document.querySelector("#content .gdp-source-tabs");
|
|
6924
|
+
if (!tabs)
|
|
6925
|
+
return false;
|
|
6926
|
+
const button = tabs.querySelector('button[data-source-tab="' + tab + '"]');
|
|
6927
|
+
if (!button || button.hidden || button.disabled)
|
|
6928
|
+
return false;
|
|
6929
|
+
button.click();
|
|
6930
|
+
focusMainPanel();
|
|
6931
|
+
return true;
|
|
6932
|
+
}
|
|
6933
|
+
function isFocusableClickTarget(target) {
|
|
6934
|
+
if (!(target instanceof Element))
|
|
6935
|
+
return false;
|
|
6936
|
+
return !!target.closest('a, button, input, textarea, select, summary, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]');
|
|
6937
|
+
}
|
|
6584
6938
|
function invalidateRepoSidebar() {
|
|
6585
6939
|
REPO_SIDEBAR_REF = null;
|
|
6586
6940
|
REPO_SIDEBAR_LOAD_REF = null;
|
|
@@ -6923,6 +7277,7 @@
|
|
|
6923
7277
|
const dir = item.dir;
|
|
6924
7278
|
const li = document.createElement("li");
|
|
6925
7279
|
li.className = "tree-dir";
|
|
7280
|
+
li.tabIndex = -1;
|
|
6926
7281
|
li.dataset.dirpath = dir.path;
|
|
6927
7282
|
if (dir.explicit)
|
|
6928
7283
|
li.dataset.explicit = "true";
|
|
@@ -6980,6 +7335,7 @@
|
|
|
6980
7335
|
li.addEventListener("click", (e2) => {
|
|
6981
7336
|
e2.stopPropagation();
|
|
6982
7337
|
onFileClick({ path: dir.path, display_path: dir.path, type: "tree", children_omitted: dir.children_omitted });
|
|
7338
|
+
focusSidebarPanel();
|
|
6983
7339
|
});
|
|
6984
7340
|
} else {
|
|
6985
7341
|
li.addEventListener("click", toggleDir);
|
|
@@ -6990,6 +7346,7 @@
|
|
|
6990
7346
|
const f2 = item.file;
|
|
6991
7347
|
const li = document.createElement("li");
|
|
6992
7348
|
li.className = "tree-file";
|
|
7349
|
+
li.tabIndex = -1;
|
|
6993
7350
|
li.dataset.path = f2.path;
|
|
6994
7351
|
li.classList.toggle("viewed", !onFileClick && STATE.viewedFiles.has(f2.path));
|
|
6995
7352
|
li.style.setProperty("--lvl-pad", 12 + depth * 14 + "px");
|
|
@@ -7014,6 +7371,7 @@
|
|
|
7014
7371
|
onFileClick(f2);
|
|
7015
7372
|
else
|
|
7016
7373
|
scrollToFile(f2.path);
|
|
7374
|
+
focusSidebarPanel();
|
|
7017
7375
|
});
|
|
7018
7376
|
if (!onFileClick)
|
|
7019
7377
|
li.addEventListener("mouseenter", () => prefetchByPath(f2.path), { passive: true });
|
|
@@ -7024,6 +7382,7 @@
|
|
|
7024
7382
|
function renderFlat(files, ul, onFileClick) {
|
|
7025
7383
|
files.forEach((f2, i2) => {
|
|
7026
7384
|
const li = document.createElement("li");
|
|
7385
|
+
li.tabIndex = -1;
|
|
7027
7386
|
li.dataset.index = String(i2);
|
|
7028
7387
|
li.dataset.path = f2.path;
|
|
7029
7388
|
li.classList.toggle("viewed", !onFileClick && STATE.viewedFiles.has(f2.path));
|
|
@@ -7045,6 +7404,7 @@
|
|
|
7045
7404
|
onFileClick(f2);
|
|
7046
7405
|
else
|
|
7047
7406
|
scrollToFile(f2.path);
|
|
7407
|
+
focusSidebarPanel();
|
|
7048
7408
|
});
|
|
7049
7409
|
if (!onFileClick)
|
|
7050
7410
|
li.addEventListener("mouseenter", () => prefetchByPath(f2.path), { passive: true });
|
|
@@ -7313,6 +7673,12 @@
|
|
|
7313
7673
|
function repoFileTargetFromRoute() {
|
|
7314
7674
|
return STATE.route.screen === "file" && STATE.route.view === "blob" ? STATE.route.ref : null;
|
|
7315
7675
|
}
|
|
7676
|
+
function helpLanguageFromRoute() {
|
|
7677
|
+
return STATE.route.screen === "help" && HELP_LANGUAGES.includes(STATE.route.lang) ? STATE.route.lang : "en";
|
|
7678
|
+
}
|
|
7679
|
+
function helpSectionFromRoute() {
|
|
7680
|
+
return STATE.route.screen === "help" && HELP_SECTIONS.includes(STATE.route.section) ? STATE.route.section : "keybindings";
|
|
7681
|
+
}
|
|
7316
7682
|
function setRoute(route, replace2 = false) {
|
|
7317
7683
|
const nextRoute = route.screen === "unknown" ? { screen: "diff", range: route.range } : route;
|
|
7318
7684
|
STATE.route = nextRoute;
|
|
@@ -7333,6 +7699,7 @@
|
|
|
7333
7699
|
document.body.classList.toggle("gdp-file-detail-page", STATE.route.screen === "file");
|
|
7334
7700
|
document.body.classList.toggle("gdp-repo-blob-page", STATE.route.screen === "file" && STATE.route.view === "blob");
|
|
7335
7701
|
document.body.classList.toggle("gdp-repo-page", STATE.route.screen === "repo");
|
|
7702
|
+
document.body.classList.toggle("gdp-help-page", STATE.route.screen === "help");
|
|
7336
7703
|
syncRepoTargetInput(repoFileTargetFromRoute() || "worktree");
|
|
7337
7704
|
}
|
|
7338
7705
|
function syncHeaderMenu() {
|
|
@@ -7347,12 +7714,104 @@
|
|
|
7347
7714
|
if (link2.dataset.route === "diff") {
|
|
7348
7715
|
link2.href = buildRoute({ screen: "diff", range: currentRange() });
|
|
7349
7716
|
}
|
|
7717
|
+
if (link2.dataset.route === "help") {
|
|
7718
|
+
link2.href = buildRoute({ screen: "help", lang: helpLanguageFromRoute(), section: helpSectionFromRoute(), range: currentRange() });
|
|
7719
|
+
}
|
|
7350
7720
|
});
|
|
7351
7721
|
}
|
|
7352
7722
|
function removeStandaloneSource() {
|
|
7353
7723
|
document.querySelectorAll(".gdp-standalone-source").forEach((el) => el.remove());
|
|
7354
7724
|
document.querySelectorAll(".gdp-repo-blob-layout").forEach((el) => el.remove());
|
|
7355
7725
|
}
|
|
7726
|
+
function renderHelpPage() {
|
|
7727
|
+
cancelActiveSourceLoad("navigation");
|
|
7728
|
+
removeStandaloneSource();
|
|
7729
|
+
LOAD_QUEUE.length = 0;
|
|
7730
|
+
const target = $("#diff");
|
|
7731
|
+
const empty = $("#empty");
|
|
7732
|
+
empty.classList.add("hidden");
|
|
7733
|
+
$("#meta").textContent = "";
|
|
7734
|
+
$("#totals").textContent = "";
|
|
7735
|
+
$("#filelist").textContent = "";
|
|
7736
|
+
const lang = helpLanguageFromRoute();
|
|
7737
|
+
const section = helpSectionFromRoute();
|
|
7738
|
+
const content = HELP_CONTENT[lang];
|
|
7739
|
+
const sectionContent = content.sections[section];
|
|
7740
|
+
const shell = document.createElement("section");
|
|
7741
|
+
shell.className = "gdp-help-shell";
|
|
7742
|
+
const header = document.createElement("header");
|
|
7743
|
+
header.className = "gdp-help-header";
|
|
7744
|
+
const title = document.createElement("h1");
|
|
7745
|
+
title.textContent = content.title;
|
|
7746
|
+
const langSelect = document.createElement("select");
|
|
7747
|
+
langSelect.className = "gdp-help-language";
|
|
7748
|
+
langSelect.setAttribute("aria-label", content.languageLabel);
|
|
7749
|
+
HELP_LANGUAGES.forEach((optionLang) => {
|
|
7750
|
+
const option = document.createElement("option");
|
|
7751
|
+
option.value = optionLang;
|
|
7752
|
+
option.textContent = optionLang.toUpperCase();
|
|
7753
|
+
option.selected = optionLang === lang;
|
|
7754
|
+
langSelect.appendChild(option);
|
|
7755
|
+
});
|
|
7756
|
+
langSelect.addEventListener("change", () => {
|
|
7757
|
+
setRoute({ screen: "help", lang: langSelect.value, section, range: currentRange() });
|
|
7758
|
+
setPageMode();
|
|
7759
|
+
renderHelpPage();
|
|
7760
|
+
syncHeaderMenu();
|
|
7761
|
+
});
|
|
7762
|
+
header.append(title, langSelect);
|
|
7763
|
+
const layout = document.createElement("div");
|
|
7764
|
+
layout.className = "gdp-help-layout";
|
|
7765
|
+
const helpNav = document.createElement("nav");
|
|
7766
|
+
helpNav.className = "gdp-help-nav";
|
|
7767
|
+
HELP_SECTIONS.forEach((helpSection) => {
|
|
7768
|
+
const button = document.createElement("button");
|
|
7769
|
+
button.type = "button";
|
|
7770
|
+
button.className = helpSection === section ? "active" : "";
|
|
7771
|
+
button.textContent = content.sections[helpSection].nav;
|
|
7772
|
+
button.addEventListener("click", () => {
|
|
7773
|
+
setRoute({ screen: "help", lang, section: helpSection, range: currentRange() });
|
|
7774
|
+
renderHelpPage();
|
|
7775
|
+
syncHeaderMenu();
|
|
7776
|
+
});
|
|
7777
|
+
helpNav.appendChild(button);
|
|
7778
|
+
});
|
|
7779
|
+
const article = document.createElement("article");
|
|
7780
|
+
article.className = "gdp-help-content";
|
|
7781
|
+
const h2 = document.createElement("h2");
|
|
7782
|
+
h2.textContent = sectionContent.title;
|
|
7783
|
+
const intro = document.createElement("p");
|
|
7784
|
+
intro.textContent = sectionContent.intro;
|
|
7785
|
+
article.append(h2, intro);
|
|
7786
|
+
sectionContent.groups.forEach((group) => {
|
|
7787
|
+
const groupSection = document.createElement("section");
|
|
7788
|
+
groupSection.className = "gdp-help-group";
|
|
7789
|
+
const groupTitle = document.createElement("h3");
|
|
7790
|
+
groupTitle.textContent = group.title;
|
|
7791
|
+
const table2 = document.createElement("table");
|
|
7792
|
+
group.rows.forEach(([keys, description]) => {
|
|
7793
|
+
const tr = document.createElement("tr");
|
|
7794
|
+
const keyCell = document.createElement("th");
|
|
7795
|
+
keyCell.scope = "row";
|
|
7796
|
+
keys.split(" / ").forEach((key, index) => {
|
|
7797
|
+
if (index > 0)
|
|
7798
|
+
keyCell.append(" / ");
|
|
7799
|
+
const kbd = document.createElement("kbd");
|
|
7800
|
+
kbd.textContent = key;
|
|
7801
|
+
keyCell.appendChild(kbd);
|
|
7802
|
+
});
|
|
7803
|
+
const desc = document.createElement("td");
|
|
7804
|
+
desc.textContent = description;
|
|
7805
|
+
tr.append(keyCell, desc);
|
|
7806
|
+
table2.appendChild(tr);
|
|
7807
|
+
});
|
|
7808
|
+
groupSection.append(groupTitle, table2);
|
|
7809
|
+
article.appendChild(groupSection);
|
|
7810
|
+
});
|
|
7811
|
+
layout.append(helpNav, article);
|
|
7812
|
+
shell.append(header, layout);
|
|
7813
|
+
target.replaceChildren(shell);
|
|
7814
|
+
}
|
|
7356
7815
|
function renderShell(meta) {
|
|
7357
7816
|
const newFiles = meta.files || [];
|
|
7358
7817
|
STATE.files = newFiles;
|
|
@@ -8842,11 +9301,35 @@
|
|
|
8842
9301
|
});
|
|
8843
9302
|
return info;
|
|
8844
9303
|
}
|
|
8845
|
-
function
|
|
9304
|
+
function createSourceCopyButton(textValue) {
|
|
9305
|
+
const copy = document.createElement("button");
|
|
9306
|
+
copy.type = "button";
|
|
9307
|
+
copy.className = "gdp-file-header-icon gdp-copy-source";
|
|
9308
|
+
copy.title = "Copy source";
|
|
9309
|
+
copy.setAttribute("aria-label", "Copy source");
|
|
9310
|
+
copy.innerHTML = iconSvg("octicon-copy", COPY_16_PATHS);
|
|
9311
|
+
copy.addEventListener("click", async () => {
|
|
9312
|
+
try {
|
|
9313
|
+
await navigator.clipboard.writeText(textValue);
|
|
9314
|
+
copy.classList.add("copied");
|
|
9315
|
+
setTimeout(() => {
|
|
9316
|
+
copy.classList.remove("copied");
|
|
9317
|
+
}, 1200);
|
|
9318
|
+
} catch {
|
|
9319
|
+
copy.classList.add("failed");
|
|
9320
|
+
setTimeout(() => {
|
|
9321
|
+
copy.classList.remove("failed");
|
|
9322
|
+
}, 1200);
|
|
9323
|
+
}
|
|
9324
|
+
});
|
|
9325
|
+
return copy;
|
|
9326
|
+
}
|
|
9327
|
+
function createSourceTabs(active, textValue) {
|
|
8846
9328
|
const tabs = document.createElement("div");
|
|
8847
9329
|
tabs.className = "gdp-source-tabs";
|
|
8848
9330
|
const codeButton = document.createElement("button");
|
|
8849
9331
|
codeButton.type = "button";
|
|
9332
|
+
codeButton.dataset.sourceTab = "code";
|
|
8850
9333
|
codeButton.textContent = "Code";
|
|
8851
9334
|
codeButton.classList.toggle("active", active === "code");
|
|
8852
9335
|
tabs.appendChild(codeButton);
|
|
@@ -8854,10 +9337,13 @@
|
|
|
8854
9337
|
if (active === "preview") {
|
|
8855
9338
|
previewButton = document.createElement("button");
|
|
8856
9339
|
previewButton.type = "button";
|
|
9340
|
+
previewButton.dataset.sourceTab = "preview";
|
|
8857
9341
|
previewButton.className = "active";
|
|
8858
9342
|
previewButton.textContent = "Preview";
|
|
8859
9343
|
tabs.prepend(previewButton);
|
|
8860
9344
|
}
|
|
9345
|
+
if (textValue != null)
|
|
9346
|
+
tabs.appendChild(createSourceCopyButton(textValue));
|
|
8861
9347
|
return { tabs, codeButton, previewButton };
|
|
8862
9348
|
}
|
|
8863
9349
|
async function renderSourceText(card, target, textValue, signal) {
|
|
@@ -8865,6 +9351,8 @@
|
|
|
8865
9351
|
`).replace(/\r/g, `
|
|
8866
9352
|
`).split(`
|
|
8867
9353
|
`) : [""];
|
|
9354
|
+
SOURCE_CURSOR_TOTALS.set(sourceCursorKey(target), lines.length);
|
|
9355
|
+
resetSourceCursorForTarget(target, lines.length);
|
|
8868
9356
|
const body = card.querySelector(".gdp-file-detail-body, .d2h-files-diff, .d2h-file-diff, .gdp-media, .gdp-source-viewer");
|
|
8869
9357
|
const isStandalone = card.classList.contains("gdp-standalone-source");
|
|
8870
9358
|
const view = document.createElement("div");
|
|
@@ -8885,7 +9373,7 @@
|
|
|
8885
9373
|
if (usesVirtualSource) {
|
|
8886
9374
|
const virtualCode = renderVirtualSource(target, textValue, lines, hljsRef, lang);
|
|
8887
9375
|
if (previewable) {
|
|
8888
|
-
const { tabs: tabs2, codeButton: codeButton2, previewButton: previewButton2 } = createSourceTabs("preview");
|
|
9376
|
+
const { tabs: tabs2, codeButton: codeButton2, previewButton: previewButton2 } = createSourceTabs("preview", textValue);
|
|
8889
9377
|
if (tabsHost) {
|
|
8890
9378
|
tabsHost.hidden = false;
|
|
8891
9379
|
tabsHost.replaceChildren(tabs2);
|
|
@@ -8947,6 +9435,7 @@
|
|
|
8947
9435
|
const tr = document.createElement("tr");
|
|
8948
9436
|
tr.dataset.line = String(index + 1);
|
|
8949
9437
|
tr.classList.toggle("gdp-source-line-target", lineInSourceTarget(index + 1, currentSourceLineTarget(target)));
|
|
9438
|
+
tr.classList.toggle("gdp-source-cursor", sourceCursorMatches(target, index + 1));
|
|
8950
9439
|
const num = document.createElement("td");
|
|
8951
9440
|
num.className = "gdp-source-line-number";
|
|
8952
9441
|
num.textContent = String(index + 1);
|
|
@@ -8969,7 +9458,7 @@
|
|
|
8969
9458
|
}
|
|
8970
9459
|
}
|
|
8971
9460
|
table2.appendChild(tbody);
|
|
8972
|
-
const { tabs, codeButton, previewButton } = createSourceTabs(previewable ? "preview" : "code");
|
|
9461
|
+
const { tabs, codeButton, previewButton } = createSourceTabs(previewable ? "preview" : "code", textValue);
|
|
8973
9462
|
if (tabsHost) {
|
|
8974
9463
|
tabsHost.hidden = false;
|
|
8975
9464
|
tabsHost.replaceChildren(tabs);
|
|
@@ -9120,19 +9609,21 @@
|
|
|
9120
9609
|
actions.className = "gdp-source-virtual-actions";
|
|
9121
9610
|
const copy = document.createElement("button");
|
|
9122
9611
|
copy.type = "button";
|
|
9123
|
-
copy.className = "gdp-source-virtual-
|
|
9124
|
-
copy.
|
|
9612
|
+
copy.className = "gdp-file-header-icon gdp-copy-source gdp-source-virtual-copy";
|
|
9613
|
+
copy.title = "Copy source";
|
|
9614
|
+
copy.setAttribute("aria-label", "Copy source");
|
|
9615
|
+
copy.innerHTML = iconSvg("octicon-copy", COPY_16_PATHS);
|
|
9125
9616
|
copy.addEventListener("click", async () => {
|
|
9126
9617
|
try {
|
|
9127
9618
|
await navigator.clipboard.writeText(textValue);
|
|
9128
|
-
copy.
|
|
9619
|
+
copy.classList.add("copied");
|
|
9129
9620
|
setTimeout(() => {
|
|
9130
|
-
copy.
|
|
9621
|
+
copy.classList.remove("copied");
|
|
9131
9622
|
}, 1200);
|
|
9132
9623
|
} catch {
|
|
9133
|
-
copy.
|
|
9624
|
+
copy.classList.add("failed");
|
|
9134
9625
|
setTimeout(() => {
|
|
9135
|
-
copy.
|
|
9626
|
+
copy.classList.remove("failed");
|
|
9136
9627
|
}, 1600);
|
|
9137
9628
|
}
|
|
9138
9629
|
});
|
|
@@ -9180,6 +9671,7 @@
|
|
|
9180
9671
|
row.className = "gdp-source-virtual-row";
|
|
9181
9672
|
row.dataset.line = String(index + 1);
|
|
9182
9673
|
row.classList.toggle("gdp-source-line-target", lineInSourceTarget(index + 1, currentSourceLineTarget(target)));
|
|
9674
|
+
row.classList.toggle("gdp-source-cursor", sourceCursorMatches(target, index + 1));
|
|
9183
9675
|
const num = document.createElement("span");
|
|
9184
9676
|
num.className = "gdp-source-virtual-line-number";
|
|
9185
9677
|
num.textContent = String(index + 1);
|
|
@@ -9206,6 +9698,7 @@
|
|
|
9206
9698
|
if (!raf)
|
|
9207
9699
|
raf = requestAnimationFrame(render);
|
|
9208
9700
|
};
|
|
9701
|
+
scroller.__gdpRenderVirtualSource = render;
|
|
9209
9702
|
scroller.addEventListener("scroll", schedule, { passive: true });
|
|
9210
9703
|
let resizeObserver = null;
|
|
9211
9704
|
resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(() => {
|
|
@@ -9922,6 +10415,15 @@
|
|
|
9922
10415
|
});
|
|
9923
10416
|
$("#sb-expand-all").addEventListener("click", () => setAllSidebarDirsCollapsed(false));
|
|
9924
10417
|
$("#sb-collapse-all").addEventListener("click", () => setAllSidebarDirsCollapsed(true));
|
|
10418
|
+
prepareKeyboardPanels();
|
|
10419
|
+
const contentPanel = document.querySelector("#content");
|
|
10420
|
+
contentPanel?.addEventListener("focusin", () => setPanelFocusScope("main"));
|
|
10421
|
+
contentPanel?.addEventListener("mousedown", (event) => {
|
|
10422
|
+
if (isFocusableClickTarget(event.target))
|
|
10423
|
+
setPanelFocusScope("main");
|
|
10424
|
+
else
|
|
10425
|
+
focusMainPanel();
|
|
10426
|
+
});
|
|
9925
10427
|
function applySidebarWidth(w) {
|
|
9926
10428
|
const cw = Math.max(180, Math.min(900, w));
|
|
9927
10429
|
document.documentElement.style.setProperty("--sidebar-w", cw + "px");
|
|
@@ -9940,6 +10442,13 @@
|
|
|
9940
10442
|
sb.addEventListener("mousedown", mark);
|
|
9941
10443
|
sb.addEventListener("touchstart", mark, { passive: true });
|
|
9942
10444
|
sb.addEventListener("scroll", mark, { passive: true });
|
|
10445
|
+
sb.addEventListener("focusin", () => setPanelFocusScope("sidebar"));
|
|
10446
|
+
sb.addEventListener("mousedown", (event) => {
|
|
10447
|
+
if (isFocusableClickTarget(event.target))
|
|
10448
|
+
setPanelFocusScope("sidebar");
|
|
10449
|
+
else
|
|
10450
|
+
focusSidebarPanel();
|
|
10451
|
+
});
|
|
9943
10452
|
})();
|
|
9944
10453
|
(function setupResizer() {
|
|
9945
10454
|
const handle = $("#sidebar-resizer");
|
|
@@ -10002,6 +10511,32 @@
|
|
|
10002
10511
|
function visibleSidebarItems() {
|
|
10003
10512
|
return $$("#filelist li[data-path], #filelist .tree-dir[data-dirpath]").filter(isSidebarRowVisible);
|
|
10004
10513
|
}
|
|
10514
|
+
function scrollSidebarItemIntoView(item, block2 = "nearest") {
|
|
10515
|
+
const sidebar = document.querySelector("#sidebar");
|
|
10516
|
+
if (!sidebar) {
|
|
10517
|
+
item.scrollIntoView({ block: block2 });
|
|
10518
|
+
return;
|
|
10519
|
+
}
|
|
10520
|
+
const sidebarRect = sidebar.getBoundingClientRect();
|
|
10521
|
+
const itemRect = item.getBoundingClientRect();
|
|
10522
|
+
const stickyBottom = Math.max(sidebarRect.top, document.querySelector(".sb-head")?.getBoundingClientRect().bottom || sidebarRect.top, document.querySelector(".sb-filter-wrap")?.getBoundingClientRect().bottom || sidebarRect.top);
|
|
10523
|
+
const topPadding = Math.max(8, stickyBottom - sidebarRect.top + 8);
|
|
10524
|
+
const bottomPadding = 14;
|
|
10525
|
+
const visibleTop = sidebarRect.top + topPadding;
|
|
10526
|
+
const visibleBottom = sidebarRect.bottom - bottomPadding;
|
|
10527
|
+
if (block2 === "start") {
|
|
10528
|
+
sidebar.scrollTop += itemRect.top - visibleTop;
|
|
10529
|
+
return;
|
|
10530
|
+
}
|
|
10531
|
+
if (block2 === "end") {
|
|
10532
|
+
sidebar.scrollTop += itemRect.bottom - visibleBottom;
|
|
10533
|
+
return;
|
|
10534
|
+
}
|
|
10535
|
+
if (itemRect.top < visibleTop)
|
|
10536
|
+
sidebar.scrollTop += itemRect.top - visibleTop;
|
|
10537
|
+
else if (itemRect.bottom > visibleBottom)
|
|
10538
|
+
sidebar.scrollTop += itemRect.bottom - visibleBottom;
|
|
10539
|
+
}
|
|
10005
10540
|
function isRepositorySidebarMode() {
|
|
10006
10541
|
return document.body.classList.contains("gdp-repo-page") || document.body.classList.contains("gdp-repo-blob-page");
|
|
10007
10542
|
}
|
|
@@ -10017,7 +10552,44 @@
|
|
|
10017
10552
|
const path = target.dataset.path || target.dataset.dirpath;
|
|
10018
10553
|
if (path)
|
|
10019
10554
|
markActive(path);
|
|
10020
|
-
target
|
|
10555
|
+
scrollSidebarItemIntoView(target);
|
|
10556
|
+
if (target.dataset.path)
|
|
10557
|
+
prefetchByPath(target.dataset.path);
|
|
10558
|
+
}
|
|
10559
|
+
function moveActiveSidebarPage(direction) {
|
|
10560
|
+
const items = visibleSidebarItems();
|
|
10561
|
+
if (!items.length)
|
|
10562
|
+
return;
|
|
10563
|
+
const repoSidebar = isRepositorySidebarMode();
|
|
10564
|
+
const sidebar = document.querySelector("#sidebar");
|
|
10565
|
+
const sample = items.find((item) => item.getBoundingClientRect().height > 0);
|
|
10566
|
+
const rowHeight = sample ? sample.getBoundingClientRect().height : 28;
|
|
10567
|
+
const halfPageRows = Math.max(1, Math.floor((sidebar?.clientHeight || window.innerHeight) / 2 / rowHeight));
|
|
10568
|
+
const current = items.findIndex((li) => li.classList.contains("active"));
|
|
10569
|
+
const start = current < 0 ? 0 : current;
|
|
10570
|
+
const idx = Math.max(0, Math.min(items.length - 1, start + direction * halfPageRows));
|
|
10571
|
+
const target = items[idx];
|
|
10572
|
+
const path = target.dataset.path || target.dataset.dirpath;
|
|
10573
|
+
if (!repoSidebar && target.dataset.path)
|
|
10574
|
+
target.click();
|
|
10575
|
+
else if (path)
|
|
10576
|
+
markActive(path);
|
|
10577
|
+
scrollSidebarItemIntoView(target);
|
|
10578
|
+
if (target.dataset.path)
|
|
10579
|
+
prefetchByPath(target.dataset.path);
|
|
10580
|
+
}
|
|
10581
|
+
function moveActiveSidebarToEdge(edge) {
|
|
10582
|
+
const items = visibleSidebarItems();
|
|
10583
|
+
const repoSidebar = isRepositorySidebarMode();
|
|
10584
|
+
const target = edge === "top" ? items[0] : items[items.length - 1];
|
|
10585
|
+
if (!target)
|
|
10586
|
+
return;
|
|
10587
|
+
const path = target.dataset.path || target.dataset.dirpath;
|
|
10588
|
+
if (!repoSidebar && target.dataset.path)
|
|
10589
|
+
target.click();
|
|
10590
|
+
else if (path)
|
|
10591
|
+
markActive(path);
|
|
10592
|
+
scrollSidebarItemIntoView(target, edge === "top" ? "start" : "end");
|
|
10021
10593
|
if (target.dataset.path)
|
|
10022
10594
|
prefetchByPath(target.dataset.path);
|
|
10023
10595
|
}
|
|
@@ -10099,13 +10671,16 @@
|
|
|
10099
10671
|
function closeSearchPalette() {
|
|
10100
10672
|
if (!PALETTE)
|
|
10101
10673
|
return;
|
|
10674
|
+
const previousFocusScope = PALETTE.previousFocusScope;
|
|
10102
10675
|
PALETTE.controller?.abort();
|
|
10103
10676
|
if (PALETTE.debounce)
|
|
10104
10677
|
window.clearTimeout(PALETTE.debounce);
|
|
10105
10678
|
PALETTE.root.remove();
|
|
10106
10679
|
PALETTE = null;
|
|
10680
|
+
restorePanelFocusScope(previousFocusScope);
|
|
10107
10681
|
}
|
|
10108
10682
|
function createPalette(mode) {
|
|
10683
|
+
const previousFocusScope = PALETTE ? PALETTE.previousFocusScope : getPanelFocusScope();
|
|
10109
10684
|
closeSearchPalette();
|
|
10110
10685
|
const root = document.createElement("div");
|
|
10111
10686
|
root.className = "gdp-palette-backdrop";
|
|
@@ -10147,9 +10722,11 @@
|
|
|
10147
10722
|
selected: -1,
|
|
10148
10723
|
items: [],
|
|
10149
10724
|
composing: false,
|
|
10150
|
-
diffSnapshot: [...STATE.files]
|
|
10725
|
+
diffSnapshot: [...STATE.files],
|
|
10726
|
+
previousFocusScope
|
|
10151
10727
|
};
|
|
10152
10728
|
PALETTE = state;
|
|
10729
|
+
setPanelFocusScope(null);
|
|
10153
10730
|
root.addEventListener("mousedown", (e2) => {
|
|
10154
10731
|
if (e2.target === root)
|
|
10155
10732
|
closeSearchPalette();
|
|
@@ -10468,78 +11045,140 @@
|
|
|
10468
11045
|
function openSearchPalette(mode) {
|
|
10469
11046
|
createPalette(mode);
|
|
10470
11047
|
}
|
|
10471
|
-
|
|
10472
|
-
if (
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
return;
|
|
10476
|
-
openSearchPalette("file");
|
|
10477
|
-
return;
|
|
11048
|
+
function dispatchKeymapAction(action, scope, repeated = false) {
|
|
11049
|
+
if (action !== "start-g-sequence") {
|
|
11050
|
+
PENDING_G_SCOPE = null;
|
|
11051
|
+
PENDING_G_UNTIL = 0;
|
|
10478
11052
|
}
|
|
10479
|
-
if (
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
openSearchPalette("grep");
|
|
10484
|
-
return;
|
|
11053
|
+
if (action === "open-file-palette") {
|
|
11054
|
+
if (PALETTE?.mode !== "file")
|
|
11055
|
+
openSearchPalette("file");
|
|
11056
|
+
return true;
|
|
10485
11057
|
}
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
if (cancelActiveSourceLoad("esc")) {
|
|
10491
|
-
e2.preventDefault();
|
|
10492
|
-
return;
|
|
10493
|
-
}
|
|
11058
|
+
if (action === "open-grep-palette") {
|
|
11059
|
+
if (PALETTE?.mode !== "grep")
|
|
11060
|
+
openSearchPalette("grep");
|
|
11061
|
+
return true;
|
|
10494
11062
|
}
|
|
10495
|
-
if (
|
|
10496
|
-
e2.preventDefault();
|
|
11063
|
+
if (action === "focus-file-filter") {
|
|
10497
11064
|
focusFileFilter();
|
|
10498
|
-
|
|
10499
|
-
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
}
|
|
10504
|
-
|
|
11065
|
+
return true;
|
|
11066
|
+
}
|
|
11067
|
+
if (action === "focus-sidebar") {
|
|
11068
|
+
focusSidebarPanel();
|
|
11069
|
+
return true;
|
|
11070
|
+
}
|
|
11071
|
+
if (action === "focus-main") {
|
|
11072
|
+
focusMainPanel();
|
|
11073
|
+
return true;
|
|
11074
|
+
}
|
|
11075
|
+
if (action === "cancel-source-load") {
|
|
11076
|
+
cancelActiveSourceLoad("esc");
|
|
11077
|
+
return true;
|
|
11078
|
+
}
|
|
11079
|
+
if (action === "open-sidebar-item") {
|
|
11080
|
+
if (!isRepositorySidebarMode())
|
|
11081
|
+
return false;
|
|
11082
|
+
openActiveSidebarItem();
|
|
11083
|
+
focusMainPanel();
|
|
11084
|
+
return true;
|
|
11085
|
+
}
|
|
11086
|
+
if (action === "sidebar-next" || action === "sidebar-previous") {
|
|
10505
11087
|
const repoSidebar = isRepositorySidebarMode();
|
|
10506
11088
|
const items = repoSidebar ? visibleSidebarItems() : $$("#filelist li[data-path]:not(.hidden):not(.hidden-by-tests)");
|
|
10507
11089
|
if (!items.length)
|
|
10508
|
-
return;
|
|
11090
|
+
return true;
|
|
10509
11091
|
let idx = items.findIndex((li) => li.classList.contains("active"));
|
|
10510
11092
|
if (idx < 0)
|
|
10511
11093
|
idx = 0;
|
|
10512
11094
|
else
|
|
10513
|
-
idx =
|
|
11095
|
+
idx = action === "sidebar-next" ? Math.min(items.length - 1, idx + 1) : Math.max(0, idx - 1);
|
|
10514
11096
|
const target = items[idx];
|
|
10515
11097
|
const path = target?.dataset.path || target?.dataset.dirpath;
|
|
10516
11098
|
if (!repoSidebar && target) {
|
|
10517
11099
|
target.click();
|
|
10518
|
-
target
|
|
11100
|
+
scrollSidebarItemIntoView(target);
|
|
10519
11101
|
} else if (path) {
|
|
10520
11102
|
markActive(path);
|
|
10521
|
-
target
|
|
11103
|
+
scrollSidebarItemIntoView(target);
|
|
10522
11104
|
}
|
|
10523
|
-
const nextIdx =
|
|
11105
|
+
const nextIdx = action === "sidebar-next" ? Math.min(items.length - 1, idx + 1) : Math.max(0, idx - 1);
|
|
10524
11106
|
const nextItem = items[nextIdx];
|
|
10525
11107
|
if (nextItem && nextItem !== target && nextItem.dataset.path)
|
|
10526
11108
|
prefetchByPath(nextItem.dataset.path);
|
|
10527
|
-
|
|
10528
|
-
|
|
10529
|
-
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
}
|
|
10533
|
-
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
11109
|
+
return true;
|
|
11110
|
+
}
|
|
11111
|
+
if (action === "sidebar-page-down" || action === "sidebar-page-up") {
|
|
11112
|
+
moveActiveSidebarPage(action === "sidebar-page-down" ? 1 : -1);
|
|
11113
|
+
return true;
|
|
11114
|
+
}
|
|
11115
|
+
if (action === "sidebar-expand") {
|
|
11116
|
+
if (!isRepositorySidebarMode())
|
|
11117
|
+
return false;
|
|
11118
|
+
toggleActiveSidebarDirectoryCollapsed();
|
|
11119
|
+
return true;
|
|
11120
|
+
}
|
|
11121
|
+
if (action === "sidebar-collapse") {
|
|
11122
|
+
if (!isRepositorySidebarMode())
|
|
11123
|
+
return false;
|
|
11124
|
+
setActiveSidebarDirectoryCollapsed(true);
|
|
11125
|
+
return true;
|
|
11126
|
+
}
|
|
11127
|
+
if (action === "scroll-main-down" || action === "scroll-main-up") {
|
|
11128
|
+
scrollMainPanel(action === "scroll-main-down" ? 1 : -1, repeated);
|
|
11129
|
+
return true;
|
|
11130
|
+
}
|
|
11131
|
+
if (action === "scroll-main-page-down" || action === "scroll-main-page-up") {
|
|
11132
|
+
scrollMainPanel(action === "scroll-main-page-down" ? 1 : -1, repeated, "page");
|
|
11133
|
+
return true;
|
|
11134
|
+
}
|
|
11135
|
+
if (action === "tab-preview" || action === "tab-code") {
|
|
11136
|
+
return switchSourceTab(action === "tab-preview" ? "preview" : "code");
|
|
11137
|
+
}
|
|
11138
|
+
if (action === "start-g-sequence") {
|
|
11139
|
+
PENDING_G_SCOPE = scope;
|
|
11140
|
+
PENDING_G_UNTIL = performance.now() + 900;
|
|
11141
|
+
return true;
|
|
11142
|
+
}
|
|
11143
|
+
if (action === "goto-top" || action === "goto-bottom") {
|
|
11144
|
+
const edge = action === "goto-top" ? "top" : "bottom";
|
|
11145
|
+
if (scope === "main")
|
|
11146
|
+
scrollMainToEdge(edge);
|
|
11147
|
+
else if (scope === "sidebar")
|
|
11148
|
+
moveActiveSidebarToEdge(edge);
|
|
11149
|
+
else
|
|
11150
|
+
window.scrollTo({ top: edge === "top" ? 0 : Math.max(document.documentElement.scrollHeight, document.body.scrollHeight), behavior: "auto" });
|
|
11151
|
+
return true;
|
|
11152
|
+
}
|
|
11153
|
+
if (action === "layout-unified") {
|
|
10538
11154
|
setLayout("line-by-line");
|
|
10539
|
-
|
|
11155
|
+
return true;
|
|
11156
|
+
}
|
|
11157
|
+
if (action === "layout-split") {
|
|
10540
11158
|
setLayout("side-by-side");
|
|
10541
|
-
|
|
11159
|
+
return true;
|
|
11160
|
+
}
|
|
11161
|
+
if (action === "toggle-theme") {
|
|
10542
11162
|
$("#theme").click();
|
|
11163
|
+
return true;
|
|
11164
|
+
}
|
|
11165
|
+
return false;
|
|
11166
|
+
}
|
|
11167
|
+
document.addEventListener("keydown", (e2) => {
|
|
11168
|
+
const targetEl = e2.target;
|
|
11169
|
+
const scope = keymapScope(targetEl);
|
|
11170
|
+
const action = resolveKeymapAction(e2, {
|
|
11171
|
+
scope,
|
|
11172
|
+
editable: isEditableKeyTarget(targetEl),
|
|
11173
|
+
composing: e2.isComposing,
|
|
11174
|
+
paletteOpen: !!PALETTE,
|
|
11175
|
+
pendingG: PENDING_G_SCOPE === scope && performance.now() <= PENDING_G_UNTIL,
|
|
11176
|
+
lightboxOpen: !!document.querySelector(".mkdp-lightbox")
|
|
11177
|
+
});
|
|
11178
|
+
if (!action)
|
|
11179
|
+
return;
|
|
11180
|
+
if (dispatchKeymapAction(action, scope, e2.repeat))
|
|
11181
|
+
e2.preventDefault();
|
|
10543
11182
|
});
|
|
10544
11183
|
applyTheme();
|
|
10545
11184
|
setLayout(STATE.layout);
|
|
@@ -10566,6 +11205,12 @@
|
|
|
10566
11205
|
}).catch(() => setStatus("error"));
|
|
10567
11206
|
}
|
|
10568
11207
|
function load(options = {}) {
|
|
11208
|
+
if (STATE.route.screen === "help") {
|
|
11209
|
+
setStatus("live");
|
|
11210
|
+
renderHelpPage();
|
|
11211
|
+
syncHeaderMenu();
|
|
11212
|
+
return Promise.resolve();
|
|
11213
|
+
}
|
|
10569
11214
|
if (STATE.route.screen === "repo")
|
|
10570
11215
|
return loadRepo();
|
|
10571
11216
|
setStatus("refreshing");
|
|
@@ -10584,7 +11229,10 @@
|
|
|
10584
11229
|
setStatus("live");
|
|
10585
11230
|
}).catch(() => setStatus("error"));
|
|
10586
11231
|
}
|
|
10587
|
-
if (STATE.route.screen === "
|
|
11232
|
+
if (STATE.route.screen === "help") {
|
|
11233
|
+
setStatus("live");
|
|
11234
|
+
renderHelpPage();
|
|
11235
|
+
} else if (STATE.route.screen === "repo")
|
|
10588
11236
|
loadRepo();
|
|
10589
11237
|
else if (STATE.route.screen === "file" && STATE.route.view === "blob") {
|
|
10590
11238
|
setStatus("live");
|
|
@@ -10607,10 +11255,13 @@
|
|
|
10607
11255
|
const range = currentRange();
|
|
10608
11256
|
if (STATE.route.screen === "file") {
|
|
10609
11257
|
setRoute({ screen: "file", path: STATE.route.path, ref: STATE.route.ref, range }, true);
|
|
11258
|
+
} else if (STATE.route.screen === "help") {
|
|
11259
|
+
setRoute({ screen: "help", lang: helpLanguageFromRoute(), section: helpSectionFromRoute(), range }, true);
|
|
11260
|
+
renderHelpPage();
|
|
10610
11261
|
} else {
|
|
10611
11262
|
setRoute({ screen: "diff", range }, true);
|
|
11263
|
+
load();
|
|
10612
11264
|
}
|
|
10613
|
-
load();
|
|
10614
11265
|
}
|
|
10615
11266
|
syncRefInputs();
|
|
10616
11267
|
syncHeaderMenu();
|
|
@@ -10811,6 +11462,13 @@
|
|
|
10811
11462
|
STATE.repoRef = STATE.route.ref || "worktree";
|
|
10812
11463
|
syncRefInputs();
|
|
10813
11464
|
syncHeaderMenu();
|
|
11465
|
+
if (STATE.route.screen === "help") {
|
|
11466
|
+
cancelActiveSourceLoad("navigation");
|
|
11467
|
+
setPageMode();
|
|
11468
|
+
renderHelpPage();
|
|
11469
|
+
setStatus("live");
|
|
11470
|
+
return;
|
|
11471
|
+
}
|
|
10814
11472
|
if (STATE.route.screen === "repo") {
|
|
10815
11473
|
cancelActiveSourceLoad("navigation");
|
|
10816
11474
|
setPageMode();
|