@youtyan/code-viewer 0.1.14 → 0.1.15

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/web/app.js CHANGED
@@ -6746,8 +6746,25 @@
6746
6746
  ];
6747
6747
  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
6748
  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 UNFOLD_16_PATH = "m8.177.677 2.896 2.896a.25.25 0 0 1-.177.427H8.75v1.25a.75.75 0 0 1-1.5 0V4H5.104a.25.25 0 0 1-.177-.427L7.823.677a.25.25 0 0 1 .354 0ZM7.25 10.75a.75.75 0 0 1 1.5 0V12h2.146a.25.25 0 0 1 .177.427l-2.896 2.896a.25.25 0 0 1-.354 0l-2.896-2.896A.25.25 0 0 1 5.104 12H7.25v-1.25Zm-5-2a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5ZM6 8a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1 0-1.5h.5A.75.75 0 0 1 6 8Zm2.25.75a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5ZM12 8a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1 0-1.5h.5A.75.75 0 0 1 12 8Zm2.25.75a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5Z";
6750
- const FOLD_16_PATH = "M10.896 2H8.75V.75a.75.75 0 0 0-1.5 0V2H5.104a.25.25 0 0 0-.177.427l2.896 2.896a.25.25 0 0 0 .354 0l2.896-2.896A.25.25 0 0 0 10.896 2ZM8.75 15.25a.75.75 0 0 1-1.5 0V14H5.104a.25.25 0 0 1-.177-.427l2.896-2.896a.25.25 0 0 1 .354 0l2.896 2.896a.25.25 0 0 1-.177.427H8.75v1.25Zm-6.5-6.5a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5ZM6 8a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1 0-1.5h.5A.75.75 0 0 1 6 8Zm2.25.75a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5ZM12 8a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1 0-1.5h.5A.75.75 0 0 1 12 8Zm2.25.75a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5Z";
6749
+ 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";
6750
+ 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";
6751
+ const SIDEBAR_SHOW_16_PATHS = [
6752
+ "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",
6753
+ "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"
6754
+ ];
6755
+ const SIDEBAR_HIDE_16_PATHS = [
6756
+ "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",
6757
+ "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"
6758
+ ];
6759
+ 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";
6760
+ const EXPAND_ALL_16_PATHS = [
6761
+ "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",
6762
+ "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"
6763
+ ];
6764
+ const COLLAPSE_ALL_16_PATHS = [
6765
+ "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",
6766
+ "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"
6767
+ ];
6751
6768
  const $ = (sel) => document.querySelector(sel);
6752
6769
  const $$ = (sel) => Array.from(document.querySelectorAll(sel));
6753
6770
  const diffCardSelector = (path) => '.gdp-file-shell[data-path="' + (window.CSS && CSS.escape ? CSS.escape(path) : path) + '"]';
@@ -6766,12 +6783,44 @@
6766
6783
  let REPO_SIDEBAR_LOAD = null;
6767
6784
  let SIDEBAR_FILES = [];
6768
6785
  let SIDEBAR_ON_FILE_CLICK;
6786
+ let SERVER_SCOPE_OMIT_DIRS_DEFAULT = [];
6769
6787
  let PENDING_G_SCOPE = null;
6770
6788
  let PENDING_G_UNTIL = 0;
6771
6789
  let SOURCE_CURSOR = null;
6772
6790
  const SOURCE_CURSOR_TOTALS = new Map;
6773
6791
  const HELP_LANGUAGES = ["en", "ja"];
6774
6792
  const HELP_SECTIONS = ["keybindings"];
