@youtyan/code-viewer 0.6.8 → 0.6.10

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
@@ -8494,6 +8494,9 @@ ${frontmatter.yaml}
8494
8494
  let ANNOTATIONS = { version: 1, sessions: [] };
8495
8495
  let annotationFollow = deps.getAnnotationFollow();
8496
8496
  let activeAnnotationId = null;
8497
+ let refreshAnnotationsInFlight = null;
8498
+ let inlineAnnotationsMounted = false;
8499
+ let databaseAnnotationsMounted = false;
8497
8500
  let annotationPanelDismissed = false;
8498
8501
  let activeSessionId = new URLSearchParams(window.location.search).get(ANNOTATION_SESSION_PARAM);
8499
8502
  const annotationsChangedCallbacks = [];
@@ -8658,9 +8661,12 @@ ${frontmatter.yaml}
8658
8661
  return box;
8659
8662
  }
8660
8663
  function applyDatabaseAnnotations(session) {
8661
- document.querySelectorAll(".gdp-db-annotation-strip").forEach((el) => {
8662
- el.remove();
8663
- });
8664
+ if (databaseAnnotationsMounted) {
8665
+ document.querySelectorAll(".gdp-db-annotation-strip").forEach((el) => {
8666
+ el.remove();
8667
+ });
8668
+ databaseAnnotationsMounted = false;
8669
+ }
8664
8670
  if (!session || deps.getRoute().screen !== "database")
8665
8671
  return;
8666
8672
  const matches = session.entries.filter(databaseAnnotationMatchesRoute);
@@ -8676,6 +8682,7 @@ ${frontmatter.yaml}
8676
8682
  strip.appendChild(buildDatabaseAnnotationBlock(entry));
8677
8683
  }
8678
8684
  root.prepend(strip);
8685
+ databaseAnnotationsMounted = true;
8679
8686
  }
8680
8687
  function inlineAnnotationTargetRow(entry) {
8681
8688
  if (!entry.line)
@@ -8734,13 +8741,19 @@ ${frontmatter.yaml}
8734
8741
  return tr;
8735
8742
  }
8736
8743
  function applyInlineAnnotations() {
8737
- document.querySelectorAll(".gdp-annotation-row").forEach((row) => {
8738
- row.remove();
8739
- });
8740
8744
  const session = ANNOTATIONS.sessions.find((s2) => s2.id === activeSessionId);
8745
+ if (!session && !inlineAnnotationsMounted && !databaseAnnotationsMounted)
8746
+ return;
8747
+ if (inlineAnnotationsMounted) {
8748
+ document.querySelectorAll(".gdp-annotation-row").forEach((row) => {
8749
+ row.remove();
8750
+ });
8751
+ inlineAnnotationsMounted = false;
8752
+ }
8741
8753
  applyDatabaseAnnotations(session);
8742
8754
  if (!session)
8743
8755
  return;
8756
+ let mountedInlineRows = false;
8744
8757
  for (const entry of session.entries) {
8745
8758
  if (entry.target?.kind === "database")
8746
8759
  continue;
@@ -8758,7 +8771,9 @@ ${frontmatter.yaml}
8758
8771
  sibAnchor = sibAnchor.nextElementSibling;
8759
8772
  sibAnchor.after(buildInlineSpacerRow(entry, sibling.cells.length));
8760
8773
  }
8774
+ mountedInlineRows = true;
8761
8775
  }
8776
+ inlineAnnotationsMounted = mountedInlineRows;
8762
8777
  syncInlineAnnotationWidths();
8763
8778
  }
8764
8779
  function syncInlineAnnotationSpacerHeights() {
@@ -8883,7 +8898,17 @@ ${frontmatter.yaml}
8883
8898
  annotationCountEl.hidden = total === 0;
8884
8899
  annotationListCountEl.textContent = `${ANNOTATIONS.sessions.length} sessions / ${total} annotations`;
8885
8900
  }
