@youtyan/code-viewer 0.8.3 → 0.8.5

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.
Files changed (3) hide show
  1. package/dist/code-viewer.js +491 -483
  2. package/package.json +1 -1
  3. package/web/app.js +246 -87
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youtyan/code-viewer",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Local browser-based code and git diff viewer",
5
5
  "type": "module",
6
6
  "bin": {
package/web/app.js CHANGED
@@ -13336,6 +13336,19 @@ ${frontmatter.yaml}
13336
13336
  return btn;
13337
13337
  }
13338
13338
 
13339
+ // web-src/views/database/query-value.ts
13340
+ function formatQueryValue(value) {
13341
+ if (value === null)
13342
+ return "NULL";
13343
+ if (value instanceof Uint8Array)
13344
+ return `<blob ${value.byteLength} bytes>`;
13345
+ if (typeof value === "boolean")
13346
+ return value ? "true" : "false";
13347
+ if (typeof value === "object")
13348
+ return JSON.stringify(value);
13349
+ return String(value);
13350
+ }
13351
+
13339
13352
  // web-src/views/database/shiki-sql.ts
13340
13353
  function highlightSqlToInnerHtml(code2, highlighter) {
13341
13354
  if (!highlighter || !code2)
@@ -13517,7 +13530,7 @@ ${frontmatter.yaml}
13517
13530
  tr.appendChild(tdNum);
13518
13531
  for (const value of row) {
13519
13532
  const td = document.createElement("td");
13520
- td.textContent = formatValue(value);
13533
+ td.textContent = formatQueryValue(value);
13521
13534
  if (value === null)
13522
13535
  td.classList.add("null");
13523
13536
  tr.appendChild(td);
@@ -13677,17 +13690,6 @@ ${frontmatter.yaml}
13677
13690
  localize
13678
13691
  };
13679
13692
  }
13680
- function formatValue(value) {
13681
- if (value === null)
13682
- return "NULL";
13683
- if (value instanceof Uint8Array)
13684
- return `<blob ${value.byteLength} bytes>`;
13685
- if (typeof value === "boolean")
13686
- return value ? "true" : "false";
13687
- if (typeof value === "object")
13688
- return JSON.stringify(value);
13689
- return String(value);
13690
- }
13691
13693
 
13692
13694
  // web-src/views/database/query-history-view.ts
13693
13695
  function createQueryHistoryView(callbacks) {
@@ -13996,7 +13998,7 @@ ${frontmatter.yaml}
13996
13998
  tr.appendChild(tdNum);
13997
13999
  for (const value of row) {
13998
14000
  const td = document.createElement("td");
13999
- td.textContent = formatValue2(value);
14001
+ td.textContent = formatQueryValue(value);
14000
14002
  if (value === null)
14001
14003
  td.classList.add("null");
14002
14004
  tr.appendChild(td);
@@ -14131,17 +14133,6 @@ ${frontmatter.yaml}
14131
14133
  return iso;
14132
14134
  }
14133
14135
  }
14134
- function formatValue2(value) {
14135
- if (value === null)
14136
- return "NULL";
14137
- if (value instanceof Uint8Array)
14138
- return `<blob ${value.byteLength} bytes>`;
14139
- if (typeof value === "boolean")
14140
- return value ? "true" : "false";
14141
- if (typeof value === "object")
14142
- return JSON.stringify(value);
14143
- return String(value);
14144
- }
14145
14136
 
14146
14137
  // web-src/views/database/redis-explorer.ts
14147
14138
  function isBinaryItem(item) {
@@ -19042,7 +19033,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
19042
19033
  }