6793
+ const SCOPE_OMIT_DIRS_STORAGE_KEY_PREFIX = "gdp:scope-omit-dirs:";
6794
+ const SIDEBAR_FONT_SIZE_STORAGE_KEY = "gdp:sidebar-font-size";
6795
+ const CODE_FONT_SIZE_STORAGE_KEY = "gdp:code-font-size";
6796
+ const CLIENT_SCOPE_OMIT_DIRS_DEFAULT = [
6797
+ "node_modules",
6798
+ ".venv",
6799
+ "venv",
6800
+ ".next",
6801
+ ".nuxt",
6802
+ ".svelte-kit",
6803
+ ".astro",
6804
+ ".vercel",
6805
+ "dist",
6806
+ "build",
6807
+ "out",
6808
+ "target",
6809
+ ".gradle",
6810
+ "__pycache__",
6811
+ ".pytest_cache",
6812
+ ".tox",
6813
+ ".terraform",
6814
+ ".idea",
6815
+ ".vscode",
6816
+ "vendor",
6817
+ ".cache",
6818
+ "coverage",
6819
+ "DerivedData",
6820
+ "Pods",
6821
+ "bin",
6822
+ "obj"
6823
+ ];
6775
6824
  const HELP_CONTENT = {
6776
6825
  en: {
6777
6826
  languageLabel: "Language",
@@ -6942,6 +6991,90 @@
6942
6991
  REPO_SIDEBAR_LOAD_REF = null;
6943
6992
  REPO_SIDEBAR_LOAD = null;
6944
6993
  }
6994
+ function normalizeScopeOmitDirs(value) {
6995
+ const raw = Array.isArray(value) ? value : value.split(/[\n,]+/);
6996
+ 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));
6997
+ }
6998
+ function scopeOmitDirsStorageKey() {
6999
+ return SCOPE_OMIT_DIRS_STORAGE_KEY_PREFIX + (PROJECT_NAME || "default");
7000
+ }
7001
+ function setProjectName(project) {
7002
+ if (!project)
7003
+ return;
7004
+ PROJECT_NAME = project;
7005
+ document.title = project + " - code viewer";
7006
+ }
7007
+ function savedScopeOmitDirs() {
7008
+ const raw = localStorage.getItem(scopeOmitDirsStorageKey());
7009
+ if (raw == null)
7010
+ return null;
7011
+ try {
7012
+ const parsed = JSON.parse(raw);
7013
+ return normalizeScopeOmitDirs(Array.isArray(parsed) ? parsed : []);
7014
+ } catch {
7015
+ return normalizeScopeOmitDirs(raw);
7016
+ }
7017
+ }
7018
+ function serverScopeOmitDirsDefault() {
7019
+ return SERVER_SCOPE_OMIT_DIRS_DEFAULT.length ? SERVER_SCOPE_OMIT_DIRS_DEFAULT : CLIENT_SCOPE_OMIT_DIRS_DEFAULT;
7020
+ }
7021
+ function effectiveScopeOmitDirs() {
7022
+ return savedScopeOmitDirs() ?? serverScopeOmitDirsDefault();
7023
+ }
7024
+ function appendScopeOmitDirsParam(params) {
7025
+ const saved = savedScopeOmitDirs();
7026
+ if (saved != null)
7027
+ params.set("omit_dirs", saved.join(","));
7028
+ }
7029
+ function normalizeViewerFontSize(value) {
7030
+ return value === "compact" || value === "large" || value === "xlarge" ? value : "regular";
7031
+ }
7032
+ function savedSidebarFontSize() {
7033
+ return normalizeViewerFontSize(localStorage.getItem(SIDEBAR_FONT_SIZE_STORAGE_KEY));
7034
+ }
7035
+ function savedCodeFontSize() {
7036
+ return normalizeViewerFontSize(localStorage.getItem(CODE_FONT_SIZE_STORAGE_KEY));
7037
+ }
7038
+ function applySidebarFontSize(size = savedSidebarFontSize()) {
7039
+ document.body.dataset.sidebarFontSize = size;
7040
+ }
7041
+ function applyCodeFontSize(size = savedCodeFontSize()) {
7042
+ document.body.dataset.codeFontSize = size;
7043
+ }
7044
+ function syncSidebarHeaderHeight() {
7045
+ requestAnimationFrame(() => {
7046
+ const head = document.querySelector(".sb-head");
7047
+ if (head)
7048
+ document.documentElement.style.setProperty("--sidebar-head-h", Math.ceil(head.getBoundingClientRect().height) + "px");
7049
+ });
7050
+ }
7051
+ function observeSidebarHeaderHeight() {
7052
+ const head = document.querySelector(".sb-head");
7053
+ if (!head || typeof ResizeObserver === "undefined") {
7054
+ syncSidebarHeaderHeight();
7055
+ return;
7056
+ }
7057
+ const observer = new ResizeObserver(syncSidebarHeaderHeight);
7058
+ observer.observe(head);
7059
+ syncSidebarHeaderHeight();
7060
+ }
7061
+ function repoFileCacheKey(ref) {
7062
+ const saved = savedScopeOmitDirs();
7063
+ return `${ref}\x00${saved ? saved.join("\x00") : "server"}`;
7064
+ }
7065
+ async function loadSettings() {
7066
+ try {
7067
+ const res = await fetch("/_settings");
7068
+ if (!res.ok)
7069
+ return null;
7070
+ const settings = await res.json();
7071
+ setProjectName(settings.project || "");
7072
+ SERVER_SCOPE_OMIT_DIRS_DEFAULT = normalizeScopeOmitDirs(settings.scope.omit_dirs_effective);
7073
+ return settings;
7074
+ } catch {
7075
+ return null;
7076
+ }
7077
+ }
6945
7078
  function isRepoSidebarReusable(ref) {
6946
7079
  return REPO_SIDEBAR_REF === (ref || "worktree") && isRepositorySidebarMode();
6947
7080
  }
