@youtyan/code-viewer 0.1.13 → 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) + '"]';
@@ -6764,12 +6781,46 @@
6764
6781
  let REPO_SIDEBAR_REF = null;
6765
6782
  let REPO_SIDEBAR_LOAD_REF = null;
6766
6783
  let REPO_SIDEBAR_LOAD = null;
6784
+ let SIDEBAR_FILES = [];
6785
+ let SIDEBAR_ON_FILE_CLICK;
6786
+ let SERVER_SCOPE_OMIT_DIRS_DEFAULT = [];
6767
6787
  let PENDING_G_SCOPE = null;
6768
6788
  let PENDING_G_UNTIL = 0;
6769
6789
  let SOURCE_CURSOR = null;
6770
6790
  const SOURCE_CURSOR_TOTALS = new Map;
6771
6791
  const HELP_LANGUAGES = ["en", "ja"];
6772
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
+ ];
6773
6824
  const HELP_CONTENT = {
6774
6825
  en: {
6775
6826
  languageLabel: "Language",
@@ -6940,6 +6991,90 @@
6940
6991
  REPO_SIDEBAR_LOAD_REF = null;
6941
6992
  REPO_SIDEBAR_LOAD = null;
6942
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
+ }
6943
7078
  function isRepoSidebarReusable(ref) {
6944
7079
  return REPO_SIDEBAR_REF === (ref || "worktree") && isRepositorySidebarMode();
6945
7080
  }
@@ -6956,6 +7091,7 @@
6956
7091
  theme: localStorage.getItem("gdp:theme") || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"),
6957
7092
  sbView: localStorage.getItem("gdp:sbview") || "tree",
6958
7093
  sbWidth: parseInt(localStorage.getItem("gdp:sbwidth")) || 308,
7094
+ sidebarHidden: localStorage.getItem("gdp:sidebar-hidden") === "1",
6959
7095
  collapsedDirs: new Set(JSON.parse(localStorage.getItem("gdp:collapsed-dirs") || "[]")),
6960
7096
  ignoreWs: igRaw === null ? true : igRaw === "1",
6961
7097
  from: route.range.from,
@@ -7210,15 +7346,112 @@
7210
7346
  return;
7211
7347
  button.setAttribute("aria-pressed", expanded ? "true" : "false");
7212
7348
  button.title = expanded ? "Collapse expanded lines" : "Expand all lines";
7213
- 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);
7214
7350
  }
7215
7351
  function setSidebarTreeActionIcons() {
7352
+ const settings = document.querySelector("#viewer-settings");
7353
+ const sidebarToggle = document.querySelector("#sidebar-toggle");
7216
7354
  const expand = document.querySelector("#sb-expand-all");
7217
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);
7218
7360
  if (expand)
7219
- expand.innerHTML = iconSvg("octicon-unfold", UNFOLD_16_PATH);
7361
+ expand.innerHTML = iconSvg("octicon-chevron-down", EXPAND_ALL_16_PATHS);
7220
7362
  if (collapse)
7221
- 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();
7222
7455
  }
7223
7456
  function buildTree(files) {
7224
7457
  const root = { name: "", dirs: {}, files: [], path: "", minOrder: Infinity, explicit: true };
@@ -7239,8 +7472,10 @@
7239
7472
  }
7240
7473
  if (f2.type === "tree") {
7241
7474
  node.explicit = true;
7242
- if (f2.children_omitted === true)
7475
+ if (f2.children_omitted === true) {
7243
7476
  node.children_omitted = true;
7477
+ node.children_omitted_reason = f2.children_omitted_reason;
7478
+ }
7244
7479
  continue;
7245
7480
  }
7246
7481
  node.files.push(f2);
@@ -7283,12 +7518,18 @@
7283
7518
  li.dataset.explicit = "true";
7284
7519
  if (dir.children_omitted) {
7285
7520
  li.classList.add("children-omitted");
7286
- li.title = "Directory contents are intentionally not listed";
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";
7287
7523
  }
7288
7524
  li.style.setProperty("--lvl-pad", 12 + depth * 14 + "px");
7289
7525
  const chev = document.createElement("span");