8886
- async function refreshAnnotations() {
8901
+ function refreshAnnotations() {
8902
+ if (refreshAnnotationsInFlight)
8903
+ return refreshAnnotationsInFlight;
8904
+ const started = doRefreshAnnotations().finally(() => {
8905
+ if (refreshAnnotationsInFlight === started)
8906
+ refreshAnnotationsInFlight = null;
8907
+ });
8908
+ refreshAnnotationsInFlight = started;
8909
+ return started;
8910
+ }
8911
+ async function doRefreshAnnotations() {
8887
8912
  try {
8888
8913
  const res = await fetch("/_annotations");
8889
8914
  if (!res.ok)
@@ -16386,6 +16411,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
16386
16411
  var ROW_HEIGHT = 28;
16387
16412
  var OVERSCAN = 20;
16388
16413
  var PAGE_SIZE = 200;
16414
+ var MAX_PAGE_CACHE_PAGES = 32;
16389
16415
  var FILTER_DEBOUNCE_MS = 300;
16390
16416
  var DEFAULT_COL_WIDTH = 180;
16391
16417
  var CELL_PREVIEW_MAX_CHARS = 4000;
@@ -16520,9 +16546,6 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
16520
16546
  const filteredEmpty = document.createElement("div");
16521
16547
  filteredEmpty.className = "db-pane-empty";
16522
16548
  filteredEmpty.hidden = true;
16523
- const filteredEmptyIcon = document.createElement("div");
16524
- filteredEmptyIcon.className = "db-pane-empty-icon";
16525
- filteredEmptyIcon.innerHTML = iconSvg("octicon-search", SEARCH_16_PATH);
16526
16549
  const filteredEmptyTitle = document.createElement("div");
16527
16550
  filteredEmptyTitle.className = "db-pane-empty-title";
16528
16551
  const filteredEmptyHint = document.createElement("div");
@@ -16542,7 +16565,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
16542
16565
  filteredEmptyAction.hidden = true;
16543
16566
  filteredEmptyAction.disabled = true;
16544
16567
  filteredEmptyActions.append(filteredEmptyReloadAction, filteredEmptyAction);
16545
- filteredEmpty.append(filteredEmptyIcon, filteredEmptyTitle, filteredEmptyHint, filteredEmptyActions);
16568
+ filteredEmpty.append(filteredEmptyTitle, filteredEmptyHint, filteredEmptyActions);
16546
16569
  const detailPanel = document.createElement("div");
16547
16570
  detailPanel.className = "db-grid-detail-panel";
16548
16571
  detailPanel.hidden = true;
@@ -16563,13 +16586,34 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
16563
16586
  function setActiveCell(rowIndex, colIndex) {
16564
16587
  activeCellRowIndex = rowIndex;
16565
16588
  activeCellColIndex = colIndex;
16566
- for (const c2 of body.querySelectorAll(".db-grid-cell-active")) {
16567
- c2.classList.remove("db-grid-cell-active");
16589
+ if (activeCellElement?.isConnected) {
16590
+ activeCellElement.classList.remove("db-grid-cell-active");
16591
+ } else {
16592
+ for (const c2 of body.querySelectorAll(".db-grid-cell-active")) {
16593
+ c2.classList.remove("db-grid-cell-active");
16594
+ }
16568
16595
  }
16596
+ activeCellElement = null;
16569
16597
  if (rowIndex < 0 || colIndex < 0)
16570
16598
  return;
16571
16599
  const targetRow = body.children[rowIndex - renderStartRow];
16572
- targetRow?.children[colIndex + 1]?.classList.add("db-grid-cell-active");
16600
+ const targetCell = targetRow?.children[colIndex + 1];
16601
+ if (!targetCell)
16602
+ return;
16603
+ targetCell.classList.add("db-grid-cell-active");
16604
+ activeCellElement = targetCell;
16605
+ }
16606
+ function setSelectedRow(rowIndex, row) {
16607
+ selectedRowIndex = rowIndex;
16608
+ if (selectedRowElement?.isConnected) {
16609
+ selectedRowElement.classList.remove("selected");
16610
+ } else {
16611
+ body.querySelectorAll(".db-grid-row.selected").forEach((r2) => {
16612
+ r2.classList.remove("selected");
16613
+ });
16614
+ }
16615
+ selectedRowElement = row;
16616
+ row?.classList.add("selected");
16573
16617
  }
16574
16618
  function clearActiveCell() {
16575
16619
  setActiveCell(-1, -1);
@@ -16631,8 +16675,10 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
16631
16675
  let isRefreshing = false;
16632
16676
  let filterTimer = null;
16633
16677
  let selectedRowIndex = -1;
16678
+ let selectedRowElement = null;
16634
16679
  let activeCellRowIndex = -1;
16635
16680
  let activeCellColIndex = -1;
16681
+ let activeCellElement = null;
16636
16682
  let renderStartRow = 0;
16637
16683
  const fkColumns = new Set;
16638
16684
  let editMode = false;
@@ -16815,6 +16861,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
16815
16861
  }
16816
16862
  function resetSelectionAndDetail() {
16817
16863
  selectedRowIndex = -1;
16864
+ selectedRowElement = null;
16818
16865
  detailPanel.hidden = true;
16819
16866
  clearDetailContent();
16820
16867
  }
@@ -16910,6 +16957,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
16910
16957
  statusEl?.remove();
16911
16958
  statusEl = null;
16912
16959
  selectedRowIndex = -1;
16960
+ selectedRowElement = null;
16961
+ activeCellElement = null;
16913
16962
  closeExportMenu();
16914
16963
  hideRelatedPanel();
16915
16964
  detailPanel.hidden = true;
@@ -17433,7 +17482,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
17433
17482
  renderViewport();
17434
17483
  }
17435
17484
  function ensurePage(pageStart) {
17436
- if (pageCache.has(pageStart))
17485
+ if (getCachedPage(pageStart) !== undefined)
17437
17486
  return Promise.resolve();
17438
17487
  const pending = pendingPages.get(pageStart);
17439
17488
  if (pending)
@@ -17444,7 +17493,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
17444
17493
  const promise = callbacks.fetchPage(currentTable, pageStart, PAGE_SIZE, sort, filters, signal).then((data) => {
17445
17494
  if (gen !== loadGeneration)
17446
17495
  return;
17447
- pageCache.set(pageStart, data.rows);
17496
+ rememberPage(pageStart, data.rows);
17448
17497
  totalRows = data.totalRows;
17449
17498
  syncSpacer();
17450
17499
  updateStatus();
@@ -17464,6 +17513,24 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
17464
17513
  pendingPages.set(pageStart, tracked);
17465
17514
  return tracked;
17466
17515
  }
17516
+ function rememberPage(pageStart, rows) {
17517
+ pageCache.delete(pageStart);
17518
+ pageCache.set(pageStart, rows);
17519
+ while (pageCache.size > MAX_PAGE_CACHE_PAGES) {
17520
+ const oldest = pageCache.keys().next().value;
17521
+ if (oldest === undefined)
17522
+ break;
17523
+ pageCache.delete(oldest);
17524
+ }
17525
+ }
17526
+ function getCachedPage(pageStart) {
17527
+ const rows = pageCache.get(pageStart);
17528
+ if (rows === undefined)
17529
+ return;
17530
+ pageCache.delete(pageStart);
17531
+ pageCache.set(pageStart, rows);
17532
+ return rows;
17533
+ }
17467
17534
  function buildEditableCell(colIndex, value, opts) {
17468
17535
  const cell = document.createElement("div");
17469
17536
  cell.className = "db-grid-cell db-grid-cell-edit";
@@ -17545,14 +17612,16 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
17545
17612
  }
17546
17613
  function buildDataRow(i2) {
17547
17614
  const pageStart = Math.floor(i2 / PAGE_SIZE) * PAGE_SIZE;
17548
- const pageRows = pageCache.get(pageStart);
17615
+ const pageRows = getCachedPage(pageStart);
17549
17616
  const rowData = pageRows ? pageRows[i2 - pageStart] : null;
17550
17617
  const row = document.createElement("div");
17551
17618
  row.className = "db-grid-row";
17552
17619
  if (i2 % 2 === 1)
17553
17620
  row.classList.add("alt");
17554
- if (i2 === selectedRowIndex)
17621
+ if (i2 === selectedRowIndex) {
17555
17622
  row.classList.add("selected");
17623
+ selectedRowElement = row;
17624
+ }
17556
17625
  const rowIndex = i2;
17557
17626
  const rowNum = document.createElement("div");
17558
17627
  rowNum.className = "db-grid-cell db-grid-rownum";
@@ -17601,11 +17670,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
17601
17670
  const fkClickable = fkColumns.has(cellColName) && (!embedded || !!callbacks.onForeignKeyCellClick);
17602
17671
  const isEditingThisCell = !ro && i2 === editingCellRow && c2 === editingCellCol;
17603
17672
  const activate = () => {
17604
- selectedRowIndex = rowIndex;
17605
- body.querySelectorAll(".db-grid-row.selected").forEach((r2) => {
17606
- r2.classList.remove("selected");
17607
- });
17608
- row.classList.add("selected");
17673
+ setSelectedRow(rowIndex, row);
17609
17674
  setActiveCell(rowIndex, cellColIndex);
17610
17675
  if (fkClickable) {
17611
17676
  if (embedded) {
@@ -17656,17 +17721,14 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
17656
17721
  }
17657
17722
  if (!isEditingThisCell && i2 === activeCellRowIndex && c2 === activeCellColIndex) {
17658
17723
  cellEl.classList.add("db-grid-cell-active");
17724
+ activeCellElement = cellEl;
17659
17725
  }
17660
17726
  row.appendChild(cellEl);
17661
17727
  }
17662
17728
  return row;
17663
17729
  }
17664
17730
  row.addEventListener("click", () => {
17665
- selectedRowIndex = rowIndex;
17666
- body.querySelectorAll(".db-grid-row.selected").forEach((r2) => {
17667
- r2.classList.remove("selected");
17668
- });
17669
- row.classList.add("selected");
17731
+ setSelectedRow(rowIndex, row);
17670
17732
  });
17671
17733
  for (let c2 = 0;c2 < columnNames.length; c2++) {
17672
17734
  const cell = document.createElement("div");
@@ -17691,11 +17753,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
17691
17753
  }
17692
17754
  cell.addEventListener("click", (e2) => {
17693
17755
  e2.stopPropagation();
17694
- selectedRowIndex = rowIndex;
17695
- body.querySelectorAll(".db-grid-row.selected").forEach((r2) => {
17696
- r2.classList.remove("selected");
17697
- });
17698
- row.classList.add("selected");
17756
+ setSelectedRow(rowIndex, row);
17699
17757
  setActiveCell(rowIndex, cellColIndex);
17700
17758
  if (fkClickable) {
17701
17759
  if (embedded) {
@@ -17709,6 +17767,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
17709
17767
  });
17710
17768
  if (i2 === activeCellRowIndex && c2 === activeCellColIndex) {
17711
17769
  cell.classList.add("db-grid-cell-active");
17770
+ activeCellElement = cell;
17712
17771
  }
17713
17772
  row.appendChild(cell);
17714
17773
  }
@@ -17780,6 +17839,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
17780
17839
  };
17781
17840
  }
17782
17841
  body.innerHTML = "";
17842
+ selectedRowElement = null;
17843
+ activeCellElement = null;
17783
17844
  body.style.transform = `translateY(${startRow * ROW_HEIGHT}px)`;
17784
17845
  for (let i2 = startRow;i2 < endRow; i2++) {
17785
17846
  body.appendChild(i2 < totalRows ? buildDataRow(i2) : buildDraftRow(i2 - totalRows));
@@ -17861,7 +17922,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
17861
17922
  columns = initialData.columns;
17862
17923
  columnNames = columns.map((c2) => c2.name);
17863
17924
  totalRows = initialData.totalRows;
17864
- pageCache.set(0, initialData.rows);
17925
+ rememberPage(0, initialData.rows);
17865
17926
  } else {
17866
17927
  columns = [];
17867
17928
  columnNames = [];
@@ -18724,6 +18785,11 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
18724
18785
  const service = rest.split(/[@:]/, 1)[0];
18725
18786
  return service || "Docker";
18726
18787
  }
18788
+ if (dbId.startsWith("supabase:")) {
18789
+ const rest = dbId.slice("supabase:".length);
18790
+ const projectId = rest.split(/[@]/, 1)[0];
18791
+ return projectId || "Supabase";
18792
+ }
18727
18793
  const normalized = dbId.replace(/\\/g, "/");
18728
18794
  const lastSlash = normalized.lastIndexOf("/");
18729
18795
  return lastSlash >= 0 ? normalized.slice(lastSlash + 1) : normalized;
@@ -20016,7 +20082,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
20016
20082
  const opt = document.createElement("option");
20017
20083
  opt.value = f2.id;
20018
20084
  const isDocker = f2.id.startsWith("docker:");
20019
- const label = isDocker ? `${f2.name} (Docker)` : `${f2.path} (${formatSize(f2.sizeBytes)})`;
20085
+ const isSupabase = f2.id.startsWith("supabase:");
20086
+ const label = isDocker ? `${f2.name} (Docker)` : isSupabase ? `${f2.name} (Supabase)` : `${f2.path} (${formatSize(f2.sizeBytes)})`;
20020
20087
  opt.textContent = label;
20021
20088
  optionsFragment.appendChild(opt);
20022
20089
  }
@@ -20206,7 +20273,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
20206
20273
  if (!currentDbInfo)
20207
20274
  return labelFromDbId(initial.dbId);
20208
20275
  const suffix = currentSchema ? ` / ${currentSchema}` : "";
20209
- if (currentDbInfo.id.startsWith("docker:")) {
20276
+ if (currentDbInfo.id.startsWith("docker:") || currentDbInfo.id.startsWith("supabase:")) {
20210
20277
  const m = currentDbInfo.name.match(/^(\S+)/);
20211
20278
  return `${m ? m[1] : currentDbInfo.name}${suffix}`;
20212
20279
  }
@@ -21614,7 +21681,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
21614
21681
  const card = document.querySelector(diffCardSelector(path));
21615
21682
  if (!card?.classList.contains("pending"))
21616
21683
  return;
21617
- const f2 = STATE.files.find((x) => x.path === path);
21684
+ const f2 = card._file || STATE.files.find((x) => x.path === path);
21618
21685
  if (!f2)
21619
21686
  return;
21620
21687
  enqueueLoad(f2, card, 5);
@@ -21686,7 +21753,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
21686
21753
  };
21687
21754
  window.addEventListener("scrollend", onEnd, { once: true });
21688
21755
  if (card.classList.contains("pending")) {
21689
- const f2 = STATE.files.find((x) => x.path === path);
21756
+ const f2 = card._file || STATE.files.find((x) => x.path === path);
21690
21757
  if (f2)
21691
21758
  enqueueLoad(f2, card, 10);
21692
21759
  }
@@ -21755,7 +21822,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
21755
21822
  const card = entry.target;
21756
21823
  if (card.classList.contains("loaded") || card.classList.contains("loading"))
21757
21824
  continue;
21758
- const f2 = STATE.files.find((x) => x.path === card.dataset.path);
21825
+ const f2 = card._file || STATE.files.find((x) => x.path === card.dataset.path);
21759
21826
  if (f2)
21760
21827
  enqueueLoad(f2, card, 0);
21761
21828
  }
@@ -21823,7 +21890,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
21823
21890
  delete card.dataset.manualMode;
21824
21891
  card.style.minHeight = `${file.estimated_height_px || 80}px`;
21825
21892
  card._diffData = null;
21826
- card._file = null;
21893
+ card._file = file;
21827
21894
  }
21828
21895
  function renderShell(meta, changedPaths) {
21829
21896
  const newFiles = meta.files || [];
@@ -21989,6 +22056,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
21989
22056
  card.dataset.key = f2.key || f2.path;
21990
22057
  card.dataset.sizeClass = f2.size_class || "small";
21991
22058
  card.dataset.status = f2.status || "M";
22059
+ card._file = f2;
21992
22060
  card.classList.toggle("viewed", STATE.viewedFiles.has(f2.path));
21993
22061
  if (f2.estimated_height_px) {
21994
22062
  card.style.minHeight = `${f2.estimated_height_px}px`;
@@ -22012,7 +22080,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22012
22080
  const card = entry.target;
22013
22081
  if (card.classList.contains("loaded") || card.classList.contains("loading"))
22014
22082
  return;
22015
- const f2 = STATE.files.find((x) => x.path === card.dataset.path);
22083
+ const f2 = card._file || STATE.files.find((x) => x.path === card.dataset.path);
22016
22084
  if (!f2)
22017
22085
  return;
22018
22086
  enqueueLoad(f2, card, 0);
@@ -22028,7 +22096,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22028
22096
  const rect = card.getBoundingClientRect();
22029
22097
  if (rect.top > viewportBottom)
22030
22098
  return;
22031
- const f2 = STATE.files.find((x) => x.path === card.dataset.path);
22099
+ const f2 = card._file || STATE.files.find((x) => x.path === card.dataset.path);
22032
22100
  if (f2)
22033
22101
  enqueueLoad(f2, card, 0);
22034
22102
  });
@@ -22176,11 +22244,15 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22176
22244
  card.classList.add("pending");
22177
22245
  if (indicator)
22178
22246
  indicator.hidden = true;
22179
- const fresh = STATE.files.find((x) => x.path === card.dataset.path);
22247
+ const fresh = card._file || STATE.files.find((x) => x.path === card.dataset.path);
22180
22248
  if (fresh && card.isConnected)
22181
22249
  enqueueLoad(fresh, card, 0);
22182
22250
  };
22183
- return trackLoad(fetch(url).then((r2) => r2.json())).then(async (data) => {
22251
+ return trackLoad(fetch(url).then(async (r2) => {
22252
+ if (!r2.ok)
22253
+ throw new Error(await r2.text());
22254
+ return r2.json();
22255
+ })).then(async (data) => {
22184
22256
  if (String(myReq) !== card.dataset.reqId)
22185
22257
  return;
22186
22258
  if (myGen !== getServerGeneration()) {
@@ -22221,7 +22293,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22221
22293
  return false;
22222
22294
  if (card.classList.contains("loaded"))
22223
22295
  return true;
22224
- const file = STATE.files.find((x) => x.path === path);
22296
+ const file = card._file || STATE.files.find((x) => x.path === path);
22225
22297
  if (!file)
22226
22298
  return false;
22227
22299
  await loadFile(file, card, undefined, { immediate: true });
@@ -22247,17 +22319,13 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22247
22319
  matching: "lines",
22248
22320
  outputFormat: layout,
22249
22321
  synchronisedScroll: true,
22250
- highlight: !!(STATE.syntaxHighlight && file.highlight && hljsRef),
22322
+ highlight: false,
22251
22323
  fileListToggle: false,
22252
22324
  fileContentToggle: false
22253
22325
  }, hljsRef);
22254
22326
  ui.draw();
22255
22327
  if (STATE.ignoreWs)
22256
22328
  suppressWhitespaceOnlyInlineHighlights(body);
22257
- if (STATE.syntaxHighlight && file.highlight && hljsRef && typeof ui.highlightCode === "function") {
22258
- ui.highlightCode();
22259
- highlightPlaintextSpans(card, file);
22260
- }
22261
22329
  enhanceMediaCard(file, card);
22262
22330
  syncSideScrollCard(card);
22263
22331
  appendStatSquaresToHeader(card, file);
@@ -22586,10 +22654,6 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22586
22654
  const spans = card.querySelectorAll("tr.gdp-inserted-ctx .d2h-code-line-ctn:not([data-gdp-hl])");
22587
22655
  highlightDiffSpans(file, spans);
22588
22656
  }
22589
- function highlightPlaintextSpans(card, file) {
22590
- const spans = card.querySelectorAll(".d2h-code-line-ctn.hljs.plaintext:not([data-gdp-hl])");
22591
- highlightDiffSpans(file, spans);
22592
- }
22593
22657
  function highlightDiffSpans(file, spans, deadline) {
22594
22658
  if (file.size_class === "huge")
22595
22659
  return true;
@@ -22620,8 +22684,6 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22620
22684
  return true;
22621
22685
  }
22622
22686
  function scheduleIdleHighlight(card, file) {
22623
- if (file.highlight)
22624
- return;
22625
22687
  if (file.size_class === "huge")
22626
22688
  return;
22627
22689
  if (!STATE.syntaxHighlight)
@@ -22677,14 +22739,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22677
22739
  for (const w of cards) {
22678
22740
  const r2 = w.getBoundingClientRect();
22679
22741
  if (r2.top <= scanY && r2.bottom > scanY) {
22680
- const text2 = w.dataset.path || "";
22681
- let best = null, bestLen = 0;
22682
- STATE.files.forEach((f2) => {
22683
- if ((text2 === f2.path || text2.endsWith(f2.path)) && f2.path.length > bestLen) {
22684
- best = f2.path;
22685
- bestLen = f2.path.length;
22686
- }
22687
- });
22742
+ const best = w.dataset.path || "";
22688
22743
  if (best) {
22689
22744
  markActive(best);
22690
22745
  const recentlyTouched = performance.now() - (window.__gdpSidebarTouchedAt || 0) < 1500;
@@ -23269,6 +23324,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23269
23324
  let generation = 0;
23270
23325
  let selectionGeneration = 0;
23271
23326
  let selectedSha = "";
23327
+ let activeHistoryRow = null;
23272
23328
  let query = "";
23273
23329
  let mode = "history";
23274
23330
  let routeRef = "HEAD";
@@ -23381,6 +23437,10 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23381
23437
  return absolute;
23382
23438
  return `${relative} (${absolute})`;
23383
23439
  }
23440
+ function historyItemSelector(sha) {
23441
+ const escaped = typeof CSS !== "undefined" && CSS.escape ? CSS.escape(sha) : sha.replace(/["\\]/g, "\\$&");
23442
+ return `.history-item[data-sha="${escaped}"]`;
23443
+ }
23384
23444
  function fetchPage(skip, requestGeneration) {
23385
23445
  const params = new URLSearchParams;
23386
23446
  params.set("ref", ref);
@@ -23434,6 +23494,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23434
23494
  html.push(commitRow(commit));
23435
23495
  }
23436
23496
  list2.innerHTML = html.join("");
23497
+ activeHistoryRow = list2.querySelector(".history-item.active");
23437
23498
  syncRefreshStatusText();
23438
23499
  }
23439
23500
  function syncRefreshButton(button) {
@@ -23521,9 +23582,15 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23521
23582
  info.hidden = false;
23522
23583
  }
23523
23584
  function updateActiveRow() {
23524
- list2.querySelectorAll(".history-item").forEach((row) => {
23525
- row.classList.toggle("active", row.dataset.sha === selectedSha);
23526
- });
23585
+ if (activeHistoryRow && (!("isConnected" in activeHistoryRow) || activeHistoryRow.isConnected)) {
23586
+ activeHistoryRow.classList.remove("active");
23587
+ } else {
23588
+ list2.querySelectorAll(".history-item.active").forEach((row) => {
23589
+ row.classList.remove("active");
23590
+ });
23591
+ }
23592
+ activeHistoryRow = selectedSha ? list2.querySelector(historyItemSelector(selectedSha)) : null;
23593
+ activeHistoryRow?.classList.add("active");
23527
23594
  }
23528
23595
  function isEditableTarget(target) {
23529
23596
  return typeof Element === "function" && target instanceof Element && !!target.closest('input, textarea, select, button, [contenteditable="true"]');
@@ -23714,8 +23781,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23714
23781
  scrollToSelected();
23715
23782
  }
23716
23783
  function scrollToSelected() {
23717
- const escaped = typeof CSS !== "undefined" && CSS.escape ? CSS.escape(selectedSha) : selectedSha.replace(/["\\]/g, "\\$&");
23718
- list2.querySelector(`.history-item[data-sha="${escaped}"]`)?.scrollIntoView({ block: "center" });
23784
+ list2.querySelector(historyItemSelector(selectedSha))?.scrollIntoView({ block: "center" });
23719
23785
  }
23720
23786
  let entering = Promise.resolve();
23721
23787
  function enterHistory(options = {}) {
@@ -23791,6 +23857,10 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
23791
23857
  generation++;
23792
23858
  loading = false;
23793
23859
  inFlight = null;
23860
+ if (filterTimer) {
23861
+ clearTimeout(filterTimer);
23862
+ filterTimer = undefined;
23863
+ }
23794
23864
  selectionGeneration++;
23795
23865
  selectedSha = "";
23796
23866
  setBanner("");
@@ -24709,7 +24779,7 @@ code-viewer annotate add-db --db app.db --tab query \\
24709
24779
  ],
24710
24780
  [
24711
24781
  "PostgreSQL",
24712
- "Detected from compose files. Multiple databases per server, plus a schema selector for switching schemas without reopening. Same inline row edit / insert / delete as SQLite."
24782
+ "Detected from compose files. Multiple databases per server, plus a schema selector for switching schemas without reopening. Same inline row edit / insert / delete as SQLite. Local Supabase CLI (`supabase start`) projects are also auto-discovered from `supabase/config.toml`, without needing a docker-compose file."
24713
24783
  ],
24714
24784
  [
24715
24785
  "Redis",
@@ -25368,7 +25438,7 @@ code-viewer annotate add-db --db app.db --tab query \\
25368
25438
  ],
25369
25439
  [
25370
25440
  "PostgreSQL",
25371
- "compose ファイルから検出。同一サーバー上の複数データベースに対応し、スキーマ切替セレクターで再オープンせずスキーマを切り替えられます。SQLite と同じく行のインライン編集 / 追加 / 削除に対応。"
25441
+ "compose ファイルから検出。同一サーバー上の複数データベースに対応し、スキーマ切替セレクターで再オープンせずスキーマを切り替えられます。SQLite と同じく行のインライン編集 / 追加 / 削除に対応。ローカルの Supabase CLI (`supabase start`) プロジェクトも `supabase/config.toml` から自動検出され、docker-compose ファイルは不要です。"
25372
25442
  ],
25373
25443
  [
25374
25444
  "Redis",
@@ -31379,11 +31449,106 @@ code-viewer query agent-help`
31379
31449
  const match2 = computeFuzzyMatch(query, path);
31380
31450
  return match2 ? { score: match2.score, ranges: match2.ranges } : null;
31381
31451
  }
31382
- function rankFuzzyPaths(query, items) {
31383
- return items.map((item) => {
31452
+ function rankFuzzyPaths(query, items, limit) {
31453
+ const bounded = Number.isInteger(limit) && limit !== undefined && limit > 0 ? Math.floor(limit) : 0;
31454
+ const compare = (a2, b2) => b2.tier - a2.tier || b2.score - a2.score || a2.item.path.localeCompare(b2.item.path);
31455
+ if (!bounded) {
31456
+ return items.map((item) => {
31457
+ const match2 = computeFuzzyMatch(query, item.path);
31458
+ return match2 ? { item, score: match2.score, ranges: match2.ranges, tier: match2.tier } : null;
31459
+ }).filter((item) => item !== null).sort(compare).map(({ item, score, ranges }) => ({ item, score, ranges }));
31460
+ }
31461
+ const top = [];
31462
+ for (const item of items) {
31384
31463
  const match2 = computeFuzzyMatch(query, item.path);
31385
- return match2 ? { item, score: match2.score, ranges: match2.ranges, tier: match2.tier } : null;
31386
- }).filter((item) => item !== null).sort((a2, b2) => b2.tier - a2.tier || b2.score - a2.score || a2.item.path.localeCompare(b2.item.path)).map(({ item, score, ranges }) => ({ item, score, ranges }));
31464
+ if (!match2)
31465
+ continue;
31466
+ const ranked = {
31467
+ item,
31468
+ score: match2.score,
31469
+ ranges: match2.ranges,
31470
+ tier: match2.tier
31471
+ };
31472
+ pushBoundedTop(top, ranked, bounded, compare);
31473
+ }
31474
+ return top.sort(compare).map(({ item, score, ranges }) => ({ item, score, ranges }));
31475
+ }
31476
+ function pushBoundedTop(heap, item, limit, compareBestFirst) {
31477
+ const isWorse = (a2, b2) => compareBestFirst(a2, b2) > 0;
31478
+ const siftUp = (index) => {
31479
+ while (index > 0) {
31480
+ const parent = Math.floor((index - 1) / 2);
31481
+ if (!isWorse(heap[index], heap[parent]))
31482
+ break;
31483
+ [heap[index], heap[parent]] = [heap[parent], heap[index]];
31484
+ index = parent;
31485
+ }
31486
+ };
31487
+ const siftDown = (index) => {
31488
+ while (true) {
31489
+ const left = index * 2 + 1;
31490
+ const right = left + 1;
31491
+ let worst = index;
31492
+ if (left < heap.length && isWorse(heap[left], heap[worst]))
31493
+ worst = left;
31494
+ if (right < heap.length && isWorse(heap[right], heap[worst]))
31495
+ worst = right;
31496
+ if (worst === index)
31497
+ break;
31498
+ [heap[index], heap[worst]] = [heap[worst], heap[index]];
31499
+ index = worst;
31500
+ }
31501
+ };
31502
+ if (heap.length < limit) {
31503
+ heap.push(item);
31504
+ siftUp(heap.length - 1);
31505
+ return;
31506
+ }
31507
+ if (compareBestFirst(item, heap[0]) >= 0)
31508
+ return;
31509
+ heap[0] = item;
31510
+ siftDown(0);
31511
+ }
31512
+ function rankGlobPathMatches(query, items, limit) {
31513
+ const matchPath = createGlobPathMatcher(query);
31514
+ if (!matchPath)
31515
+ return [];
31516
+ const bounded = Number.isInteger(limit) && limit !== undefined && limit > 0 ? Math.floor(limit) : 0;
31517
+ const compare = (a2, b2) => b2.score - a2.score || a2.item.path.localeCompare(b2.item.path);
31518
+ if (!bounded) {
31519
+ return items.map((item) => {
31520
+ const match2 = matchPath(item.path);
31521
+ return match2 ? {
31522
+ item,
31523
+ score: match2.score,
31524
+ ranges: match2.ranges,
31525
+ mode: "glob"
31526
+ } : null;
31527
+ }).filter((item) => item !== null).sort(compare);
31528
+ }
31529
+ const top = [];
31530
+ for (const item of items) {
31531
+ const match2 = matchPath(item.path);
31532
+ if (!match2)
31533
+ continue;
31534
+ const ranked = {
31535
+ item,
31536
+ score: match2.score,
31537
+ ranges: match2.ranges,
31538
+ mode: "glob"
31539
+ };
31540
+ pushBoundedTop(top, ranked, bounded, compare);
31541
+ }
31542
+ return top.sort(compare);
31543
+ }
31544
+ function rankPathMatches(query, items, limit) {
31545
+ if (isGlobPathQuery(query)) {
31546
+ return rankGlobPathMatches(query, items, limit);
31547
+ }
31548
+ return rankFuzzyPaths(query, items, limit).map((item) => ({
31549
+ ...item,
31550
+ mode: "fuzzy"
31551
+ }));
31387
31552
  }
31388
31553
  function isGlobPathQuery(query) {
31389
31554
  return /[*?]/.test(query.trim());
@@ -31428,48 +31593,39 @@ code-viewer query agent-help`
31428
31593
  }
31429
31594
  }
31430
31595
  function globMatchPath(query, path) {
31596
+ return createGlobPathMatcher(query)?.(path) ?? null;
31597
+ }
31598
+ function createGlobPathMatcher(query) {
31431
31599
  const regex = globToRegExp(query);
31432
- const baseStart = basenameStart(path);
31433
- const basename = path.slice(baseStart);
31434
- if (!regex || !regex.test(path) && (query.includes("/") || !regex.test(basename)))
31600
+ if (!regex)
31435
31601
  return null;
31436
31602
  const literal = query.replace(/[*?[\]]+/g, " ").trim().split(/\s+/).filter(Boolean);
31437
- const ranges = [];
31438
- const lowerPath = path.toLowerCase();
31439
- for (const part of literal) {
31440
- const start = lowerPath.indexOf(part.toLowerCase());
31441
- if (start >= 0)
31442
- ranges.push({ start, end: start + part.length });
31443
- }
31444
- ranges.sort((a2, b2) => a2.start - b2.start || a2.end - b2.end);
31445
- const mergedRanges = [];
31446
- for (const range of ranges) {
31447
- const last = mergedRanges[mergedRanges.length - 1];
31448
- if (last && last.end >= range.start) {
31449
- last.end = Math.max(last.end, range.end);
31450
- } else {
31451
- mergedRanges.push({ ...range });
31603
+ const suffix = query.replace(/^\*+/, "").toLowerCase();
31604
+ return (path) => {
31605
+ const baseStart = basenameStart(path);
31606
+ const basename = path.slice(baseStart);
31607
+ if (!regex.test(path) && (query.includes("/") || !regex.test(basename)))
31608
+ return null;
31609
+ const ranges = [];
31610
+ const lowerPath = path.toLowerCase();
31611
+ for (const part of literal) {
31612
+ const start = lowerPath.indexOf(part.toLowerCase());
31613
+ if (start >= 0)
31614
+ ranges.push({ start, end: start + part.length });
31615
+ }
31616
+ ranges.sort((a2, b2) => a2.start - b2.start || a2.end - b2.end);
31617
+ const mergedRanges = [];
31618
+ for (const range of ranges) {
31619
+ const last = mergedRanges[mergedRanges.length - 1];
31620
+ if (last && last.end >= range.start) {
31621
+ last.end = Math.max(last.end, range.end);
31622
+ } else {
31623
+ mergedRanges.push({ ...range });
31624
+ }
31452
31625
  }
31453
- }
31454
- const score = 1000 - Math.min(path.length, 200) + (path.slice(baseStart).toLowerCase().endsWith(query.replace(/^\*+/, "").toLowerCase()) ? 50 : 0);
31455
- return { score, ranges: mergedRanges };
31456
- }
31457
- function rankPathMatches(query, items) {
31458
- if (isGlobPathQuery(query)) {
31459
- return items.map((item) => {
31460
- const match2 = globMatchPath(query, item.path);
31461
- return match2 ? {
31462
- item,
31463
- score: match2.score,
31464
- ranges: match2.ranges,
31465
- mode: "glob"
31466
- } : null;
31467
- }).filter((item) => item !== null).sort((a2, b2) => b2.score - a2.score || a2.item.path.localeCompare(b2.item.path));
31468
- }
31469
- return rankFuzzyPaths(query, items).map((item) => ({
31470
- ...item,
31471
- mode: "fuzzy"
31472
- }));
31626
+ const score = 1000 - Math.min(path.length, 200) + (path.slice(baseStart).toLowerCase().endsWith(suffix) ? 50 : 0);
31627
+ return { score, ranges: mergedRanges };
31628
+ };
31473
31629
  }
31474
31630
 
31475
31631
  // web-src/core/search-palette.ts
@@ -31767,7 +31923,7 @@ code-viewer query agent-help`
31767
31923
  const response = await repoPaletteFiles(ref);
31768
31924
  if (PALETTE !== state || state.input.value !== query)
31769
31925
  return;
31770
- state.items = limitPaletteResults(rankPathMatches(query, response.files)).map((match2) => ({
31926
+ state.items = limitPaletteResults(rankPathMatches(query, response.files, PALETTE_RESULT_LIMIT)).map((match2) => ({
31771
31927
  kind: "file",
31772
31928
  path: match2.item.path,
31773
31929
  displayPath: match2.item.path,
@@ -31849,6 +32005,8 @@ code-viewer query agent-help`
31849
32005
  const query = state.input.value;
31850
32006
  if (state.mode === "file") {
31851
32007
  updateFilePalette(state, query).catch(() => {
32008
+ if (PALETTE !== state || state.input.value !== query)
32009
+ return;
31852
32010
  state.status.textContent = "Search failed";
31853
32011
  });
31854
32012
  } else {
@@ -31992,12 +32150,13 @@ code-viewer query agent-help`
31992
32150
  let sourceShikiLoadPromise = null;
31993
32151
  let PREFERRED_SOURCE_TAB = null;
31994
32152
  let SOURCE_CURSOR = null;
32153
+ let SOURCE_CURSOR_ROWS = [];
31995
32154
  const SOURCE_CURSOR_TOTALS = new Map;
31996
32155
  function sourceLineScrollAmount() {
31997
- const virtualRow = Array.from(document.querySelectorAll("#content .gdp-source-virtual-row")).find((item) => item.offsetParent !== null);
32156
+ const virtualRow = document.querySelector("#content .gdp-source-virtual:not([hidden]) .gdp-source-virtual-row");
31998
32157
  if (virtualRow)
31999
32158
  return virtualRow.getBoundingClientRect().height || VIRTUAL_SOURCE_ROW_HEIGHT;
32000
- const sourceRow = Array.from(document.querySelectorAll("#content .gdp-source-table tr")).find((item) => item.offsetParent !== null);
32159
+ const sourceRow = document.querySelector("#content .gdp-source-table:not([hidden]) tr");
32001
32160
  if (sourceRow)
32002
32161
  return sourceRow.getBoundingClientRect().height || 20;
32003
32162
  const preview = document.querySelector("#content .gdp-markdown-preview:not([hidden])");
@@ -32014,10 +32173,32 @@ code-viewer query agent-help`
32014
32173
  return !!SOURCE_CURSOR && sourceTargetsEqual(SOURCE_CURSOR.target, target) && SOURCE_CURSOR.line === line;
32015
32174
  }
32016
32175
  function syncSourceCursorRows(target) {
32017
- document.querySelectorAll("#content [data-line]").forEach((row) => {
32018
- const line = Number(row.dataset.line || "0");
32019
- row.classList.toggle("gdp-source-cursor", sourceCursorMatches(target, line));
32020
- });
32176
+ if (SOURCE_CURSOR_ROWS.length === 0 || SOURCE_CURSOR_ROWS.every((row) => !row.isConnected)) {
32177
+ SOURCE_CURSOR_ROWS = Array.from(document.querySelectorAll("#content .gdp-source-table tr.gdp-source-cursor, #content .gdp-source-virtual-row.gdp-source-cursor"));
32178
+ }
32179
+ for (const row of SOURCE_CURSOR_ROWS) {
32180
+ row.classList.remove("gdp-source-cursor");
32181
+ }
32182
+ SOURCE_CURSOR_ROWS = [];
32183
+ if (!SOURCE_CURSOR || !sourceTargetsEqual(SOURCE_CURSOR.target, target))
32184
+ return;
32185
+ const line = String(SOURCE_CURSOR.line);
32186
+ const table2 = document.querySelector("#content .gdp-source-table");
32187
+ const tableRow = table2?.rows[SOURCE_CURSOR.line - 1];
32188
+ if (tableRow?.dataset.line === line) {
32189
+ tableRow.classList.add("gdp-source-cursor");
32190
+ SOURCE_CURSOR_ROWS.push(tableRow);
32191
+ }
32192
+ const windowEl = document.querySelector("#content .gdp-source-virtual-window");
32193
+ if (windowEl) {
32194
+ const firstLine = Number(windowEl.firstElementChild?.dataset.line || "0");
32195
+ const offset = SOURCE_CURSOR.line - firstLine;
32196
+ const row = offset >= 0 ? windowEl.children[offset] : null;
32197
+ if (row?.dataset.line === line) {
32198
+ row.classList.add("gdp-source-cursor");
32199
+ SOURCE_CURSOR_ROWS.push(row);
32200
+ }
32201
+ }
32021
32202
  }
32022
32203
  function visibleSourceLineFallback() {
32023
32204
  const scroller = findMainScrollTarget();
@@ -35540,7 +35721,7 @@ code-viewer query agent-help`
35540
35721
  }
35541
35722
  let SERVER_GENERATION = 0;
35542
35723
  function trackLoad(promise) {
35543
- return NETWORK_ACTIVITY.track(promise);
35724
+ return promise;
35544
35725
  }
35545
35726
  function escapeHtml3(s2) {
35546
35727
  return String(s2 == null ? "" : s2).replace(/[&<>"']/g, (c2) => ({