@@ -6958,6 +7091,7 @@
6958
7091
  theme: localStorage.getItem("gdp:theme") || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"),
6959
7092
  sbView: localStorage.getItem("gdp:sbview") || "tree",
6960
7093
  sbWidth: parseInt(localStorage.getItem("gdp:sbwidth")) || 308,
7094
+ sidebarHidden: localStorage.getItem("gdp:sidebar-hidden") === "1",
6961
7095
  collapsedDirs: new Set(JSON.parse(localStorage.getItem("gdp:collapsed-dirs") || "[]")),
6962
7096
  ignoreWs: igRaw === null ? true : igRaw === "1",
6963
7097
  from: route.range.from,
@@ -7212,15 +7346,112 @@
7212
7346
  return;
7213
7347
  button.setAttribute("aria-pressed", expanded ? "true" : "false");
7214
7348
  button.title = expanded ? "Collapse expanded lines" : "Expand all lines";
7215
- button.innerHTML = expanded ? iconSvg("octicon-fold", FOLD_16_PATH) : iconSvg("octicon-unfold", UNFOLD_16_PATH);
7349
+ button.innerHTML = expanded ? iconSvg("octicon-fold", COLLAPSE_ALL_16_PATHS) : iconSvg("octicon-unfold", EXPAND_ALL_16_PATHS);
7216
7350
  }