7290
- chev.className = "chev";
7291
- setChevronIcon(chev);
7526
+ if (dir.children_omitted) {
7527
+ chev.className = "chev-spacer";
7528
+ chev.setAttribute("aria-hidden", "true");
7529
+ } else {
7530
+ chev.className = "chev";
7531
+ setChevronIcon(chev);
7532
+ }
7292
7533
  li.appendChild(chev);
7293
7534
  const dirIcon = document.createElement("span");
7294
7535
  dirIcon.className = "dir-icon";
@@ -7302,9 +7543,9 @@
7302
7543
  label.appendChild(dn);
7303
7544
  if (dir.children_omitted) {
7304
7545
  const omitted = document.createElement("span");
7305
- omitted.className = "dir-omitted";
7306
- omitted.textContent = "skipped";
7307
- omitted.title = "Directory contents are intentionally not listed";
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";
7308
7549
  label.appendChild(omitted);
7309
7550
  }
7310
7551
  li.appendChild(label);
@@ -7329,12 +7570,22 @@
7329
7570
  STATE.collapsedDirs.delete(dir.path);
7330
7571
  localStorage.setItem("gdp:collapsed-dirs", JSON.stringify([...STATE.collapsedDirs]));
7331
7572
  };
7332
- chev.addEventListener("click", toggleDir);
7333
- dirIcon.addEventListener("click", toggleDir);
7573
+ if (!dir.children_omitted) {
7574
+ chev.addEventListener("click", toggleDir);
7575
+ dirIcon.addEventListener("click", toggleDir);
7576
+ }
7334
7577
  if (onFileClick) {
7335
7578
  li.addEventListener("click", (e2) => {
7336
7579
  e2.stopPropagation();
7337
- onFileClick({ path: dir.path, display_path: dir.path, type: "tree", children_omitted: dir.children_omitted });
7580
+ if (dir.children_omitted_reason === "internal" || dir.children_omitted_reason === "truncated")
7581
+ return;
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
+ });
7338
7589
  focusSidebarPanel();
7339
7590
  });
7340
7591
  } else {
@@ -7416,6 +7667,8 @@
7416
7667
  ul.innerHTML = "";
7417
7668
  ul.classList.toggle("tree", STATE.sbView === "tree");
7418
7669
  STATE.files = files;
7670
+ SIDEBAR_FILES = files;
7671
+ SIDEBAR_ON_FILE_CLICK = onFileClick;
7419
7672
  if (!onFileClick)
7420
7673
  REPO_SIDEBAR_REF = null;
7421
7674
  if (STATE.sbView === "tree") {
@@ -7458,6 +7711,49 @@
7458
7711
  return;
7459
7712
  input.value = ref || "worktree";
7460
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
+ });
7461
7757
  }
7462
7758
  function renderMeta(meta) {
7463
7759
  const el = $("#meta");
@@ -7465,8 +7761,7 @@
7465
7761
  el.textContent = "";
7466
7762
  return;
7467
7763
  }
7468
- PROJECT_NAME = meta.project || PROJECT_NAME;
7469
- document.title = (meta.project ? meta.project + " - " : "") + "code viewer";
7764
+ setProjectName(meta.project || "");
7470
7765
  el.innerHTML = "";
7471
7766
  if (meta.branch) {
7472
7767
  const b2 = document.createElement("span");
@@ -7550,13 +7845,43 @@
7550
7845
  card.scrollIntoView({ behavior: "smooth", block: "start" });
7551
7846
  }
7552
7847
  }