19043
19034
  for (let c2 = 0;c2 < columnNames.length; c2++) {
19044
19035
  const original = rowData[c2];
19045
- const originalShown = original === null ? null : original instanceof Uint8Array ? formatValue3(original) : String(original);
19036
+ const originalShown = original === null ? null : original instanceof Uint8Array ? formatValue(original) : String(original);
19046
19037
  const hasOverride = pendingForRow?.edits.has(c2) ?? false;
19047
19038
  const shown = hasOverride ? pendingForRow?.edits.get(c2) ?? null : originalShown;
19048
19039
  const ro = !editableRow || columns[c2]?.primaryKey === true || original instanceof Uint8Array;
@@ -19118,7 +19109,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
19118
19109
  cell.className = "db-grid-cell";
19119
19110
  cell.style.width = `${getColWidth(columnNames[c2])}px`;
19120
19111
  const val = rowData[c2];
19121
- cell.textContent = formatValue3(val);
19112
+ cell.textContent = formatValue(val);
19122
19113
  if (val === null)
19123
19114
  cell.classList.add("null");
19124
19115
  else if (val instanceof Uint8Array)
@@ -19715,7 +19706,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
19715
19706
  onEditModeChange
19716
19707
  };
19717
19708
  }
19718
- function formatValue3(value) {
19709
+ function formatValue(value) {
19719
19710
  if (value === null)
19720
19711
  return "NULL";
19721
19712
  if (value instanceof Uint8Array)
@@ -22954,15 +22945,16 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22954
22945
  function shouldRenderDiffSidebar(listSame, domIntact) {
22955
22946
  return !listSame || !domIntact;
22956
22947
  }
22957
- function findDirectDiffCardByKey(target, key) {
22948
+ function collectDiffCardsByKey(target) {
22949
+ const cards = new Map;
22958
22950
  for (const child of Array.from(target.children)) {
22959
22951
  if (!child.classList.contains("gdp-file-shell"))
22960
22952
  continue;
22961
22953
  const card = child;
22962
- if (card.dataset.key === key)
22963
- return card;
22954
+ if (card.dataset.key)
22955
+ cards.set(card.dataset.key, card);
22964
22956
  }
22965
- return null;
22957
+ return cards;
22966
22958
  }
22967
22959
  function createDiffView(deps) {
22968
22960
  const {
@@ -23363,21 +23355,16 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23363
23355
  enqueueLoad(file, card, 0);
23364
23356
  }
23365
23357
  }
23366
- function invalidateLoadedCard(card, file, changedPaths) {
23358
+ function invalidateLoadedCard(card, file, changedPaths, measuredHeight) {
23367
23359
  if (!card.classList.contains("loaded") && !card.classList.contains("loading"))
23368
23360
  return false;
23369
23361
  if (changedPaths && !changedPaths.has(file.path))
23370
23362
  return false;
23371
23363
  card.dataset.reqId = String(++CLIENT_REQ_SEQ);
23364
+ card.style.minHeight = `${measuredHeight ?? card.offsetHeight}px`;
23372
23365
  card.classList.remove("loaded", "loading", "error");
23373
23366
  card.classList.add("pending");
23374
23367
  card._diffData = null;
23375
- const body = card.querySelector(".gdp-shell-body");
23376
- if (body)
23377
- body.innerHTML = "";
23378
- const head = card.querySelector(".gdp-shell-header");
23379
- if (head)
23380
- head.style.display = "";
23381
23368
  const indicator = card.querySelector(".loading-indicator");
23382
23369
  if (indicator)
23383
23370
  indicator.hidden = false;
@@ -23468,6 +23455,14 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23468
23455
  let invalidatedCards = 0;
23469
23456
  if (listSame && domIntact) {
23470
23457
  const pathsUnknown2 = !changedPaths;
23458
+ const cardsByKey = collectDiffCardsByKey(target);
23459
+ const measuredHeights = new Map;
23460
+ for (const f2 of newFiles) {
23461
+ const key = fileKey(f2);
23462
+ const card = cardsByKey.get(key);
23463
+ if (card && (card.classList.contains("loaded") || card.classList.contains("loading")))
23464
+ measuredHeights.set(key, card.offsetHeight);
23465
+ }
23471
23466
  let sidebarNeedsStatsUpdate = false;
23472
23467
  for (const f2 of newFiles) {
23473
23468
  const key = fileKey(f2);
@@ -23478,7 +23473,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23478
23473
  if (!sigChanged && !pathHint) {
23479
23474
  continue;
23480
23475
  }
23481
- const card = findDirectDiffCardByKey(target, key);
23476
+ const card = cardsByKey.get(key);
23482
23477
  if (!card)
23483
23478
  continue;
23484
23479
  const sizeChanged = card.dataset.sizeClass !== (f2.size_class || "small");
@@ -23494,7 +23489,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23494
23489
  stats.innerHTML = '<span class="a">+' + (f2.additions || 0) + "</span>" + '<span class="d">−' + (f2.deletions || 0) + "</span>";
23495
23490
  }
23496
23491
  card._file = f2;
23497
- const didInvalidate = sigChanged ? invalidateLoadedCard(card, f2, null) : invalidateLoadedCard(card, f2, changedPaths);
23492
+ const didInvalidate = sigChanged ? invalidateLoadedCard(card, f2, null, measuredHeights.get(key)) : invalidateLoadedCard(card, f2, changedPaths, measuredHeights.get(key));
23498
23493
  if (didInvalidate)
23499
23494
  invalidatedCards++;
23500
23495
  else if ((sigChanged || pathHint) && card.classList.contains("pending")) {
@@ -23522,11 +23517,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23522
23517
  }
23523
23518
  if (sidebarNeedsRender && canUpdateSidebar)
23524
23519
  renderSidebar(newFiles);
23525
- const oldByKey = new Map;
23526
- target.querySelectorAll(".gdp-file-shell").forEach((c2) => {
23527
- if (c2.dataset.key)
23528
- oldByKey.set(c2.dataset.key, c2);
23529
- });
23520
+ const oldByKey = collectDiffCardsByKey(target);
23530
23521
  const ordered = [];
23531
23522
  for (const f2 of newFiles) {
23532
23523
  const key = fileKey(f2);
@@ -23799,6 +23790,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23799
23790
  return;
23800
23791
  card.classList.remove("loading");
23801
23792
  card.classList.add("error");
23793
+ card.style.minHeight = "";
23802
23794
  const body = card.querySelector(".gdp-shell-body");
23803
23795
  if (!body)
23804
23796
  return;
@@ -23859,6 +23851,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23859
23851
  }
23860
23852
  function setFileCollapsed(card, collapsed) {
23861
23853
  card.classList.toggle("gdp-file-collapsed", collapsed);
23854
+ if (collapsed)
23855
+ card.style.minHeight = "";
23862
23856
  card.querySelectorAll(".d2h-files-diff, .d2h-file-diff, .gdp-source-viewer, .gdp-media").forEach((body) => {
23863
23857
  body.classList.toggle("d2h-d-none", collapsed);
23864
23858
  });
@@ -30574,6 +30568,7 @@ code-viewer query agent-help`
30574
30568
  let SIDEBAR_TREE_ITEMS_CACHE = new WeakMap;
30575
30569
  const SIDEBAR_LAZY_LOADED_DIRS = new Set;
30576
30570
  const SIDEBAR_LAZY_LOADING_DIRS = new Map;
30571
+ const SIDEBAR_LAZY_DIR_SIGNATURES = new Map;
30577
30572
  function savedSidebarFontSize() {
30578
30573
  return normalizeViewerFontSize(getSidebarFontSize());
30579
30574
  }
@@ -30939,37 +30934,40 @@ code-viewer query agent-help`
30939
30934
  if (SIDEBAR_TREE_ROOT)
30940
30935
  buildSidebarTreeRows(SIDEBAR_TREE_ROOT);
30941
30936
  }
30942
- function ensureVirtualSidebarDirLoaded(dir) {
30943
- if (!shouldLazyLoadSidebarDir(dir))
30944
- return Promise.resolve();
30945
- const existing = SIDEBAR_LAZY_LOADING_DIRS.get(dir.path);
30946
- if (existing)
30947
- return existing;
30937
+ function fetchSidebarDirEntries(dir) {
30948
30938
  const params = new URLSearchParams;
30949
30939
  params.set("ref", getRepoSidebarRef() || "worktree");
30950
30940
  params.set("path", dir.path);
30951
30941
  appendScopeParams(params);
30952
- const load = trackLoad(fetch(`/_tree?${params.toString()}`).then((response) => {
30942
+ return trackLoad(fetch(`/_tree?${params.toString()}`).then((response) => {
30953
30943
  if (!response.ok)
30954
30944
  throw new Error("failed to load repository tree");
30955
30945
  return response.json();
30956
- })).then((meta) => {
30957
- const entries = meta.entries.map((entry, index) => ({
30958
- order: dir.minOrder + (index + 1) / 1e5,
30959
- path: entry.path,
30960
- display_path: entry.path,
30961
- type: meta.ref === "worktree" && entry.type === "commit" && !entry.submodule ? "tree" : entry.type,
30962
- submodule: entry.submodule,
30963
- children_omitted: entry.children_omitted,
30964
- children_omitted_reason: entry.children_omitted_reason,
30965
- is_symlink: entry.is_symlink,
30966
- symlink_target: entry.symlink_target,
30967
- symlink_target_type: entry.symlink_target_type,
30968
- resolved_path: entry.resolved_path,
30969
- status: entry.status
30970
- }));
30946
+ })).then((meta) => meta.entries.map((entry, index) => ({
30947
+ order: dir.minOrder + (index + 1) / 1e5,
30948
+ path: entry.path,
30949
+ display_path: entry.path,
30950
+ type: meta.ref === "worktree" && entry.type === "commit" && !entry.submodule ? "tree" : entry.type,
30951
+ submodule: entry.submodule,
30952
+ children_omitted: entry.children_omitted,
30953
+ children_omitted_reason: entry.children_omitted_reason,
30954
+ is_symlink: entry.is_symlink,
30955
+ symlink_target: entry.symlink_target,
30956
+ symlink_target_type: entry.symlink_target_type,
30957
+ resolved_path: entry.resolved_path,
30958
+ status: entry.status
30959
+ })));
30960
+ }
30961
+ function ensureVirtualSidebarDirLoaded(dir) {
30962
+ if (!shouldLazyLoadSidebarDir(dir))
30963
+ return Promise.resolve();
30964
+ const existing = SIDEBAR_LAZY_LOADING_DIRS.get(dir.path);
30965
+ if (existing)
30966
+ return existing;
30967
+ const load = fetchSidebarDirEntries(dir).then((entries) => {
30971
30968
  mergeSidebarTreeEntries(entries);
30972
30969
  SIDEBAR_LAZY_LOADED_DIRS.add(dir.path);
30970
+ SIDEBAR_LAZY_DIR_SIGNATURES.set(dir.path, sidebarItemsSignature(entries));
30973
30971
  }).finally(() => {
30974
30972
  SIDEBAR_LAZY_LOADING_DIRS.delete(dir.path);
30975
30973
  });
@@ -31372,6 +31370,102 @@ code-viewer query agent-help`
31372
31370
  };
31373
31371
  visit(root);
31374
31372
  }
31373
+ function sidebarItemsSignature(items) {
31374
+ return items.map((f2) => [
31375
+ f2.path,
31376
+ f2.type || "blob",
31377
+ f2.status || "",
31378
+ f2.children_omitted ? 1 : 0,
31379
+ f2.children_omitted_reason || "",
31380
+ f2.is_symlink ? 1 : 0,
31381
+ f2.symlink_target || "",
31382
+ f2.resolved_path || "",
31383
+ f2.submodule ? 1 : 0
31384
+ ].join("\x00")).join(`
31385
+ `);
31386
+ }
31387
+ function findSidebarTreeDir(node, path) {
31388
+ if (node.path === path)
31389
+ return node;
31390
+ for (const child of Object.values(node.dirs)) {
31391
+ if (path === child.path || path.startsWith(`${child.path}/`))
31392
+ return findSidebarTreeDir(child, path);
31393
+ }
31394
+ return null;
31395
+ }
31396
+ function graftLoadedSidebarDirs(oldRoot, newRoot) {
31397
+ const scope = newRoot.path ? `${newRoot.path}/` : "";
31398
+ const paths = [...SIDEBAR_LAZY_LOADED_DIRS].filter((p2) => p2 !== newRoot.path && (!scope || p2.startsWith(scope))).sort((a2, b2) => a2.length - b2.length);
31399
+ for (const path of paths) {
31400
+ const oldNode = findSidebarTreeDir(oldRoot, path);
31401
+ const newNode = findSidebarTreeDir(newRoot, path);
31402
+ if (!oldNode || !newNode || newNode.children_omitted) {
31403
+ SIDEBAR_LAZY_LOADED_DIRS.delete(path);
31404
+ SIDEBAR_LAZY_DIR_SIGNATURES.delete(path);
31405
+ continue;
31406
+ }
31407
+ if (sidebarTreeNodeHasChildren(newNode))
31408
+ continue;
31409
+ newNode.dirs = oldNode.dirs;
31410
+ newNode.files = oldNode.files;
31411
+ }
31412
+ }
31413
+ function applySidebarDirEntries(dir, entries) {
31414
+ const signature = sidebarItemsSignature(entries);
31415
+ if (SIDEBAR_LAZY_DIR_SIGNATURES.get(dir.path) === signature)
31416
+ return false;
31417
+ SIDEBAR_LAZY_DIR_SIGNATURES.set(dir.path, signature);
31418
+ const oldSubtree = { ...dir };
31419
+ dir.dirs = {};
31420
+ dir.files = [];
31421
+ for (const entry of entries)
31422
+ upsertSidebarTreeEntry(entry, entry.order ?? 0);
31423
+ graftLoadedSidebarDirs(oldSubtree, dir);
31424
+ return true;
31425
+ }
31426
+ function rebuildVirtualSidebarRows() {
31427
+ if (!SIDEBAR_TREE_ROOT)
31428
+ return;
31429
+ SIDEBAR_TREE_ITEMS_CACHE = new WeakMap;
31430
+ buildSidebarTreeRows(SIDEBAR_TREE_ROOT);
31431
+ rerenderVirtualSidebar();
31432
+ if (STATE.activeFile)
31433
+ markActive(STATE.activeFile);
31434
+ applyFilter();
31435
+ }
31436
+ async function refreshRepoSidebarTree(rootFiles) {
31437
+ if (!isRepositorySidebarMode() || !isVirtualSidebarActive())
31438
+ return;
31439
+ if (!SIDEBAR_TREE_ROOT)
31440
+ return;
31441
+ if (sidebarItemsSignature(rootFiles) !== sidebarItemsSignature(SIDEBAR_FILES)) {
31442
+ const newRoot = buildTree(rootFiles);
31443
+ graftLoadedSidebarDirs(SIDEBAR_TREE_ROOT, newRoot);
31444
+ SIDEBAR_FILES = rootFiles;
31445
+ SIDEBAR_TREE_ROOT = newRoot;
31446
+ rebuildVirtualSidebarRows();
31447
+ }
31448
+ const root = SIDEBAR_TREE_ROOT;
31449
+ const refreshed = await Promise.all([...SIDEBAR_LAZY_LOADED_DIRS].map(async (path) => {
31450
+ const node = findSidebarTreeDir(root, path);
31451
+ if (!node)
31452
+ return null;
31453
+ const entries = await fetchSidebarDirEntries(node).catch(() => null);
31454
+ return entries ? { path, entries } : null;
31455
+ }));
31456
+ if (SIDEBAR_TREE_ROOT !== root)
31457
+ return;
31458
+ let childrenChanged = false;
31459
+ for (const result of refreshed) {
31460
+ if (!result)
31461
+ continue;
31462
+ const node = findSidebarTreeDir(root, result.path);
31463
+ if (node && applySidebarDirEntries(node, result.entries))
31464
+ childrenChanged = true;
31465
+ }
31466
+ if (childrenChanged)
31467
+ rebuildVirtualSidebarRows();
31468
+ }
31375
31469
  function renderFlat(files, ul, onFileClick) {
31376
31470
  files.forEach((f2, i2) => {
31377
31471
  const li = document.createElement("li");
@@ -31435,6 +31529,7 @@ code-viewer query agent-help`
31435
31529
  SIDEBAR_ROW_BY_PATH = new Map;
31436
31530
  SIDEBAR_LAZY_LOADED_DIRS.clear();
31437
31531
  SIDEBAR_LAZY_LOADING_DIRS.clear();
31532
+ SIDEBAR_LAZY_DIR_SIGNATURES.clear();
31438
31533
  if (!onFileClick)
31439
31534
  STATE.files = files;
31440
31535
  SIDEBAR_FILES = files;
@@ -31555,7 +31650,8 @@ code-viewer query agent-help`
31555
31650
  setActiveSidebarItem(sidebarItemByPath(path));
31556
31651
  if ($("#filelist").classList.contains("tree-virtual")) {
31557
31652
  renderVirtualSidebarWindow();
31558
- scrollVirtualSidebarPathIntoView(path);
31653
+ if (options.reveal)
31654
+ scrollVirtualSidebarPathIntoView(path);
31559
31655
  return;
31560
31656
  }
31561
31657
  if (options.reveal) {
@@ -31981,6 +32077,7 @@ code-viewer query agent-help`
31981
32077
  }
31982
32078
  return {
31983
32079
  renderSidebar,
32080
+ refreshRepoSidebarTree,
31984
32081
  applyFilter,
31985
32082
  scheduleApplyFilter,
31986
32083
  flushSidebarFilter,
@@ -32050,6 +32147,7 @@ code-viewer query agent-help`
32050
32147
  markActive,
32051
32148
  applyFilter,
32052
32149
  renderSidebar,
32150
+ refreshRepoSidebarTree,
32053
32151
  rerenderVirtualSidebar,
32054
32152
  ensureVirtualSidebarDirLoaded,
32055
32153
  scrollVirtualSidebarPathIntoView,
@@ -32445,18 +32543,26 @@ code-viewer query agent-help`
32445
32543
  });
32446
32544
  return nav;
32447
32545
  }
32546
+ let REPO_RENDER_SIGNATURE = "";
32547
+ let REPO_RENDER_SEQ = 0;
32548
+ let REPO_RENDER_LOCATION = "";
32448
32549
  async function renderRepo(meta) {
32550
+ const myRender = ++REPO_RENDER_SEQ;
32449
32551
  setProjectName(meta.project || "");
32450
32552
  setPageMode();
32451
32553
  removeStandaloneSource();
32452
32554
  $("#empty").classList.add("hidden");
32453
- $("#diff").replaceChildren();
32454
32555
  if (!isRepoSidebarReusable(meta.ref))
32455
32556
  $("#totals").textContent = "";
32456
32557
  STATE.files = [];
32457
32558
  clearLoadQueue();
32458
32559
  renderRepoBlobSidebar(meta.path || "", meta.ref);
32459
32560
  const target = $("#diff");
32561
+ const signature = JSON.stringify(meta);
32562
+ if (signature === REPO_RENDER_SIGNATURE && target.querySelector(":scope > .gdp-repo-shell")) {
32563
+ placeSidebarToggle();
32564
+ return;
32565
+ }
32460
32566
  const shell = document.createElement("section");
32461
32567
  shell.className = "gdp-repo-shell";
32462
32568
  const toolbar = document.createElement("div");
@@ -32615,9 +32721,33 @@ code-viewer query agent-help`
32615
32721
  readme.appendChild(wrapper);
32616
32722
  shell.appendChild(readme);
32617
32723
  }
32618
- target.appendChild(shell);
32724
+ if (myRender !== REPO_RENDER_SEQ || !isActiveRepoRoute(meta.ref || "worktree", meta.path || ""))
32725
+ return;
32726
+ REPO_RENDER_SIGNATURE = signature;
32727
+ const location2 = `${meta.ref || "worktree"}\x00${meta.path || ""}`;
32728
+ const sameLocation = location2 === REPO_RENDER_LOCATION;
32729
+ REPO_RENDER_LOCATION = location2;
32730
+ const savedScroll = window.scrollY;
32731
+ target.replaceChildren(shell);
32732
+ window.scrollTo(0, sameLocation ? savedScroll : 0);
32619
32733
  placeSidebarToggle();
32620
32734
  }
32735
+ function repoTreeEntriesToSidebarItems(entries, ref) {
32736
+ return entries.map((entry, index) => ({
32737
+ order: index + 1,
32738
+ path: entry.path,
32739
+ display_path: entry.path,
32740
+ type: canBrowseRepoEntry(entry, ref) ? "tree" : entry.type,
32741
+ submodule: entry.submodule,
32742
+ children_omitted: entry.children_omitted,
32743
+ children_omitted_reason: entry.children_omitted_reason,
32744
+ is_symlink: entry.is_symlink,
32745
+ symlink_target: entry.symlink_target,
32746
+ symlink_target_type: entry.symlink_target_type,
32747
+ resolved_path: entry.resolved_path,
32748
+ status: entry.status
32749
+ }));
32750
+ }
32621
32751
  function renderRepoBlobSidebar(currentPath, ref) {
32622
32752
  syncRepoTargetInput(ref);
32623
32753
  const normalizedRef = ref || "worktree";
@@ -32648,20 +32778,7 @@ code-viewer query agent-help`
32648
32778
  })).then(async (meta) => {
32649
32779
  if (!isActiveRepoTreeRef(normalizedRef))
32650
32780
  return;
32651
- const files = meta.entries.map((entry, index) => ({
32652
- order: index + 1,
32653
- path: entry.path,
32654
- display_path: entry.path,
32655
- type: canBrowseRepoEntry(entry, normalizedRef) ? "tree" : entry.type,
32656
- submodule: entry.submodule,
32657
- children_omitted: entry.children_omitted,
32658
- children_omitted_reason: entry.children_omitted_reason,
32659
- is_symlink: entry.is_symlink,
32660
- symlink_target: entry.symlink_target,
32661
- symlink_target_type: entry.symlink_target_type,
32662
- resolved_path: entry.resolved_path,
32663
- status: entry.status
32664
- }));
32781
+ const files = repoTreeEntriesToSidebarItems(meta.entries, normalizedRef);
32665
32782
  setRepoSidebarRef(normalizedRef);
32666
32783
  renderSidebar(files, (file) => {
32667
32784
  if (file.type === "tree") {
@@ -32709,15 +32826,17 @@ code-viewer query agent-help`
32709
32826
  rerenderVirtualSidebar();
32710
32827
  }
32711
32828
  async function activateRepoSidebarPath(currentPath) {
32829
+ const reveal = getSidebarVirtualActivePath() !== currentPath;
32712
32830
  await loadRepoSidebarAncestors(currentPath);
32713
- markActive(currentPath, { reveal: true });
32831
+ markActive(currentPath, { reveal });
32714
32832
  applyFilter();
32715
32833
  const row = getSidebarRowByPath(currentPath);
32716
32834
  if (row?.kind === "dir" && row.dir && shouldLazyLoadSidebarDir(row.dir))
32717
32835
  ensureVirtualSidebarDirLoaded(row.dir).then(() => {
32718
32836
  if (getSidebarVirtualActivePath() === currentPath) {
32719
32837
  rerenderVirtualSidebar();
32720
- scrollVirtualSidebarPathIntoView(currentPath);
32838
+ if (reveal)
32839
+ scrollVirtualSidebarPathIntoView(currentPath);
32721
32840
  }
32722
32841
  });
32723
32842
  }
@@ -32968,6 +33087,42 @@ code-viewer query agent-help`
32968
33087
  }
32969
33088
  let REPO_SIDEBAR_LOAD_REF = null;
32970
33089
  let REPO_SIDEBAR_LOAD = null;
33090
+ let REPO_SIDEBAR_REFRESHING = false;
33091
+ let REPO_SIDEBAR_REFRESH_QUEUED = false;
33092
+ async function refreshRepoSidebar() {
33093
+ const ref = activeRepoTreeRef();
33094
+ if (ref == null)
33095
+ return;
33096
+ if (!isRepoSidebarReusable(ref)) {
33097
+ invalidateRepoSidebar();
33098
+ await renderRepoBlobSidebar(STATE.route.screen === "repo" ? STATE.route.path || "" : "", ref);
33099
+ return;
33100
+ }
33101
+ if (REPO_SIDEBAR_REFRESHING) {
33102
+ REPO_SIDEBAR_REFRESH_QUEUED = true;
33103
+ return;
33104
+ }
33105
+ REPO_SIDEBAR_REFRESHING = true;
33106
+ try {
33107
+ const params = new URLSearchParams;
33108
+ params.set("ref", ref);
33109
+ appendScopeParams(params);
33110
+ const meta = await trackLoad(fetch(`/_tree?${params.toString()}`).then((r2) => {
33111
+ if (!r2.ok)
33112
+ throw new Error("failed to load repository tree");
33113
+ return r2.json();
33114
+ }));
33115
+ if (!isActiveRepoTreeRef(ref) || !isRepoSidebarReusable(ref))
33116
+ return;
33117
+ await refreshRepoSidebarTree(repoTreeEntriesToSidebarItems(meta.entries, ref));
33118
+ } catch {} finally {
33119
+ REPO_SIDEBAR_REFRESHING = false;
33120
+ if (REPO_SIDEBAR_REFRESH_QUEUED) {
33121
+ REPO_SIDEBAR_REFRESH_QUEUED = false;
33122
+ refreshRepoSidebar();
33123
+ }
33124
+ }
33125
+ }
32971
33126
  return {
32972
33127
  loadRepo,
32973
33128
  createFileDetailMeta,
@@ -32981,6 +33136,7 @@ code-viewer query agent-help`
32981
33136
  handleSidebarContextMenu,
32982
33137
  fileEntryIcon,
32983
33138
  invalidateRepoSidebar,
33139
+ refreshRepoSidebar,
32984
33140
  showTrashError
32985
33141
  };
32986
33142
  }
@@ -35998,6 +36154,7 @@ code-viewer query agent-help`
35998
36154
  });
35999
36155
  const {
36000
36156
  renderSidebar,
36157
+ refreshRepoSidebarTree,
36001
36158
  applyFilter,
36002
36159
  scheduleApplyFilter,
36003
36160
  flushSidebarFilter,
@@ -36114,6 +36271,7 @@ code-viewer query agent-help`
36114
36271
  markActive,
36115
36272
  applyFilter,
36116
36273
  renderSidebar,
36274
+ refreshRepoSidebarTree,
36117
36275
  rerenderVirtualSidebar,
36118
36276
  ensureVirtualSidebarDirLoaded,
36119
36277
  scrollVirtualSidebarPathIntoView,
@@ -36178,6 +36336,7 @@ code-viewer query agent-help`
36178
36336
  closeRepoContextMenu,
36179
36337
  handleSidebarContextMenu,
36180
36338
  invalidateRepoSidebar,
36339
+ refreshRepoSidebar,
36181
36340
  showTrashError
36182
36341
  } = REPO_VIEW;
36183
36342
  const SEARCH_PALETTE = createSearchPalette({
@@ -39227,8 +39386,8 @@ code-viewer query agent-help`
39227
39386
  return;
39228
39387
  }
39229
39388
  if (route.screen === "repo") {
39230
- invalidateRepoSidebar();
39231
39389
  loadRepo();
39390
+ refreshRepoSidebar();
39232
39391
  return;
39233
39392
  }
39234
39393
  if (route.screen !== "diff" && route.screen !== "history") {