7217
7351
  function setSidebarTreeActionIcons() {
7352
+ const settings = document.querySelector("#viewer-settings");
7353
+ const sidebarToggle = document.querySelector("#sidebar-toggle");
7218
7354
  const expand = document.querySelector("#sb-expand-all");
7219
7355
  const collapse = document.querySelector("#sb-collapse-all");
7356
+ if (settings)
7357
+ settings.innerHTML = iconSvg("octicon-gear", GEAR_16_PATH);
7358
+ if (sidebarToggle)
7359
+ sidebarToggle.innerHTML = iconSvg("octicon-sidebar", STATE.sidebarHidden ? SIDEBAR_SHOW_16_PATHS : SIDEBAR_HIDE_16_PATHS);
7220
7360
  if (expand)
7221
- expand.innerHTML = iconSvg("octicon-unfold", UNFOLD_16_PATH);
7361
+ expand.innerHTML = iconSvg("octicon-chevron-down", EXPAND_ALL_16_PATHS);
7222
7362
  if (collapse)
7223
- collapse.innerHTML = iconSvg("octicon-fold", FOLD_16_PATH);
7363
+ collapse.innerHTML = iconSvg("octicon-chevron-up", COLLAPSE_ALL_16_PATHS);
7364
+ }
7365
+ function attachSidebarToggle(host) {
7366
+ const button = document.querySelector("#sidebar-toggle");
7367
+ if (!button || button.parentElement === host)
7368
+ return;
7369
+ host.prepend(button);
7370
+ }
7371
+ function placeSidebarToggle() {
7372
+ const sidebarHead = document.querySelector(".sb-head");
7373
+ const toolbar = document.querySelector(".gdp-repo-toolbar, .gdp-file-detail-header");
7374
+ const restoreHost = toolbar || document.querySelector("#topbar") || document.querySelector("#global-header");
7375
+ if (STATE.sidebarHidden && restoreHost)
7376
+ attachSidebarToggle(restoreHost);
7377
+ else if (sidebarHead)
7378
+ attachSidebarToggle(sidebarHead);
7379
+ }
7380
+ function applySidebarHidden(hidden = STATE.sidebarHidden) {
7381
+ STATE.sidebarHidden = hidden;
7382
+ document.body.classList.toggle("gdp-sidebar-hidden", hidden);
7383
+ localStorage.setItem("gdp:sidebar-hidden", hidden ? "1" : "0");
7384
+ const button = document.querySelector("#sidebar-toggle");
7385
+ if (button) {
7386
+ button.setAttribute("aria-pressed", hidden ? "true" : "false");
7387
+ button.title = hidden ? "show sidebar" : "hide sidebar";
7388
+ button.setAttribute("aria-label", hidden ? "show sidebar" : "hide sidebar");
7389
+ }
7390
+ setSidebarTreeActionIcons();
7391
+ placeSidebarToggle();
7392
+ syncSidebarHeaderHeight();
7393
+ }
7394
+ function toggleSidebarHidden() {
7395
+ applySidebarHidden(!STATE.sidebarHidden);
7396
+ }
7397
+ function scopeOmitSourceLabel() {
7398
+ return savedScopeOmitDirs() != null ? "Browser override" : "Server default";
7399
+ }
7400
+ function refreshRepositoryTreeAfterSettings() {
7401
+ REPO_FILE_CACHE.clear();
7402
+ invalidateRepoSidebar();
7403
+ if (STATE.route.screen === "repo") {
7404
+ loadRepo();
7405
+ return;
7406
+ }
7407
+ const target = sourceTargetFromRoute();
7408
+ if (target)
7409
+ renderRepoBlobSidebar(target.path, target.ref || "worktree");
7410
+ }
7411
+ async function openScopeSettings() {
7412
+ const pop = document.querySelector("#scope-settings-popover");
7413
+ const input = document.querySelector("#scope-omit-dirs");
7414
+ const sidebarFontSize = document.querySelector("#sidebar-font-size");
7415
+ const codeFontSize = document.querySelector("#code-font-size");
7416
+ const source = document.querySelector("#scope-omit-source");
7417
+ if (!pop || !input || !sidebarFontSize || !codeFontSize || !source)
7418
+ return;
7419
+ await loadSettings();
7420
+ sidebarFontSize.value = savedSidebarFontSize();
7421
+ codeFontSize.value = savedCodeFontSize();
7422
+ input.value = effectiveScopeOmitDirs().join(`
7423
+ `);
7424
+ 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.";
7425
+ pop.hidden = false;
7426
+ sidebarFontSize.focus();
7427
+ }
7428
+ function closeScopeSettings() {
7429
+ const pop = document.querySelector("#scope-settings-popover");
7430
+ if (pop)
7431
+ pop.hidden = true;
7432
+ }
7433
+ function saveScopeSettings() {
7434
+ const input = document.querySelector("#scope-omit-dirs");
7435
+ const sidebarFontSize = document.querySelector("#sidebar-font-size");
7436
+ const codeFontSize = document.querySelector("#code-font-size");
7437
+ if (!input || !sidebarFontSize || !codeFontSize)
7438
+ return;
7439
+ localStorage.setItem(SIDEBAR_FONT_SIZE_STORAGE_KEY, normalizeViewerFontSize(sidebarFontSize.value));
7440
+ localStorage.setItem(CODE_FONT_SIZE_STORAGE_KEY, normalizeViewerFontSize(codeFontSize.value));
7441
+ applySidebarFontSize();
7442
+ applyCodeFontSize();
7443
+ localStorage.setItem(scopeOmitDirsStorageKey(), JSON.stringify(normalizeScopeOmitDirs(input.value)));
7444
+ closeScopeSettings();
7445
+ refreshRepositoryTreeAfterSettings();
7446
+ }
7447
+ function resetScopeSettings() {
7448
+ localStorage.removeItem(SIDEBAR_FONT_SIZE_STORAGE_KEY);
7449
+ localStorage.removeItem(CODE_FONT_SIZE_STORAGE_KEY);
7450
+ applySidebarFontSize("regular");
7451
+ applyCodeFontSize("regular");
7452
+ localStorage.removeItem(scopeOmitDirsStorageKey());
7453
+ closeScopeSettings();
7454
+ refreshRepositoryTreeAfterSettings();
7224
7455
  }