7553
- function markActive(path) {
7848
+ function sidebarAncestorDirs(path) {
7849
+ const parts = path.split("/").filter(Boolean);
7850
+ const dirs = [];
7851
+ for (let i2 = 1;i2 < parts.length; i2++)
7852
+ dirs.push(parts.slice(0, i2).join("/"));
7853
+ return dirs;
7854
+ }
7855
+ function expandSidebarAncestors(path) {
7856
+ if (STATE.sbView !== "tree")
7857
+ return;
7858
+ let changed = false;
7859
+ for (const dir of sidebarAncestorDirs(path)) {
7860
+ if (STATE.collapsedDirs.delete(dir))
7861
+ changed = true;
7862
+ const row = document.querySelector('#filelist .tree-dir[data-dirpath="' + CSS.escape(dir) + '"]');
7863
+ row?.classList.remove("collapsed");
7864
+ const icon = row?.querySelector(".dir-icon");
7865
+ if (icon)
7866
+ setFolderIcon(icon, false);
7867
+ }
7868
+ if (changed)
7869
+ localStorage.setItem("gdp:collapsed-dirs", JSON.stringify([...STATE.collapsedDirs]));
7870
+ }
7871
+ function markActive(path, options = {}) {
7554
7872
  STATE.activeFile = path;
7873
+ if (options.reveal && STATE.sbView === "tree")
7874
+ expandSidebarAncestors(path);
7555
7875
  $$("#filelist li").forEach((li) => {
7556
7876
  const itemPath = li.dataset.path || li.dataset.dirpath;
7557
7877
  if (itemPath)
7558
7878
  li.classList.toggle("active", itemPath === path);
7559
7879
  });
7880
+ if (options.reveal) {
7881
+ const active = document.querySelector("#filelist li.active[data-path], #filelist .tree-dir.active[data-dirpath]");
7882
+ if (active)
7883
+ requestAnimationFrame(() => scrollSidebarItemIntoView(active));
7884
+ }
7560
7885
  }
7561
7886
  function applyViewedState() {
7562
7887
  $$("#filelist li[data-path]").forEach((li) => {
@@ -7703,7 +8028,7 @@
7703
8028
  syncRepoTargetInput(repoFileTargetFromRoute() || "worktree");
7704
8029
  }
7705
8030
  function syncHeaderMenu() {
7706
- document.querySelectorAll(".app-menu-item").forEach((link2) => {
8031
+ document.querySelectorAll(".app-menu-item, .global-help-link").forEach((link2) => {
7707
8032
  const fileRouteOwner = STATE.route.screen === "file" && STATE.route.view === "blob" ? "repo" : "diff";
7708
8033
  const active = link2.dataset.route === STATE.route.screen || STATE.route.screen === "file" && link2.dataset.route === fileRouteOwner;
7709
8034
  link2.classList.toggle("active", active);
@@ -8007,28 +8332,23 @@
8007
8332
  function repoRoute(ref, path) {
8008
8333
  return { screen: "repo", ref: ref || "worktree", path, range: currentRange() };
8009
8334
  }
8010
- function wireRepoTargetPicker(input, onPick) {
8011
- input.addEventListener("focus", () => openPopover(input));
8012
- input.addEventListener("click", (e2) => {
8335
+ function wireRefSelectorInput(input, onPick) {
8336
+ const wrap = input.closest("[data-ref-selector]");
8337
+ wrap?.addEventListener("click", (e2) => {
8013
8338
  e2.stopPropagation();
8014
8339
  openPopover(input);
8015
8340
  });
8016
- input.addEventListener("mousedown", (e2) => {
8017
- if (popover.hidden) {
8018
- e2.preventDefault();
8019
- input.focus();
8020
- }
8021
- });
8022
8341
  input.addEventListener("keydown", (e2) => {
8023
- if (e2.key === "Enter") {
8342
+ if (e2.key === "Enter" || e2.key === " ") {
8024
8343
  e2.preventDefault();
8025
- closePopover();
8344
+ openPopover(input);
8026
8345
  } else if (e2.key === "Escape") {
8027
8346
  closePopover();
8028
8347
  input.blur();
8029
8348
  }
8030
8349
  });
8031
- input.addEventListener("change", () => onPick(input.value || "worktree"));
8350
+ if (onPick)
8351
+ input.addEventListener("change", () => onPick(input.value || "worktree"));
8032
8352
  }
8033
8353
  function createRepoBreadcrumb(target, path) {
8034
8354
  const nav = document.createElement("nav");
@@ -8063,7 +8383,7 @@
8063
8383
  return nav;
8064
8384
  }
8065
8385
  async function renderRepo(meta) {
8066
- PROJECT_NAME = meta.project || PROJECT_NAME;
8386
+ setProjectName(meta.project || "");
8067
8387
  setPageMode();
8068
8388
  removeStandaloneSource();
8069
8389
  $("#empty").classList.add("hidden");
@@ -8076,21 +8396,19 @@
8076
8396
  const target = $("#diff");
8077
8397
  const shell = document.createElement("section");
8078
8398
  shell.className = "gdp-repo-shell";
8079
- const targetPicker = document.createElement("input");
8080
- targetPicker.className = "ref-input gdp-repo-target";
8081
- targetPicker.id = "repo-ref";
8082
- targetPicker.readOnly = true;
8083
- targetPicker.autocomplete = "off";
8084
- targetPicker.value = meta.ref || "worktree";
8085
- targetPicker.placeholder = "ref...";
8086
- targetPicker.title = "repository ref";
8087
- 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) => {
8088
8406
  setRoute(repoRoute(ref, ""));
8089
8407
  loadRepo();
8090
8408
  });
8091
8409
  const toolbar = document.createElement("div");
8092
8410
  toolbar.className = "gdp-file-detail-header gdp-repo-toolbar";
8093
- 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"));
8094
8412
  shell.appendChild(toolbar);
8095
8413
  const listCard = document.createElement("section");
8096
8414
  listCard.className = "gdp-file-shell loaded gdp-repo-list-shell";
@@ -8209,6 +8527,7 @@
8209
8527
  shell.appendChild(readme);
8210
8528
  }
8211
8529
  target.appendChild(shell);
8530
+ placeSidebarToggle();
8212
8531
  }
8213
8532
  function renderRepoBlobSidebar(currentPath, ref) {
8214
8533
  syncRepoTargetInput(ref);
@@ -8225,6 +8544,7 @@
8225
8544
  const params = new URLSearchParams;
8226
8545
  params.set("ref", normalizedRef);
8227
8546
  params.set("recursive", "1");
8547
+ appendScopeOmitDirsParam(params);
8228
8548
  REPO_SIDEBAR_LOAD_REF = normalizedRef;
8229
8549
  const load2 = trackLoad(fetch("/_tree?" + params.toString()).then((r2) => {
8230
8550
  if (!r2.ok)
@@ -8239,7 +8559,8 @@
8239
8559
  path: entry.path,
8240
8560
  display_path: entry.path,
8241
8561
  type: entry.type,
8242
- children_omitted: entry.children_omitted
8562
+ children_omitted: entry.children_omitted,
8563
+ children_omitted_reason: entry.children_omitted_reason
8243
8564
  }));
8244
8565
  renderSidebar(files, (file) => {
8245
8566
  if (file.type === "tree") {
@@ -8266,7 +8587,7 @@
8266
8587
  return load2;
8267
8588
  }
8268
8589
  function activateRepoSidebarPath(currentPath) {
8269
- markActive(currentPath);
8590
+ markActive(currentPath, { reveal: true });
8270
8591
  applyFilter();
8271
8592
  }
8272
8593
  function createPlaceholder(f2) {
@@ -8308,7 +8629,10 @@
8308
8629
  document.querySelectorAll(".gdp-file-shell.pending").forEach((c2) => lazyObserver.observe(c2));
8309
8630
  }
8310
8631
  window.addEventListener("scroll", () => enqueueInitialLoads(), { passive: true });
8311
- window.addEventListener("resize", () => enqueueInitialLoads(), { passive: true });
8632
+ window.addEventListener("resize", () => {
8633
+ enqueueInitialLoads();
8634
+ syncSidebarHeaderHeight();
8635
+ }, { passive: true });
8312
8636
  document.addEventListener("visibilitychange", () => {
8313
8637
  if (!document.hidden)
8314
8638
  enqueueInitialLoads();
@@ -9213,6 +9537,8 @@
9213
9537
  function sourceDisplayKind(path) {
9214
9538
  if (isVideo(path))
9215
9539
  return "video";
9540
+ if (isAudio(path))
9541
+ return "audio";
9216
9542
  if (isImage(path))
9217
9543
  return "image";
9218
9544
  if (/\.pdf$/i.test(path))
@@ -9259,10 +9585,28 @@
9259
9585
  return "MP4 video";
9260
9586
  if (ext === "webm")
9261
9587
  return "WebM video";
9588
+ if (ext === "mp3")
9589
+ return "MP3 audio";
9590
+ if (ext === "wav")
9591
+ return "WAV audio";
9592
+ if (ext === "ogg")
9593
+ return "Ogg audio";
9594
+ if (ext === "flac")
9595
+ return "FLAC audio";
9596
+ if (ext === "m4a")
9597
+ return "M4A audio";
9598
+ if (ext === "aac")
9599
+ return "AAC audio";
9600
+ if (ext === "opus")
9601
+ return "Opus audio";
9602
+ if (ext === "mid" || ext === "midi")
9603
+ return "MIDI file";
9262
9604
  if (mime?.startsWith("image/"))
9263
9605
  return "Image";
9264
9606
  if (mime?.startsWith("video/"))
9265
9607
  return "Video";
9608
+ if (mime?.startsWith("audio/"))
9609
+ return "Audio";
9266
9610
  if (mime === "application/pdf")
9267
9611
  return "PDF document";
9268
9612
  if (fallback === "unsupported file")
@@ -9734,6 +10078,12 @@
9734
10078
  video.controls = true;
9735
10079
  video.preload = "metadata";
9736
10080
  view.appendChild(video);
10081
+ } else if (mediaKind === "audio") {
10082
+ const audio = document.createElement("audio");
10083
+ audio.src = url;
10084
+ audio.controls = true;
10085
+ audio.preload = "metadata";
10086
+ view.appendChild(audio);
9737
10087
  } else if (mediaKind === "pdf") {
9738
10088
  const frame = document.createElement("iframe");
9739
10089
  frame.src = url;
@@ -9888,6 +10238,7 @@
9888
10238
  } else {
9889
10239
  root.prepend(card);
9890
10240
  }
10241
+ placeSidebarToggle();
9891
10242
  const controller = new AbortController;
9892
10243
  ACTIVE_SOURCE_LOAD = { controller, req, target, card };
9893
10244
  renderSourceLoading(card, target, () => cancelActiveSourceLoad("user"));
@@ -9900,7 +10251,7 @@
9900
10251
  renderSourceUnsupported(card, target);
9901
10252
  return;
9902
10253
  }
9903
- if (displayKind === "image" || displayKind === "video" || displayKind === "pdf") {
10254
+ if (displayKind === "image" || displayKind === "video" || displayKind === "audio" || displayKind === "pdf") {
9904
10255
  if (req !== SOURCE_REQ_SEQ || !sourceTargetsEqual(sourceTargetFromRoute(), target))
9905
10256
  return;
9906
10257
  finishSourceLoad(req);
@@ -10296,9 +10647,10 @@
10296
10647
  b2.addEventListener("scroll", () => mirror(b2, a2), { passive: true });
10297
10648
  });
10298
10649
  }
10299
- const MEDIA_RE = /\.(png|jpe?g|gif|webp|svg|avif|bmp|ico|mp4|webm|mov)(\?.*)?$/i;
10650
+ const MEDIA_RE = /\.(png|jpe?g|gif|webp|svg|avif|bmp|ico|mp4|webm|mov|mp3|wav|ogg|flac|m4a|aac|opus)(\?.*)?$/i;
10300
10651
  const IMAGE_RE = /\.(png|jpe?g|gif|webp|svg|avif|bmp|ico)(\?.*)?$/i;
10301
10652
  const VIDEO_RE = /\.(mp4|webm|mov)$/i;
10653
+ const AUDIO_RE = /\.(mp3|wav|ogg|flac|m4a|aac|opus)$/i;
10302
10654
  function isMedia(p2) {
10303
10655
  return MEDIA_RE.test(p2);
10304
10656
  }
@@ -10308,6 +10660,9 @@
10308
10660
  function isVideo(p2) {
10309
10661
  return VIDEO_RE.test(p2);
10310
10662
  }
10663
+ function isAudio(p2) {
10664
+ return AUDIO_RE.test(p2);
10665
+ }
10311
10666
  function fileURL(path, ref) {
10312
10667
  return "/_file?path=" + encodeURIComponent(path) + "&ref=" + ref;
10313
10668
  }
@@ -10316,6 +10671,9 @@
10316
10671
  if (isVideo(path)) {
10317
10672
  return '<video src="' + url + '" controls preload="metadata"></video>';
10318
10673
  }
10674
+ if (isAudio(path)) {
10675
+ return '<audio src="' + url + '" controls preload="metadata"></audio>';
10676
+ }
10319
10677
  return '<img src="' + url + '" alt="" loading="lazy">';
10320
10678
  }
10321
10679
  function enhanceMediaCard(file, card) {
@@ -10404,17 +10762,31 @@
10404
10762
  body.style.display = STATE.collapsed ? "none" : "";
10405
10763
  });
10406
10764
  }
10765
+ applySidebarFontSize();
10766
+ applyCodeFontSize();
10767
+ applySidebarHidden();
10768
+ observeSidebarHeaderHeight();
10769
+ hydrateRefSelectorMounts();
10407
10770
  setSidebarTreeActionIcons();
10408
10771
  $$(".sb-view-seg button").forEach((b2) => {
10409
10772
  b2.addEventListener("click", () => {
10410
10773
  STATE.sbView = b2.dataset.view || "tree";
10411
10774
  localStorage.setItem("gdp:sbview", STATE.sbView);
10412
- if (STATE.files && STATE.files.length)
10413
- renderSidebar(STATE.files);
10775
+ if (SIDEBAR_FILES.length)
10776
+ renderSidebar(SIDEBAR_FILES, SIDEBAR_ON_FILE_CLICK);
10414
10777
  });
10415
10778
  });
10416
10779
  $("#sb-expand-all").addEventListener("click", () => setAllSidebarDirsCollapsed(false));
10417
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
+ });
10418
10790
  prepareKeyboardPanels();
10419
10791
  const contentPanel = document.querySelector("#content");
10420
10792
  contentPanel?.addEventListener("focusin", () => setPanelFocusScope("main"));
@@ -10849,17 +11221,19 @@
10849
11221
  });
10850
11222
  }
10851
11223
  async function repoPaletteFiles(ref) {
10852
- const cached = REPO_FILE_CACHE.get(ref);
11224
+ const cacheKey = repoFileCacheKey(ref);
11225
+ const cached = REPO_FILE_CACHE.get(cacheKey);
10853
11226
  if (cached && cached.generation === SERVER_GENERATION)
10854
11227
  return cached;
10855
11228
  const params = new URLSearchParams;
10856
11229
  params.set("ref", ref);
11230
+ appendScopeOmitDirsParam(params);
10857
11231
  const res = await trackLoad(fetch("/_files?" + params.toString()).then((r2) => {
10858
11232
  if (!r2.ok)
10859
11233
  throw new Error("failed to load files");
10860
11234
  return r2.json();
10861
11235
  }));
10862
- REPO_FILE_CACHE.set(ref, res);
11236
+ REPO_FILE_CACHE.set(cacheKey, res);
10863
11237
  return res;
10864
11238
  }
10865
11239
  function diffFilePaletteItems(state, query) {
@@ -10947,6 +11321,7 @@
10947
11321
  params.set("max", "200");
10948
11322
  if (state.grepRegex)
10949
11323
  params.set("regex", "1");
11324
+ appendScopeOmitDirsParam(params);
10950
11325
  if (source === "diff") {
10951
11326
  for (const file of state.diffSnapshot)
10952
11327
  params.append("path", file.path);
@@ -11065,6 +11440,8 @@
11065
11440
  return true;
11066
11441
  }
11067
11442
  if (action === "focus-sidebar") {
11443
+ if (STATE.sidebarHidden)
11444
+ applySidebarHidden(false);
11068
11445
  focusSidebarPanel();
11069
11446
  return true;
11070
11447
  }
@@ -11194,6 +11571,7 @@
11194
11571
  params.set("ref", STATE.route.ref || "worktree");
11195
11572
  if (STATE.route.path)
11196
11573
  params.set("path", STATE.route.path);
11574
+ appendScopeOmitDirsParam(params);
11197
11575
  return trackLoad(fetch("/_tree?" + params.toString()).then((r2) => {
11198
11576
  if (!r2.ok)
11199
11577
  throw new Error("failed to load repository tree");
@@ -11229,16 +11607,18 @@
11229
11607
  setStatus("live");
11230
11608
  }).catch(() => setStatus("error"));
11231
11609
  }
11232
- if (STATE.route.screen === "help") {
11233
- setStatus("live");
11234
- renderHelpPage();
11235
- } else if (STATE.route.screen === "repo")
11236
- loadRepo();
11237
- else if (STATE.route.screen === "file" && STATE.route.view === "blob") {
11238
- setStatus("live");
11239
- applySourceRouteToShell();
11240
- } else
11241
- 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
+ });
11242
11622
  function syncRefInputs() {
11243
11623
  const fi = $("#ref-from"), ti = $("#ref-to");
11244
11624
  if (fi)
@@ -11357,40 +11737,21 @@
11357
11737
  popover.hidden = true;
11358
11738
  popTarget = null;
11359
11739
  }
11360
- ["#ref-from", "#ref-to"].forEach((sel) => {
11361
- const el = $(sel);
11362
- el.addEventListener("focus", () => openPopover(el));
11363
- el.addEventListener("mousedown", (e2) => {
11364
- if (popover.hidden) {
11365
- e2.preventDefault();
11366
- el.focus();
11367
- }
11368
- });
11369
- el.addEventListener("click", (e2) => {
11370
- e2.stopPropagation();
11371
- openPopover(el);
11372
- });
11373
- el.addEventListener("keydown", (e2) => {
11374
- if (e2.key === "Enter") {
11375
- e2.preventDefault();
11376
- closePopover();
11377
- } else if (e2.key === "Escape") {
11378
- closePopover();
11379
- el.blur();
11380
- }
11381
- });
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);
11382
11747
  });
11383
- wireRepoTargetPicker($("#repo-target"), (ref) => {
11748
+ wireRefSelectorInput(refToInput, () => setRange(refFromInput.value, refToInput.value));
11749
+ wireRefSelectorInput($("#repo-target"), (ref) => {
11384
11750
  if (STATE.route.screen !== "file")
11385
11751
  return;
11386
11752
  setRoute({ screen: "file", path: STATE.route.path, ref, view: "blob", range: currentRange() });
11387
11753
  renderStandaloneSource({ path: STATE.route.path, ref });
11388
11754
  });
11389
- document.addEventListener("focusin", (e2) => {
11390
- const el = e2.target;
11391
- if (el instanceof HTMLInputElement && (el.id === "repo-ref" || el.id === "repo-target"))
11392
- openPopover(el);
11393
- });
11394
11755
  popSearch.addEventListener("input", () => buildPopBody(popSearch.value));
11395
11756
  popSearch.addEventListener("keydown", (e2) => {
11396
11757
  if (e2.key === "Escape") {
@@ -11407,24 +11768,8 @@
11407
11768
  return;
11408
11769
  const pickedTarget = popTarget;
11409
11770
  pickedTarget.value = val;
11410
- if (pickedTarget.id === "repo-ref") {
11411
- closePopover();
11412
- pickedTarget.dispatchEvent(new Event("change"));
11413
- return;
11414
- }
11415
- if (pickedTarget.id === "repo-target") {
11416
- closePopover();
11417
- pickedTarget.dispatchEvent(new Event("change"));
11418
- return;
11419
- }
11420
- const targetWasFrom = pickedTarget.id === "ref-from";
11421
- const otherEmpty = !$("#ref-to").value;
11422
11771
  closePopover();
11423
- setRange($("#ref-from").value, $("#ref-to").value);
11424
- if (targetWasFrom && otherEmpty) {
11425
- const ti = $("#ref-to");
11426
- setTimeout(() => ti.focus(), 0);
11427
- }
11772
+ pickedTarget.dispatchEvent(new Event("change"));
11428
11773
  }
11429
11774
  popBody.addEventListener("click", (e2) => {
11430
11775
  const item = e2.target.closest(".rp-item-commit, .rp-item-ref");