@youtyan/code-viewer 0.1.14 → 0.1.16
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/README.md +9 -0
- package/package.json +1 -1
- package/web/app.js +875 -109
- package/web/index.html +45 -8
- package/web/style.css +414 -72
- package/web-src/server/git.ts +90 -29
- package/web-src/server/preview.ts +301 -49
- package/web-src/server/range.ts +228 -0
- package/web-src/server/runtime.d.ts +10 -0
- package/web-src/server/search.ts +10 -7
- package/web-src/types.ts +16 -1
package/web/app.js
CHANGED
|
@@ -378,6 +378,18 @@
|
|
|
378
378
|
{ action: "scroll-main-up", key: "k", scope: "main" },
|
|
379
379
|
{ action: "scroll-main-page-down", key: "d", scope: "main", ctrl: true },
|
|
380
380
|
{ action: "scroll-main-page-up", key: "u", scope: "main", ctrl: true },
|
|
381
|
+
{ action: "scroll-main-page-down", key: "pagedown", scope: "main" },
|
|
382
|
+
{ action: "scroll-main-page-up", key: "pageup", scope: "main" },
|
|
383
|
+
{ action: "scroll-main-page-down", key: "pagedown", scope: "global" },
|
|
384
|
+
{ action: "scroll-main-page-up", key: "pageup", scope: "global" },
|
|
385
|
+
{ action: "scroll-main-page-down", key: "pagedown", scope: "sidebar" },
|
|
386
|
+
{ action: "scroll-main-page-up", key: "pageup", scope: "sidebar" },
|
|
387
|
+
{ action: "scroll-main-page-down", key: "arrowdown", scope: "main", ctrl: true },
|
|
388
|
+
{ action: "scroll-main-page-up", key: "arrowup", scope: "main", ctrl: true },
|
|
389
|
+
{ action: "scroll-main-page-down", key: "arrowdown", scope: "global", ctrl: true },
|
|
390
|
+
{ action: "scroll-main-page-up", key: "arrowup", scope: "global", ctrl: true },
|
|
391
|
+
{ action: "scroll-main-page-down", key: "arrowdown", scope: "sidebar", ctrl: true },
|
|
392
|
+
{ action: "scroll-main-page-up", key: "arrowup", scope: "sidebar", ctrl: true },
|
|
381
393
|
{ action: "tab-preview", key: "p", scope: "main", pendingG: true },
|
|
382
394
|
{ action: "tab-code", key: "c", scope: "main", pendingG: true },
|
|
383
395
|
{ action: "goto-top", key: "g", pendingG: true },
|
|
@@ -6746,8 +6758,25 @@
|
|
|
6746
6758
|
];
|
|
6747
6759
|
const FILE_16_PATH = "M2 1.75C2 .784 2.784 0 3.75 0h5.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 12.25 16h-8.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 8 4.25V1.5Zm5.75.062V4.25c0 .138.112.25.25.25h2.688Z";
|
|
6748
6760
|
const OPEN_EXTERNAL_16_PATH = "M3.75 2A1.75 1.75 0 0 0 2 3.75v8.5C2 13.216 2.784 14 3.75 14h8.5A1.75 1.75 0 0 0 14 12.25v-3.5a.75.75 0 0 0-1.5 0v3.5a.25.25 0 0 1-.25.25h-8.5a.25.25 0 0 1-.25-.25v-8.5a.25.25 0 0 1 .25-.25h3.5a.75.75 0 0 0 0-1.5h-3.5Zm6.5 0a.75.75 0 0 0 0 1.5h1.19L7.72 7.22a.749.749 0 1 0 1.06 1.06l3.72-3.72v1.19a.75.75 0 0 0 1.5 0v-3A.75.75 0 0 0 13.25 2h-3Z";
|
|
6749
|
-
const
|
|
6750
|
-
const
|
|
6761
|
+
const GIT_BRANCH_16_PATH = "M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z";
|
|
6762
|
+
const TRIANGLE_DOWN_16_PATH = "m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z";
|
|
6763
|
+
const SIDEBAR_SHOW_16_PATHS = [
|
|
6764
|
+
"M6.823 7.823a.25.25 0 0 1 0 .354l-2.396 2.396A.25.25 0 0 1 4 10.396V5.604a.25.25 0 0 1 .427-.177Z",
|
|
6765
|
+
"M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0ZM1.5 1.75v12.5c0 .138.112.25.25.25H9.5v-13H1.75a.25.25 0 0 0-.25.25ZM11 14.5h3.25a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H11Z"
|
|
6766
|
+
];
|
|
6767
|
+
const SIDEBAR_HIDE_16_PATHS = [
|
|
6768
|
+
"m4.177 7.823 2.396-2.396A.25.25 0 0 1 7 5.604v4.792a.25.25 0 0 1-.427.177L4.177 8.177a.25.25 0 0 1 0-.354Z",
|
|
6769
|
+
"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25H9.5v-13Zm12.5 13a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H11v13Z"
|
|
6770
|
+
];
|
|
6771
|
+
const GEAR_16_PATH = "M8 0a8.2 8.2 0 0 1 1.7.18.75.75 0 0 1 .6.86l-.14.93c.23.1.45.21.67.34l.73-.6a.75.75 0 0 1 1.03.08c.47.37.89.78 1.23 1.23a.75.75 0 0 1 .07 1.03l-.59.73c.13.22.24.44.34.67l.93-.14a.75.75 0 0 1 .86.6c.12.55.18 1.12.18 1.7s-.06 1.15-.18 1.7a.75.75 0 0 1-.86.6l-.93-.14c-.1.23-.21.45-.34.67l.59.73a.75.75 0 0 1-.07 1.03c-.34.45-.76.86-1.23 1.23a.75.75 0 0 1-1.03.08l-.73-.6c-.22.13-.44.24-.67.34l.14.93a.75.75 0 0 1-.6.86A8.2 8.2 0 0 1 8 16a8.2 8.2 0 0 1-1.7-.18.75.75 0 0 1-.6-.86l.14-.93a5.9 5.9 0 0 1-.67-.34l-.73.6a.75.75 0 0 1-1.03-.08 8.1 8.1 0 0 1-1.23-1.23.75.75 0 0 1-.07-1.03l.59-.73a5.9 5.9 0 0 1-.34-.67l-.93.14a.75.75 0 0 1-.86-.6A8.2 8.2 0 0 1 0 8c0-.58.06-1.15.18-1.7a.75.75 0 0 1 .86-.6l.93.14c.1-.23.21-.45.34-.67l-.59-.73a.75.75 0 0 1 .07-1.03 8.1 8.1 0 0 1 1.23-1.23.75.75 0 0 1 1.03-.08l.73.6c.22-.13.44-.24.67-.34l-.14-.93a.75.75 0 0 1 .6-.86A8.2 8.2 0 0 1 8 0Zm0 5a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z";
|
|
6772
|
+
const EXPAND_ALL_16_PATHS = [
|
|
6773
|
+
"M3.22 4.47a.75.75 0 0 1 1.06 0L8 8.19l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L3.22 5.53a.75.75 0 0 1 0-1.06Z",
|
|
6774
|
+
"M3.22 8.47a.75.75 0 0 1 1.06 0L8 12.19l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L3.22 9.53a.75.75 0 0 1 0-1.06Z"
|
|
6775
|
+
];
|
|
6776
|
+
const COLLAPSE_ALL_16_PATHS = [
|
|
6777
|
+
"M7.47 2.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 1 1-1.06 1.06L8 3.81 4.28 7.53a.75.75 0 0 1-1.06-1.06Z",
|
|
6778
|
+
"M7.47 6.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 1 1-1.06 1.06L8 7.81l-3.72 3.72a.75.75 0 1 1-1.06-1.06Z"
|
|
6779
|
+
];
|
|
6751
6780
|
const $ = (sel) => document.querySelector(sel);
|
|
6752
6781
|
const $$ = (sel) => Array.from(document.querySelectorAll(sel));
|
|
6753
6782
|
const diffCardSelector = (path) => '.gdp-file-shell[data-path="' + (window.CSS && CSS.escape ? CSS.escape(path) : path) + '"]';
|
|
@@ -6755,6 +6784,7 @@
|
|
|
6755
6784
|
const DEFAULT_RANGE = { from: "HEAD", to: "worktree" };
|
|
6756
6785
|
const VIRTUAL_SOURCE_LINE_THRESHOLD = 3000;
|
|
6757
6786
|
const VIRTUAL_SOURCE_SIZE_THRESHOLD = 1024 * 1024;
|
|
6787
|
+
const VIRTUAL_SOURCE_PAGE_SIZE = 2000;
|
|
6758
6788
|
const VIRTUAL_SOURCE_ROW_HEIGHT = 20;
|
|
6759
6789
|
const VIRTUAL_SOURCE_HIGHLIGHT_MAX_LINE_LENGTH = 2000;
|
|
6760
6790
|
let highlightLoadPromise = null;
|
|
@@ -6766,12 +6796,44 @@
|
|
|
6766
6796
|
let REPO_SIDEBAR_LOAD = null;
|
|
6767
6797
|
let SIDEBAR_FILES = [];
|
|
6768
6798
|
let SIDEBAR_ON_FILE_CLICK;
|
|
6799
|
+
let SERVER_SCOPE_OMIT_DIRS_DEFAULT = [];
|
|
6769
6800
|
let PENDING_G_SCOPE = null;
|
|
6770
6801
|
let PENDING_G_UNTIL = 0;
|
|
6771
6802
|
let SOURCE_CURSOR = null;
|
|
6772
6803
|
const SOURCE_CURSOR_TOTALS = new Map;
|
|
6773
6804
|
const HELP_LANGUAGES = ["en", "ja"];
|
|
6774
6805
|
const HELP_SECTIONS = ["keybindings"];
|
|
6806
|
+
const SCOPE_OMIT_DIRS_STORAGE_KEY_PREFIX = "gdp:scope-omit-dirs:";
|
|
6807
|
+
const SIDEBAR_FONT_SIZE_STORAGE_KEY = "gdp:sidebar-font-size";
|
|
6808
|
+
const CODE_FONT_SIZE_STORAGE_KEY = "gdp:code-font-size";
|
|
6809
|
+
const CLIENT_SCOPE_OMIT_DIRS_DEFAULT = [
|
|
6810
|
+
"node_modules",
|
|
6811
|
+
".venv",
|
|
6812
|
+
"venv",
|
|
6813
|
+
".next",
|
|
6814
|
+
".nuxt",
|
|
6815
|
+
".svelte-kit",
|
|
6816
|
+
".astro",
|
|
6817
|
+
".vercel",
|
|
6818
|
+
"dist",
|
|
6819
|
+
"build",
|
|
6820
|
+
"out",
|
|
6821
|
+
"target",
|
|
6822
|
+
".gradle",
|
|
6823
|
+
"__pycache__",
|
|
6824
|
+
".pytest_cache",
|
|
6825
|
+
".tox",
|
|
6826
|
+
".terraform",
|
|
6827
|
+
".idea",
|
|
6828
|
+
".vscode",
|
|
6829
|
+
"vendor",
|
|
6830
|
+
".cache",
|
|
6831
|
+
"coverage",
|
|
6832
|
+
"DerivedData",
|
|
6833
|
+
"Pods",
|
|
6834
|
+
"bin",
|
|
6835
|
+
"obj"
|
|
6836
|
+
];
|
|
6775
6837
|
const HELP_CONTENT = {
|
|
6776
6838
|
en: {
|
|
6777
6839
|
languageLabel: "Language",
|
|
@@ -6863,6 +6925,8 @@
|
|
|
6863
6925
|
const before = scroller.scrollTop;
|
|
6864
6926
|
if (edge === "center")
|
|
6865
6927
|
scroller.scrollTop = Math.max(0, top - Math.round(scroller.clientHeight / 2));
|
|
6928
|
+
else if (edge === "start")
|
|
6929
|
+
scroller.scrollTop = top;
|
|
6866
6930
|
else if (top < scroller.scrollTop)
|
|
6867
6931
|
scroller.scrollTop = top;
|
|
6868
6932
|
else if (bottom > scroller.scrollTop + scroller.clientHeight)
|
|
@@ -6895,7 +6959,7 @@
|
|
|
6895
6959
|
const delta = unit === "page" ? pageRows : 1;
|
|
6896
6960
|
cursor.line = Math.max(1, Math.min(total, cursor.line + direction * delta));
|
|
6897
6961
|
syncSourceCursorRows(target);
|
|
6898
|
-
scrollSourceCursorIntoView(cursor);
|
|
6962
|
+
scrollSourceCursorIntoView(cursor, unit === "page" ? "start" : "nearest");
|
|
6899
6963
|
return true;
|
|
6900
6964
|
}
|
|
6901
6965
|
function scrollMainPanel(direction, repeated = false, unit = "line") {
|
|
@@ -6910,6 +6974,31 @@
|
|
|
6910
6974
|
else
|
|
6911
6975
|
window.scrollBy({ top, behavior });
|
|
6912
6976
|
}
|
|
6977
|
+
let MAIN_SURFACE_FOCUS_SEQ = 0;
|
|
6978
|
+
function focusMainSurface() {
|
|
6979
|
+
const target = findMainScrollTarget();
|
|
6980
|
+
if (target?.matches("#content .gdp-source-virtual-scroller")) {
|
|
6981
|
+
target.focus({ preventScroll: true });
|
|
6982
|
+
setPanelFocusScope("main");
|
|
6983
|
+
return;
|
|
6984
|
+
}
|
|
6985
|
+
focusMainPanel();
|
|
6986
|
+
}
|
|
6987
|
+
function scheduleMainSurfaceFocus() {
|
|
6988
|
+
const seq = ++MAIN_SURFACE_FOCUS_SEQ;
|
|
6989
|
+
const apply = () => {
|
|
6990
|
+
if (seq !== MAIN_SURFACE_FOCUS_SEQ || PALETTE)
|
|
6991
|
+
return;
|
|
6992
|
+
if (isEditableKeyTarget(document.activeElement))
|
|
6993
|
+
return;
|
|
6994
|
+
focusMainSurface();
|
|
6995
|
+
};
|
|
6996
|
+
focusMainPanel();
|
|
6997
|
+
queueMicrotask(apply);
|
|
6998
|
+
requestAnimationFrame(apply);
|
|
6999
|
+
setTimeout(apply, 100);
|
|
7000
|
+
setTimeout(apply, 300);
|
|
7001
|
+
}
|
|
6913
7002
|
function scrollMainToEdge(edge) {
|
|
6914
7003
|
if (moveSourceCursor(edge === "bottom" ? 1 : -1, "edge", edge))
|
|
6915
7004
|
return;
|
|
@@ -6942,6 +7031,90 @@
|
|
|
6942
7031
|
REPO_SIDEBAR_LOAD_REF = null;
|
|
6943
7032
|
REPO_SIDEBAR_LOAD = null;
|
|
6944
7033
|
}
|
|
7034
|
+
function normalizeScopeOmitDirs(value) {
|
|
7035
|
+
const raw = Array.isArray(value) ? value : value.split(/[\n,]+/);
|
|
7036
|
+
return [...new Set(raw.map((item) => item.trim()).filter((item) => item && item.length <= 64 && !item.includes("/") && !item.includes("\\") && item !== "." && item !== ".." && item !== ".git"))].slice(0, 100).sort((a2, b2) => a2.localeCompare(b2));
|
|
7037
|
+
}
|
|
7038
|
+
function scopeOmitDirsStorageKey() {
|
|
7039
|
+
return SCOPE_OMIT_DIRS_STORAGE_KEY_PREFIX + (PROJECT_NAME || "default");
|
|
7040
|
+
}
|
|
7041
|
+
function setProjectName(project) {
|
|
7042
|
+
if (!project)
|
|
7043
|
+
return;
|
|
7044
|
+
PROJECT_NAME = project;
|
|
7045
|
+
document.title = project + " - code viewer";
|
|
7046
|
+
}
|
|
7047
|
+
function savedScopeOmitDirs() {
|
|
7048
|
+
const raw = localStorage.getItem(scopeOmitDirsStorageKey());
|
|
7049
|
+
if (raw == null)
|
|
7050
|
+
return null;
|
|
7051
|
+
try {
|
|
7052
|
+
const parsed = JSON.parse(raw);
|
|
7053
|
+
return normalizeScopeOmitDirs(Array.isArray(parsed) ? parsed : []);
|
|
7054
|
+
} catch {
|
|
7055
|
+
return normalizeScopeOmitDirs(raw);
|
|
7056
|
+
}
|
|
7057
|
+
}
|
|
7058
|
+
function serverScopeOmitDirsDefault() {
|
|
7059
|
+
return SERVER_SCOPE_OMIT_DIRS_DEFAULT.length ? SERVER_SCOPE_OMIT_DIRS_DEFAULT : CLIENT_SCOPE_OMIT_DIRS_DEFAULT;
|
|
7060
|
+
}
|
|
7061
|
+
function effectiveScopeOmitDirs() {
|
|
7062
|
+
return savedScopeOmitDirs() ?? serverScopeOmitDirsDefault();
|
|
7063
|
+
}
|
|
7064
|
+
function appendScopeOmitDirsParam(params) {
|
|
7065
|
+
const saved = savedScopeOmitDirs();
|
|
7066
|
+
if (saved != null)
|
|
7067
|
+
params.set("omit_dirs", saved.join(","));
|
|
7068
|
+
}
|
|
7069
|
+
function normalizeViewerFontSize(value) {
|
|
7070
|
+
return value === "compact" || value === "large" || value === "xlarge" ? value : "regular";
|
|
7071
|
+
}
|
|
7072
|
+
function savedSidebarFontSize() {
|
|
7073
|
+
return normalizeViewerFontSize(localStorage.getItem(SIDEBAR_FONT_SIZE_STORAGE_KEY));
|
|
7074
|
+
}
|
|
7075
|
+
function savedCodeFontSize() {
|
|
7076
|
+
return normalizeViewerFontSize(localStorage.getItem(CODE_FONT_SIZE_STORAGE_KEY));
|
|
7077
|
+
}
|
|
7078
|
+
function applySidebarFontSize(size = savedSidebarFontSize()) {
|
|
7079
|
+
document.body.dataset.sidebarFontSize = size;
|
|
7080
|
+
}
|
|
7081
|
+
function applyCodeFontSize(size = savedCodeFontSize()) {
|
|
7082
|
+
document.body.dataset.codeFontSize = size;
|
|
7083
|
+
}
|
|
7084
|
+
function syncSidebarHeaderHeight() {
|
|
7085
|
+
requestAnimationFrame(() => {
|
|
7086
|
+
const head = document.querySelector(".sb-head");
|
|
7087
|
+
if (head)
|
|
7088
|
+
document.documentElement.style.setProperty("--sidebar-head-h", Math.ceil(head.getBoundingClientRect().height) + "px");
|
|
7089
|
+
});
|
|
7090
|
+
}
|
|
7091
|
+
function observeSidebarHeaderHeight() {
|
|
7092
|
+
const head = document.querySelector(".sb-head");
|
|
7093
|
+
if (!head || typeof ResizeObserver === "undefined") {
|
|
7094
|
+
syncSidebarHeaderHeight();
|
|
7095
|
+
return;
|
|
7096
|
+
}
|
|
7097
|
+
const observer = new ResizeObserver(syncSidebarHeaderHeight);
|
|
7098
|
+
observer.observe(head);
|
|
7099
|
+
syncSidebarHeaderHeight();
|
|
7100
|
+
}
|
|
7101
|
+
function repoFileCacheKey(ref) {
|
|
7102
|
+
const saved = savedScopeOmitDirs();
|
|
7103
|
+
return `${ref}\x00${saved ? saved.join("\x00") : "server"}`;
|
|
7104
|
+
}
|
|
7105
|
+
async function loadSettings() {
|
|
7106
|
+
try {
|
|
7107
|
+
const res = await fetch("/_settings");
|
|
7108
|
+
if (!res.ok)
|
|
7109
|
+
return null;
|
|
7110
|
+
const settings = await res.json();
|
|
7111
|
+
setProjectName(settings.project || "");
|
|
7112
|
+
SERVER_SCOPE_OMIT_DIRS_DEFAULT = normalizeScopeOmitDirs(settings.scope.omit_dirs_effective);
|
|
7113
|
+
return settings;
|
|
7114
|
+
} catch {
|
|
7115
|
+
return null;
|
|
7116
|
+
}
|
|
7117
|
+
}
|
|
6945
7118
|
function isRepoSidebarReusable(ref) {
|
|
6946
7119
|
return REPO_SIDEBAR_REF === (ref || "worktree") && isRepositorySidebarMode();
|
|
6947
7120
|
}
|
|
@@ -6958,6 +7131,7 @@
|
|
|
6958
7131
|
theme: localStorage.getItem("gdp:theme") || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"),
|
|
6959
7132
|
sbView: localStorage.getItem("gdp:sbview") || "tree",
|
|
6960
7133
|
sbWidth: parseInt(localStorage.getItem("gdp:sbwidth")) || 308,
|
|
7134
|
+
sidebarHidden: localStorage.getItem("gdp:sidebar-hidden") === "1",
|
|
6961
7135
|
collapsedDirs: new Set(JSON.parse(localStorage.getItem("gdp:collapsed-dirs") || "[]")),
|
|
6962
7136
|
ignoreWs: igRaw === null ? true : igRaw === "1",
|
|
6963
7137
|
from: route.range.from,
|
|
@@ -7212,15 +7386,112 @@
|
|
|
7212
7386
|
return;
|
|
7213
7387
|
button.setAttribute("aria-pressed", expanded ? "true" : "false");
|
|
7214
7388
|
button.title = expanded ? "Collapse expanded lines" : "Expand all lines";
|
|
7215
|
-
button.innerHTML = expanded ? iconSvg("octicon-fold",
|
|
7389
|
+
button.innerHTML = expanded ? iconSvg("octicon-fold", COLLAPSE_ALL_16_PATHS) : iconSvg("octicon-unfold", EXPAND_ALL_16_PATHS);
|
|
7216
7390
|
}
|
|
7217
7391
|
function setSidebarTreeActionIcons() {
|
|
7392
|
+
const settings = document.querySelector("#viewer-settings");
|
|
7393
|
+
const sidebarToggle = document.querySelector("#sidebar-toggle");
|
|
7218
7394
|
const expand = document.querySelector("#sb-expand-all");
|
|
7219
7395
|
const collapse = document.querySelector("#sb-collapse-all");
|
|
7396
|
+
if (settings)
|
|
7397
|
+
settings.innerHTML = iconSvg("octicon-gear", GEAR_16_PATH);
|
|
7398
|
+
if (sidebarToggle)
|
|
7399
|
+
sidebarToggle.innerHTML = iconSvg("octicon-sidebar", STATE.sidebarHidden ? SIDEBAR_SHOW_16_PATHS : SIDEBAR_HIDE_16_PATHS);
|
|
7220
7400
|
if (expand)
|
|
7221
|
-
expand.innerHTML = iconSvg("octicon-
|
|
7401
|
+
expand.innerHTML = iconSvg("octicon-chevron-down", EXPAND_ALL_16_PATHS);
|
|
7222
7402
|
if (collapse)
|
|
7223
|
-
collapse.innerHTML = iconSvg("octicon-
|
|
7403
|
+
collapse.innerHTML = iconSvg("octicon-chevron-up", COLLAPSE_ALL_16_PATHS);
|
|
7404
|
+
}
|
|
7405
|
+
function attachSidebarToggle(host) {
|
|
7406
|
+
const button = document.querySelector("#sidebar-toggle");
|
|
7407
|
+
if (!button || button.parentElement === host)
|
|
7408
|
+
return;
|
|
7409
|
+
host.prepend(button);
|
|
7410
|
+
}
|
|
7411
|
+
function placeSidebarToggle() {
|
|
7412
|
+
const sidebarHead = document.querySelector(".sb-head");
|
|
7413
|
+
const toolbar = document.querySelector(".gdp-repo-toolbar, .gdp-file-detail-header");
|
|
7414
|
+
const restoreHost = toolbar || document.querySelector("#topbar") || document.querySelector("#global-header");
|
|
7415
|
+
if (STATE.sidebarHidden && restoreHost)
|
|
7416
|
+
attachSidebarToggle(restoreHost);
|
|
7417
|
+
else if (sidebarHead)
|
|
7418
|
+
attachSidebarToggle(sidebarHead);
|
|
7419
|
+
}
|
|
7420
|
+
function applySidebarHidden(hidden = STATE.sidebarHidden) {
|
|
7421
|
+
STATE.sidebarHidden = hidden;
|
|
7422
|
+
document.body.classList.toggle("gdp-sidebar-hidden", hidden);
|
|
7423
|
+
localStorage.setItem("gdp:sidebar-hidden", hidden ? "1" : "0");
|
|
7424
|
+
const button = document.querySelector("#sidebar-toggle");
|
|
7425
|
+
if (button) {
|
|
7426
|
+
button.setAttribute("aria-pressed", hidden ? "true" : "false");
|
|
7427
|
+
button.title = hidden ? "show sidebar" : "hide sidebar";
|
|
7428
|
+
button.setAttribute("aria-label", hidden ? "show sidebar" : "hide sidebar");
|
|
7429
|
+
}
|
|
7430
|
+
setSidebarTreeActionIcons();
|
|
7431
|
+
placeSidebarToggle();
|
|
7432
|
+
syncSidebarHeaderHeight();
|
|
7433
|
+
}
|
|
7434
|
+
function toggleSidebarHidden() {
|
|
7435
|
+
applySidebarHidden(!STATE.sidebarHidden);
|
|
7436
|
+
}
|
|
7437
|
+
function scopeOmitSourceLabel() {
|
|
7438
|
+
return savedScopeOmitDirs() != null ? "Browser override" : "Server default";
|
|
7439
|
+
}
|
|
7440
|
+
function refreshRepositoryTreeAfterSettings() {
|
|
7441
|
+
REPO_FILE_CACHE.clear();
|
|
7442
|
+
invalidateRepoSidebar();
|
|
7443
|
+
if (STATE.route.screen === "repo") {
|
|
7444
|
+
loadRepo();
|
|
7445
|
+
return;
|
|
7446
|
+
}
|
|
7447
|
+
const target = sourceTargetFromRoute();
|
|
7448
|
+
if (target)
|
|
7449
|
+
renderRepoBlobSidebar(target.path, target.ref || "worktree");
|
|
7450
|
+
}
|
|
7451
|
+
async function openScopeSettings() {
|
|
7452
|
+
const pop = document.querySelector("#scope-settings-popover");
|
|
7453
|
+
const input = document.querySelector("#scope-omit-dirs");
|
|
7454
|
+
const sidebarFontSize = document.querySelector("#sidebar-font-size");
|
|
7455
|
+
const codeFontSize = document.querySelector("#code-font-size");
|
|
7456
|
+
const source = document.querySelector("#scope-omit-source");
|
|
7457
|
+
if (!pop || !input || !sidebarFontSize || !codeFontSize || !source)
|
|
7458
|
+
return;
|
|
7459
|
+
await loadSettings();
|
|
7460
|
+
sidebarFontSize.value = savedSidebarFontSize();
|
|
7461
|
+
codeFontSize.value = savedCodeFontSize();
|
|
7462
|
+
input.value = effectiveScopeOmitDirs().join(`
|
|
7463
|
+
`);
|
|
7464
|
+
source.textContent = 'Saved for project "' + (PROJECT_NAME || "default") + '" in this browser. Source: ' + scopeOmitSourceLabel() + ". Used by tree, Ctrl+K, and Ctrl+G. Reset removes the browser override.";
|
|
7465
|
+
pop.hidden = false;
|
|
7466
|
+
sidebarFontSize.focus();
|
|
7467
|
+
}
|
|
7468
|
+
function closeScopeSettings() {
|
|
7469
|
+
const pop = document.querySelector("#scope-settings-popover");
|
|
7470
|
+
if (pop)
|
|
7471
|
+
pop.hidden = true;
|
|
7472
|
+
}
|
|
7473
|
+
function saveScopeSettings() {
|
|
7474
|
+
const input = document.querySelector("#scope-omit-dirs");
|
|
7475
|
+
const sidebarFontSize = document.querySelector("#sidebar-font-size");
|
|
7476
|
+
const codeFontSize = document.querySelector("#code-font-size");
|
|
7477
|
+
if (!input || !sidebarFontSize || !codeFontSize)
|
|
7478
|
+
return;
|
|
7479
|
+
localStorage.setItem(SIDEBAR_FONT_SIZE_STORAGE_KEY, normalizeViewerFontSize(sidebarFontSize.value));
|
|
7480
|
+
localStorage.setItem(CODE_FONT_SIZE_STORAGE_KEY, normalizeViewerFontSize(codeFontSize.value));
|
|
7481
|
+
applySidebarFontSize();
|
|
7482
|
+
applyCodeFontSize();
|
|
7483
|
+
localStorage.setItem(scopeOmitDirsStorageKey(), JSON.stringify(normalizeScopeOmitDirs(input.value)));
|
|
7484
|
+
closeScopeSettings();
|
|
7485
|
+
refreshRepositoryTreeAfterSettings();
|
|
7486
|
+
}
|
|
7487
|
+
function resetScopeSettings() {
|
|
7488
|
+
localStorage.removeItem(SIDEBAR_FONT_SIZE_STORAGE_KEY);
|
|
7489
|
+
localStorage.removeItem(CODE_FONT_SIZE_STORAGE_KEY);
|
|
7490
|
+
applySidebarFontSize("regular");
|
|
7491
|
+
applyCodeFontSize("regular");
|
|
7492
|
+
localStorage.removeItem(scopeOmitDirsStorageKey());
|
|
7493
|
+
closeScopeSettings();
|
|
7494
|
+
refreshRepositoryTreeAfterSettings();
|
|
7224
7495
|
}
|
|
7225
7496
|
function buildTree(files) {
|
|
7226
7497
|
const root = { name: "", dirs: {}, files: [], path: "", minOrder: Infinity, explicit: true };
|
|
@@ -7287,8 +7558,8 @@
|
|
|
7287
7558
|
li.dataset.explicit = "true";
|
|
7288
7559
|
if (dir.children_omitted) {
|
|
7289
7560
|
li.classList.add("children-omitted");
|
|
7290
|
-
li.classList.add(dir.children_omitted_reason === "
|
|
7291
|
-
li.title = dir.children_omitted_reason === "
|
|
7561
|
+
li.classList.add(dir.children_omitted_reason === "heavy" ? "children-omitted-heavy" : "children-omitted-internal");
|
|
7562
|
+
li.title = dir.children_omitted_reason === "heavy" ? "Large generated/vendor directory: open the detail pane to browse its contents" : "Internal Git metadata is not browsed";
|
|
7292
7563
|
}
|
|
7293
7564
|
li.style.setProperty("--lvl-pad", 12 + depth * 14 + "px");
|
|
7294
7565
|
const chev = document.createElement("span");
|
|
@@ -7312,9 +7583,9 @@
|
|
|
7312
7583
|
label.appendChild(dn);
|
|
7313
7584
|
if (dir.children_omitted) {
|
|
7314
7585
|
const omitted = document.createElement("span");
|
|
7315
|
-
omitted.className = "dir-omitted " + (dir.children_omitted_reason === "
|
|
7316
|
-
omitted.textContent = dir.children_omitted_reason === "
|
|
7317
|
-
omitted.title = dir.children_omitted_reason === "
|
|
7586
|
+
omitted.className = "dir-omitted " + (dir.children_omitted_reason === "heavy" ? "dir-omitted-heavy" : "dir-omitted-internal");
|
|
7587
|
+
omitted.textContent = dir.children_omitted_reason === "heavy" ? "skipped" : "private";
|
|
7588
|
+
omitted.title = dir.children_omitted_reason === "heavy" ? "Tree expansion is skipped, but the directory detail can be opened" : "This directory cannot be opened from the browser";
|
|
7318
7589
|
label.appendChild(omitted);
|
|
7319
7590
|
}
|
|
7320
7591
|
li.appendChild(label);
|
|
@@ -7346,18 +7617,16 @@
|
|
|
7346
7617
|
if (onFileClick) {
|
|
7347
7618
|
li.addEventListener("click", (e2) => {
|
|
7348
7619
|
e2.stopPropagation();
|
|
7349
|
-
if (dir.children_omitted_reason === "internal")
|
|
7620
|
+
if (dir.children_omitted_reason === "internal" || dir.children_omitted_reason === "truncated")
|
|
7350
7621
|
return;
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
}
|
|
7360
|
-
focusSidebarPanel();
|
|
7622
|
+
onFileClick({
|
|
7623
|
+
path: dir.path,
|
|
7624
|
+
display_path: dir.path,
|
|
7625
|
+
type: "tree",
|
|
7626
|
+
children_omitted: dir.children_omitted,
|
|
7627
|
+
children_omitted_reason: dir.children_omitted_reason
|
|
7628
|
+
});
|
|
7629
|
+
scheduleMainSurfaceFocus();
|
|
7361
7630
|
});
|
|
7362
7631
|
} else {
|
|
7363
7632
|
li.addEventListener("click", toggleDir);
|
|
@@ -7393,7 +7662,7 @@
|
|
|
7393
7662
|
onFileClick(f2);
|
|
7394
7663
|
else
|
|
7395
7664
|
scrollToFile(f2.path);
|
|
7396
|
-
|
|
7665
|
+
scheduleMainSurfaceFocus();
|
|
7397
7666
|
});
|
|
7398
7667
|
if (!onFileClick)
|
|
7399
7668
|
li.addEventListener("mouseenter", () => prefetchByPath(f2.path), { passive: true });
|
|
@@ -7426,7 +7695,7 @@
|
|
|
7426
7695
|
onFileClick(f2);
|
|
7427
7696
|
else
|
|
7428
7697
|
scrollToFile(f2.path);
|
|
7429
|
-
|
|
7698
|
+
scheduleMainSurfaceFocus();
|
|
7430
7699
|
});
|
|
7431
7700
|
if (!onFileClick)
|
|
7432
7701
|
li.addEventListener("mouseenter", () => prefetchByPath(f2.path), { passive: true });
|
|
@@ -7482,6 +7751,49 @@
|
|
|
7482
7751
|
return;
|
|
7483
7752
|
input.value = ref || "worktree";
|
|
7484
7753
|
wrap.hidden = !(STATE.route.screen === "file" && STATE.route.view === "blob");
|
|
7754
|
+
syncSidebarHeaderHeight();
|
|
7755
|
+
}
|
|
7756
|
+
function createRefSelectorInput(options) {
|
|
7757
|
+
const wrap = document.createElement("div");
|
|
7758
|
+
wrap.className = "ref-selector" + (options.extraClass ? " " + options.extraClass : "");
|
|
7759
|
+
wrap.dataset.refSelector = "";
|
|
7760
|
+
if (options.wrapperId)
|
|
7761
|
+
wrap.id = options.wrapperId;
|
|
7762
|
+
if (options.hidden)
|
|
7763
|
+
wrap.hidden = true;
|
|
7764
|
+
const icon = document.createElement("span");
|
|
7765
|
+
icon.className = "ref-selector-icon";
|
|
7766
|
+
icon.setAttribute("aria-hidden", "true");
|
|
7767
|
+
icon.innerHTML = iconSvg("octicon-git-branch", GIT_BRANCH_16_PATH);
|
|
7768
|
+
const input = document.createElement("input");
|
|
7769
|
+
input.className = "ref-input";
|
|
7770
|
+
input.id = options.id;
|
|
7771
|
+
input.readOnly = true;
|
|
7772
|
+
input.autocomplete = "off";
|
|
7773
|
+
input.placeholder = options.placeholder;
|
|
7774
|
+
if (options.title)
|
|
7775
|
+
input.title = options.title;
|
|
7776
|
+
if (options.value != null)
|
|
7777
|
+
input.value = options.value;
|
|
7778
|
+
const caret = document.createElement("span");
|
|
7779
|
+
caret.className = "ref-selector-caret";
|
|
7780
|
+
caret.setAttribute("aria-hidden", "true");
|
|
7781
|
+
caret.innerHTML = iconSvg("octicon-triangle-down", TRIANGLE_DOWN_16_PATH);
|
|
7782
|
+
wrap.append(icon, input, caret);
|
|
7783
|
+
return { wrap, input };
|
|
7784
|
+
}
|
|
7785
|
+
function hydrateRefSelectorMounts() {
|
|
7786
|
+
document.querySelectorAll("[data-ref-selector-mount]").forEach((mount) => {
|
|
7787
|
+
const { wrap } = createRefSelectorInput({
|
|
7788
|
+
id: mount.dataset.refId || "",
|
|
7789
|
+
placeholder: mount.dataset.placeholder || "ref...",
|
|
7790
|
+
title: mount.dataset.title,
|
|
7791
|
+
wrapperId: mount.dataset.wrapperId,
|
|
7792
|
+
extraClass: mount.dataset.extraClass,
|
|
7793
|
+
hidden: mount.hidden
|
|
7794
|
+
});
|
|
7795
|
+
mount.replaceWith(wrap);
|
|
7796
|
+
});
|
|
7485
7797
|
}
|
|
7486
7798
|
function renderMeta(meta) {
|
|
7487
7799
|
const el = $("#meta");
|
|
@@ -7489,8 +7801,7 @@
|
|
|
7489
7801
|
el.textContent = "";
|
|
7490
7802
|
return;
|
|
7491
7803
|
}
|
|
7492
|
-
|
|
7493
|
-
document.title = (meta.project ? meta.project + " - " : "") + "code viewer";
|
|
7804
|
+
setProjectName(meta.project || "");
|
|
7494
7805
|
el.innerHTML = "";
|
|
7495
7806
|
if (meta.branch) {
|
|
7496
7807
|
const b2 = document.createElement("span");
|
|
@@ -7757,7 +8068,7 @@
|
|
|
7757
8068
|
syncRepoTargetInput(repoFileTargetFromRoute() || "worktree");
|
|
7758
8069
|
}
|
|
7759
8070
|
function syncHeaderMenu() {
|
|
7760
|
-
document.querySelectorAll(".app-menu-item").forEach((link2) => {
|
|
8071
|
+
document.querySelectorAll(".app-menu-item, .global-help-link").forEach((link2) => {
|
|
7761
8072
|
const fileRouteOwner = STATE.route.screen === "file" && STATE.route.view === "blob" ? "repo" : "diff";
|
|
7762
8073
|
const active = link2.dataset.route === STATE.route.screen || STATE.route.screen === "file" && link2.dataset.route === fileRouteOwner;
|
|
7763
8074
|
link2.classList.toggle("active", active);
|
|
@@ -8061,28 +8372,23 @@
|
|
|
8061
8372
|
function repoRoute(ref, path) {
|
|
8062
8373
|
return { screen: "repo", ref: ref || "worktree", path, range: currentRange() };
|
|
8063
8374
|
}
|
|
8064
|
-
function
|
|
8065
|
-
input.
|
|
8066
|
-
|
|
8375
|
+
function wireRefSelectorInput(input, onPick) {
|
|
8376
|
+
const wrap = input.closest("[data-ref-selector]");
|
|
8377
|
+
wrap?.addEventListener("click", (e2) => {
|
|
8067
8378
|
e2.stopPropagation();
|
|
8068
8379
|
openPopover(input);
|
|
8069
8380
|
});
|
|
8070
|
-
input.addEventListener("mousedown", (e2) => {
|
|
8071
|
-
if (popover.hidden) {
|
|
8072
|
-
e2.preventDefault();
|
|
8073
|
-
input.focus();
|
|
8074
|
-
}
|
|
8075
|
-
});
|
|
8076
8381
|
input.addEventListener("keydown", (e2) => {
|
|
8077
|
-
if (e2.key === "Enter") {
|
|
8382
|
+
if (e2.key === "Enter" || e2.key === " ") {
|
|
8078
8383
|
e2.preventDefault();
|
|
8079
|
-
|
|
8384
|
+
openPopover(input);
|
|
8080
8385
|
} else if (e2.key === "Escape") {
|
|
8081
8386
|
closePopover();
|
|
8082
8387
|
input.blur();
|
|
8083
8388
|
}
|
|
8084
8389
|
});
|
|
8085
|
-
|
|
8390
|
+
if (onPick)
|
|
8391
|
+
input.addEventListener("change", () => onPick(input.value || "worktree"));
|
|
8086
8392
|
}
|
|
8087
8393
|
function createRepoBreadcrumb(target, path) {
|
|
8088
8394
|
const nav = document.createElement("nav");
|
|
@@ -8117,7 +8423,7 @@
|
|
|
8117
8423
|
return nav;
|
|
8118
8424
|
}
|
|
8119
8425
|
async function renderRepo(meta) {
|
|
8120
|
-
|
|
8426
|
+
setProjectName(meta.project || "");
|
|
8121
8427
|
setPageMode();
|
|
8122
8428
|
removeStandaloneSource();
|
|
8123
8429
|
$("#empty").classList.add("hidden");
|
|
@@ -8130,21 +8436,19 @@
|
|
|
8130
8436
|
const target = $("#diff");
|
|
8131
8437
|
const shell = document.createElement("section");
|
|
8132
8438
|
shell.className = "gdp-repo-shell";
|
|
8133
|
-
const targetPicker =
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
targetPicker
|
|
8140
|
-
targetPicker.title = "repository ref";
|
|
8141
|
-
wireRepoTargetPicker(targetPicker, (ref) => {
|
|
8439
|
+
const { wrap: targetPickerWrap, input: targetPicker } = createRefSelectorInput({
|
|
8440
|
+
id: "repo-ref",
|
|
8441
|
+
placeholder: "ref...",
|
|
8442
|
+
title: "repository ref",
|
|
8443
|
+
value: meta.ref || "worktree"
|
|
8444
|
+
});
|
|
8445
|
+
wireRefSelectorInput(targetPicker, (ref) => {
|
|
8142
8446
|
setRoute(repoRoute(ref, ""));
|
|
8143
8447
|
loadRepo();
|
|
8144
8448
|
});
|
|
8145
8449
|
const toolbar = document.createElement("div");
|
|
8146
8450
|
toolbar.className = "gdp-file-detail-header gdp-repo-toolbar";
|
|
8147
|
-
toolbar.append(createRepoBreadcrumb(meta.ref, meta.path || ""), createOpenPathButton(meta.path || "", "directory", "open this folder in OS")
|
|
8451
|
+
toolbar.append(targetPickerWrap, createRepoBreadcrumb(meta.ref, meta.path || ""), createOpenPathButton(meta.path || "", "directory", "open this folder in OS"));
|
|
8148
8452
|
shell.appendChild(toolbar);
|
|
8149
8453
|
const listCard = document.createElement("section");
|
|
8150
8454
|
listCard.className = "gdp-file-shell loaded gdp-repo-list-shell";
|
|
@@ -8263,6 +8567,7 @@
|
|
|
8263
8567
|
shell.appendChild(readme);
|
|
8264
8568
|
}
|
|
8265
8569
|
target.appendChild(shell);
|
|
8570
|
+
placeSidebarToggle();
|
|
8266
8571
|
}
|
|
8267
8572
|
function renderRepoBlobSidebar(currentPath, ref) {
|
|
8268
8573
|
syncRepoTargetInput(ref);
|
|
@@ -8279,6 +8584,7 @@
|
|
|
8279
8584
|
const params = new URLSearchParams;
|
|
8280
8585
|
params.set("ref", normalizedRef);
|
|
8281
8586
|
params.set("recursive", "1");
|
|
8587
|
+
appendScopeOmitDirsParam(params);
|
|
8282
8588
|
REPO_SIDEBAR_LOAD_REF = normalizedRef;
|
|
8283
8589
|
const load2 = trackLoad(fetch("/_tree?" + params.toString()).then((r2) => {
|
|
8284
8590
|
if (!r2.ok)
|
|
@@ -8363,7 +8669,10 @@
|
|
|
8363
8669
|
document.querySelectorAll(".gdp-file-shell.pending").forEach((c2) => lazyObserver.observe(c2));
|
|
8364
8670
|
}
|
|
8365
8671
|
window.addEventListener("scroll", () => enqueueInitialLoads(), { passive: true });
|
|
8366
|
-
window.addEventListener("resize", () =>
|
|
8672
|
+
window.addEventListener("resize", () => {
|
|
8673
|
+
enqueueInitialLoads();
|
|
8674
|
+
syncSidebarHeaderHeight();
|
|
8675
|
+
}, { passive: true });
|
|
8367
8676
|
document.addEventListener("visibilitychange", () => {
|
|
8368
8677
|
if (!document.hidden)
|
|
8369
8678
|
enqueueInitialLoads();
|
|
@@ -9606,6 +9915,9 @@
|
|
|
9606
9915
|
url.searchParams.delete("virtual");
|
|
9607
9916
|
return url.pathname + url.search;
|
|
9608
9917
|
}
|
|
9918
|
+
function buildFileRangeUrl(target, start, end) {
|
|
9919
|
+
return "/file_range?path=" + encodeURIComponent(target.path) + "&ref=" + encodeURIComponent(target.ref || "worktree") + "&start=" + encodeURIComponent(String(start)) + "&end=" + encodeURIComponent(String(end));
|
|
9920
|
+
}
|
|
9609
9921
|
function currentSourceLineTarget(target) {
|
|
9610
9922
|
const routeTarget = sourceTargetFromRoute();
|
|
9611
9923
|
return sourceTargetsEqual(routeTarget, target) && STATE.route.screen === "file" ? STATE.route.line : undefined;
|
|
@@ -9669,6 +9981,161 @@
|
|
|
9669
9981
|
document.addEventListener("mouseup", () => {
|
|
9670
9982
|
SOURCE_LINE_DRAG = null;
|
|
9671
9983
|
});
|
|
9984
|
+
function virtualSourceSearchRanges(line, query) {
|
|
9985
|
+
const needle = query.toLowerCase();
|
|
9986
|
+
if (!needle)
|
|
9987
|
+
return [];
|
|
9988
|
+
const haystack = line.toLowerCase();
|
|
9989
|
+
const ranges = [];
|
|
9990
|
+
let cursor = 0;
|
|
9991
|
+
while (cursor <= haystack.length) {
|
|
9992
|
+
const index = haystack.indexOf(needle, cursor);
|
|
9993
|
+
if (index < 0)
|
|
9994
|
+
break;
|
|
9995
|
+
ranges.push({ start: index, end: index + query.length });
|
|
9996
|
+
cursor = Math.max(index + query.length, index + 1);
|
|
9997
|
+
}
|
|
9998
|
+
return ranges;
|
|
9999
|
+
}
|
|
10000
|
+
function collectVirtualSourceSearchMatches(lines, query, max = 5000) {
|
|
10001
|
+
const matches = [];
|
|
10002
|
+
for (let index = 0;index < lines.length && matches.length < max; index++) {
|
|
10003
|
+
for (const range of virtualSourceSearchRanges(lines[index] || "", query)) {
|
|
10004
|
+
matches.push({ line: index + 1, start: range.start, end: range.end });
|
|
10005
|
+
if (matches.length >= max)
|
|
10006
|
+
break;
|
|
10007
|
+
}
|
|
10008
|
+
}
|
|
10009
|
+
return matches;
|
|
10010
|
+
}
|
|
10011
|
+
function appendVirtualSourceLineCode(code2, line, query, activeRange, lineNumber) {
|
|
10012
|
+
const ranges = virtualSourceSearchRanges(line, query);
|
|
10013
|
+
if (!ranges.length)
|
|
10014
|
+
return false;
|
|
10015
|
+
let cursor = 0;
|
|
10016
|
+
for (const range of ranges) {
|
|
10017
|
+
if (range.start > cursor)
|
|
10018
|
+
code2.appendChild(document.createTextNode(line.slice(cursor, range.start)));
|
|
10019
|
+
const mark = document.createElement("mark");
|
|
10020
|
+
const active = !!activeRange && activeRange.line === lineNumber && activeRange.start === range.start && activeRange.end === range.end;
|
|
10021
|
+
mark.className = active ? "gdp-source-virtual-search-hit active" : "gdp-source-virtual-search-hit";
|
|
10022
|
+
mark.textContent = line.slice(range.start, range.end);
|
|
10023
|
+
code2.appendChild(mark);
|
|
10024
|
+
cursor = range.end;
|
|
10025
|
+
}
|
|
10026
|
+
if (cursor < line.length)
|
|
10027
|
+
code2.appendChild(document.createTextNode(line.slice(cursor)));
|
|
10028
|
+
return true;
|
|
10029
|
+
}
|
|
10030
|
+
function createVirtualSourceSearch(wrap, scroller, findMatches, renderFn) {
|
|
10031
|
+
const bar = document.createElement("div");
|
|
10032
|
+
bar.className = "gdp-source-virtual-search";
|
|
10033
|
+
const input = document.createElement("input");
|
|
10034
|
+
input.type = "search";
|
|
10035
|
+
input.placeholder = "Find in file";
|
|
10036
|
+
input.autocomplete = "off";
|
|
10037
|
+
input.spellcheck = false;
|
|
10038
|
+
const count = document.createElement("span");
|
|
10039
|
+
count.className = "gdp-source-virtual-search-count";
|
|
10040
|
+
const previous = document.createElement("button");
|
|
10041
|
+
previous.type = "button";
|
|
10042
|
+
previous.textContent = "Prev";
|
|
10043
|
+
const next = document.createElement("button");
|
|
10044
|
+
next.type = "button";
|
|
10045
|
+
next.textContent = "Next";
|
|
10046
|
+
const close = document.createElement("button");
|
|
10047
|
+
close.type = "button";
|
|
10048
|
+
close.textContent = "Close";
|
|
10049
|
+
bar.append(input, count, previous, next, close);
|
|
10050
|
+
wrap.querySelector(".gdp-source-virtual-info")?.appendChild(bar);
|
|
10051
|
+
bar.hidden = true;
|
|
10052
|
+
let matches = [];
|
|
10053
|
+
let active = -1;
|
|
10054
|
+
let debounce = 0;
|
|
10055
|
+
let searchVersion = 0;
|
|
10056
|
+
const hide = () => {
|
|
10057
|
+
bar.hidden = true;
|
|
10058
|
+
renderFn();
|
|
10059
|
+
scroller.focus({ preventScroll: true });
|
|
10060
|
+
};
|
|
10061
|
+
const sync = () => {
|
|
10062
|
+
const query = input.value;
|
|
10063
|
+
const version = ++searchVersion;
|
|
10064
|
+
if (!query) {
|
|
10065
|
+
matches = [];
|
|
10066
|
+
active = -1;
|
|
10067
|
+
count.textContent = "";
|
|
10068
|
+
renderFn();
|
|
10069
|
+
return;
|
|
10070
|
+
}
|
|
10071
|
+
count.textContent = "Searching...";
|
|
10072
|
+
findMatches(query).then((nextMatches) => {
|
|
10073
|
+
if (version !== searchVersion)
|
|
10074
|
+
return;
|
|
10075
|
+
matches = nextMatches;
|
|
10076
|
+
active = matches.length ? Math.max(0, Math.min(active, matches.length - 1)) : -1;
|
|
10077
|
+
count.textContent = matches.length ? active + 1 + " / " + matches.length : "0 / 0";
|
|
10078
|
+
if (active >= 0)
|
|
10079
|
+
scroller.scrollTop = Math.max(0, (matches[active].line - 1) * VIRTUAL_SOURCE_ROW_HEIGHT - VIRTUAL_SOURCE_ROW_HEIGHT * 3);
|
|
10080
|
+
renderFn();
|
|
10081
|
+
}).catch(() => {
|
|
10082
|
+
if (version !== searchVersion)
|
|
10083
|
+
return;
|
|
10084
|
+
matches = [];
|
|
10085
|
+
active = -1;
|
|
10086
|
+
count.textContent = "Search failed";
|
|
10087
|
+
renderFn();
|
|
10088
|
+
});
|
|
10089
|
+
};
|
|
10090
|
+
const scheduleSync = () => {
|
|
10091
|
+
if (debounce)
|
|
10092
|
+
window.clearTimeout(debounce);
|
|
10093
|
+
debounce = window.setTimeout(sync, 120);
|
|
10094
|
+
};
|
|
10095
|
+
const move = (direction) => {
|
|
10096
|
+
if (!matches.length)
|
|
10097
|
+
return;
|
|
10098
|
+
active = (active + direction + matches.length) % matches.length;
|
|
10099
|
+
count.textContent = active + 1 + " / " + matches.length;
|
|
10100
|
+
scroller.scrollTop = Math.max(0, (matches[active].line - 1) * VIRTUAL_SOURCE_ROW_HEIGHT - VIRTUAL_SOURCE_ROW_HEIGHT * 3);
|
|
10101
|
+
renderFn();
|
|
10102
|
+
};
|
|
10103
|
+
input.addEventListener("input", () => {
|
|
10104
|
+
active = 0;
|
|
10105
|
+
scheduleSync();
|
|
10106
|
+
});
|
|
10107
|
+
input.addEventListener("keydown", (e2) => {
|
|
10108
|
+
if (e2.key === "Escape") {
|
|
10109
|
+
e2.preventDefault();
|
|
10110
|
+
hide();
|
|
10111
|
+
} else if (e2.key === "Enter") {
|
|
10112
|
+
e2.preventDefault();
|
|
10113
|
+
move(e2.shiftKey ? -1 : 1);
|
|
10114
|
+
}
|
|
10115
|
+
});
|
|
10116
|
+
previous.addEventListener("click", () => move(-1));
|
|
10117
|
+
next.addEventListener("click", () => move(1));
|
|
10118
|
+
close.addEventListener("click", hide);
|
|
10119
|
+
return {
|
|
10120
|
+
open: () => {
|
|
10121
|
+
bar.hidden = false;
|
|
10122
|
+
input.focus();
|
|
10123
|
+
input.select();
|
|
10124
|
+
sync();
|
|
10125
|
+
},
|
|
10126
|
+
query: () => bar.hidden ? "" : input.value,
|
|
10127
|
+
activeRange: () => active >= 0 ? matches[active] || null : null
|
|
10128
|
+
};
|
|
10129
|
+
}
|
|
10130
|
+
function openVirtualSourceSearchFromKeyboard(targetEl) {
|
|
10131
|
+
const active = targetEl?.closest("#content .gdp-source-virtual");
|
|
10132
|
+
const fallback = document.querySelector("#content .gdp-source-viewer.virtual .gdp-source-virtual:not([hidden])");
|
|
10133
|
+
const search = active?.__gdpVirtualSourceSearch || fallback?.__gdpVirtualSourceSearch;
|
|
10134
|
+
if (!search)
|
|
10135
|
+
return false;
|
|
10136
|
+
search.open();
|
|
10137
|
+
return true;
|
|
10138
|
+
}
|
|
9672
10139
|
function renderVirtualSource(target, textValue, lines, hljsRef, lang) {
|
|
9673
10140
|
const wrap = document.createElement("div");
|
|
9674
10141
|
wrap.className = "gdp-source-virtual";
|
|
@@ -9717,6 +10184,9 @@
|
|
|
9717
10184
|
info.append(badge, summary, actions);
|
|
9718
10185
|
const scroller = document.createElement("div");
|
|
9719
10186
|
scroller.className = "gdp-source-virtual-scroller";
|
|
10187
|
+
scroller.tabIndex = 0;
|
|
10188
|
+
scroller.setAttribute("role", "region");
|
|
10189
|
+
scroller.setAttribute("aria-label", target.path + " source code");
|
|
9720
10190
|
const spacer = document.createElement("div");
|
|
9721
10191
|
spacer.className = "gdp-source-virtual-spacer";
|
|
9722
10192
|
spacer.style.height = Math.max(1, lines.length * VIRTUAL_SOURCE_ROW_HEIGHT) + "px";
|
|
@@ -9728,6 +10198,7 @@
|
|
|
9728
10198
|
let raf = 0;
|
|
9729
10199
|
let renderedStart = -1;
|
|
9730
10200
|
let renderedEnd = -1;
|
|
10201
|
+
let search = null;
|
|
9731
10202
|
const render = () => {
|
|
9732
10203
|
raf = 0;
|
|
9733
10204
|
const viewportHeight = scroller.clientHeight || window.innerHeight;
|
|
@@ -9754,7 +10225,9 @@
|
|
|
9754
10225
|
const code2 = document.createElement("span");
|
|
9755
10226
|
code2.className = "gdp-source-virtual-line-code";
|
|
9756
10227
|
const line = lines[index] ?? "";
|
|
9757
|
-
|
|
10228
|
+
const searchQuery = search?.query() || "";
|
|
10229
|
+
const activeRange = search?.activeRange() || null;
|
|
10230
|
+
if (appendVirtualSourceLineCode(code2, line, searchQuery, activeRange, index + 1)) {} else if (hljsRef && hljsRef.highlight && lang && line.length <= VIRTUAL_SOURCE_HIGHLIGHT_MAX_LINE_LENGTH && (!hljsRef.getLanguage || hljsRef.getLanguage(lang))) {
|
|
9758
10231
|
try {
|
|
9759
10232
|
code2.innerHTML = hljsRef.highlight(line, { language: lang, ignoreIllegals: true }).value;
|
|
9760
10233
|
code2.classList.add("hljs");
|
|
@@ -9775,6 +10248,8 @@
|
|
|
9775
10248
|
};
|
|
9776
10249
|
scroller.__gdpRenderVirtualSource = render;
|
|
9777
10250
|
scroller.addEventListener("scroll", schedule, { passive: true });
|
|
10251
|
+
search = createVirtualSourceSearch(wrap, scroller, (query) => Promise.resolve(collectVirtualSourceSearchMatches(lines, query)), render);
|
|
10252
|
+
wrap.__gdpVirtualSourceSearch = search;
|
|
9778
10253
|
let resizeObserver = null;
|
|
9779
10254
|
resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(() => {
|
|
9780
10255
|
if (!scroller.isConnected) {
|
|
@@ -9789,6 +10264,253 @@
|
|
|
9789
10264
|
schedule();
|
|
9790
10265
|
return wrap;
|
|
9791
10266
|
}
|
|
10267
|
+
function renderPagedVirtualSource(target, size, initialStart, initialLines, initialComplete, initialTotal, hljsRef, lang, signal) {
|
|
10268
|
+
const wrap = document.createElement("div");
|
|
10269
|
+
wrap.className = "gdp-source-virtual";
|
|
10270
|
+
const info = document.createElement("div");
|
|
10271
|
+
info.className = "gdp-source-virtual-info";
|
|
10272
|
+
const badge = document.createElement("span");
|
|
10273
|
+
badge.className = "gdp-source-virtual-badge";
|
|
10274
|
+
badge.textContent = "Virtual mode";
|
|
10275
|
+
const summary = document.createElement("span");
|
|
10276
|
+
summary.className = "gdp-source-virtual-summary";
|
|
10277
|
+
const actions = document.createElement("span");
|
|
10278
|
+
actions.className = "gdp-source-virtual-actions";
|
|
10279
|
+
const raw = document.createElement("a");
|
|
10280
|
+
raw.className = "gdp-source-virtual-action";
|
|
10281
|
+
raw.href = buildRawFileUrl(target);
|
|
10282
|
+
raw.target = "_blank";
|
|
10283
|
+
raw.rel = "noreferrer";
|
|
10284
|
+
raw.textContent = "Open raw";
|
|
10285
|
+
const full = document.createElement("a");
|
|
10286
|
+
full.className = "gdp-source-virtual-action";
|
|
10287
|
+
full.href = buildCurrentFileRouteWithVirtualMode(target, "off");
|
|
10288
|
+
full.textContent = "Open full view";
|
|
10289
|
+
full.title = "Render every line without paged loading. This can be slow for large files.";
|
|
10290
|
+
full.addEventListener("click", (e2) => {
|
|
10291
|
+
e2.preventDefault();
|
|
10292
|
+
const url = new URL(full.href, window.location.origin);
|
|
10293
|
+
setRoute(parseRoute(url.pathname, url.search, currentRange()), true);
|
|
10294
|
+
renderStandaloneSource(target);
|
|
10295
|
+
});
|
|
10296
|
+
actions.append(raw, full);
|
|
10297
|
+
info.append(badge, summary, actions);
|
|
10298
|
+
const scroller = document.createElement("div");
|
|
10299
|
+
scroller.className = "gdp-source-virtual-scroller";
|
|
10300
|
+
scroller.tabIndex = 0;
|
|
10301
|
+
scroller.setAttribute("role", "region");
|
|
10302
|
+
scroller.setAttribute("aria-label", target.path + " source code");
|
|
10303
|
+
const spacer = document.createElement("div");
|
|
10304
|
+
spacer.className = "gdp-source-virtual-spacer";
|
|
10305
|
+
const windowEl = document.createElement("div");
|
|
10306
|
+
windowEl.className = "gdp-source-virtual-window";
|
|
10307
|
+
spacer.appendChild(windowEl);
|
|
10308
|
+
scroller.appendChild(spacer);
|
|
10309
|
+
wrap.append(info, scroller);
|
|
10310
|
+
const lines = new Map;
|
|
10311
|
+
const requestedPages = new Set;
|
|
10312
|
+
const failedPages = new Set;
|
|
10313
|
+
const targetLine = lineTargetStart(currentSourceLineTarget(target)) || 1;
|
|
10314
|
+
let complete = initialComplete;
|
|
10315
|
+
let totalRows = initialComplete ? Math.max(1, initialTotal) : Math.max(initialTotal || 1, initialStart + initialLines.length - 1, targetLine + VIRTUAL_SOURCE_PAGE_SIZE);
|
|
10316
|
+
initialLines.forEach((line, index) => lines.set(initialStart + index, line));
|
|
10317
|
+
requestedPages.add(Math.max(0, Math.floor((initialStart - 1) / VIRTUAL_SOURCE_PAGE_SIZE)));
|
|
10318
|
+
for (let line = initialStart;line < initialStart + initialLines.length; line += VIRTUAL_SOURCE_PAGE_SIZE) {
|
|
10319
|
+
requestedPages.add(Math.max(0, Math.floor((line - 1) / VIRTUAL_SOURCE_PAGE_SIZE)));
|
|
10320
|
+
}
|
|
10321
|
+
const updateTotals = () => {
|
|
10322
|
+
SOURCE_CURSOR_TOTALS.set(sourceCursorKey(target), totalRows);
|
|
10323
|
+
summary.textContent = (complete ? totalRows.toLocaleString() : lines.size.toLocaleString() + "+") + " lines loaded from " + formatBytes(size) + ". More rows load as you scroll.";
|
|
10324
|
+
spacer.style.height = Math.max(1, totalRows * VIRTUAL_SOURCE_ROW_HEIGHT) + "px";
|
|
10325
|
+
};
|
|
10326
|
+
const loadPage = (line) => {
|
|
10327
|
+
if (signal?.aborted || complete && line > totalRows)
|
|
10328
|
+
return;
|
|
10329
|
+
const page = Math.max(0, Math.floor((line - 1) / VIRTUAL_SOURCE_PAGE_SIZE));
|
|
10330
|
+
if (requestedPages.has(page))
|
|
10331
|
+
return;
|
|
10332
|
+
if (failedPages.has(page))
|
|
10333
|
+
return;
|
|
10334
|
+
requestedPages.add(page);
|
|
10335
|
+
const start = page * VIRTUAL_SOURCE_PAGE_SIZE + 1;
|
|
10336
|
+
const end = start + VIRTUAL_SOURCE_PAGE_SIZE - 1;
|
|
10337
|
+
trackLoad(fetch(buildFileRangeUrl(target, start, end), { signal }).then((res) => res.ok ? res.json() : null).then((data) => {
|
|
10338
|
+
if (!data || signal?.aborted)
|
|
10339
|
+
return;
|
|
10340
|
+
data.lines.forEach((lineValue, index) => lines.set(data.start + index, lineValue));
|
|
10341
|
+
totalRows = data.complete ? Math.max(1, data.total) : Math.max(totalRows, data.total, end + VIRTUAL_SOURCE_PAGE_SIZE);
|
|
10342
|
+
complete = data.complete === true;
|
|
10343
|
+
updateTotals();
|
|
10344
|
+
renderedStart = -1;
|
|
10345
|
+
renderedEnd = -1;
|
|
10346
|
+
render();
|
|
10347
|
+
}).catch((err) => {
|
|
10348
|
+
if (!isAbortError(err)) {
|
|
10349
|
+
failedPages.add(page);
|
|
10350
|
+
renderedStart = -1;
|
|
10351
|
+
renderedEnd = -1;
|
|
10352
|
+
schedule();
|
|
10353
|
+
}
|
|
10354
|
+
}));
|
|
10355
|
+
};
|
|
10356
|
+
let raf = 0;
|
|
10357
|
+
let renderedStart = -1;
|
|
10358
|
+
let renderedEnd = -1;
|
|
10359
|
+
let search = null;
|
|
10360
|
+
let searchController = null;
|
|
10361
|
+
const render = () => {
|
|
10362
|
+
raf = 0;
|
|
10363
|
+
const viewportHeight = scroller.clientHeight || window.innerHeight;
|
|
10364
|
+
const overscan = 20;
|
|
10365
|
+
const start = Math.max(0, Math.floor(scroller.scrollTop / VIRTUAL_SOURCE_ROW_HEIGHT) - overscan);
|
|
10366
|
+
const end = Math.min(totalRows, Math.ceil((scroller.scrollTop + viewportHeight) / VIRTUAL_SOURCE_ROW_HEIGHT) + overscan);
|
|
10367
|
+
if (start === renderedStart && end === renderedEnd)
|
|
10368
|
+
return;
|
|
10369
|
+
renderedStart = start;
|
|
10370
|
+
renderedEnd = end;
|
|
10371
|
+
windowEl.replaceChildren();
|
|
10372
|
+
windowEl.style.transform = "translateY(" + start * VIRTUAL_SOURCE_ROW_HEIGHT + "px)";
|
|
10373
|
+
const fragment = document.createDocumentFragment();
|
|
10374
|
+
for (let index = start;index < end; index++) {
|
|
10375
|
+
const lineNumber = index + 1;
|
|
10376
|
+
if (!lines.has(lineNumber))
|
|
10377
|
+
loadPage(lineNumber);
|
|
10378
|
+
const row = document.createElement("div");
|
|
10379
|
+
row.className = "gdp-source-virtual-row";
|
|
10380
|
+
row.dataset.line = String(lineNumber);
|
|
10381
|
+
row.classList.toggle("gdp-source-line-target", lineInSourceTarget(lineNumber, currentSourceLineTarget(target)));
|
|
10382
|
+
row.classList.toggle("gdp-source-cursor", sourceCursorMatches(target, lineNumber));
|
|
10383
|
+
const num = document.createElement("span");
|
|
10384
|
+
num.className = "gdp-source-virtual-line-number";
|
|
10385
|
+
num.textContent = String(lineNumber);
|
|
10386
|
+
bindSourceLineNumber(num, wrap, target, lineNumber);
|
|
10387
|
+
const code2 = document.createElement("span");
|
|
10388
|
+
code2.className = "gdp-source-virtual-line-code";
|
|
10389
|
+
const line = lines.get(lineNumber);
|
|
10390
|
+
if (line == null) {
|
|
10391
|
+
code2.textContent = "";
|
|
10392
|
+
} else if (appendVirtualSourceLineCode(code2, line, search?.query() || "", search?.activeRange() || null, lineNumber)) {} else if (hljsRef && hljsRef.highlight && lang && line.length <= VIRTUAL_SOURCE_HIGHLIGHT_MAX_LINE_LENGTH && (!hljsRef.getLanguage || hljsRef.getLanguage(lang))) {
|
|
10393
|
+
try {
|
|
10394
|
+
code2.innerHTML = hljsRef.highlight(line, { language: lang, ignoreIllegals: true }).value;
|
|
10395
|
+
code2.classList.add("hljs");
|
|
10396
|
+
} catch {
|
|
10397
|
+
code2.textContent = line;
|
|
10398
|
+
}
|
|
10399
|
+
} else {
|
|
10400
|
+
code2.textContent = line;
|
|
10401
|
+
}
|
|
10402
|
+
row.append(num, code2);
|
|
10403
|
+
fragment.appendChild(row);
|
|
10404
|
+
}
|
|
10405
|
+
windowEl.appendChild(fragment);
|
|
10406
|
+
if (!complete && totalRows - end < VIRTUAL_SOURCE_PAGE_SIZE) {
|
|
10407
|
+
totalRows += VIRTUAL_SOURCE_PAGE_SIZE;
|
|
10408
|
+
updateTotals();
|
|
10409
|
+
}
|
|
10410
|
+
};
|
|
10411
|
+
const schedule = () => {
|
|
10412
|
+
if (!raf)
|
|
10413
|
+
raf = requestAnimationFrame(render);
|
|
10414
|
+
};
|
|
10415
|
+
scroller.__gdpRenderVirtualSource = render;
|
|
10416
|
+
scroller.addEventListener("scroll", schedule, { passive: true });
|
|
10417
|
+
const findPagedMatches = async (query, matchSignal) => {
|
|
10418
|
+
const matches = [];
|
|
10419
|
+
let startLine = 1;
|
|
10420
|
+
let done = false;
|
|
10421
|
+
while (!done && matches.length < 5000) {
|
|
10422
|
+
const endLine = startLine + VIRTUAL_SOURCE_PAGE_SIZE - 1;
|
|
10423
|
+
const data = await trackLoad(fetch(buildFileRangeUrl(target, startLine, endLine), { signal: matchSignal }).then((res) => {
|
|
10424
|
+
if (!res.ok)
|
|
10425
|
+
throw new Error("file range failed");
|
|
10426
|
+
return res.json();
|
|
10427
|
+
}));
|
|
10428
|
+
if (matchSignal?.aborted)
|
|
10429
|
+
return [];
|
|
10430
|
+
data.lines.forEach((lineValue, index) => {
|
|
10431
|
+
const lineNumber = data.start + index;
|
|
10432
|
+
lines.set(lineNumber, lineValue);
|
|
10433
|
+
for (const range of virtualSourceSearchRanges(lineValue, query)) {
|
|
10434
|
+
matches.push({ line: lineNumber, start: range.start, end: range.end });
|
|
10435
|
+
if (matches.length >= 5000)
|
|
10436
|
+
break;
|
|
10437
|
+
}
|
|
10438
|
+
});
|
|
10439
|
+
totalRows = data.complete ? Math.max(1, data.total) : Math.max(totalRows, data.total, endLine + VIRTUAL_SOURCE_PAGE_SIZE);
|
|
10440
|
+
complete = data.complete === true;
|
|
10441
|
+
updateTotals();
|
|
10442
|
+
if (data.complete || !data.lines.length)
|
|
10443
|
+
done = true;
|
|
10444
|
+
else
|
|
10445
|
+
startLine = data.start + data.lines.length;
|
|
10446
|
+
}
|
|
10447
|
+
renderedStart = -1;
|
|
10448
|
+
renderedEnd = -1;
|
|
10449
|
+
schedule();
|
|
10450
|
+
return matches;
|
|
10451
|
+
};
|
|
10452
|
+
search = createVirtualSourceSearch(wrap, scroller, (query) => {
|
|
10453
|
+
searchController?.abort();
|
|
10454
|
+
searchController = new AbortController;
|
|
10455
|
+
return findPagedMatches(query, searchController.signal);
|
|
10456
|
+
}, render);
|
|
10457
|
+
wrap.__gdpVirtualSourceSearch = search;
|
|
10458
|
+
let resizeObserver = null;
|
|
10459
|
+
resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(() => {
|
|
10460
|
+
if (!scroller.isConnected) {
|
|
10461
|
+
resizeObserver?.disconnect();
|
|
10462
|
+
resizeObserver = null;
|
|
10463
|
+
return;
|
|
10464
|
+
}
|
|
10465
|
+
schedule();
|
|
10466
|
+
}) : null;
|
|
10467
|
+
resizeObserver?.observe(scroller);
|
|
10468
|
+
updateTotals();
|
|
10469
|
+
if (targetLine <= 1) {
|
|
10470
|
+
render();
|
|
10471
|
+
schedule();
|
|
10472
|
+
}
|
|
10473
|
+
return wrap;
|
|
10474
|
+
}
|
|
10475
|
+
async function renderPagedSourceText(card, target, size, signal) {
|
|
10476
|
+
const body = card.querySelector(".gdp-file-detail-body, .d2h-files-diff, .d2h-file-diff, .gdp-media, .gdp-source-viewer");
|
|
10477
|
+
const isStandalone = card.classList.contains("gdp-standalone-source");
|
|
10478
|
+
const view = document.createElement("div");
|
|
10479
|
+
view.className = "gdp-source-viewer virtual";
|
|
10480
|
+
const header = isStandalone ? null : document.createElement("div");
|
|
10481
|
+
if (header) {
|
|
10482
|
+
header.className = "gdp-source-meta";
|
|
10483
|
+
header.textContent = target.path + " @ " + target.ref;
|
|
10484
|
+
view.appendChild(header);
|
|
10485
|
+
}
|
|
10486
|
+
const lineTarget = lineTargetStart(currentSourceLineTarget(target)) || 1;
|
|
10487
|
+
const initialPage = Math.max(0, Math.floor((lineTarget - 1) / VIRTUAL_SOURCE_PAGE_SIZE));
|
|
10488
|
+
const initialStart = initialPage * VIRTUAL_SOURCE_PAGE_SIZE + 1;
|
|
10489
|
+
const initialEnd = initialStart + VIRTUAL_SOURCE_PAGE_SIZE - 1;
|
|
10490
|
+
const lang = inferLang(target.path);
|
|
10491
|
+
const hljsRef = STATE.syntaxHighlight ? await loadSyntaxHighlighter() : null;
|
|
10492
|
+
if (signal?.aborted)
|
|
10493
|
+
return false;
|
|
10494
|
+
const initial = await trackLoad(fetch(buildFileRangeUrl(target, initialStart, initialEnd), { signal }).then((res) => res.ok ? res.json() : null));
|
|
10495
|
+
if (!initial)
|
|
10496
|
+
return false;
|
|
10497
|
+
if (signal?.aborted)
|
|
10498
|
+
return false;
|
|
10499
|
+
const tabsHost = card.querySelector(".gdp-file-detail-tabs");
|
|
10500
|
+
if (tabsHost) {
|
|
10501
|
+
tabsHost.hidden = false;
|
|
10502
|
+
tabsHost.replaceChildren(createSourceTabs("code").tabs);
|
|
10503
|
+
}
|
|
10504
|
+
SOURCE_CURSOR_TOTALS.set(sourceCursorKey(target), Math.max(1, initial.total, lineTarget));
|
|
10505
|
+
resetSourceCursorForTarget(target, Math.max(1, initial.total, lineTarget));
|
|
10506
|
+
const virtualCode = renderPagedVirtualSource(target, size, initialStart, initial.lines, initial.complete === true, initial.total, hljsRef, lang, signal);
|
|
10507
|
+
view.appendChild(virtualCode);
|
|
10508
|
+
if (body)
|
|
10509
|
+
body.replaceWith(view);
|
|
10510
|
+
else
|
|
10511
|
+
card.appendChild(view);
|
|
10512
|
+
return true;
|
|
10513
|
+
}
|
|
9792
10514
|
function renderSourceMedia(card, target, mediaKind) {
|
|
9793
10515
|
const body = card.querySelector(".gdp-file-detail-body, .d2h-files-diff, .d2h-file-diff, .gdp-media, .gdp-source-viewer");
|
|
9794
10516
|
const isStandalone = card.classList.contains("gdp-standalone-source");
|
|
@@ -9969,6 +10691,7 @@
|
|
|
9969
10691
|
} else {
|
|
9970
10692
|
root.prepend(card);
|
|
9971
10693
|
}
|
|
10694
|
+
placeSidebarToggle();
|
|
9972
10695
|
const controller = new AbortController;
|
|
9973
10696
|
ACTIVE_SOURCE_LOAD = { controller, req, target, card };
|
|
9974
10697
|
renderSourceLoading(card, target, () => cancelActiveSourceLoad("user"));
|
|
@@ -9989,6 +10712,19 @@
|
|
|
9989
10712
|
return;
|
|
9990
10713
|
}
|
|
9991
10714
|
if (displayKind === "text") {
|
|
10715
|
+
const meta = await loadRawFileInfo(target);
|
|
10716
|
+
if (req !== SOURCE_REQ_SEQ || !sourceTargetsEqual(sourceTargetFromRoute(), target))
|
|
10717
|
+
return;
|
|
10718
|
+
if (!isVirtualSourceDisabled() && meta.size != null && meta.size >= VIRTUAL_SOURCE_SIZE_THRESHOLD) {
|
|
10719
|
+
const rendered2 = await renderPagedSourceText(card, target, meta.size, controller.signal);
|
|
10720
|
+
if (req !== SOURCE_REQ_SEQ || !sourceTargetsEqual(sourceTargetFromRoute(), target))
|
|
10721
|
+
return;
|
|
10722
|
+
if (!rendered2)
|
|
10723
|
+
return;
|
|
10724
|
+
scrollStandaloneSourceLine(card, lineTargetStart(STATE.route.screen === "file" ? STATE.route.line : undefined));
|
|
10725
|
+
finishSourceLoad(req);
|
|
10726
|
+
return;
|
|
10727
|
+
}
|
|
9992
10728
|
const response = await trackLoad(fetch(buildRawFileUrl(target), { signal: controller.signal }));
|
|
9993
10729
|
if (req !== SOURCE_REQ_SEQ || !sourceTargetsEqual(sourceTargetFromRoute(), target))
|
|
9994
10730
|
return;
|
|
@@ -10026,6 +10762,7 @@
|
|
|
10026
10762
|
if (virtualScroller) {
|
|
10027
10763
|
const centeredOffset = virtualScroller.clientHeight / 2 - VIRTUAL_SOURCE_ROW_HEIGHT / 2;
|
|
10028
10764
|
virtualScroller.scrollTop = Math.max(0, (line - 1) * VIRTUAL_SOURCE_ROW_HEIGHT - Math.max(0, centeredOffset));
|
|
10765
|
+
virtualScroller.__gdpRenderVirtualSource?.();
|
|
10029
10766
|
return;
|
|
10030
10767
|
}
|
|
10031
10768
|
const row = card.querySelector('.gdp-source-table tr[data-line="' + String(line) + '"]');
|
|
@@ -10492,6 +11229,11 @@
|
|
|
10492
11229
|
body.style.display = STATE.collapsed ? "none" : "";
|
|
10493
11230
|
});
|
|
10494
11231
|
}
|
|
11232
|
+
applySidebarFontSize();
|
|
11233
|
+
applyCodeFontSize();
|
|
11234
|
+
applySidebarHidden();
|
|
11235
|
+
observeSidebarHeaderHeight();
|
|
11236
|
+
hydrateRefSelectorMounts();
|
|
10495
11237
|
setSidebarTreeActionIcons();
|
|
10496
11238
|
$$(".sb-view-seg button").forEach((b2) => {
|
|
10497
11239
|
b2.addEventListener("click", () => {
|
|
@@ -10503,6 +11245,15 @@
|
|
|
10503
11245
|
});
|
|
10504
11246
|
$("#sb-expand-all").addEventListener("click", () => setAllSidebarDirsCollapsed(false));
|
|
10505
11247
|
$("#sb-collapse-all").addEventListener("click", () => setAllSidebarDirsCollapsed(true));
|
|
11248
|
+
$("#sidebar-toggle")?.addEventListener("click", toggleSidebarHidden);
|
|
11249
|
+
$("#viewer-settings")?.addEventListener("click", openScopeSettings);
|
|
11250
|
+
$("#scope-settings-close")?.addEventListener("click", closeScopeSettings);
|
|
11251
|
+
$("#scope-omit-save")?.addEventListener("click", saveScopeSettings);
|
|
11252
|
+
$("#scope-omit-reset")?.addEventListener("click", resetScopeSettings);
|
|
11253
|
+
$("#scope-settings-popover")?.addEventListener("keydown", (e2) => {
|
|
11254
|
+
if (e2.key === "Escape")
|
|
11255
|
+
closeScopeSettings();
|
|
11256
|
+
});
|
|
10506
11257
|
prepareKeyboardPanels();
|
|
10507
11258
|
const contentPanel = document.querySelector("#content");
|
|
10508
11259
|
contentPanel?.addEventListener("focusin", () => setPanelFocusScope("main"));
|
|
@@ -10937,17 +11688,19 @@
|
|
|
10937
11688
|
});
|
|
10938
11689
|
}
|
|
10939
11690
|
async function repoPaletteFiles(ref) {
|
|
10940
|
-
const
|
|
11691
|
+
const cacheKey = repoFileCacheKey(ref);
|
|
11692
|
+
const cached = REPO_FILE_CACHE.get(cacheKey);
|
|
10941
11693
|
if (cached && cached.generation === SERVER_GENERATION)
|
|
10942
11694
|
return cached;
|
|
10943
11695
|
const params = new URLSearchParams;
|
|
10944
11696
|
params.set("ref", ref);
|
|
11697
|
+
appendScopeOmitDirsParam(params);
|
|
10945
11698
|
const res = await trackLoad(fetch("/_files?" + params.toString()).then((r2) => {
|
|
10946
11699
|
if (!r2.ok)
|
|
10947
11700
|
throw new Error("failed to load files");
|
|
10948
11701
|
return r2.json();
|
|
10949
11702
|
}));
|
|
10950
|
-
REPO_FILE_CACHE.set(
|
|
11703
|
+
REPO_FILE_CACHE.set(cacheKey, res);
|
|
10951
11704
|
return res;
|
|
10952
11705
|
}
|
|
10953
11706
|
function diffFilePaletteItems(state, query) {
|
|
@@ -11035,6 +11788,7 @@
|
|
|
11035
11788
|
params.set("max", "200");
|
|
11036
11789
|
if (state.grepRegex)
|
|
11037
11790
|
params.set("regex", "1");
|
|
11791
|
+
appendScopeOmitDirsParam(params);
|
|
11038
11792
|
if (source === "diff") {
|
|
11039
11793
|
for (const file of state.diffSnapshot)
|
|
11040
11794
|
params.append("path", file.path);
|
|
@@ -11153,6 +11907,8 @@
|
|
|
11153
11907
|
return true;
|
|
11154
11908
|
}
|
|
11155
11909
|
if (action === "focus-sidebar") {
|
|
11910
|
+
if (STATE.sidebarHidden)
|
|
11911
|
+
applySidebarHidden(false);
|
|
11156
11912
|
focusSidebarPanel();
|
|
11157
11913
|
return true;
|
|
11158
11914
|
}
|
|
@@ -11252,8 +12008,50 @@
|
|
|
11252
12008
|
}
|
|
11253
12009
|
return false;
|
|
11254
12010
|
}
|
|
12011
|
+
function handleVirtualSourcePagingKey(e2, targetEl) {
|
|
12012
|
+
if (e2.__gdpVirtualSourcePagingHandled)
|
|
12013
|
+
return true;
|
|
12014
|
+
if (e2.defaultPrevented || e2.isComposing || PALETTE || document.querySelector(".mkdp-lightbox"))
|
|
12015
|
+
return false;
|
|
12016
|
+
const editable = isEditableKeyTarget(targetEl);
|
|
12017
|
+
const inVirtualSearch = !!targetEl?.closest(".gdp-source-virtual-search");
|
|
12018
|
+
if (editable && !inVirtualSearch)
|
|
12019
|
+
return false;
|
|
12020
|
+
const key = e2.key.toLowerCase();
|
|
12021
|
+
if (e2.altKey || e2.metaKey)
|
|
12022
|
+
return false;
|
|
12023
|
+
const isPlainPageKey = (key === "pagedown" || key === "pageup") && !e2.ctrlKey && !e2.shiftKey;
|
|
12024
|
+
const isCtrlArrowKey = (key === "arrowdown" || key === "arrowup") && e2.ctrlKey && !e2.shiftKey;
|
|
12025
|
+
if (!isPlainPageKey && !isCtrlArrowKey)
|
|
12026
|
+
return false;
|
|
12027
|
+
const scroller = findMainScrollTarget();
|
|
12028
|
+
if (!scroller || !scroller.matches("#content .gdp-source-virtual-scroller"))
|
|
12029
|
+
return false;
|
|
12030
|
+
const pageDown = key === "pagedown" || key === "arrowdown";
|
|
12031
|
+
const pageUp = key === "pageup" || key === "arrowup";
|
|
12032
|
+
if (!pageDown && !pageUp)
|
|
12033
|
+
return false;
|
|
12034
|
+
e2.__gdpVirtualSourcePagingHandled = true;
|
|
12035
|
+
e2.preventDefault();
|
|
12036
|
+
e2.stopPropagation();
|
|
12037
|
+
scrollMainPanel(pageDown ? 1 : -1, e2.repeat, "page");
|
|
12038
|
+
focusMainSurface();
|
|
12039
|
+
return true;
|
|
12040
|
+
}
|
|
12041
|
+
function handleVirtualSourcePagingKeydown(e2) {
|
|
12042
|
+
handleVirtualSourcePagingKey(e2, e2.target);
|
|
12043
|
+
}
|
|
12044
|
+
document.addEventListener("keydown", handleVirtualSourcePagingKeydown, { capture: true });
|
|
11255
12045
|
document.addEventListener("keydown", (e2) => {
|
|
12046
|
+
if (e2.__gdpVirtualSourcePagingHandled)
|
|
12047
|
+
return;
|
|
11256
12048
|
const targetEl = e2.target;
|
|
12049
|
+
if ((e2.ctrlKey || e2.metaKey) && e2.key.toLowerCase() === "f" && !isEditableKeyTarget(targetEl)) {
|
|
12050
|
+
if (openVirtualSourceSearchFromKeyboard(targetEl)) {
|
|
12051
|
+
e2.preventDefault();
|
|
12052
|
+
return;
|
|
12053
|
+
}
|
|
12054
|
+
}
|
|
11257
12055
|
const scope = keymapScope(targetEl);
|
|
11258
12056
|
const action = resolveKeymapAction(e2, {
|
|
11259
12057
|
scope,
|
|
@@ -11282,6 +12080,7 @@
|
|
|
11282
12080
|
params.set("ref", STATE.route.ref || "worktree");
|
|
11283
12081
|
if (STATE.route.path)
|
|
11284
12082
|
params.set("path", STATE.route.path);
|
|
12083
|
+
appendScopeOmitDirsParam(params);
|
|
11285
12084
|
return trackLoad(fetch("/_tree?" + params.toString()).then((r2) => {
|
|
11286
12085
|
if (!r2.ok)
|
|
11287
12086
|
throw new Error("failed to load repository tree");
|
|
@@ -11317,16 +12116,18 @@
|
|
|
11317
12116
|
setStatus("live");
|
|
11318
12117
|
}).catch(() => setStatus("error"));
|
|
11319
12118
|
}
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
12119
|
+
loadSettings().finally(() => {
|
|
12120
|
+
if (STATE.route.screen === "help") {
|
|
12121
|
+
setStatus("live");
|
|
12122
|
+
renderHelpPage();
|
|
12123
|
+
} else if (STATE.route.screen === "repo")
|
|
12124
|
+
loadRepo();
|
|
12125
|
+
else if (STATE.route.screen === "file" && STATE.route.view === "blob") {
|
|
12126
|
+
setStatus("live");
|
|
12127
|
+
applySourceRouteToShell();
|
|
12128
|
+
} else
|
|
12129
|
+
load();
|
|
12130
|
+
});
|
|
11330
12131
|
function syncRefInputs() {
|
|
11331
12132
|
const fi = $("#ref-from"), ti = $("#ref-to");
|
|
11332
12133
|
if (fi)
|
|
@@ -11445,40 +12246,21 @@
|
|
|
11445
12246
|
popover.hidden = true;
|
|
11446
12247
|
popTarget = null;
|
|
11447
12248
|
}
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
}
|
|
11456
|
-
});
|
|
11457
|
-
el.addEventListener("click", (e2) => {
|
|
11458
|
-
e2.stopPropagation();
|
|
11459
|
-
openPopover(el);
|
|
11460
|
-
});
|
|
11461
|
-
el.addEventListener("keydown", (e2) => {
|
|
11462
|
-
if (e2.key === "Enter") {
|
|
11463
|
-
e2.preventDefault();
|
|
11464
|
-
closePopover();
|
|
11465
|
-
} else if (e2.key === "Escape") {
|
|
11466
|
-
closePopover();
|
|
11467
|
-
el.blur();
|
|
11468
|
-
}
|
|
11469
|
-
});
|
|
12249
|
+
const refFromInput = $("#ref-from");
|
|
12250
|
+
const refToInput = $("#ref-to");
|
|
12251
|
+
wireRefSelectorInput(refFromInput, () => {
|
|
12252
|
+
const otherEmpty = !refToInput.value;
|
|
12253
|
+
setRange(refFromInput.value, refToInput.value);
|
|
12254
|
+
if (otherEmpty)
|
|
12255
|
+
setTimeout(() => openPopover(refToInput), 0);
|
|
11470
12256
|
});
|
|
11471
|
-
|
|
12257
|
+
wireRefSelectorInput(refToInput, () => setRange(refFromInput.value, refToInput.value));
|
|
12258
|
+
wireRefSelectorInput($("#repo-target"), (ref) => {
|
|
11472
12259
|
if (STATE.route.screen !== "file")
|
|
11473
12260
|
return;
|
|
11474
12261
|
setRoute({ screen: "file", path: STATE.route.path, ref, view: "blob", range: currentRange() });
|
|
11475
12262
|
renderStandaloneSource({ path: STATE.route.path, ref });
|
|
11476
12263
|
});
|
|
11477
|
-
document.addEventListener("focusin", (e2) => {
|
|
11478
|
-
const el = e2.target;
|
|
11479
|
-
if (el instanceof HTMLInputElement && (el.id === "repo-ref" || el.id === "repo-target"))
|
|
11480
|
-
openPopover(el);
|
|
11481
|
-
});
|
|
11482
12264
|
popSearch.addEventListener("input", () => buildPopBody(popSearch.value));
|
|
11483
12265
|
popSearch.addEventListener("keydown", (e2) => {
|
|
11484
12266
|
if (e2.key === "Escape") {
|
|
@@ -11495,24 +12277,8 @@
|
|
|
11495
12277
|
return;
|
|
11496
12278
|
const pickedTarget = popTarget;
|
|
11497
12279
|
pickedTarget.value = val;
|
|
11498
|
-
if (pickedTarget.id === "repo-ref") {
|
|
11499
|
-
closePopover();
|
|
11500
|
-
pickedTarget.dispatchEvent(new Event("change"));
|
|
11501
|
-
return;
|
|
11502
|
-
}
|
|
11503
|
-
if (pickedTarget.id === "repo-target") {
|
|
11504
|
-
closePopover();
|
|
11505
|
-
pickedTarget.dispatchEvent(new Event("change"));
|
|
11506
|
-
return;
|
|
11507
|
-
}
|
|
11508
|
-
const targetWasFrom = pickedTarget.id === "ref-from";
|
|
11509
|
-
const otherEmpty = !$("#ref-to").value;
|
|
11510
12280
|
closePopover();
|
|
11511
|
-
|
|
11512
|
-
if (targetWasFrom && otherEmpty) {
|
|
11513
|
-
const ti = $("#ref-to");
|
|
11514
|
-
setTimeout(() => ti.focus(), 0);
|
|
11515
|
-
}
|
|
12281
|
+
pickedTarget.dispatchEvent(new Event("change"));
|
|
11516
12282
|
}
|
|
11517
12283
|
popBody.addEventListener("click", (e2) => {
|
|
11518
12284
|
const item = e2.target.closest(".rp-item-commit, .rp-item-ref");
|