7225
7456
  function buildTree(files) {
7226
7457
  const root = { name: "", dirs: {}, files: [], path: "", minOrder: Infinity, explicit: true };
@@ -7287,8 +7518,8 @@
7287
7518
  li.dataset.explicit = "true";
7288
7519
  if (dir.children_omitted) {
7289
7520
  li.classList.add("children-omitted");
7290
- li.classList.add(dir.children_omitted_reason === "ignored" ? "children-omitted-ignored" : "children-omitted-internal");
7291
- li.title = dir.children_omitted_reason === "ignored" ? "Ignored directory: open the detail pane to browse its contents" : "Internal Git metadata is not browsed";
7521
+ li.classList.add(dir.children_omitted_reason === "heavy" ? "children-omitted-heavy" : "children-omitted-internal");
7522
+ 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
7523
  }
7293
7524
  li.style.setProperty("--lvl-pad", 12 + depth * 14 + "px");
7294
7525
  const chev = document.createElement("span");
@@ -7312,9 +7543,9 @@
7312
7543
  label.appendChild(dn);
7313
7544
  if (dir.children_omitted) {
7314
7545
  const omitted = document.createElement("span");
7315
- omitted.className = "dir-omitted " + (dir.children_omitted_reason === "ignored" ? "dir-omitted-ignored" : "dir-omitted-internal");
7316
- omitted.textContent = dir.children_omitted_reason === "ignored" ? "ignored" : "private";
7317
- omitted.title = dir.children_omitted_reason === "ignored" ? "Tree expansion is skipped, but the directory detail can be opened" : "This directory cannot be opened from the browser";
7546
+ omitted.className = "dir-omitted " + (dir.children_omitted_reason === "heavy" ? "dir-omitted-heavy" : "dir-omitted-internal");
7547
+ omitted.textContent = dir.children_omitted_reason === "heavy" ? "skipped" : "private";
7548
+ 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
7549
  label.appendChild(omitted);
7319
7550
  }
7320
7551
  li.appendChild(label);
@@ -7346,17 +7577,15 @@
7346
7577
  if (onFileClick) {
7347
7578
  li.addEventListener("click", (e2) => {
7348
7579
  e2.stopPropagation();
7349
- if (dir.children_omitted_reason === "internal")
7580
+ if (dir.children_omitted_reason === "internal" || dir.children_omitted_reason === "truncated")
7350
7581
  return;
7351
- if (dir.children_omitted_reason !== "internal") {
7352
- onFileClick({
7353
- path: dir.path,
7354
- display_path: dir.path,
7355
- type: "tree",
7356
- children_omitted: dir.children_omitted,
7357
- children_omitted_reason: dir.children_omitted_reason
7358
- });
7359
- }
7582
+ onFileClick({
7583
+ path: dir.path,
7584
+ display_path: dir.path,
7585
+ type: "tree",
7586
+ children_omitted: dir.children_omitted,
7587
+ children_omitted_reason: dir.children_omitted_reason
7588
+ });
7360
7589
  focusSidebarPanel();
7361
7590
  });
7362
7591
  } else {
@@ -7482,6 +7711,49 @@
7482
7711
  return;
7483
7712
  input.value = ref || "worktree";
7484
7713
  wrap.hidden = !(STATE.route.screen === "file" && STATE.route.view === "blob");
7714
+ syncSidebarHeaderHeight();
7715
+ }
7716
+ function createRefSelectorInput(options) {
7717
+ const wrap = document.createElement("div");
7718
+ wrap.className = "ref-selector" + (options.extraClass ? " " + options.extraClass : "");
7719
+ wrap.dataset.refSelector = "";
7720
+ if (options.wrapperId)
7721
+ wrap.id = options.wrapperId;
7722
+ if (options.hidden)
7723
+ wrap.hidden = true;
7724
+ const icon = document.createElement("span");
7725
+ icon.className = "ref-selector-icon";
7726
+ icon.setAttribute("aria-hidden", "true");
7727
+ icon.innerHTML = iconSvg("octicon-git-branch", GIT_BRANCH_16_PATH);
7728
+ const input = document.createElement("input");
7729
+ input.className = "ref-input";
7730
+ input.id = options.id;
7731
+ input.readOnly = true;
7732
+ input.autocomplete = "off";
7733
+ input.placeholder = options.placeholder;
7734
+ if (options.title)
7735
+ input.title = options.title;
7736
+ if (options.value != null)
7737
+ input.value = options.value;
7738
+ const caret = document.createElement("span");
7739
+ caret.className = "ref-selector-caret";
7740
+ caret.setAttribute("aria-hidden", "true");
7741
+ caret.innerHTML = iconSvg("octicon-triangle-down", TRIANGLE_DOWN_16_PATH);
7742
+ wrap.append(icon, input, caret);
7743
+ return { wrap, input };
7744
+ }
7745
+ function hydrateRefSelectorMounts() {
7746
+ document.querySelectorAll("[data-ref-selector-mount]").forEach((mount) => {
7747
+ const { wrap } = createRefSelectorInput({
7748
+ id: mount.dataset.refId || "",
7749
+ placeholder: mount.dataset.placeholder || "ref...",
7750
+ title: mount.dataset.title,
7751
+ wrapperId: mount.dataset.wrapperId,
7752
+ extraClass: mount.dataset.extraClass,
7753
+ hidden: mount.hidden
7754
+ });
7755
+ mount.replaceWith(wrap);
7756
+ });
7485
7757
  }
7486
7758
  function renderMeta(meta) {
7487
7759
  const el = $("#meta");
@@ -7489,8 +7761,7 @@
7489
7761
  el.textContent = "";
7490
7762
  return;
7491
7763
  }
7492
- PROJECT_NAME = meta.project || PROJECT_NAME;
7493
- document.title = (meta.project ? meta.project + " - " : "") + "code viewer";
7764
+ setProjectName(meta.project || "");
7494
7765
  el.innerHTML = "";
7495
7766
  if (meta.branch) {
7496
7767
  const b2 = document.createElement("span");
@@ -7757,7 +8028,7 @@
7757
8028
  syncRepoTargetInput(repoFileTargetFromRoute() || "worktree");
7758
8029
  }
7759
8030
  function syncHeaderMenu() {
7760
- document.querySelectorAll(".app-menu-item").forEach((link2) => {
8031
+ document.querySelectorAll(".app-menu-item, .global-help-link").forEach((link2) => {
7761
8032
  const fileRouteOwner = STATE.route.screen === "file" && STATE.route.view === "blob" ? "repo" : "diff";
7762
8033
  const active = link2.dataset.route === STATE.route.screen || STATE.route.screen === "file" && link2.dataset.route === fileRouteOwner;
7763
8034
  link2.classList.toggle("active", active);
@@ -8061,28 +8332,23 @@
8061
8332
  function repoRoute(ref, path) {
8062
8333
  return { screen: "repo", ref: ref || "worktree", path, range: currentRange() };
8063
8334
  }
8064
- function wireRepoTargetPicker(input, onPick) {
8065
- input.addEventListener("focus", () => openPopover(input));
8066
- input.addEventListener("click", (e2) => {
8335
+ function wireRefSelectorInput(input, onPick) {
8336
+ const wrap = input.closest("[data-ref-selector]");
8337
+ wrap?.addEventListener("click", (e2) => {
8067
8338
  e2.stopPropagation();
8068
8339
  openPopover(input);
8069
8340
  });
8070
- input.addEventListener("mousedown", (e2) => {
8071
- if (popover.hidden) {
8072
- e2.preventDefault();
8073
- input.focus();
8074
- }
8075
- });
8076
8341
  input.addEventListener("keydown", (e2) => {
8077
- if (e2.key === "Enter") {
8342
+ if (e2.key === "Enter" || e2.key === " ") {
8078
8343
  e2.preventDefault();
8079
- closePopover();
8344
+ openPopover(input);
8080
8345
  } else if (e2.key === "Escape") {
8081
8346
  closePopover();
8082
8347
  input.blur();
8083
8348
  }
8084
8349
  });
8085
- input.addEventListener("change", () => onPick(input.value || "worktree"));
8350
+ if (onPick)
8351
+ input.addEventListener("change", () => onPick(input.value || "worktree"));
8086
8352
  }
8087
8353
  function createRepoBreadcrumb(target, path) {
8088
8354
  const nav = document.createElement("nav");
@@ -8117,7 +8383,7 @@
8117
8383
  return nav;
8118
8384
  }
8119
8385
  async function renderRepo(meta) {
8120
- PROJECT_NAME = meta.project || PROJECT_NAME;
8386
+ setProjectName(meta.project || "");
8121
8387
  setPageMode();
8122
8388
  removeStandaloneSource();
8123
8389
  $("#empty").classList.add("hidden");
@@ -8130,21 +8396,19 @@
8130
8396
  const target = $("#diff");
8131
8397
  const shell = document.createElement("section");
8132
8398
  shell.className = "gdp-repo-shell";
8133
- const targetPicker = document.createElement("input");
8134
- targetPicker.className = "ref-input gdp-repo-target";
8135
- targetPicker.id = "repo-ref";
8136
- targetPicker.readOnly = true;
8137
- targetPicker.autocomplete = "off";
8138
- targetPicker.value = meta.ref || "worktree";
8139
- targetPicker.placeholder = "ref...";
8140
- targetPicker.title = "repository ref";
8141
- wireRepoTargetPicker(targetPicker, (ref) => {
8399
+ const { wrap: targetPickerWrap, input: targetPicker } = createRefSelectorInput({
8400
+ id: "repo-ref",
8401
+ placeholder: "ref...",
8402
+ title: "repository ref",
8403
+ value: meta.ref || "worktree"
8404
+ });
8405
+ wireRefSelectorInput(targetPicker, (ref) => {
8142
8406
  setRoute(repoRoute(ref, ""));
8143
8407
  loadRepo();
8144
8408
  });
8145
8409
  const toolbar = document.createElement("div");
8146
8410
  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"), targetPicker);
8411
+ toolbar.append(targetPickerWrap, createRepoBreadcrumb(meta.ref, meta.path || ""), createOpenPathButton(meta.path || "", "directory", "open this folder in OS"));
8148
8412
  shell.appendChild(toolbar);
8149
8413
  const listCard = document.createElement("section");
8150
8414
  listCard.className = "gdp-file-shell loaded gdp-repo-list-shell";
@@ -8263,6 +8527,7 @@
8263
8527
  shell.appendChild(readme);
8264
8528
  }
8265
8529
  target.appendChild(shell);
8530
+ placeSidebarToggle();
8266
8531
  }
8267
8532
  function renderRepoBlobSidebar(currentPath, ref) {
8268
8533
  syncRepoTargetInput(ref);
@@ -8279,6 +8544,7 @@
8279
8544
  const params = new URLSearchParams;
8280
8545
  params.set("ref", normalizedRef);
8281
8546
  params.set("recursive", "1");
8547
+ appendScopeOmitDirsParam(params);
8282
8548
  REPO_SIDEBAR_LOAD_REF = normalizedRef;
8283
8549
  const load2 = trackLoad(fetch("/_tree?" + params.toString()).then((r2) => {
8284
8550
  if (!r2.ok)
@@ -8363,7 +8629,10 @@
8363
8629
  document.querySelectorAll(".gdp-file-shell.pending").forEach((c2) => lazyObserver.observe(c2));
8364
8630
  }
8365
8631
  window.addEventListener("scroll", () => enqueueInitialLoads(), { passive: true });
8366
- window.addEventListener("resize", () => enqueueInitialLoads(), { passive: true });
8632
+ window.addEventListener("resize", () => {
8633
+ enqueueInitialLoads();
8634
+ syncSidebarHeaderHeight();
8635
+ }, { passive: true });
8367
8636
  document.addEventListener("visibilitychange", () => {
8368
8637
  if (!document.hidden)
8369
8638
  enqueueInitialLoads();
@@ -9969,6 +10238,7 @@
9969
10238
  } else {
9970
10239
  root.prepend(card);
9971
10240
  }
10241
+ placeSidebarToggle();
9972
10242
  const controller = new AbortController;
9973
10243
  ACTIVE_SOURCE_LOAD = { controller, req, target, card };
9974
10244
  renderSourceLoading(card, target, () => cancelActiveSourceLoad("user"));
@@ -10492,6 +10762,11 @@
10492
10762
  body.style.display = STATE.collapsed ? "none" : "";
10493
10763
  });
10494
10764
  }
10765
+ applySidebarFontSize();
10766
+ applyCodeFontSize();
10767
+ applySidebarHidden();
10768
+ observeSidebarHeaderHeight();
10769
+ hydrateRefSelectorMounts();
10495
10770
  setSidebarTreeActionIcons();
10496
10771
  $$(".sb-view-seg button").forEach((b2) => {
10497
10772
  b2.addEventListener("click", () => {
@@ -10503,6 +10778,15 @@
10503
10778
  });
10504
10779
  $("#sb-expand-all").addEventListener("click", () => setAllSidebarDirsCollapsed(false));
10505
10780
  $("#sb-collapse-all").addEventListener("click", () => setAllSidebarDirsCollapsed(true));
10781
+ $("#sidebar-toggle")?.addEventListener("click", toggleSidebarHidden);
10782
+ $("#viewer-settings")?.addEventListener("click", openScopeSettings);
10783
+ $("#scope-settings-close")?.addEventListener("click", closeScopeSettings);
10784
+ $("#scope-omit-save")?.addEventListener("click", saveScopeSettings);
10785
+ $("#scope-omit-reset")?.addEventListener("click", resetScopeSettings);
10786
+ $("#scope-settings-popover")?.addEventListener("keydown", (e2) => {
10787
+ if (e2.key === "Escape")
10788
+ closeScopeSettings();
10789
+ });
10506
10790
  prepareKeyboardPanels();
10507
10791
  const contentPanel = document.querySelector("#content");
10508
10792
  contentPanel?.addEventListener("focusin", () => setPanelFocusScope("main"));
@@ -10937,17 +11221,19 @@
10937
11221
  });
10938
11222
  }
10939
11223
  async function repoPaletteFiles(ref) {
10940
- const cached = REPO_FILE_CACHE.get(ref);
11224
+ const cacheKey = repoFileCacheKey(ref);
11225
+ const cached = REPO_FILE_CACHE.get(cacheKey);
10941
11226
  if (cached && cached.generation === SERVER_GENERATION)
10942
11227
  return cached;
10943
11228
  const params = new URLSearchParams;
10944
11229
  params.set("ref", ref);
11230
+ appendScopeOmitDirsParam(params);
10945
11231
  const res = await trackLoad(fetch("/_files?" + params.toString()).then((r2) => {
10946
11232
  if (!r2.ok)
10947
11233
  throw new Error("failed to load files");
10948
11234
  return r2.json();
10949
11235
  }));
10950
- REPO_FILE_CACHE.set(ref, res);
11236
+ REPO_FILE_CACHE.set(cacheKey, res);
10951
11237
  return res;
10952
11238
  }
10953
11239
  function diffFilePaletteItems(state, query) {
@@ -11035,6 +11321,7 @@
11035
11321
  params.set("max", "200");
11036
11322
  if (state.grepRegex)
11037
11323
  params.set("regex", "1");
11324
+ appendScopeOmitDirsParam(params);
11038
11325
  if (source === "diff") {
11039
11326
  for (const file of state.diffSnapshot)
11040
11327
  params.append("path", file.path);
@@ -11153,6 +11440,8 @@
11153
11440
  return true;
11154
11441
  }
11155
11442
  if (action === "focus-sidebar") {
11443
+ if (STATE.sidebarHidden)
11444
+ applySidebarHidden(false);
11156
11445
  focusSidebarPanel();
11157
11446
  return true;
11158
11447
  }
@@ -11282,6 +11571,7 @@
11282
11571
  params.set("ref", STATE.route.ref || "worktree");
11283
11572
  if (STATE.route.path)
11284
11573
  params.set("path", STATE.route.path);
11574
+ appendScopeOmitDirsParam(params);
11285
11575
  return trackLoad(fetch("/_tree?" + params.toString()).then((r2) => {
11286
11576
  if (!r2.ok)
11287
11577
  throw new Error("failed to load repository tree");
@@ -11317,16 +11607,18 @@
11317
11607
  setStatus("live");
11318
11608
  }).catch(() => setStatus("error"));
11319
11609
  }
11320
- if (STATE.route.screen === "help") {
11321
- setStatus("live");
11322
- renderHelpPage();
11323
- } else if (STATE.route.screen === "repo")
11324
- loadRepo();
11325
- else if (STATE.route.screen === "file" && STATE.route.view === "blob") {
11326
- setStatus("live");
11327
- applySourceRouteToShell();
11328
- } else
11329
- load();
11610
+ loadSettings().finally(() => {
11611
+ if (STATE.route.screen === "help") {
11612
+ setStatus("live");
11613
+ renderHelpPage();
11614
+ } else if (STATE.route.screen === "repo")
11615
+ loadRepo();
11616
+ else if (STATE.route.screen === "file" && STATE.route.view === "blob") {
11617
+ setStatus("live");
11618
+ applySourceRouteToShell();
11619
+ } else
11620
+ load();
11621
+ });
11330
11622
  function syncRefInputs() {
11331
11623
  const fi = $("#ref-from"), ti = $("#ref-to");
11332
11624
  if (fi)
@@ -11445,40 +11737,21 @@
11445
11737
  popover.hidden = true;
11446
11738
  popTarget = null;
11447
11739
  }
11448
- ["#ref-from", "#ref-to"].forEach((sel) => {
11449
- const el = $(sel);
11450
- el.addEventListener("focus", () => openPopover(el));
11451
- el.addEventListener("mousedown", (e2) => {
11452
- if (popover.hidden) {
11453
- e2.preventDefault();
11454
- el.focus();
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
- });
11740
+ const refFromInput = $("#ref-from");
11741
+ const refToInput = $("#ref-to");
11742
+ wireRefSelectorInput(refFromInput, () => {
11743
+ const otherEmpty = !refToInput.value;
11744
+ setRange(refFromInput.value, refToInput.value);
11745
+ if (otherEmpty)
11746
+ setTimeout(() => openPopover(refToInput), 0);
11470
11747
  });
11471
- wireRepoTargetPicker($("#repo-target"), (ref) => {
11748
+ wireRefSelectorInput(refToInput, () => setRange(refFromInput.value, refToInput.value));
11749
+ wireRefSelectorInput($("#repo-target"), (ref) => {
11472
11750
  if (STATE.route.screen !== "file")
11473
11751
  return;
11474
11752
  setRoute({ screen: "file", path: STATE.route.path, ref, view: "blob", range: currentRange() });
11475
11753
  renderStandaloneSource({ path: STATE.route.path, ref });
11476
11754
  });
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
11755
  popSearch.addEventListener("input", () => buildPopBody(popSearch.value));
11483
11756
  popSearch.addEventListener("keydown", (e2) => {
11484
11757
  if (e2.key === "Escape") {
@@ -11495,24 +11768,8 @@
11495
11768
  return;
11496
11769
  const pickedTarget = popTarget;
11497
11770
  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
11771
  closePopover();
11511
- setRange($("#ref-from").value, $("#ref-to").value);
11512
- if (targetWasFrom && otherEmpty) {
11513
- const ti = $("#ref-to");
11514
- setTimeout(() => ti.focus(), 0);
11515
- }
11772
+ pickedTarget.dispatchEvent(new Event("change"));
11516
11773
  }
11517
11774
  popBody.addEventListener("click", (e2) => {
11518
11775
  const item = e2.target.closest(".rp-item-commit, .rp-item-ref");