@youtyan/code-viewer 0.8.4 → 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.
@@ -26494,7 +26494,7 @@ async function handleUploadFiles(req) {
26494
26494
  return text("file exists", 409);
26495
26495
  return text("upload failed", 500);
26496
26496
  }
26497
- triggerUpdate();
26497
+ triggerUpdate(uploads.map((upload) => dir ? `${dir}/${upload.name}` : upload.name));
26498
26498
  return json2({
26499
26499
  ok: true,
26500
26500
  files: uploads.map((upload) => upload.name),
@@ -26727,6 +26727,12 @@ async function handleTrashPath(req) {
26727
26727
  const originalFullPath = safeWorktreePath2(path);
26728
26728
  if (!originalFullPath)
26729
26729
  return text("not found", 404);
26730
+ let changedPaths;
26731
+ try {
26732
+ const stats = statSync6(originalFullPath);
26733
+ if (!stats.isDirectory())
26734
+ changedPaths = [path];
26735
+ } catch {}
26730
26736
  const moved = await movePathToTrash(worktreePath(path));
26731
26737
  if (!moved.ok)
26732
26738
  return text(moved.error || "trash failed", 500);
@@ -26739,7 +26745,7 @@ async function handleTrashPath(req) {
26739
26745
  trashPath: moved.trashPath
26740
26746
  }
26741
26747
  };
26742
- triggerUpdate();
26748
+ triggerUpdate(changedPaths);
26743
26749
  return json2({ ok: true, generation, undo });
26744
26750
  }
26745
26751
  async function handleCreateDirectory(req) {
@@ -26792,7 +26798,7 @@ async function handleCreateDirectory(req) {
26792
26798
  return text("already exists", 409);
26793
26799
  return text("create failed", 500);
26794
26800
  }
26795
- triggerUpdate();
26801
+ triggerUpdate([targetPath]);
26796
26802
  return json2({ ok: true, path: targetPath, generation });
26797
26803
  }
26798
26804
  async function handleRestoreTrash(req) {
@@ -26824,7 +26830,13 @@ async function handleRestoreTrash(req) {
26824
26830
  const restored = await restoreTrashPath(originalPath, trashPath || undefined);
26825
26831
  if (!restored.ok)
26826
26832
  return text(restored.error || "undo failed", 409);
26827
- triggerUpdate();
26833
+ let changedPaths;
26834
+ try {
26835
+ const stats = statSync6(worktreePath(originalPath));
26836
+ if (!stats.isDirectory())
26837
+ changedPaths = [originalPath];
26838
+ } catch {}
26839
+ triggerUpdate(changedPaths);
26828
26840
  return json2({ ok: true, generation });
26829
26841
  }
26830
26842
  function annotationSse(kind, sessionId, entryId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youtyan/code-viewer",
3
- "version": "0.8.4",
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
@@ -22945,15 +22945,16 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22945
22945
  function shouldRenderDiffSidebar(listSame, domIntact) {
22946
22946
  return !listSame || !domIntact;
22947
22947
  }
22948
- function findDirectDiffCardByKey(target, key) {
22948
+ function collectDiffCardsByKey(target) {
22949
+ const cards = new Map;
22949
22950
  for (const child of Array.from(target.children)) {
22950
22951
  if (!child.classList.contains("gdp-file-shell"))
22951
22952
  continue;
22952
22953
  const card = child;
22953
- if (card.dataset.key === key)
22954
- return card;
22954
+ if (card.dataset.key)
22955
+ cards.set(card.dataset.key, card);
22955
22956
  }
22956
- return null;
22957
+ return cards;
22957
22958
  }
22958
22959
  function createDiffView(deps) {
22959
22960
  const {
@@ -23354,21 +23355,16 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23354
23355
  enqueueLoad(file, card, 0);
23355
23356
  }
23356
23357
  }
23357
- function invalidateLoadedCard(card, file, changedPaths) {
23358
+ function invalidateLoadedCard(card, file, changedPaths, measuredHeight) {
23358
23359
  if (!card.classList.contains("loaded") && !card.classList.contains("loading"))
23359
23360
  return false;
23360
23361
  if (changedPaths && !changedPaths.has(file.path))
23361
23362
  return false;
23362
23363
  card.dataset.reqId = String(++CLIENT_REQ_SEQ);
23364
+ card.style.minHeight = `${measuredHeight ?? card.offsetHeight}px`;
23363
23365
  card.classList.remove("loaded", "loading", "error");
23364
23366
  card.classList.add("pending");
23365
23367
  card._diffData = null;
23366
- const body = card.querySelector(".gdp-shell-body");
23367
- if (body)
23368
- body.innerHTML = "";
23369
- const head = card.querySelector(".gdp-shell-header");
23370
- if (head)
23371
- head.style.display = "";
23372
23368
  const indicator = card.querySelector(".loading-indicator");
23373
23369
  if (indicator)
23374
23370
  indicator.hidden = false;
@@ -23459,6 +23455,14 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23459
23455
  let invalidatedCards = 0;
23460
23456
  if (listSame && domIntact) {
23461
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
+ }
23462
23466
  let sidebarNeedsStatsUpdate = false;
23463
23467
  for (const f2 of newFiles) {
23464
23468
  const key = fileKey(f2);
@@ -23469,7 +23473,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23469
23473
  if (!sigChanged && !pathHint) {
23470
23474
  continue;
23471
23475
  }
23472
- const card = findDirectDiffCardByKey(target, key);
23476
+ const card = cardsByKey.get(key);
23473
23477
  if (!card)
23474
23478
  continue;
23475
23479
  const sizeChanged = card.dataset.sizeClass !== (f2.size_class || "small");
@@ -23485,7 +23489,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23485
23489
  stats.innerHTML = '<span class="a">+' + (f2.additions || 0) + "</span>" + '<span class="d">−' + (f2.deletions || 0) + "</span>";
23486
23490
  }
23487
23491
  card._file = f2;
23488
- 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));
23489
23493
  if (didInvalidate)
23490
23494
  invalidatedCards++;
23491
23495
  else if ((sigChanged || pathHint) && card.classList.contains("pending")) {
@@ -23513,11 +23517,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23513
23517
  }
23514
23518
  if (sidebarNeedsRender && canUpdateSidebar)
23515
23519
  renderSidebar(newFiles);
23516
- const oldByKey = new Map;
23517
- target.querySelectorAll(".gdp-file-shell").forEach((c2) => {
23518
- if (c2.dataset.key)
23519
- oldByKey.set(c2.dataset.key, c2);
23520
- });
23520
+ const oldByKey = collectDiffCardsByKey(target);
23521
23521
  const ordered = [];
23522
23522
  for (const f2 of newFiles) {
23523
23523
  const key = fileKey(f2);
@@ -23790,6 +23790,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23790
23790
  return;
23791
23791
  card.classList.remove("loading");
23792
23792
  card.classList.add("error");
23793
+ card.style.minHeight = "";
23793
23794
  const body = card.querySelector(".gdp-shell-body");
23794
23795
  if (!body)
23795
23796
  return;
@@ -23850,6 +23851,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23850
23851
  }
23851
23852
  function setFileCollapsed(card, collapsed) {
23852
23853
  card.classList.toggle("gdp-file-collapsed", collapsed);
23854
+ if (collapsed)
23855
+ card.style.minHeight = "";
23853
23856
  card.querySelectorAll(".d2h-files-diff, .d2h-file-diff, .gdp-source-viewer, .gdp-media").forEach((body) => {
23854
23857
  body.classList.toggle("d2h-d-none", collapsed);
23855
23858
  });
@@ -30565,6 +30568,7 @@ code-viewer query agent-help`
30565
30568
  let SIDEBAR_TREE_ITEMS_CACHE = new WeakMap;
30566
30569
  const SIDEBAR_LAZY_LOADED_DIRS = new Set;
30567
30570
  const SIDEBAR_LAZY_LOADING_DIRS = new Map;
30571
+ const SIDEBAR_LAZY_DIR_SIGNATURES = new Map;
30568
30572
  function savedSidebarFontSize() {
30569
30573
  return normalizeViewerFontSize(getSidebarFontSize());
30570
30574
  }
@@ -30930,37 +30934,40 @@ code-viewer query agent-help`
30930
30934
  if (SIDEBAR_TREE_ROOT)
30931
30935
  buildSidebarTreeRows(SIDEBAR_TREE_ROOT);
30932
30936
  }
30933
- function ensureVirtualSidebarDirLoaded(dir) {
30934
- if (!shouldLazyLoadSidebarDir(dir))
30935
- return Promise.resolve();
30936
- const existing = SIDEBAR_LAZY_LOADING_DIRS.get(dir.path);
30937
- if (existing)
30938
- return existing;
30937
+ function fetchSidebarDirEntries(dir) {
30939
30938
  const params = new URLSearchParams;
30940
30939
  params.set("ref", getRepoSidebarRef() || "worktree");
30941
30940
  params.set("path", dir.path);
30942
30941
  appendScopeParams(params);
30943
- const load = trackLoad(fetch(`/_tree?${params.toString()}`).then((response) => {
30942
+ return trackLoad(fetch(`/_tree?${params.toString()}`).then((response) => {
30944
30943
  if (!response.ok)
30945
30944
  throw new Error("failed to load repository tree");
30946
30945
  return response.json();
30947
- })).then((meta) => {
30948
- const entries = meta.entries.map((entry, index) => ({
30949
- order: dir.minOrder + (index + 1) / 1e5,
30950
- path: entry.path,
30951
- display_path: entry.path,
30952
- type: meta.ref === "worktree" && entry.type === "commit" && !entry.submodule ? "tree" : entry.type,
30953
- submodule: entry.submodule,
30954
- children_omitted: entry.children_omitted,
30955
- children_omitted_reason: entry.children_omitted_reason,
30956
- is_symlink: entry.is_symlink,
30957
- symlink_target: entry.symlink_target,
30958
- symlink_target_type: entry.symlink_target_type,
30959
- resolved_path: entry.resolved_path,
30960
- status: entry.status
30961
- }));
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) => {
30962
30968
  mergeSidebarTreeEntries(entries);
30963
30969
  SIDEBAR_LAZY_LOADED_DIRS.add(dir.path);
30970
+ SIDEBAR_LAZY_DIR_SIGNATURES.set(dir.path, sidebarItemsSignature(entries));
30964
30971
  }).finally(() => {
30965
30972
  SIDEBAR_LAZY_LOADING_DIRS.delete(dir.path);
30966
30973
  });
@@ -31363,6 +31370,102 @@ code-viewer query agent-help`
31363
31370
  };
31364
31371
  visit(root);
31365
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
+ }
31366
31469
  function renderFlat(files, ul, onFileClick) {
31367
31470
  files.forEach((f2, i2) => {
31368
31471
  const li = document.createElement("li");
@@ -31426,6 +31529,7 @@ code-viewer query agent-help`
31426
31529
  SIDEBAR_ROW_BY_PATH = new Map;
31427
31530
  SIDEBAR_LAZY_LOADED_DIRS.clear();
31428
31531
  SIDEBAR_LAZY_LOADING_DIRS.clear();
31532
+ SIDEBAR_LAZY_DIR_SIGNATURES.clear();
31429
31533
  if (!onFileClick)
31430
31534
  STATE.files = files;
31431
31535
  SIDEBAR_FILES = files;
@@ -31546,7 +31650,8 @@ code-viewer query agent-help`
31546
31650
  setActiveSidebarItem(sidebarItemByPath(path));
31547
31651
  if ($("#filelist").classList.contains("tree-virtual")) {
31548
31652
  renderVirtualSidebarWindow();
31549
- scrollVirtualSidebarPathIntoView(path);
31653
+ if (options.reveal)
31654
+ scrollVirtualSidebarPathIntoView(path);
31550
31655
  return;
31551
31656
  }
31552
31657
  if (options.reveal) {
@@ -31972,6 +32077,7 @@ code-viewer query agent-help`
31972
32077
  }
31973
32078
  return {
31974
32079
  renderSidebar,
32080
+ refreshRepoSidebarTree,
31975
32081
  applyFilter,
31976
32082
  scheduleApplyFilter,
31977
32083
  flushSidebarFilter,
@@ -32041,6 +32147,7 @@ code-viewer query agent-help`
32041
32147
  markActive,
32042
32148
  applyFilter,
32043
32149
  renderSidebar,
32150
+ refreshRepoSidebarTree,
32044
32151
  rerenderVirtualSidebar,
32045
32152
  ensureVirtualSidebarDirLoaded,
32046
32153
  scrollVirtualSidebarPathIntoView,
@@ -32436,18 +32543,26 @@ code-viewer query agent-help`
32436
32543
  });
32437
32544
  return nav;
32438
32545
  }
32546
+ let REPO_RENDER_SIGNATURE = "";
32547
+ let REPO_RENDER_SEQ = 0;
32548
+ let REPO_RENDER_LOCATION = "";
32439
32549
  async function renderRepo(meta) {
32550
+ const myRender = ++REPO_RENDER_SEQ;
32440
32551
  setProjectName(meta.project || "");
32441
32552
  setPageMode();
32442
32553
  removeStandaloneSource();
32443
32554
  $("#empty").classList.add("hidden");
32444
- $("#diff").replaceChildren();
32445
32555
  if (!isRepoSidebarReusable(meta.ref))
32446
32556
  $("#totals").textContent = "";
32447
32557
  STATE.files = [];
32448
32558
  clearLoadQueue();
32449
32559
  renderRepoBlobSidebar(meta.path || "", meta.ref);
32450
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
+ }
32451
32566
  const shell = document.createElement("section");
32452
32567
  shell.className = "gdp-repo-shell";
32453
32568
  const toolbar = document.createElement("div");
@@ -32606,9 +32721,33 @@ code-viewer query agent-help`
32606
32721
  readme.appendChild(wrapper);
32607
32722
  shell.appendChild(readme);
32608
32723
  }
32609
- 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);
32610
32733
  placeSidebarToggle();
32611
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
+ }
32612
32751
  function renderRepoBlobSidebar(currentPath, ref) {
32613
32752
  syncRepoTargetInput(ref);
32614
32753
  const normalizedRef = ref || "worktree";
@@ -32639,20 +32778,7 @@ code-viewer query agent-help`
32639
32778
  })).then(async (meta) => {
32640
32779
  if (!isActiveRepoTreeRef(normalizedRef))
32641
32780
  return;
32642
- const files = meta.entries.map((entry, index) => ({
32643
- order: index + 1,
32644
- path: entry.path,
32645
- display_path: entry.path,
32646
- type: canBrowseRepoEntry(entry, normalizedRef) ? "tree" : entry.type,
32647
- submodule: entry.submodule,
32648
- children_omitted: entry.children_omitted,
32649
- children_omitted_reason: entry.children_omitted_reason,
32650
- is_symlink: entry.is_symlink,
32651
- symlink_target: entry.symlink_target,
32652
- symlink_target_type: entry.symlink_target_type,
32653
- resolved_path: entry.resolved_path,
32654
- status: entry.status
32655
- }));
32781
+ const files = repoTreeEntriesToSidebarItems(meta.entries, normalizedRef);
32656
32782
  setRepoSidebarRef(normalizedRef);
32657
32783
  renderSidebar(files, (file) => {
32658
32784
  if (file.type === "tree") {
@@ -32700,15 +32826,17 @@ code-viewer query agent-help`
32700
32826
  rerenderVirtualSidebar();
32701
32827
  }
32702
32828
  async function activateRepoSidebarPath(currentPath) {
32829
+ const reveal = getSidebarVirtualActivePath() !== currentPath;
32703
32830
  await loadRepoSidebarAncestors(currentPath);
32704
- markActive(currentPath, { reveal: true });
32831
+ markActive(currentPath, { reveal });
32705
32832
  applyFilter();
32706
32833
  const row = getSidebarRowByPath(currentPath);
32707
32834
  if (row?.kind === "dir" && row.dir && shouldLazyLoadSidebarDir(row.dir))
32708
32835
  ensureVirtualSidebarDirLoaded(row.dir).then(() => {
32709
32836
  if (getSidebarVirtualActivePath() === currentPath) {
32710
32837
  rerenderVirtualSidebar();
32711
- scrollVirtualSidebarPathIntoView(currentPath);
32838
+ if (reveal)
32839
+ scrollVirtualSidebarPathIntoView(currentPath);
32712
32840
  }
32713
32841
  });
32714
32842
  }
@@ -32959,6 +33087,42 @@ code-viewer query agent-help`
32959
33087
  }
32960
33088
  let REPO_SIDEBAR_LOAD_REF = null;
32961
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
+ }
32962
33126
  return {
32963
33127
  loadRepo,
32964
33128
  createFileDetailMeta,
@@ -32972,6 +33136,7 @@ code-viewer query agent-help`
32972
33136
  handleSidebarContextMenu,
32973
33137
  fileEntryIcon,
32974
33138
  invalidateRepoSidebar,
33139
+ refreshRepoSidebar,
32975
33140
  showTrashError
32976
33141
  };
32977
33142
  }
@@ -35989,6 +36154,7 @@ code-viewer query agent-help`
35989
36154
  });
35990
36155
  const {
35991
36156
  renderSidebar,
36157
+ refreshRepoSidebarTree,
35992
36158
  applyFilter,
35993
36159
  scheduleApplyFilter,
35994
36160
  flushSidebarFilter,
@@ -36105,6 +36271,7 @@ code-viewer query agent-help`
36105
36271
  markActive,
36106
36272
  applyFilter,
36107
36273
  renderSidebar,
36274
+ refreshRepoSidebarTree,
36108
36275
  rerenderVirtualSidebar,
36109
36276
  ensureVirtualSidebarDirLoaded,
36110
36277
  scrollVirtualSidebarPathIntoView,
@@ -36169,6 +36336,7 @@ code-viewer query agent-help`
36169
36336
  closeRepoContextMenu,
36170
36337
  handleSidebarContextMenu,
36171
36338
  invalidateRepoSidebar,
36339
+ refreshRepoSidebar,
36172
36340
  showTrashError
36173
36341
  } = REPO_VIEW;
36174
36342
  const SEARCH_PALETTE = createSearchPalette({
@@ -39218,8 +39386,8 @@ code-viewer query agent-help`
39218
39386
  return;
39219
39387
  }
39220
39388
  if (route.screen === "repo") {
39221
- invalidateRepoSidebar();
39222
39389
  loadRepo();
39390
+ refreshRepoSidebar();
39223
39391
  return;
39224
39392
  }
39225
39393
  if (route.screen !== "diff" && route.screen !== "history") {