@youtyan/code-viewer 0.12.0 → 0.13.1

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
@@ -875,6 +875,17 @@ Details: ${JSON.stringify(output)}` : "";
875
875
  };
876
876
  }
877
877
 
878
+ // web-src/core/file-refresh.ts
879
+ function rawFileInfoSignature(info) {
880
+ if (info.missing) return "missing";
881
+ if (info.size == null && !info.updated_at && !info.commit_updated_at)
882
+ return null;
883
+ return `${info.size ?? ""}|${info.updated_at ?? ""}|${info.commit_updated_at ?? ""}`;
884
+ }
885
+ function fileSignatureUnchanged(stored, key, sig) {
886
+ return stored !== null && stored.key === key && stored.sig === sig;
887
+ }
888
+
878
889
  // web-src/core/focus-scope.ts
879
890
  function isEditableKeyTarget(target) {
880
891
  if (!target) return false;
@@ -1119,6 +1130,7 @@ Details: ${JSON.stringify(output)}` : "";
1119
1130
  var PENCIL_16_PATH = "M11.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 0 1-.927-.928l.929-3.25c.081-.286.235-.547.445-.758l8.61-8.609Zm.176 4.823L9.75 4.81l-6.286 6.287a.253.253 0 0 0-.064.108l-.558 1.953 1.953-.558a.253.253 0 0 0 .108-.064Zm1.238-3.763a.25.25 0 0 0-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 0 0 0-.354Z";
1120
1131
  var COMMENT_DISCUSSION_16_PATH = "M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z";
1121
1132
  var GRABBER_16_PATH = "M10 13h4v-2h-4v2Zm-8 0h4v-2H2v2Zm8-6h4V5h-4v2ZM2 7h4V5H2v2Zm8-6v2h4V1h-4ZM2 3h4V1H2v2Z";
1133
+ var KEBAB_16_PATH = "M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z";
1122
1134
  var PULSE_16_PATH = "M6 2c.306 0 .582.187.696.471L10 10.731l1.304-3.26A.751.751 0 0 1 12 7h3.25a.75.75 0 0 1 0 1.5h-2.742l-1.812 4.528a.751.751 0 0 1-1.392 0L6 4.77 4.696 8.03A.75.75 0 0 1 4 8.5H.75a.75.75 0 0 1 0-1.5h2.742l1.812-4.529A.751.751 0 0 1 6 2Z";
1123
1135
  var MOON_16_PATH = "M9.598 1.591a.749.749 0 0 1 .785-.175 7.001 7.001 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786Zm1.616 1.945a7 7 0 0 1-7.678 7.678 5.499 5.499 0 1 0 7.678-7.678Z";
1124
1136
  var QUESTION_16_PATH = "M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.92 6.085h.001a.749.749 0 1 1-1.342-.67c.169-.339.436-.701.849-.977C6.845 4.16 7.369 4 8 4a2.756 2.756 0 0 1 1.637.525c.503.377.863.965.863 1.725 0 .448-.115.83-.329 1.15-.205.307-.47.513-.692.662-.109.072-.22.138-.313.195l-.006.004a6.24 6.24 0 0 0-.26.16.952.952 0 0 0-.276.245.75.75 0 0 1-1.248-.832c.184-.264.42-.489.692-.661.103-.067.207-.132.313-.195l.007-.004c.1-.061.182-.11.258-.161a.969.969 0 0 0 .277-.245C8.96 6.514 9 6.427 9 6.25a.612.612 0 0 0-.262-.525A1.27 1.27 0 0 0 8 5.5c-.369 0-.595.09-.74.187a1.01 1.01 0 0 0-.34.398ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z";
@@ -1795,6 +1807,19 @@ Details: ${JSON.stringify(output)}` : "";
1795
1807
  lang: params.get("lang") || "en",
1796
1808
  section: params.get("section") || "overview"
1797
1809
  };
1810
+ case "/worktree": {
1811
+ const wt = params.get("wt") || "";
1812
+ const file = params.get("file") || "";
1813
+ const rawOrigin = params.get("origin");
1814
+ const origin = rawOrigin === "committed" || rawOrigin === "uncommitted" ? rawOrigin : void 0;
1815
+ return {
1816
+ screen: "worktree",
1817
+ ...wt ? { wt } : {},
1818
+ ...file ? { file } : {},
1819
+ ...origin ? { origin } : {},
1820
+ range
1821
+ };
1822
+ }
1798
1823
  case "/doctor":
1799
1824
  return {
1800
1825
  screen: "repo",
@@ -1878,6 +1903,14 @@ Details: ${JSON.stringify(output)}` : "";
1878
1903
  return "/file?path=" + encodeURIComponent(route.path) + "&ref=" + encodeURIComponent(route.ref || "worktree") + "&from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "") + (route.virtual === "off" ? "&virtual=off" : "");
1879
1904
  case "diff":
1880
1905
  return "/todif?from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree") + (route.path ? `&path=${encodeURIComponent(route.path)}` : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "");
1906
+ case "worktree": {
1907
+ const params = new URLSearchParams();
1908
+ if (route.wt) params.set("wt", route.wt);
1909
+ if (route.file) params.set("file", route.file);
1910
+ if (route.file && route.origin) params.set("origin", route.origin);
1911
+ const qs = params.toString();
1912
+ return `/worktree${qs ? `?${qs}` : ""}`;
1913
+ }
1881
1914
  case "help": {
1882
1915
  const params = new URLSearchParams();
1883
1916
  if (route.lang && route.lang !== "en") params.set("lang", route.lang);
@@ -1937,8 +1970,8 @@ Details: ${JSON.stringify(output)}` : "";
1937
1970
  const qs = params.toString();
1938
1971
  return qs ? `${base2}?${qs}` : base2;
1939
1972
  }
1940
- function withDoctorOverlay(url, open) {
1941
- return withQueryParam(url, "doctor", open ? "open" : null);
1973
+ function withDoctorOverlay(url, open2) {
1974
+ return withQueryParam(url, "doctor", open2 ? "open" : null);
1942
1975
  }
1943
1976
  function parseToolsOverlay(search) {
1944
1977
  const raw = new URLSearchParams(search).get("tools");
@@ -7924,8 +7957,8 @@ Details: ${JSON.stringify(output)}` : "";
7924
7957
  compile(this);
7925
7958
  return this;
7926
7959
  }
7927
- this.__tlds__ = this.__tlds__.concat(list2).sort().filter(function(el, idx, arr) {
7928
- return el !== arr[idx - 1];
7960
+ this.__tlds__ = this.__tlds__.concat(list2).sort().filter(function(el2, idx, arr) {
7961
+ return el2 !== arr[idx - 1];
7929
7962
  }).reverse();
7930
7963
  compile(this);
7931
7964
  return this;
@@ -9165,8 +9198,8 @@ Details: ${JSON.stringify(output)}` : "";
9165
9198
  const markerRe = new RegExp(`^\\[!(${ALERT_TYPES.join("|")})\\]\\s*`, "i");
9166
9199
  let i2 = 0;
9167
9200
  while (i2 < state.tokens.length) {
9168
- const open = state.tokens[i2];
9169
- if (open.type !== "blockquote_open") {
9201
+ const open2 = state.tokens[i2];
9202
+ if (open2.type !== "blockquote_open") {
9170
9203
  i2++;
9171
9204
  continue;
9172
9205
  }
@@ -9191,7 +9224,7 @@ Details: ${JSON.stringify(output)}` : "";
9191
9224
  continue;
9192
9225
  }
9193
9226
  const type = match2[1].toLowerCase();
9194
- open.attrSet("class", `markdown-alert markdown-alert-${type}`);
9227
+ open2.attrSet("class", `markdown-alert markdown-alert-${type}`);
9195
9228
  const rest = first.content.slice(match2[0].length);
9196
9229
  if (rest) {
9197
9230
  first.content = rest;
@@ -9729,8 +9762,8 @@ ${frontmatter.yaml}
9729
9762
 
9730
9763
  // web-src/views/diff-line-select.ts
9731
9764
  var SELECTED_CLASS = "gdp-diff-line-selected";
9732
- function cardPath(el) {
9733
- return el.closest(".gdp-file-shell[data-path]")?.dataset.path || "";
9765
+ function cardPath(el2) {
9766
+ return el2.closest(".gdp-file-shell[data-path]")?.dataset.path || "";
9734
9767
  }
9735
9768
  function afterLineFromCell(cell) {
9736
9769
  const sideCell = cell.closest("td.d2h-code-side-linenumber");
@@ -10076,6 +10109,7 @@ ${frontmatter.yaml}
10076
10109
  const submit = document.createElement("button");
10077
10110
  submit.type = "button";
10078
10111
  submit.className = "gdp-btn gdp-btn-sm";
10112
+ if (opts.danger) submit.classList.add("gdp-dialog-danger");
10079
10113
  submit.textContent = opts.submitLabel ?? "Save";
10080
10114
  const error2 = document.createElement("div");
10081
10115
  error2.className = "gdp-dialog-error";
@@ -10218,10 +10252,10 @@ ${frontmatter.yaml}
10218
10252
  function updateDatabaseCaptureButton() {
10219
10253
  annotationCaptureDb.hidden = deps.getRoute().screen !== "database";
10220
10254
  }
10221
- function setAnnotationPanelOpen(open) {
10222
- annotationPanel.hidden = !open;
10223
- document.body.classList.toggle("annotation-panel-open", open);
10224
- if (open) {
10255
+ function setAnnotationPanelOpen(open2) {
10256
+ annotationPanel.hidden = !open2;
10257
+ document.body.classList.toggle("annotation-panel-open", open2);
10258
+ if (open2) {
10225
10259
  annotationPanelDismissed = false;
10226
10260
  const qhPanel = document.getElementById("query-history-panel");
10227
10261
  if (qhPanel) qhPanel.hidden = true;
@@ -10232,7 +10266,7 @@ ${frontmatter.yaml}
10232
10266
  updateActiveHighlights();
10233
10267
  syncInlineAnnotationActive();
10234
10268
  }
10235
- deps.setAnnotationPanelOpenState(open);
10269
+ deps.setAnnotationPanelOpenState(open2);
10236
10270
  syncSessionUrl();
10237
10271
  applyInlineAnnotations();
10238
10272
  }
@@ -10364,20 +10398,20 @@ ${frontmatter.yaml}
10364
10398
  }
10365
10399
  function applyDatabaseAnnotations(session) {
10366
10400
  if (databaseAnnotationsMounted) {
10367
- document.querySelectorAll(".gdp-db-annotation-strip").forEach((el) => {
10368
- el.remove();
10401
+ document.querySelectorAll(".gdp-db-annotation-strip").forEach((el2) => {
10402
+ el2.remove();
10369
10403
  });
10370
10404
  databaseAnnotationsMounted = false;
10371
10405
  }
10372
10406
  if (!session || deps.getRoute().screen !== "database") return;
10373
- const matches = session.entries.filter(databaseAnnotationMatchesRoute);
10374
- if (!matches.length) return;
10407
+ const matches2 = session.entries.filter(databaseAnnotationMatchesRoute);
10408
+ if (!matches2.length) return;
10375
10409
  const root = document.querySelector(".db-root");
10376
10410
  if (!root) return;
10377
10411
  const strip = document.createElement("section");
10378
10412
  strip.className = "gdp-db-annotation-strip";
10379
10413
  strip.setAttribute("aria-label", "Datastore annotations");
10380
- for (const entry of matches) {
10414
+ for (const entry of matches2) {
10381
10415
  strip.appendChild(
10382
10416
  buildDatabaseAnnotationBlock(entry, {
10383
10417
  index: session.entries.indexOf(entry),
@@ -10493,10 +10527,10 @@ ${frontmatter.yaml}
10493
10527
  function syncInlineAnnotationWidths() {
10494
10528
  document.querySelectorAll(".gdp-annotation-inline").forEach((box) => {
10495
10529
  let scroller = null;
10496
- for (let el = box.parentElement; el && el !== document.body; el = el.parentElement) {
10497
- const overflowX = getComputedStyle(el).overflowX;
10530
+ for (let el2 = box.parentElement; el2 && el2 !== document.body; el2 = el2.parentElement) {
10531
+ const overflowX = getComputedStyle(el2).overflowX;
10498
10532
  if (overflowX === "auto" || overflowX === "scroll") {
10499
- scroller = el;
10533
+ scroller = el2;
10500
10534
  break;
10501
10535
  }
10502
10536
  }
@@ -10695,8 +10729,8 @@ ${frontmatter.yaml}
10695
10729
  const head = annotationDetail.querySelector(
10696
10730
  ".annotation-detail-head"
10697
10731
  );
10698
- head?.querySelectorAll(".annotation-detail-head-action").forEach((el) => {
10699
- el.remove();
10732
+ head?.querySelectorAll(".annotation-detail-head-action").forEach((el2) => {
10733
+ el2.remove();
10700
10734
  });
10701
10735
  const body = $("#annotation-detail-body");
10702
10736
  body.replaceChildren();
@@ -10850,25 +10884,25 @@ ${frontmatter.yaml}
10850
10884
  const item = document.createElement("li");
10851
10885
  item.dataset.entryId = entry.id;
10852
10886
  item.classList.toggle("active", entry.id === activeAnnotationId);
10853
- const open = document.createElement("button");
10854
- open.type = "button";
10855
- open.className = "annotation-entry-open";
10887
+ const open2 = document.createElement("button");
10888
+ open2.type = "button";
10889
+ open2.className = "annotation-entry-open";
10856
10890
  const location2 = document.createElement("span");
10857
10891
  location2.className = "annotation-entry-location";
10858
10892
  location2.textContent = annotationLocationLabel(entry);
10859
10893
  const summary = document.createElement("span");
10860
10894
  summary.className = "annotation-entry-summary";
10861
10895
  summary.textContent = annotationEntrySummary(entry);
10862
- open.append(location2, summary);
10896
+ open2.append(location2, summary);
10863
10897
  const entryTimeLabel = annotationTimeLabel(entry.created_at);
10864
10898
  if (entryTimeLabel) {
10865
10899
  const time2 = document.createElement("span");
10866
10900
  time2.className = "annotation-entry-time";
10867
10901
  time2.textContent = entryTimeLabel;
10868
10902
  time2.title = entry.created_at;
10869
- open.appendChild(time2);
10903
+ open2.appendChild(time2);
10870
10904
  }
10871
- open.addEventListener("click", () => {
10905
+ open2.addEventListener("click", () => {
10872
10906
  void openAnnotationEntry(entry.id);
10873
10907
  });
10874
10908
  const remove = annotationIconButton(
@@ -10886,7 +10920,7 @@ ${frontmatter.yaml}
10886
10920
  if (!ok) return;
10887
10921
  void postAnnotationAction({ action: "delete", id: entry.id });
10888
10922
  });
10889
- item.append(open, remove);
10923
+ item.append(open2, remove);
10890
10924
  list2.appendChild(item);
10891
10925
  });
10892
10926
  sessionEl.appendChild(list2);
@@ -10894,13 +10928,13 @@ ${frontmatter.yaml}
10894
10928
  }
10895
10929
  }
10896
10930
  function updateActiveHighlights() {
10897
- annotationSessionsEl.querySelectorAll(".annotation-session").forEach((el) => {
10898
- el.classList.toggle("active", el.dataset.sessionId === activeSessionId);
10931
+ annotationSessionsEl.querySelectorAll(".annotation-session").forEach((el2) => {
10932
+ el2.classList.toggle("active", el2.dataset.sessionId === activeSessionId);
10899
10933
  });
10900
- annotationSessionsEl.querySelectorAll(".annotation-entries li").forEach((el) => {
10901
- el.classList.toggle(
10934
+ annotationSessionsEl.querySelectorAll(".annotation-entries li").forEach((el2) => {
10935
+ el2.classList.toggle(
10902
10936
  "active",
10903
- el.dataset.entryId === activeAnnotationId
10937
+ el2.dataset.entryId === activeAnnotationId
10904
10938
  );
10905
10939
  });
10906
10940
  }
@@ -10939,8 +10973,8 @@ ${frontmatter.yaml}
10939
10973
  const head = annotationDetail.querySelector(
10940
10974
  ".annotation-detail-head"
10941
10975
  );
10942
- head?.querySelectorAll(".annotation-detail-head-action").forEach((el) => {
10943
- el.remove();
10976
+ head?.querySelectorAll(".annotation-detail-head-action").forEach((el2) => {
10977
+ el2.remove();
10944
10978
  });
10945
10979
  const copyRef = createCopyRefButton(entry, "annotation-detail-head-action");
10946
10980
  const edit = annotationIconButton(
@@ -11532,8 +11566,8 @@ ${frontmatter.yaml}
11532
11566
  function createBlameView(deps) {
11533
11567
  let activeGeneration = 0;
11534
11568
  function cleanup() {
11535
- document.querySelectorAll(".gdp-standalone-blame").forEach((el) => {
11536
- el.remove();
11569
+ document.querySelectorAll(".gdp-standalone-blame").forEach((el2) => {
11570
+ el2.remove();
11537
11571
  });
11538
11572
  }
11539
11573
  function colourBarFor(commit, bin, binCount) {
@@ -13102,16 +13136,16 @@ ${frontmatter.yaml}
13102
13136
  }
13103
13137
 
13104
13138
  // web-src/views/database/pane-status.ts
13105
- function setPaneStatus(el, message, options = {}) {
13106
- el.innerHTML = "";
13139
+ function setPaneStatus(el2, message, options = {}) {
13140
+ el2.innerHTML = "";
13107
13141
  options.afterClear?.();
13108
13142
  const note = document.createElement("div");
13109
13143
  note.className = options.error ? "db-pane-error" : "db-pane-note";
13110
13144
  note.textContent = message;
13111
- el.appendChild(note);
13145
+ el2.appendChild(note);
13112
13146
  }
13113
- function setPaneEmpty(el, message, options = {}) {
13114
- el.innerHTML = "";
13147
+ function setPaneEmpty(el2, message, options = {}) {
13148
+ el2.innerHTML = "";
13115
13149
  const empty = document.createElement("div");
13116
13150
  empty.className = "db-pane-empty";
13117
13151
  const icon = document.createElement("div");
@@ -13127,7 +13161,7 @@ ${frontmatter.yaml}
13127
13161
  hint.textContent = options.hint;
13128
13162
  empty.appendChild(hint);
13129
13163
  }
13130
- el.appendChild(empty);
13164
+ el2.appendChild(empty);
13131
13165
  }
13132
13166
 
13133
13167
  // web-src/views/database/dynamodb-explorer.ts
@@ -14821,9 +14855,9 @@ ${frontmatter.yaml}
14821
14855
  }
14822
14856
  function createErDiagram(deps = {}) {
14823
14857
  const text3 = () => deps.getText?.() ?? dbText("en");
14824
- const el = document.createElement("div");
14825
- el.className = "db-er-diagram";
14826
- el.hidden = true;
14858
+ const el2 = document.createElement("div");
14859
+ el2.className = "db-er-diagram";
14860
+ el2.hidden = true;
14827
14861
  const toolbar = document.createElement("div");
14828
14862
  toolbar.className = "db-er-toolbar";
14829
14863
  const zoomIn = document.createElement("button");
@@ -14853,7 +14887,7 @@ ${frontmatter.yaml}
14853
14887
  });
14854
14888
  const container = viewport.container;
14855
14889
  const svgWrap = viewport.content;
14856
- el.append(toolbar, container);
14890
+ el2.append(toolbar, container);
14857
14891
  let lastMarkup = "";
14858
14892
  zoomIn.addEventListener("click", () => viewport.zoomIn());
14859
14893
  zoomOut.addEventListener("click", () => viewport.zoomOut());
@@ -14872,7 +14906,7 @@ ${frontmatter.yaml}
14872
14906
  }
14873
14907
  });
14874
14908
  async function render(schema, columnsMap) {
14875
- el.hidden = false;
14909
+ el2.hidden = false;
14876
14910
  svgWrap.innerHTML = "";
14877
14911
  viewport.reset();
14878
14912
  const tables = schema.tables.filter((t2) => t2.type === "table");
@@ -14898,7 +14932,7 @@ ${frontmatter.yaml}
14898
14932
  }
14899
14933
  }
14900
14934
  function clear() {
14901
- el.hidden = true;
14935
+ el2.hidden = true;
14902
14936
  svgWrap.innerHTML = "";
14903
14937
  lastMarkup = "";
14904
14938
  }
@@ -14914,14 +14948,14 @@ ${frontmatter.yaml}
14914
14948
  copyBtn.title = t2.copyMermaidTitle;
14915
14949
  if (copyBtn.textContent !== t2.copied) copyBtn.textContent = t2.copyMermaid;
14916
14950
  }
14917
- return { el, render, clear, dispose, localize };
14951
+ return { el: el2, render, clear, dispose, localize };
14918
14952
  }
14919
14953
 
14920
14954
  // web-src/views/database/global-search-view.ts
14921
14955
  function createGlobalSearchView(deps) {
14922
14956
  const text3 = () => deps.getText?.() ?? dbText("en");
14923
- const el = document.createElement("div");
14924
- el.className = "db-global-search";
14957
+ const el2 = document.createElement("div");
14958
+ el2.className = "db-global-search";
14925
14959
  const header = document.createElement("div");
14926
14960
  header.className = "db-global-search-header";
14927
14961
  const input2 = document.createElement("input");
@@ -14951,7 +14985,7 @@ ${frontmatter.yaml}
14951
14985
  progress.hidden = true;
14952
14986
  const results = document.createElement("div");
14953
14987
  results.className = "db-global-search-results";
14954
- el.append(header, optionsRow, progress, results);
14988
+ el2.append(header, optionsRow, progress, results);
14955
14989
  let currentJobId = null;
14956
14990
  let pollTimer = null;
14957
14991
  let disposed = false;
@@ -15147,7 +15181,7 @@ ${frontmatter.yaml}
15147
15181
  cancelBtn.textContent = t2.cancelButton;
15148
15182
  nonTextText.textContent = t2.includeNonText;
15149
15183
  }
15150
- return { el, setSearch, getSearch, dispose, localize };
15184
+ return { el: el2, setSearch, getSearch, dispose, localize };
15151
15185
  }
15152
15186
 
15153
15187
  // web-src/views/database/pref-toggle.ts
@@ -15196,8 +15230,8 @@ ${frontmatter.yaml}
15196
15230
  var MAX_HISTORY = 50;
15197
15231
  function createQueryEditor(callbacks) {
15198
15232
  const text3 = () => callbacks.getText?.() ?? dbText("en");
15199
- const el = document.createElement("div");
15200
- el.className = "db-query-editor";
15233
+ const el2 = document.createElement("div");
15234
+ el2.className = "db-query-editor";
15201
15235
  const inputArea = document.createElement("div");
15202
15236
  inputArea.className = "db-query-input";
15203
15237
  const editorWrap = document.createElement("div");
@@ -15275,7 +15309,7 @@ ${frontmatter.yaml}
15275
15309
  const resultArea = document.createElement("div");
15276
15310
  resultArea.className = "db-query-result";
15277
15311
  resultArea.hidden = true;
15278
- el.append(inputArea, resultArea);
15312
+ el2.append(inputArea, resultArea);
15279
15313
  async function run() {
15280
15314
  const sql = textarea.value.trim();
15281
15315
  if (!sql) return;
@@ -15510,7 +15544,7 @@ ${frontmatter.yaml}
15510
15544
  historyBtn.title = t2.localHistoryTitle;
15511
15545
  }
15512
15546
  return {
15513
- el,
15547
+ el: el2,
15514
15548
  focus,
15515
15549
  setSql,
15516
15550
  getSql,
@@ -15524,8 +15558,8 @@ ${frontmatter.yaml}
15524
15558
  // web-src/views/database/query-history-view.ts
15525
15559
  function createQueryHistoryView(callbacks) {
15526
15560
  const text3 = () => callbacks.getText?.() ?? dbText("en");
15527
- const el = document.createElement("div");
15528
- el.className = "db-query-history";
15561
+ const el2 = document.createElement("div");
15562
+ el2.className = "db-query-history";
15529
15563
  const toolbar = document.createElement("div");
15530
15564
  toolbar.className = "db-query-history-toolbar";
15531
15565
  const refreshBtn = document.createElement("button");
@@ -15559,7 +15593,7 @@ ${frontmatter.yaml}
15559
15593
  detailPlaceholder.textContent = text3().history.selectPlaceholder;
15560
15594
  detailCol.appendChild(detailPlaceholder);
15561
15595
  body.append(listCol, detailCol);
15562
- el.append(toolbar, body);
15596
+ el2.append(toolbar, body);
15563
15597
  let entries = [];
15564
15598
  const expandedIds = /* @__PURE__ */ new Set();
15565
15599
  let clearConfirmTimer = null;
@@ -15967,7 +16001,7 @@ ${frontmatter.yaml}
15967
16001
  }
15968
16002
  }
15969
16003
  }
15970
- return { el, refresh, clear, localize };
16004
+ return { el: el2, refresh, clear, localize };
15971
16005
  }
15972
16006
  function formatTime(iso) {
15973
16007
  try {
@@ -15983,17 +16017,17 @@ ${frontmatter.yaml}
15983
16017
  function isBinaryItem(item) {
15984
16018
  return typeof item === "object" && item !== null && "binaryBase64" in item;
15985
16019
  }
15986
- function renderItemInto(el, item, binaryBadge) {
15987
- el.textContent = "";
16020
+ function renderItemInto(el2, item, binaryBadge) {
16021
+ el2.textContent = "";
15988
16022
  if (isBinaryItem(item)) {
15989
16023
  const badge = document.createElement("span");
15990
16024
  badge.className = "redis-binary-badge";
15991
16025
  badge.textContent = binaryBadge;
15992
- el.appendChild(badge);
15993
- el.appendChild(document.createTextNode(item.binaryBase64));
16026
+ el2.appendChild(badge);
16027
+ el2.appendChild(document.createTextNode(item.binaryBase64));
15994
16028
  return;
15995
16029
  }
15996
- el.textContent = item;
16030
+ el2.textContent = item;
15997
16031
  }
15998
16032
  function createRedisExplorer(callbacks = {}) {
15999
16033
  const text3 = () => (callbacks.getText?.() ?? dbText("en")).explorer;
@@ -17207,8 +17241,8 @@ ${frontmatter.yaml}
17207
17241
  function isAbortError(err) {
17208
17242
  return err instanceof DOMException && err.name === "AbortError";
17209
17243
  }
17210
- function setExplorerFolderIcon(el, collapsed) {
17211
- el.innerHTML = iconSvg(
17244
+ function setExplorerFolderIcon(el2, collapsed) {
17245
+ el2.innerHTML = iconSvg(
17212
17246
  collapsed ? "octicon-file-directory-fill" : "octicon-file-directory-open-fill",
17213
17247
  collapsed ? FOLDER_ICON_PATHS.closed : FOLDER_ICON_PATHS.open
17214
17248
  );
@@ -17767,12 +17801,12 @@ ${frontmatter.yaml}
17767
17801
  actions.className = "s3-preview-actions";
17768
17802
  if (currentDbId && currentBucket) {
17769
17803
  const rawUrl = buildS3RawUrl(currentDbId, currentBucket, object.key);
17770
- const open = document.createElement("a");
17771
- open.className = "db-btn db-btn-sm";
17772
- open.href = rawUrl;
17773
- open.target = "_blank";
17774
- open.rel = "noreferrer";
17775
- open.textContent = text3().openRaw;
17804
+ const open2 = document.createElement("a");
17805
+ open2.className = "db-btn db-btn-sm";
17806
+ open2.href = rawUrl;
17807
+ open2.target = "_blank";
17808
+ open2.rel = "noreferrer";
17809
+ open2.textContent = text3().openRaw;
17776
17810
  const download = document.createElement("a");
17777
17811
  download.className = "db-btn db-btn-sm";
17778
17812
  download.href = rawUrl;
@@ -17795,7 +17829,7 @@ ${frontmatter.yaml}
17795
17829
  copy.textContent = text3().copyFailed;
17796
17830
  }
17797
17831
  });
17798
- actions.append(open, download, copy);
17832
+ actions.append(open2, download, copy);
17799
17833
  if (sourceDisplayKind(object.key) === "text") {
17800
17834
  const editBtn = mkBtn(tCommon().edit, "db-btn db-btn-sm");
17801
17835
  editBtn.addEventListener("click", () => void startObjectEdit(object));
@@ -17922,8 +17956,8 @@ ${frontmatter.yaml}
17922
17956
  function indentPad(depth) {
17923
17957
  return `${12 + depth * 14}px`;
17924
17958
  }
17925
- function setExplorerChevron(el) {
17926
- el.innerHTML = `<svg class="octicon octicon-chevron-down" viewBox="0 0 12 12" width="12" height="12" fill="currentColor" aria-hidden="true"><path fill="currentColor" d="${CHEVRON_DOWN_12_PATH}"></path></svg>`;
17959
+ function setExplorerChevron(el2) {
17960
+ el2.innerHTML = `<svg class="octicon octicon-chevron-down" viewBox="0 0 12 12" width="12" height="12" fill="currentColor" aria-hidden="true"><path fill="currentColor" d="${CHEVRON_DOWN_12_PATH}"></path></svg>`;
17927
17961
  }
17928
17962
  function resetExplorer() {
17929
17963
  explorerAbort?.abort();
@@ -17951,11 +17985,11 @@ ${frontmatter.yaml}
17951
17985
  return await res.json();
17952
17986
  }
17953
17987
  function makeTreeMessageRow(cls, depth, text4) {
17954
- const el = document.createElement("div");
17955
- el.className = cls;
17956
- el.style.setProperty("--lvl-pad", indentPad(depth));
17957
- el.textContent = text4;
17958
- return el;
17988
+ const el2 = document.createElement("div");
17989
+ el2.className = cls;
17990
+ el2.style.setProperty("--lvl-pad", indentPad(depth));
17991
+ el2.textContent = text4;
17992
+ return el2;
17959
17993
  }
17960
17994
  function createExplorerFileRow(object, depth) {
17961
17995
  objectsByKey.set(object.key, object);
@@ -18422,9 +18456,9 @@ ${frontmatter.yaml}
18422
18456
  // web-src/views/database/schema-view.ts
18423
18457
  function createSchemaView(deps = {}) {
18424
18458
  const text3 = () => deps.getText?.() ?? dbText("en");
18425
- const el = document.createElement("div");
18426
- el.className = "db-schema-view";
18427
- el.hidden = true;
18459
+ const el2 = document.createElement("div");
18460
+ el2.className = "db-schema-view";
18461
+ el2.hidden = true;
18428
18462
  let refreshBusy = false;
18429
18463
  let refreshBtn = null;
18430
18464
  let refreshLabel = null;
@@ -18446,8 +18480,8 @@ ${frontmatter.yaml}
18446
18480
  function render(table2, columns, indexes, extra) {
18447
18481
  lastArgs = { table: table2, columns, indexes, extra };
18448
18482
  const t2 = text3().schema;
18449
- el.hidden = false;
18450
- el.innerHTML = "";
18483
+ el2.hidden = false;
18484
+ el2.innerHTML = "";
18451
18485
  const header = document.createElement("div");
18452
18486
  header.className = "db-schema-header";
18453
18487
  const headerTitle = document.createElement("span");
@@ -18473,11 +18507,11 @@ ${frontmatter.yaml}
18473
18507
  refreshBtn = null;
18474
18508
  refreshLabel = null;
18475
18509
  }
18476
- el.appendChild(header);
18510
+ el2.appendChild(header);
18477
18511
  const colSectionHeader = document.createElement("div");
18478
18512
  colSectionHeader.className = "db-schema-section-header";
18479
18513
  colSectionHeader.textContent = t2.columns;
18480
- el.appendChild(colSectionHeader);
18514
+ el2.appendChild(colSectionHeader);
18481
18515
  const colTable = document.createElement("table");
18482
18516
  colTable.className = "db-schema-table";
18483
18517
  const thead = document.createElement("thead");
@@ -18520,13 +18554,13 @@ ${frontmatter.yaml}
18520
18554
  tbody.appendChild(tr);
18521
18555
  }
18522
18556
  colTable.append(thead, tbody);
18523
- el.appendChild(colTable);
18557
+ el2.appendChild(colTable);
18524
18558
  const fks = extra?.foreignKeys?.filter((fk) => fk.fromTable === table2);
18525
18559
  if (fks && fks.length > 0) {
18526
18560
  const fkHeader = document.createElement("div");
18527
18561
  fkHeader.className = "db-schema-section-header";
18528
18562
  fkHeader.textContent = t2.foreignKeys;
18529
- el.appendChild(fkHeader);
18563
+ el2.appendChild(fkHeader);
18530
18564
  const fkTable = document.createElement("table");
18531
18565
  fkTable.className = "db-schema-table";
18532
18566
  const fkThead = document.createElement("thead");
@@ -18550,14 +18584,14 @@ ${frontmatter.yaml}
18550
18584
  fkTbody.appendChild(tr);
18551
18585
  }
18552
18586
  fkTable.append(fkThead, fkTbody);
18553
- el.appendChild(fkTable);
18587
+ el2.appendChild(fkTable);
18554
18588
  }
18555
18589
  const tableIndexes = indexes.filter((idx) => idx.table === table2);
18556
18590
  if (tableIndexes.length > 0) {
18557
18591
  const idxHeader = document.createElement("div");
18558
18592
  idxHeader.className = "db-schema-section-header";
18559
18593
  idxHeader.textContent = t2.indexes;
18560
- el.appendChild(idxHeader);
18594
+ el2.appendChild(idxHeader);
18561
18595
  const idxTable = document.createElement("table");
18562
18596
  idxTable.className = "db-schema-table";
18563
18597
  const idxThead = document.createElement("thead");
@@ -18581,14 +18615,14 @@ ${frontmatter.yaml}
18581
18615
  idxTbody.appendChild(tr);
18582
18616
  }
18583
18617
  idxTable.append(idxThead, idxTbody);
18584
- el.appendChild(idxTable);
18618
+ el2.appendChild(idxTable);
18585
18619
  }
18586
18620
  const triggers = extra?.triggers;
18587
18621
  if (triggers && triggers.length > 0) {
18588
18622
  const trigHeader = document.createElement("div");
18589
18623
  trigHeader.className = "db-schema-section-header";
18590
18624
  trigHeader.textContent = t2.triggers;
18591
- el.appendChild(trigHeader);
18625
+ el2.appendChild(trigHeader);
18592
18626
  for (const trig of triggers) {
18593
18627
  const trigBlock = document.createElement("div");
18594
18628
  trigBlock.className = "db-schema-trigger-block";
@@ -18599,7 +18633,7 @@ ${frontmatter.yaml}
18599
18633
  trigPre.className = "db-schema-ddl-pre";
18600
18634
  trigPre.textContent = trig.sql;
18601
18635
  trigBlock.append(trigName, trigPre);
18602
- el.appendChild(trigBlock);
18636
+ el2.appendChild(trigBlock);
18603
18637
  }
18604
18638
  }
18605
18639
  const ddl = extra?.ddl;
@@ -18607,7 +18641,7 @@ ${frontmatter.yaml}
18607
18641
  const ddlHeader = document.createElement("div");
18608
18642
  ddlHeader.className = "db-schema-section-header";
18609
18643
  ddlHeader.textContent = t2.ddl;
18610
- el.appendChild(ddlHeader);
18644
+ el2.appendChild(ddlHeader);
18611
18645
  const ddlWrap = document.createElement("div");
18612
18646
  ddlWrap.className = "db-schema-ddl-wrap";
18613
18647
  const copyBtn = document.createElement("button");
@@ -18630,22 +18664,22 @@ ${frontmatter.yaml}
18630
18664
  ddlPre.className = "db-schema-ddl-pre";
18631
18665
  ddlPre.textContent = ddl;
18632
18666
  ddlWrap.append(copyBtn, ddlPre);
18633
- el.appendChild(ddlWrap);
18667
+ el2.appendChild(ddlWrap);
18634
18668
  }
18635
18669
  }
18636
18670
  function clear() {
18637
- el.hidden = true;
18638
- el.innerHTML = "";
18671
+ el2.hidden = true;
18672
+ el2.innerHTML = "";
18639
18673
  lastArgs = null;
18640
18674
  refreshBtn = null;
18641
18675
  refreshLabel = null;
18642
18676
  refreshBusy = false;
18643
18677
  }
18644
18678
  function localize() {
18645
- if (!lastArgs || el.hidden) return;
18679
+ if (!lastArgs || el2.hidden) return;
18646
18680
  render(lastArgs.table, lastArgs.columns, lastArgs.indexes, lastArgs.extra);
18647
18681
  }
18648
- return { el, render, clear, localize, setRefreshBusy };
18682
+ return { el: el2, render, clear, localize, setRefreshBusy };
18649
18683
  }
18650
18684
 
18651
18685
  // web-src/views/database/session-log.ts
@@ -18688,8 +18722,8 @@ ${frontmatter.yaml}
18688
18722
  // web-src/views/database/session-log-view.ts
18689
18723
  function createSessionLogView(callbacks) {
18690
18724
  const text3 = () => callbacks.getText?.() ?? dbText("en");
18691
- const el = document.createElement("div");
18692
- el.className = "db-session-log";
18725
+ const el2 = document.createElement("div");
18726
+ el2.className = "db-session-log";
18693
18727
  const toolbar = document.createElement("div");
18694
18728
  toolbar.className = "db-session-log-toolbar";
18695
18729
  const followBtn = document.createElement("button");
@@ -18718,7 +18752,7 @@ ${frontmatter.yaml}
18718
18752
  placeholder.textContent = text3().sessionLog.selectPlaceholder;
18719
18753
  detailCol.appendChild(placeholder);
18720
18754
  body.append(listCol, detailCol);
18721
- el.append(toolbar, body);
18755
+ el2.append(toolbar, body);
18722
18756
  let selectedId = null;
18723
18757
  const rowById = /* @__PURE__ */ new Map();
18724
18758
  let autoFollow = true;
@@ -18906,7 +18940,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
18906
18940
  placeholder.textContent = text3().sessionLog.selectPlaceholder;
18907
18941
  render();
18908
18942
  }
18909
- return { el, clear, localize };
18943
+ return { el: el2, clear, localize };
18910
18944
  }
18911
18945
  function formatTime2(timestamp) {
18912
18946
  const d2 = new Date(timestamp);
@@ -18944,8 +18978,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
18944
18978
  const note = s2.note ? ` — ${s2.note}` : "";
18945
18979
  return text3().snapshot.label(date, s2.tables.length, note);
18946
18980
  };
18947
- const el = document.createElement("div");
18948
- el.className = "db-snapshot-view";
18981
+ const el2 = document.createElement("div");
18982
+ el2.className = "db-snapshot-view";
18949
18983
  const guide = document.createElement("div");
18950
18984
  guide.className = "db-snapshot-guide";
18951
18985
  const guideTitle = document.createElement("div");
@@ -19039,7 +19073,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
19039
19073
  rightPane.className = "db-snapshot-right-pane";
19040
19074
  splitContainer.append(leftPane, rightPane);
19041
19075
  mainArea.appendChild(splitContainer);
19042
- el.append(guide, toolbar, progressArea, tableSelector, mainArea);
19076
+ el2.append(guide, toolbar, progressArea, tableSelector, mainArea);
19043
19077
  let snapshots = [];
19044
19078
  let currentDiff = null;
19045
19079
  let disposed = false;
@@ -19717,8 +19751,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
19717
19751
  threshold: 0
19718
19752
  }
19719
19753
  );
19720
- for (const el2 of section2.querySelectorAll(".db-snapshot-diff-table")) {
19721
- observer.observe(el2);
19754
+ for (const el3 of section2.querySelectorAll(".db-snapshot-diff-table")) {
19755
+ observer.observe(el3);
19722
19756
  }
19723
19757
  section2.__tocObserver = observer;
19724
19758
  rightPane.appendChild(section2);
@@ -19877,7 +19911,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
19877
19911
  }
19878
19912
  }
19879
19913
  async function editNote(snapshotId, currentNote) {
19880
- const existing = el.querySelector(".db-snapshot-inline-dialog");
19914
+ const existing = el2.querySelector(".db-snapshot-inline-dialog");
19881
19915
  if (existing) existing.remove();
19882
19916
  const dialog = document.createElement("div");
19883
19917
  dialog.className = "db-snapshot-inline-dialog";
@@ -19911,7 +19945,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
19911
19945
  });
19912
19946
  dialog.append(input2, saveBtn, cancelDlgBtn);
19913
19947
  const item = Array.from(
19914
- el.querySelectorAll(".db-snapshot-item")
19948
+ el2.querySelectorAll(".db-snapshot-item")
19915
19949
  ).find((node) => node.dataset.snapshotId === snapshotId);
19916
19950
  if (item) {
19917
19951
  item.after(dialog);
@@ -20040,7 +20074,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
20040
20074
  await showDiffInline(target.before, target.after);
20041
20075
  }
20042
20076
  return {
20043
- el,
20077
+ el: el2,
20044
20078
  refresh,
20045
20079
  handleSse,
20046
20080
  dispose,
@@ -20067,8 +20101,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
20067
20101
  function createTableGrid(callbacks, options = {}) {
20068
20102
  const embedded = options.embedded === true;
20069
20103
  const text3 = () => callbacks.getText?.() ?? dbText("en");
20070
- const el = document.createElement("div");
20071
- el.className = "db-grid";
20104
+ const el2 = document.createElement("div");
20105
+ el2.className = "db-grid";
20072
20106
  const filterBar = document.createElement("div");
20073
20107
  filterBar.className = "db-grid-filter-bar";
20074
20108
  const filterIcon = document.createElement("span");
@@ -20243,9 +20277,9 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
20243
20277
  detailResize.addEventListener("mousedown", startDetailResize);
20244
20278
  detailPanel.appendChild(detailResize);
20245
20279
  viewport.append(spacer, body, filteredEmpty);
20246
- el.append(filterBar, headerWrap, filterRowWrap, viewport, detailPanel);
20280
+ el2.append(filterBar, headerWrap, filterRowWrap, viewport, detailPanel);
20247
20281
  function panelMaxHeight() {
20248
- const containerH = el.clientHeight || window.innerHeight;
20282
+ const containerH = el2.clientHeight || window.innerHeight;
20249
20283
  return Math.max(
20250
20284
  DETAIL_PANEL_MIN_HEIGHT + 1,
20251
20285
  containerH - PANEL_MAX_RESERVE
@@ -20383,7 +20417,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
20383
20417
  relatedPanel.className = "db-related-panel";
20384
20418
  relatedPanel.hidden = true;
20385
20419
  relatedPanel.style.height = `${relatedHeight}px`;
20386
- el.appendChild(relatedPanel);
20420
+ el2.appendChild(relatedPanel);
20387
20421
  }
20388
20422
  const colWidths = /* @__PURE__ */ new Map();
20389
20423
  let activeResize = null;
@@ -21611,7 +21645,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
21611
21645
  if (!statusEl) {
21612
21646
  statusEl = document.createElement("div");
21613
21647
  statusEl.className = "db-grid-status";
21614
- el.appendChild(statusEl);
21648
+ el2.appendChild(statusEl);
21615
21649
  }
21616
21650
  const t2 = text3().grid;
21617
21651
  const parts = [t2.statusRows(totalRows.toLocaleString())];
@@ -21636,7 +21670,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
21636
21670
  statusEl = document.createElement("div");
21637
21671
  statusEl.className = "db-grid-status db-pane-error";
21638
21672
  statusEl.textContent = message;
21639
- el.appendChild(statusEl);
21673
+ el2.appendChild(statusEl);
21640
21674
  }
21641
21675
  function load(table2, initialData) {
21642
21676
  clear();
@@ -21857,12 +21891,12 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
21857
21891
  discardBtn.disabled = count === 0;
21858
21892
  notifyEditableIfChanged();
21859
21893
  }
21860
- function setControlVisibility(el2, visible) {
21861
- el2.style.visibility = visible ? "" : "hidden";
21862
- if ("tabIndex" in el2) {
21863
- el2.tabIndex = visible ? 0 : -1;
21894
+ function setControlVisibility(el3, visible) {
21895
+ el3.style.visibility = visible ? "" : "hidden";
21896
+ if ("tabIndex" in el3) {
21897
+ el3.tabIndex = visible ? 0 : -1;
21864
21898
  }
21865
- el2.setAttribute("aria-hidden", visible ? "false" : "true");
21899
+ el3.setAttribute("aria-hidden", visible ? "false" : "true");
21866
21900
  }
21867
21901
  const editableListeners = /* @__PURE__ */ new Set();
21868
21902
  const editModeListeners = /* @__PURE__ */ new Set();
@@ -22018,7 +22052,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22018
22052
  embeddedGrid?.refreshForeignKeys();
22019
22053
  }
22020
22054
  return {
22021
- el,
22055
+ el: el2,
22022
22056
  load,
22023
22057
  refresh: refreshCurrentTable,
22024
22058
  showError,
@@ -22078,9 +22112,9 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22078
22112
  filterClear.innerHTML = iconSvg("octicon-x", X_16_PATH);
22079
22113
  filterClear.hidden = true;
22080
22114
  filterWrap.append(filterInput, filterClear);
22081
- const el = document.createElement("div");
22082
- el.className = "db-table-list";
22083
- wrapper.append(filterWrap, el);
22115
+ const el2 = document.createElement("div");
22116
+ el2.className = "db-table-list";
22117
+ wrapper.append(filterWrap, el2);
22084
22118
  let activeTable = null;
22085
22119
  let allTables = [];
22086
22120
  const expandedTables = /* @__PURE__ */ new Set();
@@ -22089,7 +22123,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22089
22123
  let contextMenuClick = null;
22090
22124
  let contextMenuKeyDown = null;
22091
22125
  let contextMenuTimer = null;
22092
- function closeContextMenu() {
22126
+ function closeContextMenu2() {
22093
22127
  if (contextMenuTimer) {
22094
22128
  clearTimeout(contextMenuTimer);
22095
22129
  contextMenuTimer = null;
@@ -22107,9 +22141,9 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22107
22141
  contextMenu = null;
22108
22142
  }
22109
22143
  }
22110
- function showContextMenu(e2, tableName) {
22144
+ function showContextMenu2(e2, tableName) {
22111
22145
  e2.preventDefault();
22112
- closeContextMenu();
22146
+ closeContextMenu2();
22113
22147
  const menu = document.createElement("div");
22114
22148
  menu.className = "db-context-menu";
22115
22149
  menu.style.position = "absolute";
@@ -22150,7 +22184,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22150
22184
  row.textContent = item.label;
22151
22185
  row.addEventListener("click", () => {
22152
22186
  item.action();
22153
- closeContextMenu();
22187
+ closeContextMenu2();
22154
22188
  });
22155
22189
  menu.appendChild(row);
22156
22190
  }
@@ -22158,13 +22192,13 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22158
22192
  contextMenu = menu;
22159
22193
  const onDocClick = (ev) => {
22160
22194
  if (!menu.contains(ev.target)) {
22161
- closeContextMenu();
22195
+ closeContextMenu2();
22162
22196
  }
22163
22197
  };
22164
22198
  const onKeyDown = (ev) => {
22165
22199
  if (isImeComposing(ev)) return;
22166
22200
  if (ev.key === "Escape") {
22167
- closeContextMenu();
22201
+ closeContextMenu2();
22168
22202
  }
22169
22203
  };
22170
22204
  contextMenuClick = onDocClick;
@@ -22246,7 +22280,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22246
22280
  filterInput.focus();
22247
22281
  }
22248
22282
  function renderFiltered(tables, filter) {
22249
- el.innerHTML = "";
22283
+ el2.innerHTML = "";
22250
22284
  syncFilterClearButton();
22251
22285
  const filtered = filter ? tables.filter(
22252
22286
  (t2) => t2.name.toLowerCase().includes(filter.toLowerCase())
@@ -22268,7 +22302,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22268
22302
  actions.appendChild(clear);
22269
22303
  empty.appendChild(actions);
22270
22304
  }
22271
- el.appendChild(empty);
22305
+ el2.appendChild(empty);
22272
22306
  return;
22273
22307
  }
22274
22308
  const groups = { table: [], view: [] };
@@ -22280,7 +22314,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22280
22314
  const header = document.createElement("div");
22281
22315
  header.className = "db-table-group-header";
22282
22316
  header.textContent = type === "view" ? "Views" : "Tables";
22283
- el.appendChild(header);
22317
+ el2.appendChild(header);
22284
22318
  for (const table2 of items) {
22285
22319
  const node = document.createElement("div");
22286
22320
  node.className = "db-table-node";
@@ -22329,10 +22363,10 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22329
22363
  );
22330
22364
  row.addEventListener(
22331
22365
  "contextmenu",
22332
- (e2) => showContextMenu(e2, table2.name)
22366
+ (e2) => showContextMenu2(e2, table2.name)
22333
22367
  );
22334
22368
  node.append(row, children);
22335
- el.appendChild(node);
22369
+ el2.appendChild(node);
22336
22370
  }
22337
22371
  }
22338
22372
  }
@@ -22345,7 +22379,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22345
22379
  }
22346
22380
  columnCache.clear();
22347
22381
  activeTable = null;
22348
- closeContextMenu();
22382
+ closeContextMenu2();
22349
22383
  filterInput.value = "";
22350
22384
  renderFiltered(tables, "");
22351
22385
  }
@@ -22356,7 +22390,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22356
22390
  filterClear.addEventListener("click", clearFilter);
22357
22391
  function setActive(table2) {
22358
22392
  activeTable = table2;
22359
- el.querySelectorAll(".db-table-item").forEach((item) => {
22393
+ el2.querySelectorAll(".db-table-item").forEach((item) => {
22360
22394
  item.classList.toggle("active", item.dataset.table === table2);
22361
22395
  });
22362
22396
  }
@@ -22364,7 +22398,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22364
22398
  allTables = allTables.map(
22365
22399
  (entry) => entry.name === table2 ? { ...entry, rowCount } : entry
22366
22400
  );
22367
- for (const node of el.querySelectorAll(".db-table-node")) {
22401
+ for (const node of el2.querySelectorAll(".db-table-node")) {
22368
22402
  if (node.dataset.table !== table2) continue;
22369
22403
  const count = node.querySelector(".db-table-count");
22370
22404
  if (count)
@@ -22372,7 +22406,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
22372
22406
  }
22373
22407
  }
22374
22408
  function dispose() {
22375
- closeContextMenu();
22409
+ closeContextMenu2();
22376
22410
  allTables = [];
22377
22411
  expandedTables.clear();
22378
22412
  columnCache.clear();
@@ -25234,11 +25268,11 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25234
25268
  return !!text3 && !/\S/.test(text3);
25235
25269
  }
25236
25270
  function suppressWhitespaceOnlyInlineHighlights(root) {
25237
- root.querySelectorAll("ins, del").forEach((el) => {
25238
- if (!isWhitespaceOnlyInlineHighlight(el.textContent)) return;
25239
- const parent = el.parentNode;
25271
+ root.querySelectorAll("ins, del").forEach((el2) => {
25272
+ if (!isWhitespaceOnlyInlineHighlight(el2.textContent)) return;
25273
+ const parent = el2.parentNode;
25240
25274
  if (!parent) return;
25241
- parent.replaceChild(document.createTextNode(el.textContent || ""), el);
25275
+ parent.replaceChild(document.createTextNode(el2.textContent || ""), el2);
25242
25276
  });
25243
25277
  }
25244
25278
 
@@ -25365,12 +25399,12 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25365
25399
  button.innerHTML = expanded ? iconSvg("octicon-fold", COLLAPSE_ALL_16_PATHS) : iconSvg("octicon-unfold", EXPAND_ALL_16_PATHS);
25366
25400
  }
25367
25401
  function setProjectBranch(branch) {
25368
- const el = document.querySelector("#project-branch");
25369
- if (!el) return;
25370
- el.hidden = !branch;
25371
- const name = el.querySelector(".project-branch-name");
25402
+ const el2 = document.querySelector("#project-branch");
25403
+ if (!el2) return;
25404
+ el2.hidden = !branch;
25405
+ const name = el2.querySelector(".project-branch-name");
25372
25406
  if (name) name.textContent = branch;
25373
- el.title = branch ? `Current branch: ${branch}` : "";
25407
+ el2.title = branch ? `Current branch: ${branch}` : "";
25374
25408
  }
25375
25409
  let metaFilesForViewedProgress = [];
25376
25410
  function viewedProgressFor(files) {
@@ -25487,36 +25521,36 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25487
25521
  return true;
25488
25522
  }
25489
25523
  function renderMeta(meta) {
25490
- const el = $("#meta");
25524
+ const el2 = $("#meta");
25491
25525
  if (!meta) {
25492
- el.textContent = "";
25526
+ el2.textContent = "";
25493
25527
  metaFilesForViewedProgress = [];
25494
25528
  return;
25495
25529
  }
25496
25530
  setProjectName(meta.project || "");
25497
25531
  setProjectBranch(meta.branch || "");
25498
25532
  metaFilesForViewedProgress = meta.files || [];
25499
- el.innerHTML = "";
25533
+ el2.innerHTML = "";
25500
25534
  const text3 = diffText();
25501
25535
  if (meta.error) {
25502
25536
  const error2 = document.createElement("span");
25503
25537
  error2.className = "chip chip-error";
25504
25538
  error2.textContent = meta.error;
25505
- el.appendChild(error2);
25539
+ el2.appendChild(error2);
25506
25540
  }
25507
25541
  if (meta.totals) {
25508
25542
  const files = document.createElement("span");
25509
25543
  files.className = "chip chip-files";
25510
25544
  files.textContent = text3.files(meta.totals.files);
25511
- el.appendChild(files);
25545
+ el2.appendChild(files);
25512
25546
  const add2 = document.createElement("span");
25513
25547
  add2.className = "chip chip-add";
25514
25548
  add2.textContent = `+${meta.totals.additions}`;
25515
- el.appendChild(add2);
25549
+ el2.appendChild(add2);
25516
25550
  const del = document.createElement("span");
25517
25551
  del.className = "chip chip-del";
25518
25552
  del.textContent = `−${meta.totals.deletions}`;
25519
- el.appendChild(del);
25553
+ el2.appendChild(del);
25520
25554
  }
25521
25555
  const kinds = summarizeDiffFileKinds(meta.files);
25522
25556
  const kindChip = (className, label, count) => {
@@ -25524,7 +25558,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25524
25558
  const chip = document.createElement("span");
25525
25559
  chip.className = `chip ${className}`;
25526
25560
  chip.textContent = `${count} ${label}`;
25527
- el.appendChild(chip);
25561
+ el2.appendChild(chip);
25528
25562
  };
25529
25563
  kindChip("chip-added", text3.kindAdded, kinds.added);
25530
25564
  kindChip("chip-deleted", text3.kindDeleted, kinds.deleted);
@@ -25542,7 +25576,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25542
25576
  viewedProgress.viewed,
25543
25577
  viewedProgress.total
25544
25578
  );
25545
- el.appendChild(viewed);
25579
+ el2.appendChild(viewed);
25546
25580
  const nextUnviewed = document.createElement("button");
25547
25581
  nextUnviewed.type = "button";
25548
25582
  nextUnviewed.className = "chip chip-next-unviewed";
@@ -25553,7 +25587,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25553
25587
  nextUnviewed.addEventListener("click", () => {
25554
25588
  scrollToNextUnviewedFile();
25555
25589
  });
25556
- el.appendChild(nextUnviewed);
25590
+ el2.appendChild(nextUnviewed);
25557
25591
  }
25558
25592
  const u2 = document.createElement("span");
25559
25593
  u2.className = "chip chip-updated";
@@ -25561,7 +25595,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25561
25595
  u2.textContent = text3.updated(
25562
25596
  (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour12: false })
25563
25597
  );
25564
- el.appendChild(u2);
25598
+ el2.appendChild(u2);
25565
25599
  }
25566
25600
  let SUPPRESS_SPY_UNTIL = 0;
25567
25601
  function prefetchByPath(path) {
@@ -25683,17 +25717,29 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25683
25717
  LOAD_QUEUE.length = 0;
25684
25718
  ACTIVE_LOADS = 0;
25685
25719
  }
25686
- function fileKey(f2) {
25720
+ function fileKey2(f2) {
25687
25721
  return f2.key || f2.path;
25688
25722
  }
25689
25723
  function computeListSignature(files) {
25690
25724
  return files.map(
25691
- (f2) => `${fileKey(f2)}\0${f2.path}\0${f2.old_path || ""}\0${f2.status || "M"}`
25725
+ (f2) => `${fileKey2(f2)}\0${f2.path}\0${f2.old_path || ""}\0${f2.status || "M"}`
25692
25726
  ).join("\n");
25693
25727
  }
25728
+ function stripLoadUrlGeneration(url) {
25729
+ const queryStart = url.indexOf("?");
25730
+ if (queryStart === -1) return url;
25731
+ const params = new URLSearchParams(url.slice(queryStart + 1));
25732
+ if (!params.has("generation")) return url;
25733
+ params.delete("generation");
25734
+ const s2 = params.toString();
25735
+ return s2 ? `${url.slice(0, queryStart)}?${s2}` : url.slice(0, queryStart);
25736
+ }
25737
+ function diffResponseSignature(data) {
25738
+ return JSON.stringify({ ...data, generation: void 0 });
25739
+ }
25694
25740
  function computeCardSignature(f2) {
25695
25741
  return [
25696
- fileKey(f2),
25742
+ fileKey2(f2),
25697
25743
  f2.status || "M",
25698
25744
  f2.additions || 0,
25699
25745
  f2.deletions || 0,
@@ -25702,12 +25748,18 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25702
25748
  f2.size_class || "small",
25703
25749
  f2.force_layout || "",
25704
25750
  f2.highlight ? 1 : 0,
25705
- f2.load_url,
25706
- f2.preview_url || "",
25751
+ stripLoadUrlGeneration(f2.load_url),
25752
+ f2.preview_url ? stripLoadUrlGeneration(f2.preview_url) : "",
25707
25753
  f2.estimated_height_px || 0,
25708
25754
  f2.untracked ? 1 : 0
25709
25755
  ].join("\0");
25710
25756
  }
25757
+ function cardNeedsLoad(card) {
25758
+ if (card.classList.contains("loading")) return false;
25759
+ if (card.dataset.staleLoading === "1") return false;
25760
+ if (card.classList.contains("loaded")) return card.dataset.stale === "1";
25761
+ return true;
25762
+ }
25711
25763
  function ensureLazyObserver() {
25712
25764
  if (!lazyObserver) {
25713
25765
  lazyObserver = new IntersectionObserver(
@@ -25715,8 +25767,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25715
25767
  for (const entry of entries) {
25716
25768
  if (!entry.isIntersecting) continue;
25717
25769
  const card = entry.target;
25718
- if (card.classList.contains("loaded") || card.classList.contains("loading"))
25719
- continue;
25770
+ if (!cardNeedsLoad(card)) continue;
25720
25771
  const f2 = card._file || STATE.files.find((x) => x.path === card.dataset.path);
25721
25772
  if (f2) enqueueLoad(f2, card, 0);
25722
25773
  }
@@ -25738,15 +25789,27 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25738
25789
  return false;
25739
25790
  if (!changedPathsCoverPath(changedPaths, file.path)) return false;
25740
25791
  card.dataset.reqId = String(++CLIENT_REQ_SEQ);
25792
+ delete card.dataset.stale;
25793
+ delete card.dataset.staleLoading;
25741
25794
  card.style.minHeight = `${measuredHeight ?? card.offsetHeight}px`;
25742
25795
  card.classList.remove("loaded", "loading", "error");
25743
25796
  card.classList.add("pending");
25744
25797
  card._diffData = null;
25798
+ card._loadedSig = null;
25799
+ card._loadedSigUrl = null;
25745
25800
  const indicator = card.querySelector(".loading-indicator");
25746
25801
  if (indicator) indicator.hidden = false;
25747
25802
  activatePendingCard(card, file);
25748
25803
  return true;
25749
25804
  }
25805
+ function revalidateCardSilently(card, file) {
25806
+ if (!card.classList.contains("loaded")) return false;
25807
+ card.dataset.reqId = String(++CLIENT_REQ_SEQ);
25808
+ card.dataset.stale = "1";
25809
+ card._file = file;
25810
+ activatePendingCard(card, file);
25811
+ return true;
25812
+ }
25750
25813
  function updateSidebarStats(files) {
25751
25814
  for (const f2 of files) {
25752
25815
  const li = document.querySelector(
@@ -25770,15 +25833,19 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25770
25833
  const tmp = createPlaceholder(file);
25771
25834
  while (tmp.firstChild) card.appendChild(tmp.firstChild);
25772
25835
  card.dataset.path = file.path;
25773
- card.dataset.key = fileKey(file);
25836
+ card.dataset.key = fileKey2(file);
25774
25837
  card.dataset.sizeClass = file.size_class || "small";
25775
25838
  card.dataset.status = file.status || "M";
25776
25839
  card.dataset.reqId = String(++CLIENT_REQ_SEQ);
25777
25840
  delete card.dataset.manualRendered;
25778
25841
  delete card.dataset.manualLoad;
25779
25842
  delete card.dataset.manualMode;
25843
+ delete card.dataset.stale;
25844
+ delete card.dataset.staleLoading;
25780
25845
  card.style.minHeight = `${file.estimated_height_px || 80}px`;
25781
25846
  card._diffData = null;
25847
+ card._loadedSig = null;
25848
+ card._loadedSigUrl = null;
25782
25849
  card._file = file;
25783
25850
  }
25784
25851
  function renderShell(meta, changedPaths) {
@@ -25793,7 +25860,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25793
25860
  renderMeta(meta);
25794
25861
  const target = getDiffRoot?.() || $("#diff");
25795
25862
  const empty = getEmptyPane?.() || $("#empty");
25796
- const expectedKeys = newFiles.map(fileKey);
25863
+ const expectedKeys = newFiles.map(fileKey2);
25797
25864
  const domIntact = isDiffShellDomIntact(target, expectedKeys);
25798
25865
  const sidebarNeedsRender = shouldRenderDiffSidebar(listSame, domIntact);
25799
25866
  if (!newFiles.length) {
@@ -25817,11 +25884,11 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25817
25884
  empty.classList.add("hidden");
25818
25885
  const newCardSigs = /* @__PURE__ */ new Map();
25819
25886
  for (const f2 of newFiles) {
25820
- newCardSigs.set(fileKey(f2), computeCardSignature(f2));
25887
+ newCardSigs.set(fileKey2(f2), computeCardSignature(f2));
25821
25888
  }
25822
25889
  const pathsUnknown = !changedPaths;
25823
25890
  const contentMayNeedReload = !listSame || pathsUnknown || (changedPaths ? newFiles.some((file) => {
25824
- const key = fileKey(file);
25891
+ const key = fileKey2(file);
25825
25892
  return prevCardSignatures.get(key) !== newCardSigs.get(key) || changedPathsCoverPath(changedPaths, file.path);
25826
25893
  }) : false);
25827
25894
  if (contentMayNeedReload) resetLoadScheduling();
@@ -25830,14 +25897,14 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25830
25897
  const cardsByKey = collectDiffCardsByKey(target);
25831
25898
  const measuredHeights = /* @__PURE__ */ new Map();
25832
25899
  for (const f2 of newFiles) {
25833
- const key = fileKey(f2);
25900
+ const key = fileKey2(f2);
25834
25901
  const card = cardsByKey.get(key);
25835
25902
  if (card && (card.classList.contains("loaded") || card.classList.contains("loading")))
25836
25903
  measuredHeights.set(key, card.offsetHeight);
25837
25904
  }
25838
25905
  let sidebarNeedsStatsUpdate = false;
25839
25906
  for (const f2 of newFiles) {
25840
- const key = fileKey(f2);
25907
+ const key = fileKey2(f2);
25841
25908
  const oldSig = prevCardSignatures.get(key);
25842
25909
  const newSig = newCardSigs.get(key) ?? computeCardSignature(f2);
25843
25910
  const sigChanged = oldSig !== newSig;
@@ -25854,23 +25921,37 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25854
25921
  activatePendingCard(card, f2);
25855
25922
  invalidatedCards++;
25856
25923
  sidebarNeedsStatsUpdate = true;
25857
- } else {
25924
+ } else if (sigChanged) {
25858
25925
  const stats = card.querySelector(".gdp-shell-header .stats");
25859
25926
  if (stats) {
25860
25927
  stats.innerHTML = '<span class="a">+' + (f2.additions || 0) + '</span><span class="d">−' + (f2.deletions || 0) + "</span>";
25861
25928
  }
25862
25929
  card._file = f2;
25863
- const didInvalidate = sigChanged ? invalidateLoadedCard(card, f2, null, measuredHeights.get(key)) : invalidateLoadedCard(
25930
+ const didInvalidate = invalidateLoadedCard(
25864
25931
  card,
25865
25932
  f2,
25866
- changedPaths,
25933
+ null,
25867
25934
  measuredHeights.get(key)
25868
25935
  );
25869
25936
  if (didInvalidate) invalidatedCards++;
25870
- else if ((sigChanged || pathHint) && card.classList.contains("pending")) {
25937
+ else if (card.classList.contains("pending")) {
25871
25938
  activatePendingCard(card, f2);
25872
25939
  }
25873
- if (sigChanged) sidebarNeedsStatsUpdate = true;
25940
+ sidebarNeedsStatsUpdate = true;
25941
+ } else {
25942
+ card._file = f2;
25943
+ if (!revalidateCardSilently(card, f2)) {
25944
+ const didInvalidate = invalidateLoadedCard(
25945
+ card,
25946
+ f2,
25947
+ changedPaths,
25948
+ measuredHeights.get(key)
25949
+ );
25950
+ if (didInvalidate) invalidatedCards++;
25951
+ else if (card.classList.contains("pending")) {
25952
+ activatePendingCard(card, f2);
25953
+ }
25954
+ }
25874
25955
  }
25875
25956
  }
25876
25957
  if (sidebarNeedsStatsUpdate && canUpdateSidebar)
@@ -25893,7 +25974,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25893
25974
  const oldByKey = collectDiffCardsByKey(target);
25894
25975
  const ordered = [];
25895
25976
  for (const f2 of newFiles) {
25896
- const key = fileKey(f2);
25977
+ const key = fileKey2(f2);
25897
25978
  const old = oldByKey.get(key);
25898
25979
  if (old) {
25899
25980
  oldByKey.delete(key);
@@ -25965,8 +26046,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
25965
26046
  entries.forEach((entry) => {
25966
26047
  if (!entry.isIntersecting) return;
25967
26048
  const card = entry.target;
25968
- if (card.classList.contains("loaded") || card.classList.contains("loading"))
25969
- return;
26049
+ if (!cardNeedsLoad(card)) return;
25970
26050
  const f2 = card._file || STATE.files.find((x) => x.path === card.dataset.path);
25971
26051
  if (!f2) return;
25972
26052
  enqueueLoad(f2, card, 0);
@@ -26007,8 +26087,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
26007
26087
  while (ACTIVE_LOADS < MAX_PARALLEL && LOAD_QUEUE.length) {
26008
26088
  const item = LOAD_QUEUE.shift();
26009
26089
  if (item.epoch !== LOAD_EPOCH) continue;
26010
- if (item.card.classList.contains("loaded") || item.card.classList.contains("loading"))
26011
- continue;
26090
+ if (!cardNeedsLoad(item.card)) continue;
26012
26091
  ACTIVE_LOADS++;
26013
26092
  loadFile(item.file, item.card).finally(() => {
26014
26093
  if (item.epoch === LOAD_EPOCH) {
@@ -26103,20 +26182,29 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
26103
26182
  });
26104
26183
  }
26105
26184
  function loadFile(file, card, urlOverride, options) {
26106
- card.classList.remove("pending");
26107
- card.classList.add("loading");
26108
- if (lazyObserver) lazyObserver.unobserve(card);
26185
+ const silent = card.classList.contains("loaded") && card.dataset.stale === "1";
26109
26186
  const indicator = card.querySelector(".loading-indicator");
26110
- if (indicator) indicator.hidden = false;
26187
+ if (silent) {
26188
+ card.dataset.staleLoading = "1";
26189
+ } else {
26190
+ card.classList.remove("pending");
26191
+ card.classList.add("loading");
26192
+ if (indicator) indicator.hidden = false;
26193
+ }
26194
+ if (lazyObserver) lazyObserver.unobserve(card);
26111
26195
  const url = urlOverride || (card.dataset.manualMode === "full" ? file.load_url : file.preview_url || file.load_url);
26112
26196
  const myGen = getServerGeneration();
26113
26197
  const myReq = ++CLIENT_REQ_SEQ;
26114
26198
  card.dataset.reqId = String(myReq);
26115
26199
  const retryStale = () => {
26116
26200
  if (String(myReq) !== card.dataset.reqId) return;
26117
- card.classList.remove("loading");
26118
- card.classList.add("pending");
26119
- if (indicator) indicator.hidden = true;
26201
+ if (silent) {
26202
+ delete card.dataset.staleLoading;
26203
+ } else {
26204
+ card.classList.remove("loading");
26205
+ card.classList.add("pending");
26206
+ if (indicator) indicator.hidden = true;
26207
+ }
26120
26208
  const fresh = card._file || STATE.files.find((x) => x.path === card.dataset.path);
26121
26209
  if (fresh && card.isConnected) enqueueLoad(fresh, card, 0);
26122
26210
  };
@@ -26135,11 +26223,32 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
26135
26223
  retryStale();
26136
26224
  return;
26137
26225
  }
26226
+ const strippedUrl = stripLoadUrlGeneration(url);
26227
+ if (silent) {
26228
+ const sig = diffResponseSignature(data);
26229
+ if (card._loadedSigUrl === strippedUrl && card._loadedSig === sig) {
26230
+ delete card.dataset.staleLoading;
26231
+ delete card.dataset.stale;
26232
+ return;
26233
+ }
26234
+ }
26138
26235
  if (!options?.immediate) await nextIdle();
26139
26236
  if (String(myReq) !== card.dataset.reqId) return;
26237
+ if (silent) {
26238
+ delete card.dataset.staleLoading;
26239
+ delete card.dataset.stale;
26240
+ }
26241
+ card._loadedSig = diffResponseSignature(data);
26242
+ card._loadedSigUrl = strippedUrl;
26140
26243
  renderFile(file, data, card);
26141
26244
  }).catch((error2) => {
26142
26245
  if (String(myReq) !== card.dataset.reqId) return;
26246
+ if (silent) {
26247
+ delete card.dataset.staleLoading;
26248
+ delete card.dataset.stale;
26249
+ console.error("[code-viewer] silent diff revalidation failed", error2);
26250
+ return;
26251
+ }
26143
26252
  console.error("[code-viewer] failed to load diff", error2);
26144
26253
  card.classList.remove("loading");
26145
26254
  card.classList.add("error");
@@ -26851,7 +26960,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
26851
26960
  const mount = getMount();
26852
26961
  return mount ? !mount.hidden : false;
26853
26962
  }
26854
- async function open() {
26963
+ async function open2() {
26855
26964
  const mount = getMount();
26856
26965
  if (!mount) return;
26857
26966
  bindOnce(mount);
@@ -26886,7 +26995,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
26886
26995
  if (!mount) return;
26887
26996
  await load(mount);
26888
26997
  }
26889
- return { open, close, isOpen, refresh };
26998
+ return { open: open2, close, isOpen, refresh };
26890
26999
  }
26891
27000
 
26892
27001
  // web-src/views/empty-diff-pane.ts
@@ -27372,12 +27481,12 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
27372
27481
  if (label) label.textContent = "";
27373
27482
  }
27374
27483
  function syncRefreshResult(result) {
27375
- const el = result ?? activeMount.refreshResult;
27376
- if (!el) return;
27377
- el.textContent = "";
27378
- el.hidden = true;
27379
- el.classList.toggle("changed", false);
27380
- el.classList.toggle("pending", false);
27484
+ const el2 = result ?? activeMount.refreshResult;
27485
+ if (!el2) return;
27486
+ el2.textContent = "";
27487
+ el2.hidden = true;
27488
+ el2.classList.toggle("changed", false);
27489
+ el2.classList.toggle("pending", false);
27381
27490
  }
27382
27491
  function syncFilterClearButton(button) {
27383
27492
  const clearButton = button ?? activeMount.filterClearButton;
@@ -27398,8 +27507,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
27398
27507
  }
27399
27508
  const gen = generation;
27400
27509
  const set2 = (sel, text3) => {
27401
- const el = info.querySelector(sel);
27402
- if (el) el.textContent = text3;
27510
+ const el2 = info.querySelector(sel);
27511
+ if (el2) el2.textContent = text3;
27403
27512
  };
27404
27513
  info.querySelector(".hci-head")?.removeAttribute("hidden");
27405
27514
  set2(".hci-sha", commit.sha);
@@ -27935,8 +28044,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
27935
28044
 
27936
28045
  // web-src/views/file-history-shell.ts
27937
28046
  function removeFileHistoryShell() {
27938
- document.querySelectorAll(".gdp-standalone-file-history").forEach((el) => {
27939
- (el.closest(".gdp-repo-blob-layout") || el).remove();
28047
+ document.querySelectorAll(".gdp-standalone-file-history").forEach((el2) => {
28048
+ (el2.closest(".gdp-repo-blob-layout") || el2).remove();
27940
28049
  });
27941
28050
  }
27942
28051
  function renderFileHistoryShell(deps, route, options = {}) {
@@ -28901,6 +29010,31 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
28901
29010
  }
28902
29011
  ]
28903
29012
  },
29013
+ {
29014
+ title: "Worktrees",
29015
+ blocks: [
29016
+ {
29017
+ kind: "paragraph",
29018
+ text: "The Worktrees item in the header menu lists every worktree of this repository — the main one and every linked worktree — in the same three-pane shape as History: pick a worktree on the left, its changed files in the middle, the diff on the right. Running one coding agent per worktree makes that spread invisible from inside a single checkout, and this screen is where it becomes visible again. The selection lives in the URL (?wt=…&file=…), so a reload comes back to the same diff."
29019
+ },
29020
+ {
29021
+ kind: "paragraph",
29022
+ text: `Every row in the left pane answers two things. How far the branch has drifted from the base branch (how many commits ahead and behind), and whether it still merges: code-viewer runs git merge-tree without touching any working tree, so a row says either that it merges cleanly, or how many files would conflict, or that the check could not run — the last one is kept separate from the first, because "not checked" is not "safe". The middle pane then splits that worktree's files into work that is not committed yet and commits made since the branch point, with added and deleted line counts; the diff itself is rendered by the same viewer the Diff screen uses, from git run inside that worktree.`
29023
+ },
29024
+ {
29025
+ kind: "paragraph",
29026
+ text: "The banner above the cards is the one to read first. It names every file that more than one worktree is currently changing, and the same files are marked inside each card. Two agents editing the same file is invisible until one of them merges; this is where it shows up before that happens. The base branch is whatever origin/HEAD points at, falling back to main and then master; each row names the one it used."
29027
+ },
29028
+ {
29029
+ kind: "paragraph",
29030
+ text: "Create makes a worktree under .worktrees/ in the repository root. You give it a folder name and the dialog shows the exact path it will create as you type; the branch defaults to that same name, so an existing branch is checked out and otherwise a new one starts from where you are. Because that folder sits inside the repository, git reports it as untracked until you add .worktrees/ to .gitignore — the dialog keeps that note behind a ? mark, and code-viewer never edits .gitignore for you. Remove deletes the worktree's folder from disk — the dialog says so, with the full path and a note that it cannot be undone — and keeps the branch and its committed work. With uncommitted changes the dialog also shows a warning and requires a checkbox before it deletes. An entry whose folder is already gone is cleaned from git's bookkeeping instead (git worktree prune), and the dialog says how many such entries will go. Because prune silently skips locked entries and still succeeds, code-viewer checks the entry is actually gone before reporting success and explains why if it is not. The worktree this server is serving cannot be removed from here. Delete sits on its own line, away from the everyday buttons, and stays muted until you reach for it."
29031
+ },
29032
+ {
29033
+ kind: "paragraph",
29034
+ text: 'Every row carries a "…" on the right, and every action for that worktree lives in it — no button appears just because you picked a row, and the row you act on is the row you opened the menu from, whether or not it is the one being shown. "Open in a new tab" starts a second code-viewer for that worktree, because a server stays on the working tree it started in; an already-running server is reused instead of started again. Those servers outlive this one, so "Stop its server" is in the same menu — the Server group in doctor still lists them all, and `kill <pid>` works too, with the pid in ~/.cache/code-viewer/servers/. A worktree that merges cleanly also offers to copy the `git switch` / `git merge` command; code-viewer copies it and never runs it. "Copy its address" is there because a new tab can be stopped by a browser extension before it loads (Chrome reports ERR_BLOCKED_BY_CLIENT) — the address still works in another browser or from a terminal. "Open folder" only appears for worktrees inside the repository, since that is the only place code-viewer will open on your behalf.'
29035
+ }
29036
+ ]
29037
+ },
28904
29038
  {
28905
29039
  title: "Environment doctor",
28906
29040
  blocks: [
@@ -29643,6 +29777,31 @@ code-viewer annotate add-db --db app.db --tab query \\
29643
29777
  }
29644
29778
  ]
29645
29779
  },
29780
+ {
29781
+ title: "作業ツリー",
29782
+ blocks: [
29783
+ {
29784
+ kind: "paragraph",
29785
+ text: "ヘッダメニューの「作業ツリー」は、このリポジトリの作業ツリーを本体ぶんも含めて並べます。画面の形は履歴と同じ 3 つの列で、左で作業ツリーを選び、中央にその変更ファイル、右に差分が出ます。コーディングエージェントを作業ツリーごとに走らせると、1 つのチェックアウトの中からは全体が見えなくなります。この画面はそれを見えるようにするためのものです。選んだものは URL に載る (?wt=…&file=…) ので、読み込み直しても同じ差分に戻ります。"
29786
+ },
29787
+ {
29788
+ kind: "paragraph",
29789
+ text: "左の 1 行が答えるのは 2 つです。基準ブランチからどれだけ離れているか(進んだぶん・遅れたぶんのコミット数)。そしてまだマージできるか — 作業ツリーを一切触らない git merge-tree で試し、「そのままマージできます」「マージすると何ファイルで衝突するか」「確かめられなかった」を出し分けます。最後のものは 1 番目と混ぜません。「確かめていない」は「安全」ではないからです。中央の列は、その作業ツリーが触っているファイルを、まだコミットしていないぶんと分岐した後のコミットに分けて、追加・削除の行数つきで出します。差分そのものは Diff ビューアと同じ描画で、その作業ツリーの中で走らせた git の結果です。"
29790
+ },
29791
+ {
29792
+ kind: "paragraph",
29793
+ text: "カードの上の帯を最初に読んでください。2 本以上の作業ツリーが今まさに触っているファイルを名指しで挙げ、同じファイルは各カードの中にも印が付きます。2 つのエージェントが同じファイルを書いていることは、どちらかをマージするまで表に出ません。ここはそれが起きる前に出る場所です。基準ブランチは origin/HEAD が指す先で、無ければ main、次に master を使います。どれを使ったかは各行に出ます。"
29794
+ },
29795
+ {
29796
+ kind: "paragraph",
29797
+ text: "「作る」はリポジトリ直下の .worktrees/ の下に作業ツリーを作ります。入力するのはフォルダ名で、打つそばから作られるパスがダイアログに出ます。ブランチは同じ名前が既定で、既にあるブランチならそれを開き、無ければいまの地点から新しく作ります。この置き場所はリポジトリの中なので、.worktrees/ を .gitignore に入れるまでは git から未追跡として見えます。その案内はダイアログの「?」に入れてあり、code-viewer が .gitignore を書き換えることはしません。「削除」はその作業ツリーのフォルダをディスクから消します。ダイアログにはフルパスと「元に戻せません」が出ます。ブランチとコミット済みの内容は残します。コミットしていない変更があるときは警告が出て、チェックを入れないと削除できません。フォルダが既に無い登録は git の管理情報の掃除 (git worktree prune) になり、同じ状態の登録が他にもあれば件数を出します。prune はロックされた登録を黙って飛ばして成功を返すので、消えたことを確かめてから成功と言います (残っていれば理由つきで断ります)。このサーバが映している作業ツリーはここからは消せません。削除だけは他の操作と段を分けて置いてあり、押すまでは沈んだ字で出ます。"
29798
+ },
29799
+ {
29800
+ kind: "paragraph",
29801
+ text: "各行の右端に「…」があり、その作業ツリーへの操作は全部その中にあります。行を選んだからといってボタンが現れることはなく、操作の相手は「…」を開いた行です(いま映している行である必要はありません)。「別タブで見る」はその作業ツリーで code-viewer をもう 1 本起こします。サーバは起動したときの作業ツリーから動けないためです。既に動いているサーバがあれば使い回します。起こしたサーバはこのサーバより長く生きるので、「サーバを止める」を同じメニューに置いてあります。doctor の Server グループには変わらず一覧が出ますし、`kill <pid>` でも止まります(pid は ~/.cache/code-viewer/servers/)。そのままマージできる作業ツリーには、取り込むコマンドのコピーも出ます。コピーするだけで、code-viewer が実行することはありません。「アドレスをコピー」があるのは、開いたタブがブラウザ拡張に止められることがあるためです(Chrome は ERR_BLOCKED_BY_CLIENT と出します)。アドレスさえ手元にあれば、別のブラウザやターミナルから開けます。「フォルダを開く」はリポジトリの中にある作業ツリーにだけ出ます。code-viewer が代わりに開くのはそこまでだからです。"
29802
+ }
29803
+ ]
29804
+ },
29646
29805
  {
29647
29806
  title: "環境ドクター",
29648
29807
  blocks: [
@@ -31810,13 +31969,13 @@ code-viewer annotate add-db --db app.db --tab query \\
31810
31969
  function clearTaskDropTargets() {
31811
31970
  root.querySelectorAll(
31812
31971
  ".journal-task-card.drop-target, .journal-task-column.drop-target"
31813
- ).forEach((el) => {
31814
- el.classList.remove("drop-target", "drop-after");
31972
+ ).forEach((el2) => {
31973
+ el2.classList.remove("drop-target", "drop-after");
31815
31974
  });
31816
31975
  }
31817
31976
  function clearTaskDragState() {
31818
- root.querySelectorAll(".journal-task-card.dragging").forEach((el) => {
31819
- el.classList.remove("dragging");
31977
+ root.querySelectorAll(".journal-task-card.dragging").forEach((el2) => {
31978
+ el2.classList.remove("dragging");
31820
31979
  });
31821
31980
  stopTaskDragAutoScroll();
31822
31981
  clearTaskDropTargets();
@@ -33110,7 +33269,7 @@ code-viewer annotate add-db --db app.db --tab query \\
33110
33269
  fullLink.textContent = text3.viewAll;
33111
33270
  settingsLink.textContent = text3.settings;
33112
33271
  }
33113
- function open() {
33272
+ function open2() {
33114
33273
  applyText();
33115
33274
  renderContent();
33116
33275
  popover.hidden = false;
@@ -33122,7 +33281,7 @@ code-viewer annotate add-db --db app.db --tab query \\
33122
33281
  }
33123
33282
  function toggle() {
33124
33283
  if (isOpen()) close();
33125
- else open();
33284
+ else open2();
33126
33285
  }
33127
33286
  trigger.addEventListener("click", toggle);
33128
33287
  closeBtn.addEventListener("click", close);
@@ -33152,7 +33311,7 @@ code-viewer annotate add-db --db app.db --tab query \\
33152
33311
  applyText();
33153
33312
  if (isOpen()) renderContent();
33154
33313
  }
33155
- return { open, close, toggle, isOpen, localize };
33314
+ return { open: open2, close, toggle, isOpen, localize };
33156
33315
  }
33157
33316
 
33158
33317
  // web-src/views/ref-picker.ts
@@ -33669,9 +33828,9 @@ code-viewer annotate add-db --db app.db --tab query \\
33669
33828
  syncSidebarHeaderHeight();
33670
33829
  }
33671
33830
  let SIDEBAR_FILES = [];
33672
- function setFolderIcon(el, collapsed) {
33831
+ function setFolderIcon(el2, collapsed) {
33673
33832
  const path = collapsed ? FOLDER_ICON_PATHS.closed : FOLDER_ICON_PATHS.open;
33674
- el.innerHTML = '<svg class="octicon octicon-file-directory-' + (collapsed ? "fill" : "open-fill") + '" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true"><path fill="currentColor" d="' + path + '"></path></svg>';
33833
+ el2.innerHTML = '<svg class="octicon octicon-file-directory-' + (collapsed ? "fill" : "open-fill") + '" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true"><path fill="currentColor" d="' + path + '"></path></svg>';
33675
33834
  }
33676
33835
  function setSidebarTreeActionIcons() {
33677
33836
  const sidebarToggle = ensureSidebarToggleButton();
@@ -34281,13 +34440,13 @@ code-viewer annotate add-db --db app.db --tab query \\
34281
34440
  input2.toggleAttribute("aria-invalid", invalid);
34282
34441
  input2.title = invalid ? filter.error || "invalid regular expression" : "";
34283
34442
  const filterActive = filter.kind !== "empty" && !invalid;
34284
- const matches = invalid ? () => true : filter.match;
34443
+ const matches2 = invalid ? () => true : filter.match;
34285
34444
  const walk = (node, depth) => {
34286
34445
  let subtreeVisible = false;
34287
34446
  const rows = [];
34288
34447
  for (const item of treeNodeItems(node)) {
34289
34448
  if (item.kind === "dir") {
34290
- const dirMatches = filterActive && matches(item.dir.path);
34449
+ const dirMatches = filterActive && matches2(item.dir.path);
34291
34450
  const eff = dirEffectiveCollapsed(item.dir);
34292
34451
  const expanded = !item.dir.children_omitted && !eff.lazyNotLoaded && (filterActive ? !eff.userCollapsed : !eff.effectiveCollapsed);
34293
34452
  const child = walk(item.dir, depth + 1);
@@ -34305,7 +34464,7 @@ code-viewer annotate add-db --db app.db --tab query \\
34305
34464
  subtreeVisible = subtreeVisible || visible;
34306
34465
  } else {
34307
34466
  const testHidden = STATE.hideTests && !isRepositorySidebarMode() && isTestPath(item.file.path || "");
34308
- const visible = !testHidden && matches(item.file.path || "");
34467
+ const visible = !testHidden && matches2(item.file.path || "");
34309
34468
  if (visible) {
34310
34469
  rows.push({
34311
34470
  kind: "file",
@@ -34703,19 +34862,19 @@ code-viewer annotate add-db --db app.db --tab query \\
34703
34862
  const invalid = filter.kind === "invalid";
34704
34863
  input2.toggleAttribute("aria-invalid", invalid);
34705
34864
  input2.title = invalid ? filter.error || "invalid regular expression" : "";
34706
- const matches = invalid ? () => true : filter.match;
34865
+ const matches2 = invalid ? () => true : filter.match;
34707
34866
  const filterActive = filter.kind !== "empty" && !invalid;
34708
34867
  $$("#filelist li[data-path]").forEach((li) => {
34709
- const match2 = matches(li.dataset.path || "");
34868
+ const match2 = matches2(li.dataset.path || "");
34710
34869
  li.classList.toggle("hidden", !match2);
34711
34870
  });
34712
34871
  if (!isRepositorySidebarMode()) {
34713
34872
  document.querySelectorAll(".gdp-file-shell").forEach((card) => {
34714
- const match2 = matches(card.dataset.path || "");
34873
+ const match2 = matches2(card.dataset.path || "");
34715
34874
  card.classList.toggle("hidden-by-filter", !match2);
34716
34875
  });
34717
34876
  }
34718
- updateTreeDirVisibility(matches, filterActive);
34877
+ updateTreeDirVisibility(matches2, filterActive);
34719
34878
  if (!isRepositorySidebarMode() && typeof applyViewedState === "function")
34720
34879
  applyViewedState();
34721
34880
  }
@@ -34769,6 +34928,7 @@ code-viewer annotate add-db --db app.db --tab query \\
34769
34928
  const input2 = document.querySelector("#sb-filter");
34770
34929
  if (!input2?.value) return;
34771
34930
  input2.value = "";
34931
+ input2.dispatchEvent(new Event("input", { bubbles: true }));
34772
34932
  syncSidebarFilterClearButton();
34773
34933
  flushSidebarFilter();
34774
34934
  applyFilter();
@@ -36012,6 +36172,7 @@ code-viewer annotate add-db --db app.db --tab query \\
36012
36172
  async function loadRawFileInfo(target) {
36013
36173
  try {
36014
36174
  const res = await fetch(buildRawFileUrl(target), { method: "HEAD" });
36175
+ if (res.status === 404) return { missing: true };
36015
36176
  if (!res.ok) return {};
36016
36177
  const rawSize = res.headers.get("content-length");
36017
36178
  const size = rawSize == null ? NaN : Number(rawSize);
@@ -37242,11 +37403,11 @@ code-viewer annotate add-db --db app.db --tab query \\
37242
37403
  const grouped = /* @__PURE__ */ new Map();
37243
37404
  state.items.forEach((item, index) => {
37244
37405
  if (item.kind !== "grep") return;
37245
- const matches = grouped.get(item.path) ?? [];
37246
- matches.push({ item, index });
37247
- grouped.set(item.path, matches);
37406
+ const matches2 = grouped.get(item.path) ?? [];
37407
+ matches2.push({ item, index });
37408
+ grouped.set(item.path, matches2);
37248
37409
  });
37249
- for (const [path, matches] of grouped) {
37410
+ for (const [path, matches2] of grouped) {
37250
37411
  const group = document.createElement("section");
37251
37412
  group.className = "gdp-palette-file-group";
37252
37413
  group.setAttribute("role", "group");
@@ -37259,11 +37420,11 @@ code-viewer annotate add-db --db app.db --tab query \\
37259
37420
  name.title = path;
37260
37421
  const count = document.createElement("span");
37261
37422
  count.className = "gdp-palette-file-heading-count";
37262
- count.textContent = String(matches.length);
37423
+ count.textContent = String(matches2.length);
37263
37424
  heading2.append(name, count);
37264
37425
  const rows = document.createElement("div");
37265
37426
  rows.className = "gdp-palette-file-matches";
37266
- for (const match2 of matches) {
37427
+ for (const match2 of matches2) {
37267
37428
  rows.appendChild(
37268
37429
  createPaletteRow(state, match2.item, match2.index, true)
37269
37430
  );
@@ -38028,11 +38189,11 @@ code-viewer annotate add-db --db app.db --tab query \\
38028
38189
  return STATE.route.screen === "file" ? { path: STATE.route.path, ref: STATE.route.ref } : null;
38029
38190
  }
38030
38191
  function removeStandaloneSource() {
38031
- document.querySelectorAll(".gdp-standalone-source").forEach((el) => {
38032
- el.remove();
38192
+ document.querySelectorAll(".gdp-standalone-source").forEach((el2) => {
38193
+ el2.remove();
38033
38194
  });
38034
- document.querySelectorAll(".gdp-repo-blob-layout").forEach((el) => {
38035
- el.remove();
38195
+ document.querySelectorAll(".gdp-repo-blob-layout").forEach((el2) => {
38196
+ el2.remove();
38036
38197
  });
38037
38198
  }
38038
38199
  function renderSourceLoading(card, target, onCancel) {
@@ -38598,17 +38759,17 @@ code-viewer annotate add-db --db app.db --tab query \\
38598
38759
  return ranges;
38599
38760
  }
38600
38761
  function collectVirtualSourceSearchMatches(lines, query, max = 5e3) {
38601
- const matches = [];
38602
- for (let index = 0; index < lines.length && matches.length < max; index++) {
38762
+ const matches2 = [];
38763
+ for (let index = 0; index < lines.length && matches2.length < max; index++) {
38603
38764
  for (const range of virtualSourceSearchRanges(
38604
38765
  lines[index] || "",
38605
38766
  query
38606
38767
  )) {
38607
- matches.push({ line: index + 1, start: range.start, end: range.end });
38608
- if (matches.length >= max) break;
38768
+ matches2.push({ line: index + 1, start: range.start, end: range.end });
38769
+ if (matches2.length >= max) break;
38609
38770
  }
38610
38771
  }
38611
- return matches;
38772
+ return matches2;
38612
38773
  }
38613
38774
  function appendVirtualSourceLineCode(code2, line, query, activeRange, lineNumber) {
38614
38775
  const ranges = virtualSourceSearchRanges(line, query);
@@ -38652,7 +38813,7 @@ code-viewer annotate add-db --db app.db --tab query \\
38652
38813
  bar.append(input2, count, previous, next, close);
38653
38814
  wrap.querySelector(".gdp-source-virtual-info")?.appendChild(bar);
38654
38815
  bar.hidden = true;
38655
- let matches = [];
38816
+ let matches2 = [];
38656
38817
  let active = -1;
38657
38818
  let debounce = 0;
38658
38819
  let searchVersion = 0;
@@ -38665,7 +38826,7 @@ code-viewer annotate add-db --db app.db --tab query \\
38665
38826
  const query = input2.value;
38666
38827
  const version = ++searchVersion;
38667
38828
  if (!query) {
38668
- matches = [];
38829
+ matches2 = [];
38669
38830
  active = -1;
38670
38831
  count.textContent = "";
38671
38832
  renderFn();
@@ -38674,20 +38835,20 @@ code-viewer annotate add-db --db app.db --tab query \\
38674
38835
  count.textContent = "Searching...";
38675
38836
  findMatches(query).then((nextMatches) => {
38676
38837
  if (version !== searchVersion) return;
38677
- matches = nextMatches;
38678
- active = matches.length ? Math.max(0, Math.min(active, matches.length - 1)) : -1;
38679
- count.textContent = matches.length ? `${active + 1} / ${matches.length}` : "0 / 0";
38838
+ matches2 = nextMatches;
38839
+ active = matches2.length ? Math.max(0, Math.min(active, matches2.length - 1)) : -1;
38840
+ count.textContent = matches2.length ? `${active + 1} / ${matches2.length}` : "0 / 0";
38680
38841
  if (active >= 0) {
38681
38842
  const rowHeight = sourceLineScrollAmount() || VIRTUAL_SOURCE_ROW_HEIGHT;
38682
38843
  scroller.scrollTop = Math.max(
38683
38844
  0,
38684
- (matches[active].line - 1) * rowHeight - rowHeight * 3
38845
+ (matches2[active].line - 1) * rowHeight - rowHeight * 3
38685
38846
  );
38686
38847
  }
38687
38848
  renderFn();
38688
38849
  }).catch(() => {
38689
38850
  if (version !== searchVersion) return;
38690
- matches = [];
38851
+ matches2 = [];
38691
38852
  active = -1;
38692
38853
  count.textContent = "Search failed";
38693
38854
  renderFn();
@@ -38698,13 +38859,13 @@ code-viewer annotate add-db --db app.db --tab query \\
38698
38859
  debounce = window.setTimeout(sync, 120);
38699
38860
  };
38700
38861
  const move = (direction) => {
38701
- if (!matches.length) return;
38702
- active = (active + direction + matches.length) % matches.length;
38703
- count.textContent = `${active + 1} / ${matches.length}`;
38862
+ if (!matches2.length) return;
38863
+ active = (active + direction + matches2.length) % matches2.length;
38864
+ count.textContent = `${active + 1} / ${matches2.length}`;
38704
38865
  const rowHeight = sourceLineScrollAmount() || VIRTUAL_SOURCE_ROW_HEIGHT;
38705
38866
  scroller.scrollTop = Math.max(
38706
38867
  0,
38707
- (matches[active].line - 1) * rowHeight - rowHeight * 3
38868
+ (matches2[active].line - 1) * rowHeight - rowHeight * 3
38708
38869
  );
38709
38870
  renderFn();
38710
38871
  };
@@ -38733,7 +38894,7 @@ code-viewer annotate add-db --db app.db --tab query \\
38733
38894
  sync();
38734
38895
  },
38735
38896
  query: () => bar.hidden ? "" : input2.value,
38736
- activeRange: () => active >= 0 ? matches[active] || null : null
38897
+ activeRange: () => active >= 0 ? matches2[active] || null : null
38737
38898
  };
38738
38899
  }
38739
38900
  function openVirtualSourceSearchFromKeyboard(targetEl) {
@@ -39100,10 +39261,10 @@ code-viewer annotate add-db --db app.db --tab query \\
39100
39261
  scroller.__gdpRenderVirtualSource = render;
39101
39262
  scroller.addEventListener("scroll", schedule, { passive: true });
39102
39263
  const findPagedMatches = async (query, matchSignal) => {
39103
- const matches = [];
39264
+ const matches2 = [];
39104
39265
  let startLine = 1;
39105
39266
  let done = false;
39106
- while (!done && matches.length < 5e3) {
39267
+ while (!done && matches2.length < 5e3) {
39107
39268
  const endLine = startLine + VIRTUAL_SOURCE_PAGE_SIZE - 1;
39108
39269
  const data = await trackLoad(
39109
39270
  fetch(buildFileRangeUrl(target, startLine, endLine), {
@@ -39118,12 +39279,12 @@ code-viewer annotate add-db --db app.db --tab query \\
39118
39279
  const lineNumber = data.start + index;
39119
39280
  lines.set(lineNumber, lineValue);
39120
39281
  for (const range of virtualSourceSearchRanges(lineValue, query)) {
39121
- matches.push({
39282
+ matches2.push({
39122
39283
  line: lineNumber,
39123
39284
  start: range.start,
39124
39285
  end: range.end
39125
39286
  });
39126
- if (matches.length >= 5e3) break;
39287
+ if (matches2.length >= 5e3) break;
39127
39288
  }
39128
39289
  });
39129
39290
  totalRows = data.complete ? Math.max(1, data.total) : Math.max(totalRows, data.total, endLine + VIRTUAL_SOURCE_PAGE_SIZE);
@@ -39135,7 +39296,7 @@ code-viewer annotate add-db --db app.db --tab query \\
39135
39296
  renderedStart = -1;
39136
39297
  renderedEnd = -1;
39137
39298
  schedule();
39138
- return matches;
39299
+ return matches2;
39139
39300
  };
39140
39301
  search = createVirtualSourceSearch(
39141
39302
  wrap,
@@ -39313,8 +39474,8 @@ code-viewer annotate add-db --db app.db --tab query \\
39313
39474
  const repoTarget = repoFileTargetFromRoute();
39314
39475
  setPageMode();
39315
39476
  removeStandaloneSource();
39316
- document.querySelectorAll(".gdp-repo-blob-layout").forEach((el) => {
39317
- el.remove();
39477
+ document.querySelectorAll(".gdp-repo-blob-layout").forEach((el2) => {
39478
+ el2.remove();
39318
39479
  });
39319
39480
  const card = document.createElement("article");
39320
39481
  card.className = "gdp-file-shell loaded gdp-standalone-source gdp-source-mode";
@@ -39785,13 +39946,13 @@ code-viewer annotate add-db --db app.db --tab query \\
39785
39946
  }
39786
39947
 
39787
39948
  // web-src/core/scroll-chaining.ts
39788
- function hasScrollRoom(el, event) {
39789
- const up = event.deltaY < 0 && el.scrollTop > 1;
39790
- const down = event.deltaY > 0 && el.scrollHeight - el.clientHeight - el.scrollTop > 1;
39791
- const left = event.deltaX < 0 && el.scrollLeft > 1;
39792
- const right = event.deltaX > 0 && el.scrollWidth - el.clientWidth - el.scrollLeft > 1;
39949
+ function hasScrollRoom(el2, event) {
39950
+ const up = event.deltaY < 0 && el2.scrollTop > 1;
39951
+ const down = event.deltaY > 0 && el2.scrollHeight - el2.clientHeight - el2.scrollTop > 1;
39952
+ const left = event.deltaX < 0 && el2.scrollLeft > 1;
39953
+ const right = event.deltaX > 0 && el2.scrollWidth - el2.clientWidth - el2.scrollLeft > 1;
39793
39954
  if (!up && !down && !left && !right) return false;
39794
- const style = getComputedStyle(el);
39955
+ const style = getComputedStyle(el2);
39795
39956
  const scrollsY = style.overflowY === "auto" || style.overflowY === "scroll";
39796
39957
  const scrollsX = style.overflowX === "auto" || style.overflowX === "scroll";
39797
39958
  return (up || down) && scrollsY || (left || right) && scrollsX;
@@ -39999,8 +40160,8 @@ code-viewer annotate add-db --db app.db --tab query \\
39999
40160
  return parts.join(BRANCH_KEY_SEP);
40000
40161
  }
40001
40162
  function createSessionBoard(deps) {
40002
- const el = document.createElement("div");
40003
- el.className = "terminal-board";
40163
+ const el2 = document.createElement("div");
40164
+ el2.className = "terminal-board";
40004
40165
  const scopeBar = document.createElement("div");
40005
40166
  scopeBar.className = "terminal-scope";
40006
40167
  const scopeSelect = document.createElement("select");
@@ -40043,7 +40204,7 @@ code-viewer annotate add-db --db app.db --tab query \\
40043
40204
  tree.role = "tree";
40044
40205
  const treeEmpty = document.createElement("p");
40045
40206
  treeEmpty.className = "terminal-empty";
40046
- el.append(scopeBar, observationErrors, attentionSection, filters, tree);
40207
+ el2.append(scopeBar, observationErrors, attentionSection, filters, tree);
40047
40208
  let data = {
40048
40209
  panes: null,
40049
40210
  shells: [],
@@ -40116,12 +40277,12 @@ code-viewer annotate add-db --db app.db --tab query \\
40116
40277
  }
40117
40278
  const actions = document.createElement("div");
40118
40279
  actions.className = "terminal-card-actions";
40119
- const open = document.createElement("button");
40120
- open.type = "button";
40121
- open.className = `terminal-card-open terminal-card-open-${row.state}`;
40122
- open.textContent = text3.openTarget;
40123
- open.addEventListener("click", () => activate(row));
40124
- actions.appendChild(open);
40280
+ const open2 = document.createElement("button");
40281
+ open2.type = "button";
40282
+ open2.className = `terminal-card-open terminal-card-open-${row.state}`;
40283
+ open2.textContent = text3.openTarget;
40284
+ open2.addEventListener("click", () => activate(row));
40285
+ actions.appendChild(open2);
40125
40286
  if (row.state === "done") {
40126
40287
  const read = document.createElement("button");
40127
40288
  read.type = "button";
@@ -40415,7 +40576,7 @@ ${error2.stack}` : ""}`;
40415
40576
  }
40416
40577
  render();
40417
40578
  return {
40418
- el,
40579
+ el: el2,
40419
40580
  setData(next) {
40420
40581
  data = next;
40421
40582
  render();
@@ -40471,17 +40632,17 @@ ${error2.stack}` : ""}`;
40471
40632
  previouslyFocused?.focus?.();
40472
40633
  };
40473
40634
  const button = (label, title2, onClick) => {
40474
- const el = document.createElement("button");
40475
- el.type = "button";
40476
- el.className = "terminal-font-btn";
40477
- el.textContent = label;
40478
- el.title = title2;
40479
- el.setAttribute("aria-label", title2);
40480
- el.addEventListener("click", (event) => {
40635
+ const el2 = document.createElement("button");
40636
+ el2.type = "button";
40637
+ el2.className = "terminal-font-btn";
40638
+ el2.textContent = label;
40639
+ el2.title = title2;
40640
+ el2.setAttribute("aria-label", title2);
40641
+ el2.addEventListener("click", (event) => {
40481
40642
  event.stopPropagation();
40482
40643
  onClick();
40483
40644
  });
40484
- return el;
40645
+ return el2;
40485
40646
  };
40486
40647
  zoomReset = button("100%", text3.zoomReset, () => viewport.reset());
40487
40648
  actions.append(
@@ -40536,14 +40697,14 @@ ${error2.stack}` : ""}`;
40536
40697
  var INLINE_IMAGE_COLS = 30;
40537
40698
  var INLINE_IMAGE_ROWS = 8;
40538
40699
  function createTerminalScreen(deps) {
40539
- const el = document.createElement("div");
40540
- el.className = "terminal-pane-body";
40700
+ const el2 = document.createElement("div");
40701
+ el2.className = "terminal-pane-body";
40541
40702
  const attachments = document.createElement("div");
40542
40703
  attachments.className = "terminal-attachments";
40543
40704
  attachments.hidden = true;
40544
40705
  const screenEl = document.createElement("div");
40545
40706
  screenEl.className = "terminal-screen";
40546
- el.append(attachments, screenEl);
40707
+ el2.append(attachments, screenEl);
40547
40708
  let term = null;
40548
40709
  let fitAddon = null;
40549
40710
  let source = null;
@@ -40664,13 +40825,13 @@ ${formatErrorDetail(error2)}`
40664
40825
  function addAttachment(src, name, path) {
40665
40826
  const item = document.createElement("div");
40666
40827
  item.className = "terminal-attachment";
40667
- const open = document.createElement("button");
40668
- open.type = "button";
40669
- open.className = "terminal-attachment-open";
40828
+ const open2 = document.createElement("button");
40829
+ open2.type = "button";
40830
+ open2.className = "terminal-attachment-open";
40670
40831
  const openLabel = deps.getText().openImage;
40671
- open.title = path;
40672
- open.setAttribute("aria-label", `${openLabel}: ${name}`);
40673
- open.addEventListener("click", () => {
40832
+ open2.title = path;
40833
+ open2.setAttribute("aria-label", `${openLabel}: ${name}`);
40834
+ open2.addEventListener("click", () => {
40674
40835
  openImageLightbox({ url: src, name, path }, deps.getText());
40675
40836
  });
40676
40837
  const image2 = document.createElement("img");
@@ -40688,7 +40849,7 @@ ${formatErrorDetail(error2)}`
40688
40849
  );
40689
40850
  }
40690
40851
  });
40691
- open.appendChild(image2);
40852
+ open2.appendChild(image2);
40692
40853
  const label = document.createElement("span");
40693
40854
  label.className = "terminal-attachment-name";
40694
40855
  label.textContent = name;
@@ -40703,7 +40864,7 @@ ${formatErrorDetail(error2)}`
40703
40864
  item.remove();
40704
40865
  attachments.hidden = attachments.childElementCount === 0;
40705
40866
  });
40706
- item.append(open, label, remove);
40867
+ item.append(open2, label, remove);
40707
40868
  attachments.appendChild(item);
40708
40869
  attachments.hidden = false;
40709
40870
  }
@@ -40928,7 +41089,7 @@ ${formatErrorDetail(error2)}`
40928
41089
  }
40929
41090
  return null;
40930
41091
  }
40931
- el.addEventListener(
41092
+ el2.addEventListener(
40932
41093
  "paste",
40933
41094
  (event) => {
40934
41095
  const file = firstImage(event.clipboardData);
@@ -41080,7 +41241,7 @@ ${formatErrorDetail(error2)}`
41080
41241
  deps.onStatus(null);
41081
41242
  }
41082
41243
  return {
41083
- el,
41244
+ el: el2,
41084
41245
  applyFontSize() {
41085
41246
  if (!term) return;
41086
41247
  term.options.fontSize = deps.getFontSize();
@@ -41562,7 +41723,7 @@ ${formatErrorDetail(error2)}`);
41562
41723
  const host = getMount();
41563
41724
  return host ? !host.hidden : false;
41564
41725
  }
41565
- async function open(id) {
41726
+ async function open2(id) {
41566
41727
  const host = getMount();
41567
41728
  if (!host || disposed) return;
41568
41729
  const myGen = ++generation;
@@ -41621,7 +41782,7 @@ ${formatErrorDetail(error2)}`);
41621
41782
  renderLists();
41622
41783
  }
41623
41784
  return {
41624
- open,
41785
+ open: open2,
41625
41786
  close,
41626
41787
  isOpen,
41627
41788
  getActiveTarget: () => attached?.id ?? lastTargetId,
@@ -41772,8 +41933,8 @@ ${formatErrorDetail(error2)}`);
41772
41933
  }
41773
41934
  function createScratchpadPane(text3, outputTitle, placeholder, options) {
41774
41935
  let currentText = text3;
41775
- const el = document.createElement("div");
41776
- el.className = `tools-pane ${options.toolClassName}`;
41936
+ const el2 = document.createElement("div");
41937
+ el2.className = `tools-pane ${options.toolClassName}`;
41777
41938
  const inputSide = document.createElement("section");
41778
41939
  inputSide.className = "tools-pane-side tools-pane-input";
41779
41940
  const { head: inputHead, titleEl: inputTitle } = createHead(text3.pane.input);
@@ -41808,7 +41969,7 @@ ${formatErrorDetail(error2)}`);
41808
41969
  const output = document.createElement("div");
41809
41970
  output.className = "tools-pane-body";
41810
41971
  outputSide.append(outputHead, output);
41811
- el.append(inputSide, resizer, outputSide);
41972
+ el2.append(inputSide, resizer, outputSide);
41812
41973
  let statusTimer = null;
41813
41974
  let pinnedStatus = null;
41814
41975
  let normalStatus = { message: "", tone: "info" };
@@ -41888,21 +42049,21 @@ ${formatErrorDetail(error2)}`);
41888
42049
  });
41889
42050
  const MIN_SIDE_WIDTH = 220;
41890
42051
  function applyInputWidth(width) {
41891
- const total = el.getBoundingClientRect().width;
42052
+ const total = el2.getBoundingClientRect().width;
41892
42053
  const max = Math.max(MIN_SIDE_WIDTH, total - MIN_SIDE_WIDTH);
41893
42054
  const clamped = Math.max(MIN_SIDE_WIDTH, Math.min(max, width));
41894
- el.style.setProperty("--tools-input-width", `${Math.round(clamped)}px`);
42055
+ el2.style.setProperty("--tools-input-width", `${Math.round(clamped)}px`);
41895
42056
  }
41896
42057
  const detachResizer = attachDragResizer({
41897
42058
  handle: resizer,
41898
42059
  getSize: () => inputSide.getBoundingClientRect().width,
41899
42060
  applySize: applyInputWidth,
41900
42061
  direction: 1,
41901
- activeClassTarget: el,
42062
+ activeClassTarget: el2,
41902
42063
  activeClassName: "tools-pane-resizing"
41903
42064
  });
41904
42065
  return {
41905
- el,
42066
+ el: el2,
41906
42067
  input: input2,
41907
42068
  output,
41908
42069
  setStatus,
@@ -42426,7 +42587,7 @@ ${formatErrorDetail(error2)}`);
42426
42587
  const host = getMount();
42427
42588
  return host ? !host.hidden : false;
42428
42589
  }
42429
- async function open(tool) {
42590
+ async function open2(tool) {
42430
42591
  const host = getMount();
42431
42592
  if (!host) return;
42432
42593
  const myGen = ++generation;
@@ -42483,7 +42644,7 @@ ${formatErrorDetail(error2)}`);
42483
42644
  };
42484
42645
  window.addEventListener("beforeunload", onBeforeUnload);
42485
42646
  return {
42486
- open,
42647
+ open: open2,
42487
42648
  close,
42488
42649
  isOpen,
42489
42650
  getActiveTool: () => activeTool,
@@ -43137,6 +43298,1704 @@ ${formatErrorDetail(error2)}`);
43137
43298
  return { mount, sync, localize };
43138
43299
  }
43139
43300
 
43301
+ // web-src/views/worktree-i18n.ts
43302
+ var EN_STATUS_TITLES = {
43303
+ M: "modified",
43304
+ A: "added",
43305
+ D: "deleted",
43306
+ R: "renamed",
43307
+ C: "copied",
43308
+ U: "untracked"
43309
+ };
43310
+ var JA_STATUS_TITLES = {
43311
+ M: "変更",
43312
+ A: "追加",
43313
+ D: "削除",
43314
+ R: "リネーム",
43315
+ C: "コピー",
43316
+ U: "未追跡"
43317
+ };
43318
+ var TEXT3 = {
43319
+ en: {
43320
+ title: "Worktrees",
43321
+ ariaLabel: "Git worktrees",
43322
+ loading: "Loading worktrees…",
43323
+ loadFailed: "Failed to load worktrees.",
43324
+ empty: "No worktrees.",
43325
+ refresh: "Reload",
43326
+ refreshTitle: "Reload the worktree list",
43327
+ count: (n2) => n2 === 1 ? "1 worktree" : `${n2} worktrees`,
43328
+ baseUnknown: "no base branch found",
43329
+ gitignoreHint: "Git sees that directory as untracked. Add .worktrees/ to .gitignore to keep it out of git status.",
43330
+ badges: {
43331
+ current: "this folder",
43332
+ currentTitle: "This code-viewer is serving this folder",
43333
+ detached: "no branch",
43334
+ detachedTitle: "Not attached to any branch (detached HEAD)",
43335
+ bare: "bare",
43336
+ bareTitle: "A storage-only repository with no working files",
43337
+ locked: "locked",
43338
+ prunable: "stale entry",
43339
+ prunableTitle: "Only the git entry remains (e.g. the folder is gone)",
43340
+ missing: "folder is gone",
43341
+ running: "running",
43342
+ runningTitle: "A code-viewer for this folder is running on another port"
43343
+ },
43344
+ diverge: {
43345
+ ahead: (n2, base2) => n2 === 1 ? `1 commit ahead of ${base2}` : `${n2} commits ahead of ${base2}`,
43346
+ behind: (n2, base2) => n2 === 1 ? `1 commit behind ${base2}` : `${n2} commits behind ${base2}`,
43347
+ even: (base2) => `same point as ${base2}`,
43348
+ notComparable: "not compared"
43349
+ },
43350
+ merge: {
43351
+ clean: "merges cleanly",
43352
+ conflict: (n2) => n2 === 1 ? "1 file would conflict on merge" : `${n2} files would conflict on merge`,
43353
+ unknown: "could not check the merge",
43354
+ conflictListLabel: "Conflicting files"
43355
+ },
43356
+ files: {
43357
+ heading: (n2) => n2 === 1 ? "1 changed file" : `${n2} changed files`,
43358
+ none: "No changes.",
43359
+ uncommitted: "Not committed yet",
43360
+ committed: "Committed, not merged",
43361
+ truncated: (shown, total) => `showing ${shown} of ${total}`,
43362
+ // 行の中に置くので短くする。誰と重なっているかは title に入る。
43363
+ overlapMark: "shared",
43364
+ overlapTitle: (others) => `Also changed in: ${others.join(", ")}`,
43365
+ statusTitles: EN_STATUS_TITLES
43366
+ },
43367
+ overlaps: {
43368
+ heading: (n2) => n2 === 1 ? "1 file is touched by more than one worktree" : `${n2} files are touched by more than one worktree`,
43369
+ intro: "Merging one of them will make the others conflict on these files.",
43370
+ entry: (path, worktrees) => `${path} — ${worktrees.join(", ")}`
43371
+ },
43372
+ panes: {
43373
+ worktrees: "Worktrees",
43374
+ files: "Files",
43375
+ diff: "Diff",
43376
+ filterWorktrees: "Filter worktrees…",
43377
+ filterFiles: "Filter files…",
43378
+ noWorktreeMatch: "No worktree matches.",
43379
+ noFileMatch: "No file matches.",
43380
+ selectWorktree: "Pick a worktree to see what it is changing.",
43381
+ selectFile: "Pick a file to see its diff.",
43382
+ diffLoading: "Loading the diff…",
43383
+ diffEmpty: "No textual diff (binary, or the file is unchanged).",
43384
+ diffFailed: "Failed to load the diff.",
43385
+ diffTruncated: (shown, total) => `showing ${shown} of ${total} hunks — open the worktree in a new tab for the whole file`
43386
+ },
43387
+ noCommit: "no commit",
43388
+ lastTouched: (when) => `updated ${when}`,
43389
+ actions: {
43390
+ menuTitle: "Actions for this worktree",
43391
+ menuFor: (name) => `Actions for ${name}`,
43392
+ openFolder: "Open folder",
43393
+ openFolderTitle: "Open this folder in the OS file manager",
43394
+ openFolderFailed: "Could not open that folder.",
43395
+ copyPath: "Copy path",
43396
+ copyPathTitle: "Copy the path to this folder",
43397
+ copyFailed: "Could not copy to the clipboard.",
43398
+ stopServer: "Stop its server",
43399
+ stopServerTitle: "Stop the code-viewer running for this folder",
43400
+ stopFailed: "Failed to stop the server.",
43401
+ copyServerUrl: "Copy its address",
43402
+ copyServerUrlTitle: (url) => `Copy ${url}`,
43403
+ copyMerge: "Copy merge command",
43404
+ copyMergeTitle: (base2, branch) => `Copy the command that merges ${branch} into ${base2}`
43405
+ },
43406
+ emptyDiff: {
43407
+ title: "No changes yet.",
43408
+ body: (path) => `Edit files in ${path} and the diffs will show up here.`
43409
+ },
43410
+ intro: {
43411
+ cardTitle: "What is a worktree?",
43412
+ cardWhat: "A worktree (git worktree) is a second checkout of this repository, in a separate folder on a separate branch.",
43413
+ cardWhy: "Keep main open here while a bugfix branch stays open over there — no branch switching, no stashing.",
43414
+ cardHow: 'When you click "Create": a new folder appears at .worktrees/<name>, with the branch checked out in it. Edit files there as usual. The changes show up on this screen as diffs.',
43415
+ cardButton: "Create a worktree",
43416
+ listNote: "This is the only folder right now. New worktrees will appear here.",
43417
+ overlapLegend: "⇄ = another worktree is changing this file too"
43418
+ },
43419
+ open: "Open in a new tab",
43420
+ openTitle: "Start a code-viewer for this folder and open it in a new tab",
43421
+ opening: "Starting…",
43422
+ openFailed: "Failed to open this worktree.",
43423
+ add: "Create",
43424
+ addTitle: "Create a new worktree",
43425
+ addFailed: "Failed to create the worktree.",
43426
+ addDialog: {
43427
+ title: "Create a worktree",
43428
+ intro: "Work on two things side by side, in separate folders, without switching branches.",
43429
+ nameLabel: "Folder name",
43430
+ namePlaceholder: "feature-x",
43431
+ branchLabel: "Branch",
43432
+ branchPlaceholder: "same as the folder name",
43433
+ branchHint: "An existing branch name opens that branch. Otherwise a new one starts from where you are now.",
43434
+ targetLabel: "Creates",
43435
+ targetPending: (parent) => `${parent}/…`,
43436
+ gitignoreLabel: "About this folder and git status",
43437
+ submit: "Create"
43438
+ },
43439
+ remove: "Delete this worktree",
43440
+ removeTitle: "Delete this worktree and its folder",
43441
+ removeDialog: {
43442
+ title: "Delete worktree",
43443
+ body: (name) => `Delete the worktree "${name}"?`,
43444
+ diskNote: (path) => `The folder ${path} and everything in it will be deleted from disk. This cannot be undone.`,
43445
+ branchNote: (branch) => `The branch ${branch} and its committed work are kept.`,
43446
+ dirtyNote: (count) => count === 1 ? "1 file has uncommitted changes." : `${count} files have uncommitted changes.`,
43447
+ dirtyLose: "Deleting the worktree will lose them.",
43448
+ force: "Delete even with uncommitted changes",
43449
+ forceRequired: "To delete it anyway, check the box first.",
43450
+ missingBody: (name) => `Remove the entry for "${name}"?`,
43451
+ missingNote: "Its folder is already gone from disk; only the git entry is removed.",
43452
+ lockedNote: "This entry is locked, so git will leave it in place. Unlock it first.",
43453
+ missingOthers: (n2) => n2 === 1 ? "1 other entry whose folder is gone will be cleaned up at the same time." : `${n2} other entries whose folders are gone will be cleaned up at the same time.`,
43454
+ submit: "Delete"
43455
+ },
43456
+ removeFailed: "Failed to delete the worktree.",
43457
+ cancel: "Cancel",
43458
+ nameErrors: {
43459
+ empty: "Enter a name.",
43460
+ control: "Control characters cannot be used.",
43461
+ separator: "Path separators cannot be used.",
43462
+ relative: "This name is not allowed.",
43463
+ "leading-dot": "The name cannot start with a dot.",
43464
+ "too-long": "The name is too long."
43465
+ }
43466
+ },
43467
+ ja: {
43468
+ title: "作業ツリー",
43469
+ ariaLabel: "git の作業ツリー",
43470
+ loading: "作業ツリーを読み込んでいます…",
43471
+ loadFailed: "作業ツリーを読み込めませんでした。",
43472
+ empty: "作業ツリーがありません。",
43473
+ refresh: "再読み込み",
43474
+ refreshTitle: "一覧を読み直す",
43475
+ count: (n2) => `${n2} 本`,
43476
+ baseUnknown: "比較の基準になるブランチが見つかりません",
43477
+ gitignoreHint: "このディレクトリは git から未追跡に見えます。.worktrees/ を .gitignore に入れると git status に出なくなります。",
43478
+ badges: {
43479
+ current: "このフォルダ",
43480
+ currentTitle: "この code-viewer が開いているフォルダです",
43481
+ detached: "ブランチなし",
43482
+ detachedTitle: "どのブランチにも紐づいていません(detached HEAD)",
43483
+ bare: "bare",
43484
+ bareTitle: "作業ファイルを持たない保管用のリポジトリです",
43485
+ locked: "ロック中",
43486
+ prunable: "掃除できます",
43487
+ prunableTitle: "フォルダが無い等で管理情報だけ残っています",
43488
+ missing: "フォルダがありません",
43489
+ running: "起動中",
43490
+ runningTitle: "このフォルダ専用の code-viewer が別ポートで動いています"
43491
+ },
43492
+ diverge: {
43493
+ ahead: (n2, base2) => `${base2} より ${n2} コミット進んでいます`,
43494
+ behind: (n2, base2) => `${base2} より ${n2} コミット遅れています`,
43495
+ even: (base2) => `${base2} と同じ地点です`,
43496
+ notComparable: "比較なし"
43497
+ },
43498
+ merge: {
43499
+ clean: "そのままマージできます",
43500
+ conflict: (n2) => `マージすると ${n2} ファイルで衝突します`,
43501
+ unknown: "マージできるか確かめられませんでした",
43502
+ conflictListLabel: "衝突するファイル"
43503
+ },
43504
+ files: {
43505
+ heading: (n2) => `変更 ${n2} 件`,
43506
+ none: "変更はありません。",
43507
+ uncommitted: "未コミット",
43508
+ committed: "コミット済み・未マージ",
43509
+ truncated: (shown, total) => `${total} 件のうち ${shown} 件を表示`,
43510
+ // 行の中に置くので短くする。誰と重なっているかは title に入る。
43511
+ overlapMark: "重複",
43512
+ overlapTitle: (others) => `同じファイルを触っている: ${others.join("、")}`,
43513
+ statusTitles: JA_STATUS_TITLES
43514
+ },
43515
+ overlaps: {
43516
+ heading: (n2) => `${n2} 個のファイルを 2 本以上が触っています`,
43517
+ intro: "どれか 1 本をマージすると、残りはこのファイルで衝突します。",
43518
+ entry: (path, worktrees) => `${path} — ${worktrees.join("、")}`
43519
+ },
43520
+ panes: {
43521
+ worktrees: "作業ツリー",
43522
+ files: "ファイル",
43523
+ diff: "差分",
43524
+ filterWorktrees: "作業ツリーを絞り込み…",
43525
+ filterFiles: "ファイルを絞り込み…",
43526
+ noWorktreeMatch: "該当する作業ツリーがありません。",
43527
+ noFileMatch: "該当するファイルがありません。",
43528
+ selectWorktree: "作業ツリーを選ぶと、何を触っているかが出ます。",
43529
+ selectFile: "ファイルを選ぶと差分が出ます。",
43530
+ diffLoading: "差分を読み込んでいます…",
43531
+ diffEmpty: "テキストの差分はありません(バイナリか、変更なし)。",
43532
+ diffFailed: "差分を読み込めませんでした。",
43533
+ diffTruncated: (shown, total) => `${total} 個のうち ${shown} 個のかたまりを表示しています。全部見るには、その作業ツリーを別タブで見てください`
43534
+ },
43535
+ noCommit: "コミットなし",
43536
+ lastTouched: (when) => `最終更新 ${when}`,
43537
+ actions: {
43538
+ menuTitle: "この作業ツリーへの操作",
43539
+ menuFor: (name) => `${name} への操作`,
43540
+ openFolder: "フォルダを開く",
43541
+ openFolderTitle: "このフォルダをファイルマネージャで開きます",
43542
+ openFolderFailed: "そのフォルダを開けませんでした。",
43543
+ copyPath: "パスをコピー",
43544
+ copyPathTitle: "このフォルダの場所をコピーします",
43545
+ copyFailed: "クリップボードにコピーできませんでした。",
43546
+ stopServer: "サーバを止める",
43547
+ stopServerTitle: "このフォルダで動いている code-viewer を止めます",
43548
+ stopFailed: "サーバを止められませんでした。",
43549
+ copyServerUrl: "アドレスをコピー",
43550
+ copyServerUrlTitle: (url) => `${url} をコピーします`,
43551
+ copyMerge: "マージのコマンドをコピー",
43552
+ copyMergeTitle: (base2, branch) => `${branch} を ${base2} に取り込むコマンドをコピーします`
43553
+ },
43554
+ emptyDiff: {
43555
+ title: "まだ変更はありません。",
43556
+ body: (path) => `${path} でファイルを編集すると、ここに差分が出ます。`
43557
+ },
43558
+ intro: {
43559
+ cardTitle: "作業ツリー(git worktree)とは",
43560
+ cardWhat: "同じリポジトリを、ブランチを切り替えずに別フォルダでもう 1 つ開く仕組みです。",
43561
+ cardWhy: "main で作業しながら、別フォルダで修正用のブランチを開いておけます。2 つの作業を並べて進められます。",
43562
+ cardHow: "「作る」を押すと .worktrees/〈名前〉 にフォルダができ、指定したブランチがそこに展開されます。あとはそのフォルダで普通に編集してください。変更はこの画面に差分として出ます。",
43563
+ cardButton: "作業ツリーを作る",
43564
+ listNote: "いまはこのフォルダだけです。作業ツリーを作ると、ここに並びます。",
43565
+ overlapLegend: "⇄ = 他の作業ツリーも同じファイルを触っている"
43566
+ },
43567
+ open: "別タブで見る",
43568
+ openTitle: "このフォルダ専用の code-viewer を起動して新しいタブで開きます",
43569
+ opening: "起動中…",
43570
+ openFailed: "この作業ツリーを開けませんでした。",
43571
+ add: "作る",
43572
+ addTitle: "作業ツリーを新しく作る",
43573
+ addFailed: "作業ツリーを作れませんでした。",
43574
+ addDialog: {
43575
+ title: "作業ツリーを作る",
43576
+ intro: "ブランチを切り替えずに、別フォルダで 2 つの作業を並べて進められます。",
43577
+ nameLabel: "フォルダ名",
43578
+ namePlaceholder: "feature-x",
43579
+ branchLabel: "ブランチ",
43580
+ branchPlaceholder: "フォルダ名と同じ",
43581
+ branchHint: "既にあるブランチ名ならそれを開きます。無ければ、いまの地点から新しく作ります。",
43582
+ targetLabel: "作成先",
43583
+ targetPending: (parent) => `${parent}/…`,
43584
+ gitignoreLabel: "このフォルダと git status について",
43585
+ submit: "作る"
43586
+ },
43587
+ remove: "この作業ツリーを削除",
43588
+ removeTitle: "この作業ツリーをフォルダごと削除します",
43589
+ removeDialog: {
43590
+ title: "作業ツリーの削除",
43591
+ body: (name) => `作業ツリー「${name}」を削除しますか。`,
43592
+ diskNote: (path) => `フォルダ ${path} とその中身はディスクから消えます。元に戻せません。`,
43593
+ branchNote: (branch) => `ブランチ ${branch} と、コミット済みの内容は残ります。`,
43594
+ dirtyNote: (count) => `コミットしていない変更が ${count} 件あります。`,
43595
+ dirtyLose: "削除するとこれらの変更は失われます。",
43596
+ force: "変更が残っていても削除する",
43597
+ forceRequired: "変更を捨てて削除するには、チェックを入れてください。",
43598
+ missingBody: (name) => `作業ツリー「${name}」の登録を消しますか。`,
43599
+ missingNote: "フォルダは既にディスク上にありません。git の管理情報だけを消します。",
43600
+ lockedNote: "この登録はロックされているので、git はそのまま残します。先にロックを外してください。",
43601
+ missingOthers: (n2) => `フォルダが無くなっている他の登録 ${n2} 件も、同時に整理されます。`,
43602
+ submit: "削除する"
43603
+ },
43604
+ removeFailed: "作業ツリーを削除できませんでした。",
43605
+ cancel: "キャンセル",
43606
+ nameErrors: {
43607
+ empty: "名前を入力してください。",
43608
+ control: "制御文字は使えません。",
43609
+ separator: "パス区切りは使えません。",
43610
+ relative: "この名前は使えません。",
43611
+ "leading-dot": "ドットで始まる名前は使えません。",
43612
+ "too-long": "名前が長すぎます。"
43613
+ }
43614
+ }
43615
+ };
43616
+ function worktreeText(lang) {
43617
+ return TEXT3[lang];
43618
+ }
43619
+
43620
+ // web-src/core/worktree.ts
43621
+ var WORKTREE_NAME_MAX_LENGTH = 100;
43622
+ function worktreeNameError(name) {
43623
+ if (!name) return "empty";
43624
+ if (name.length > WORKTREE_NAME_MAX_LENGTH) return "too-long";
43625
+ for (const ch of name) {
43626
+ const code2 = ch.charCodeAt(0);
43627
+ if (code2 < 32 || code2 === 127) return "control";
43628
+ }
43629
+ if (name.includes("/") || name.includes("\\")) return "separator";
43630
+ if (name === "." || name === "..") return "relative";
43631
+ if (name.startsWith(".")) return "leading-dot";
43632
+ return null;
43633
+ }
43634
+ function worktreeBranchError(branch) {
43635
+ if (!branch) return "empty";
43636
+ if (branch.length > WORKTREE_NAME_MAX_LENGTH) return "too-long";
43637
+ for (const ch of branch) {
43638
+ const code2 = ch.charCodeAt(0);
43639
+ if (code2 < 32 || code2 === 127) return "control";
43640
+ }
43641
+ if (branch.startsWith("-") || branch.includes("..")) return "relative";
43642
+ return null;
43643
+ }
43644
+
43645
+ // web-src/views/context-menu.ts
43646
+ var open = null;
43647
+ function closeContextMenu() {
43648
+ if (!open) return;
43649
+ const closing = open;
43650
+ open = null;
43651
+ closing.cleanup();
43652
+ closing.element.remove();
43653
+ }
43654
+ function showContextMenu(anchor, items) {
43655
+ closeContextMenu();
43656
+ const menu = document.createElement("div");
43657
+ menu.className = "gdp-context-menu";
43658
+ menu.setAttribute("role", "menu");
43659
+ for (const item of items) {
43660
+ if (item.kind === "separator") {
43661
+ const line = document.createElement("hr");
43662
+ line.className = "gdp-context-menu-sep";
43663
+ menu.appendChild(line);
43664
+ continue;
43665
+ }
43666
+ const button = document.createElement("button");
43667
+ button.type = "button";
43668
+ button.setAttribute("role", "menuitem");
43669
+ button.textContent = item.label;
43670
+ if (item.title) button.title = item.title;
43671
+ if (item.danger) button.classList.add("danger");
43672
+ button.disabled = !!item.disabled;
43673
+ button.addEventListener("click", () => {
43674
+ closeContextMenu();
43675
+ item.onSelect();
43676
+ });
43677
+ menu.appendChild(button);
43678
+ }
43679
+ document.body.appendChild(menu);
43680
+ const rect = menu.getBoundingClientRect();
43681
+ const from = anchor.getBoundingClientRect();
43682
+ const left = Math.min(
43683
+ from.right - rect.width,
43684
+ window.innerWidth - rect.width - 8
43685
+ );
43686
+ const top = Math.min(from.bottom + 4, window.innerHeight - rect.height - 8);
43687
+ menu.style.left = `${Math.max(8, left)}px`;
43688
+ menu.style.top = `${Math.max(8, top)}px`;
43689
+ const onPointerDown = (event) => {
43690
+ if (menu.contains(event.target)) return;
43691
+ closeContextMenu();
43692
+ };
43693
+ const onKeyDown = (event) => {
43694
+ if (event.key !== "Escape") return;
43695
+ event.stopPropagation();
43696
+ closeContextMenu();
43697
+ anchor.focus();
43698
+ };
43699
+ const onScroll = () => closeContextMenu();
43700
+ document.addEventListener("pointerdown", onPointerDown, true);
43701
+ document.addEventListener("keydown", onKeyDown, true);
43702
+ window.addEventListener("scroll", onScroll, true);
43703
+ window.addEventListener("resize", onScroll);
43704
+ open = {
43705
+ element: menu,
43706
+ cleanup() {
43707
+ document.removeEventListener("pointerdown", onPointerDown, true);
43708
+ document.removeEventListener("keydown", onKeyDown, true);
43709
+ window.removeEventListener("scroll", onScroll, true);
43710
+ window.removeEventListener("resize", onScroll);
43711
+ }
43712
+ };
43713
+ menu.querySelector("button:not(:disabled)")?.focus();
43714
+ }
43715
+
43716
+ // web-src/views/worktree-view.ts
43717
+ var SSE_REFRESH_DELAY_MS = 400;
43718
+ var DIFF_PREFETCH_MARGIN = "600px";
43719
+ var ACTION_HEADERS = {
43720
+ "Content-Type": "application/json",
43721
+ "X-Code-Viewer-Action": "1"
43722
+ };
43723
+ async function postWorktreeAction(path, body) {
43724
+ const res = await fetch(path, {
43725
+ method: "POST",
43726
+ headers: ACTION_HEADERS,
43727
+ body: JSON.stringify(body)
43728
+ });
43729
+ if (!res.ok) throw new Error(await res.text() || `${res.status}`);
43730
+ return await res.json();
43731
+ }
43732
+ function el(tag, className, text3) {
43733
+ const node = document.createElement(tag);
43734
+ if (className) node.className = className;
43735
+ if (text3 !== void 0) node.textContent = text3;
43736
+ return node;
43737
+ }
43738
+ function fileKey(file) {
43739
+ return `${file.origin}:${file.path}`;
43740
+ }
43741
+ function chevronSvg() {
43742
+ return iconSvg("octicon-chevron-down", CHEVRON_DOWN_16_PATH);
43743
+ }
43744
+ function folderSvg(collapsed) {
43745
+ return iconSvg(
43746
+ collapsed ? "octicon-file-directory-fill" : "octicon-file-directory-open-fill",
43747
+ collapsed ? FOLDER_ICON_PATHS.closed : FOLDER_ICON_PATHS.open
43748
+ );
43749
+ }
43750
+ function matches(haystack, needle) {
43751
+ return !needle || haystack.toLowerCase().includes(needle.toLowerCase());
43752
+ }
43753
+ function createWorktreeView(deps) {
43754
+ let mounted = false;
43755
+ let lifecycle = 0;
43756
+ let acceptedServerGeneration = 0;
43757
+ let data = null;
43758
+ let message = "";
43759
+ let messageIsError = false;
43760
+ let busyPath = "";
43761
+ let sseTimer = null;
43762
+ let refreshRequested = false;
43763
+ let refreshLoop = null;
43764
+ let worktreeFilter = "";
43765
+ let fileFilter = "";
43766
+ let diffFor = null;
43767
+ let restoredRouteKey = null;
43768
+ let observer = null;
43769
+ const listPanel = document.getElementById("worktree-panel");
43770
+ function text3() {
43771
+ return deps.getText();
43772
+ }
43773
+ function route() {
43774
+ const current = deps.getRoute();
43775
+ return current.screen === "worktree" ? current : null;
43776
+ }
43777
+ function setMessage(next, isError = false) {
43778
+ message = next;
43779
+ messageIsError = isError;
43780
+ }
43781
+ function isCurrent(seq) {
43782
+ return mounted && seq === lifecycle && route() !== null;
43783
+ }
43784
+ const worktreeFilterInput = (() => {
43785
+ const input2 = el("input", "history-filter");
43786
+ input2.type = "search";
43787
+ input2.autocomplete = "off";
43788
+ input2.addEventListener("input", () => {
43789
+ worktreeFilter = input2.value;
43790
+ renderList();
43791
+ });
43792
+ return input2;
43793
+ })();
43794
+ function sidebarFilter() {
43795
+ return document.querySelector("#sb-filter");
43796
+ }
43797
+ function onSidebarFilter() {
43798
+ fileFilter = sidebarFilter()?.value || "";
43799
+ renderFiles();
43800
+ }
43801
+ function onSidebarViewToggle() {
43802
+ window.setTimeout(() => renderFiles(), 0);
43803
+ }
43804
+ function setAllDirsCollapsed(collapsed) {
43805
+ const list2 = document.getElementById("filelist");
43806
+ if (!list2) return;
43807
+ for (const dir of list2.querySelectorAll(".tree-dir")) {
43808
+ dir.classList.toggle("collapsed", collapsed);
43809
+ const icon = dir.querySelector(":scope > .dir-icon");
43810
+ if (icon) icon.innerHTML = folderSvg(collapsed);
43811
+ }
43812
+ }
43813
+ function onExpandAll() {
43814
+ setAllDirsCollapsed(false);
43815
+ }
43816
+ function onCollapseAll() {
43817
+ setAllDirsCollapsed(true);
43818
+ }
43819
+ function sidebarViewButtons() {
43820
+ return Array.from(
43821
+ document.querySelectorAll(".sb-view-seg button")
43822
+ );
43823
+ }
43824
+ function mount() {
43825
+ if (mounted || !listPanel) return;
43826
+ document.getElementById("empty")?.classList.add("hidden");
43827
+ document.getElementById("history-commit-info")?.setAttribute("hidden", "true");
43828
+ listPanel.hidden = false;
43829
+ mounted = true;
43830
+ document.body.classList.add("gdp-worktree-page");
43831
+ sidebarFilter()?.addEventListener("input", onSidebarFilter);
43832
+ document.getElementById("filelist")?.addEventListener("click", onFileListClick);
43833
+ for (const button of sidebarViewButtons()) {
43834
+ button.addEventListener("click", onSidebarViewToggle);
43835
+ }
43836
+ document.getElementById("sb-expand-all")?.addEventListener("click", onExpandAll);
43837
+ document.getElementById("sb-collapse-all")?.addEventListener("click", onCollapseAll);
43838
+ deps.setPageMode();
43839
+ deps.syncHeaderMenu();
43840
+ }
43841
+ function suspend() {
43842
+ lifecycle++;
43843
+ closeContextMenu();
43844
+ observer?.disconnect();
43845
+ observer = null;
43846
+ diffFor = null;
43847
+ restoredRouteKey = null;
43848
+ if (sseTimer !== null) {
43849
+ window.clearTimeout(sseTimer);
43850
+ sseTimer = null;
43851
+ }
43852
+ refreshRequested = false;
43853
+ if (listPanel) {
43854
+ listPanel.hidden = true;
43855
+ listPanel.replaceChildren();
43856
+ }
43857
+ sidebarFilter()?.removeEventListener("input", onSidebarFilter);
43858
+ document.getElementById("filelist")?.removeEventListener("click", onFileListClick);
43859
+ for (const button of sidebarViewButtons()) {
43860
+ button.removeEventListener("click", onSidebarViewToggle);
43861
+ }
43862
+ document.getElementById("sb-expand-all")?.removeEventListener("click", onExpandAll);
43863
+ document.getElementById("sb-collapse-all")?.removeEventListener("click", onCollapseAll);
43864
+ document.getElementById("filelist")?.replaceChildren();
43865
+ document.getElementById("diff")?.replaceChildren();
43866
+ document.body.classList.remove("gdp-worktree-page");
43867
+ mounted = false;
43868
+ }
43869
+ function selectedWorktree() {
43870
+ const id = route()?.wt;
43871
+ if (!id || !data) return null;
43872
+ return data.worktrees.find((item) => item.id === id) || null;
43873
+ }
43874
+ function worktreeLabel(id) {
43875
+ const item = data?.worktrees.find((candidate) => candidate.id === id);
43876
+ if (!item) return id;
43877
+ const sameName = data?.worktrees.filter(
43878
+ (candidate) => candidate.name === item.name
43879
+ );
43880
+ return sameName && sameName.length > 1 ? `${item.name} (${item.displayPath})` : item.name;
43881
+ }
43882
+ function navigate(next, replace2 = false) {
43883
+ const current = route();
43884
+ if (!current) return;
43885
+ deps.setRoute(
43886
+ {
43887
+ screen: "worktree",
43888
+ ...current.wt ? { wt: current.wt } : {},
43889
+ ...current.file ? { file: current.file } : {},
43890
+ ...current.origin ? { origin: current.origin } : {},
43891
+ ...next,
43892
+ range: deps.currentRange()
43893
+ },
43894
+ replace2
43895
+ );
43896
+ }
43897
+ async function refreshOnce() {
43898
+ if (!mounted || !route()) return;
43899
+ const seq = ++lifecycle;
43900
+ if (!data) setMessage(text3().loading);
43901
+ renderList();
43902
+ deps.setStatus("refreshing");
43903
+ try {
43904
+ const next = await deps.trackLoad(
43905
+ fetch("/_worktree/list").then(async (res) => {
43906
+ if (!res.ok) throw new Error(await res.text() || `${res.status}`);
43907
+ return await res.json();
43908
+ })
43909
+ );
43910
+ if (!isCurrent(seq)) return;
43911
+ if (!Number.isInteger(next.generation) || next.generation < acceptedServerGeneration) {
43912
+ throw new Error(
43913
+ `stale worktree response generation ${next.generation}; current is ${acceptedServerGeneration}`
43914
+ );
43915
+ }
43916
+ acceptedServerGeneration = next.generation;
43917
+ data = next;
43918
+ setMessage(next.error || "", !!next.error);
43919
+ deps.setStatus(next.error ? "error" : "live");
43920
+ } catch (error2) {
43921
+ if (!isCurrent(seq)) return;
43922
+ setMessage(
43923
+ error2 instanceof Error ? error2.message : text3().loadFailed,
43924
+ true
43925
+ );
43926
+ deps.setStatus("error");
43927
+ } finally {
43928
+ if (isCurrent(seq)) {
43929
+ diffFor = null;
43930
+ render();
43931
+ }
43932
+ }
43933
+ }
43934
+ function refresh() {
43935
+ if (!mounted || !route()) return Promise.resolve();
43936
+ refreshRequested = true;
43937
+ if (refreshLoop) return refreshLoop;
43938
+ const loop = (async () => {
43939
+ while (refreshRequested && mounted && route()) {
43940
+ refreshRequested = false;
43941
+ await refreshOnce();
43942
+ }
43943
+ })();
43944
+ const tracked = loop.finally(() => {
43945
+ if (refreshLoop === tracked) refreshLoop = null;
43946
+ });
43947
+ refreshLoop = tracked;
43948
+ return tracked;
43949
+ }
43950
+ function openBlankTab() {
43951
+ try {
43952
+ const tab = window.open("", "_blank");
43953
+ if (tab) tab.opener = null;
43954
+ return tab;
43955
+ } catch {
43956
+ return null;
43957
+ }
43958
+ }
43959
+ async function openWorktree(item) {
43960
+ if (busyPath) return;
43961
+ const seq = lifecycle;
43962
+ busyPath = item.path;
43963
+ setMessage(text3().opening);
43964
+ renderList();
43965
+ const tab = openBlankTab();
43966
+ try {
43967
+ const result = await deps.trackLoad(
43968
+ postWorktreeAction("/_worktree/open", { path: item.path })
43969
+ );
43970
+ const url = result.url || "";
43971
+ if (!url) throw new Error(text3().openFailed);
43972
+ if (tab) tab.location.href = url;
43973
+ if (isCurrent(seq)) setMessage(tab ? "" : url, !tab);
43974
+ } catch (error2) {
43975
+ tab?.close();
43976
+ if (isCurrent(seq)) {
43977
+ setMessage(
43978
+ error2 instanceof Error ? error2.message : text3().openFailed,
43979
+ true
43980
+ );
43981
+ }
43982
+ } finally {
43983
+ busyPath = "";
43984
+ if (isCurrent(seq)) await refresh();
43985
+ }
43986
+ }
43987
+ function labeledInput(label, placeholder, hint) {
43988
+ const wrap = document.createElement("label");
43989
+ wrap.className = "worktree-field";
43990
+ wrap.appendChild(el("span", "", label));
43991
+ const input2 = el("input", "gdp-dialog-input");
43992
+ input2.type = "text";
43993
+ input2.placeholder = placeholder;
43994
+ wrap.appendChild(input2);
43995
+ if (hint) wrap.appendChild(el("span", "worktree-hint", hint));
43996
+ return { wrap, input: input2 };
43997
+ }
43998
+ async function addWorktree() {
43999
+ const seq = lifecycle;
44000
+ const t2 = text3();
44001
+ const body = el("div", "worktree-form");
44002
+ body.appendChild(el("p", "worktree-hint", t2.addDialog.intro));
44003
+ const name = labeledInput(
44004
+ t2.addDialog.nameLabel,
44005
+ t2.addDialog.namePlaceholder
44006
+ );
44007
+ const branch = labeledInput(
44008
+ t2.addDialog.branchLabel,
44009
+ t2.addDialog.branchPlaceholder,
44010
+ t2.addDialog.branchHint
44011
+ );
44012
+ body.append(name.wrap, branch.wrap);
44013
+ const parent = data?.addParent || "";
44014
+ if (parent) {
44015
+ const targetRow = el("div", "worktree-target");
44016
+ targetRow.appendChild(
44017
+ el("span", "worktree-target-label", t2.addDialog.targetLabel)
44018
+ );
44019
+ const targetPath = el("span", "worktree-target-path");
44020
+ targetRow.appendChild(targetPath);
44021
+ const hint = el("span", "worktree-hint-mark", "?");
44022
+ hint.title = t2.gitignoreHint;
44023
+ hint.tabIndex = 0;
44024
+ hint.setAttribute("role", "note");
44025
+ hint.setAttribute(
44026
+ "aria-label",
44027
+ `${t2.addDialog.gitignoreLabel}: ${t2.gitignoreHint}`
44028
+ );
44029
+ targetRow.appendChild(hint);
44030
+ body.appendChild(targetRow);
44031
+ const syncTarget = () => {
44032
+ const value = name.input.value.trim();
44033
+ targetPath.textContent = value ? `${parent}/${value}` : t2.addDialog.targetPending(parent);
44034
+ targetPath.title = targetPath.textContent;
44035
+ };
44036
+ name.input.addEventListener("input", syncTarget);
44037
+ syncTarget();
44038
+ }
44039
+ const submitted = await showFormDialog({
44040
+ title: t2.addDialog.title,
44041
+ body,
44042
+ submitLabel: t2.addDialog.submit,
44043
+ cancelLabel: t2.cancel,
44044
+ focusTarget: name.input,
44045
+ validate: () => {
44046
+ const nameError = worktreeNameError(name.input.value.trim());
44047
+ if (nameError) return t2.nameErrors[nameError];
44048
+ const raw = branch.input.value.trim();
44049
+ if (!raw) return null;
44050
+ const branchError = worktreeBranchError(raw);
44051
+ return branchError ? t2.nameErrors[branchError] : null;
44052
+ },
44053
+ submit: () => ({
44054
+ name: name.input.value.trim(),
44055
+ branch: branch.input.value.trim()
44056
+ })
44057
+ });
44058
+ if (!submitted) return;
44059
+ if (!isCurrent(seq)) return;
44060
+ let createdPath = "";
44061
+ try {
44062
+ const result = await deps.trackLoad(
44063
+ postWorktreeAction("/_worktree/add", submitted)
44064
+ );
44065
+ if (isCurrent(seq)) setMessage("");
44066
+ createdPath = result.path || "";
44067
+ } catch (error2) {
44068
+ if (isCurrent(seq)) {
44069
+ setMessage(error2 instanceof Error ? error2.message : t2.addFailed, true);
44070
+ }
44071
+ }
44072
+ if (isCurrent(seq) && createdPath) {
44073
+ navigate({ wt: createdPath });
44074
+ }
44075
+ if (isCurrent(seq)) await refresh();
44076
+ }
44077
+ async function removeWorktree(item) {
44078
+ if (busyPath) return;
44079
+ const seq = lifecycle;
44080
+ const t2 = text3();
44081
+ const body = el("div", "worktree-form");
44082
+ if (item.missing) {
44083
+ body.appendChild(el("p", "", t2.removeDialog.missingBody(item.name)));
44084
+ body.appendChild(el("p", "worktree-hint", t2.removeDialog.missingNote));
44085
+ if (item.locked) {
44086
+ body.appendChild(el("p", "worktree-warn", t2.removeDialog.lockedNote));
44087
+ }
44088
+ const otherMissing = (data?.worktrees || []).filter(
44089
+ (candidate) => candidate.missing && !candidate.locked && candidate.id !== item.id
44090
+ ).length;
44091
+ if (otherMissing > 0) {
44092
+ body.appendChild(
44093
+ el("p", "worktree-hint", t2.removeDialog.missingOthers(otherMissing))
44094
+ );
44095
+ }
44096
+ } else {
44097
+ body.appendChild(el("p", "", t2.removeDialog.body(item.name)));
44098
+ body.appendChild(el("p", "", t2.removeDialog.diskNote(item.path)));
44099
+ }
44100
+ if (item.branch) {
44101
+ body.appendChild(
44102
+ el("p", "worktree-hint", t2.removeDialog.branchNote(item.branch))
44103
+ );
44104
+ }
44105
+ let force = null;
44106
+ if (!item.missing && item.changedCount > 0) {
44107
+ const warn = el("p", "worktree-warn");
44108
+ warn.appendChild(
44109
+ el("span", "", `⚠ ${t2.removeDialog.dirtyNote(item.changedCount)}`)
44110
+ );
44111
+ warn.appendChild(document.createElement("br"));
44112
+ warn.appendChild(el("span", "", t2.removeDialog.dirtyLose));
44113
+ body.appendChild(warn);
44114
+ const forceWrap = document.createElement("label");
44115
+ forceWrap.className = "worktree-check";
44116
+ force = document.createElement("input");
44117
+ force.type = "checkbox";
44118
+ forceWrap.append(force, el("span", "", t2.removeDialog.force));
44119
+ body.appendChild(forceWrap);
44120
+ }
44121
+ const submitted = await showFormDialog({
44122
+ title: t2.removeDialog.title,
44123
+ body,
44124
+ submitLabel: t2.removeDialog.submit,
44125
+ cancelLabel: t2.cancel,
44126
+ // フォルダごと消える不可逆の操作なので、確定は常に危険色。
44127
+ danger: true,
44128
+ validate: () => force && !force.checked ? t2.removeDialog.forceRequired : null,
44129
+ submit: () => ({ force: force?.checked ?? false })
44130
+ });
44131
+ if (!submitted) return;
44132
+ if (!isCurrent(seq)) return;
44133
+ busyPath = item.path;
44134
+ renderList();
44135
+ try {
44136
+ await deps.trackLoad(
44137
+ postWorktreeAction("/_worktree/remove", {
44138
+ path: item.path,
44139
+ force: submitted.force
44140
+ })
44141
+ );
44142
+ if (isCurrent(seq)) setMessage("");
44143
+ if (isCurrent(seq) && route()?.wt === item.id) {
44144
+ navigate({ wt: void 0, file: void 0, origin: void 0 }, true);
44145
+ }
44146
+ } catch (error2) {
44147
+ if (isCurrent(seq)) {
44148
+ setMessage(
44149
+ error2 instanceof Error ? error2.message : t2.removeFailed,
44150
+ true
44151
+ );
44152
+ }
44153
+ } finally {
44154
+ busyPath = "";
44155
+ if (isCurrent(seq)) await refresh();
44156
+ }
44157
+ }
44158
+ function overlapOthers(item, file) {
44159
+ const paths = new Set([file.path, file.oldPath].filter(Boolean));
44160
+ const ownerIds = new Set(
44161
+ (data?.overlaps || []).filter((overlap) => paths.has(overlap.path)).flatMap((overlap) => overlap.worktreeIds)
44162
+ );
44163
+ return [...ownerIds].filter((id) => id !== item.id).map((id) => worktreeLabel(id));
44164
+ }
44165
+ function divergenceSummary(item) {
44166
+ const t2 = text3();
44167
+ const divergence = item.divergence;
44168
+ if (!divergence) return t2.diverge.notComparable;
44169
+ const parts = [];
44170
+ if (divergence.mergeState === "clean") {
44171
+ parts.push(t2.merge.clean);
44172
+ } else if (divergence.mergeState === "conflict") {
44173
+ parts.push(t2.merge.conflict(divergence.conflicts.length));
44174
+ } else {
44175
+ parts.push(t2.merge.unknown);
44176
+ }
44177
+ if (divergence.ahead > 0) {
44178
+ parts.push(t2.diverge.ahead(divergence.ahead, divergence.base));
44179
+ }
44180
+ if (divergence.behind > 0) {
44181
+ parts.push(t2.diverge.behind(divergence.behind, divergence.base));
44182
+ }
44183
+ if (!divergence.ahead && !divergence.behind) {
44184
+ parts.push(t2.diverge.even(divergence.base));
44185
+ }
44186
+ return parts.join(" · ");
44187
+ }
44188
+ function worktreeBadges(item) {
44189
+ const t2 = text3();
44190
+ const badges = [];
44191
+ if (item.current) {
44192
+ badges.push({ label: t2.badges.current, title: t2.badges.currentTitle });
44193
+ }
44194
+ if (item.serverUrl) {
44195
+ badges.push({ label: t2.badges.running, title: t2.badges.runningTitle });
44196
+ }
44197
+ if (item.missing) badges.push({ label: t2.badges.missing, title: "" });
44198
+ if (item.bare) {
44199
+ badges.push({ label: t2.badges.bare, title: t2.badges.bareTitle });
44200
+ }
44201
+ if (item.locked) {
44202
+ badges.push({ label: t2.badges.locked, title: item.lockedReason });
44203
+ }
44204
+ if (item.prunable) {
44205
+ badges.push({ label: t2.badges.prunable, title: t2.badges.prunableTitle });
44206
+ }
44207
+ return badges;
44208
+ }
44209
+ function headButton(label, title, onClick) {
44210
+ const button = el("button", "gdp-btn gdp-btn-sm worktree-head-btn", label);
44211
+ button.type = "button";
44212
+ button.title = title;
44213
+ button.addEventListener("click", onClick);
44214
+ return button;
44215
+ }
44216
+ function openFolderButton(item, withLabel) {
44217
+ const arg = openPathArg(item);
44218
+ if (arg === null) return null;
44219
+ const t2 = text3();
44220
+ const button = deps.createOpenPathButton(
44221
+ arg,
44222
+ "directory",
44223
+ t2.actions.openFolderTitle
44224
+ );
44225
+ if (withLabel) {
44226
+ button.className = "gdp-btn gdp-btn-sm gdp-open-path";
44227
+ button.textContent = t2.actions.openFolder;
44228
+ }
44229
+ return button;
44230
+ }
44231
+ function copyButton(value, title, label) {
44232
+ const button = el(
44233
+ "button",
44234
+ label ? "gdp-btn gdp-btn-sm gdp-copy-path" : "gdp-file-header-icon gdp-copy-path",
44235
+ label
44236
+ );
44237
+ button.type = "button";
44238
+ button.title = title;
44239
+ button.setAttribute("aria-label", title);
44240
+ if (!label) button.innerHTML = iconSvg("octicon-copy", COPY_16_PATHS);
44241
+ button.addEventListener("click", async (event) => {
44242
+ event.stopPropagation();
44243
+ button.classList.remove("copied", "failed");
44244
+ button.classList.add(await copyText(value) ? "copied" : "failed");
44245
+ window.setTimeout(
44246
+ () => button.classList.remove("copied", "failed"),
44247
+ 1200
44248
+ );
44249
+ });
44250
+ return button;
44251
+ }
44252
+ async function runAction(run, failure) {
44253
+ const seq = lifecycle;
44254
+ if (await run()) return;
44255
+ if (!isCurrent(seq)) return;
44256
+ setMessage(failure, true);
44257
+ renderList();
44258
+ }
44259
+ async function copyText(value) {
44260
+ try {
44261
+ await navigator.clipboard.writeText(value);
44262
+ return true;
44263
+ } catch (error2) {
44264
+ console.error("[code-viewer] failed to copy to clipboard", error2);
44265
+ return false;
44266
+ }
44267
+ }
44268
+ function copyPathButton(item, withLabel) {
44269
+ const t2 = text3();
44270
+ return copyButton(
44271
+ item.path,
44272
+ t2.actions.copyPathTitle,
44273
+ withLabel ? t2.actions.copyPath : void 0
44274
+ );
44275
+ }
44276
+ function mergeCommand(item) {
44277
+ const divergence = item.divergence;
44278
+ if (!divergence || divergence.mergeState !== "clean") return null;
44279
+ if (!item.branch || !divergence.base) return null;
44280
+ return {
44281
+ base: divergence.base,
44282
+ branch: item.branch,
44283
+ command: `git switch ${divergence.base} && git merge ${item.branch}`
44284
+ };
44285
+ }
44286
+ function copyMergeButton(item) {
44287
+ const merge = mergeCommand(item);
44288
+ if (!merge) return null;
44289
+ const t2 = text3();
44290
+ return copyButton(
44291
+ merge.command,
44292
+ t2.actions.copyMergeTitle(merge.base, merge.branch)
44293
+ );
44294
+ }
44295
+ async function stopWorktreeServer(item) {
44296
+ if (busyPath) return;
44297
+ const seq = lifecycle;
44298
+ busyPath = item.path;
44299
+ renderList();
44300
+ try {
44301
+ await deps.trackLoad(
44302
+ postWorktreeAction("/_worktree/stop", { path: item.path })
44303
+ );
44304
+ if (isCurrent(seq)) setMessage("");
44305
+ } catch (error2) {
44306
+ if (isCurrent(seq)) {
44307
+ setMessage(
44308
+ error2 instanceof Error ? error2.message : text3().actions.stopFailed,
44309
+ true
44310
+ );
44311
+ }
44312
+ } finally {
44313
+ busyPath = "";
44314
+ if (isCurrent(seq)) await refresh();
44315
+ }
44316
+ }
44317
+ function openPathArg(item) {
44318
+ const shown = item.displayPath;
44319
+ if (shown === ".") return "";
44320
+ if (!shown || shown.startsWith("/") || shown.startsWith("\\")) return null;
44321
+ return shown;
44322
+ }
44323
+ function rowMenuItems(item) {
44324
+ const t2 = text3();
44325
+ const items = [];
44326
+ if (!item.missing && !item.bare) {
44327
+ const openArg = openPathArg(item);
44328
+ if (openArg !== null) {
44329
+ items.push({
44330
+ label: t2.actions.openFolder,
44331
+ title: t2.actions.openFolderTitle,
44332
+ onSelect: () => {
44333
+ void runAction(
44334
+ () => deps.openPathInOs(openArg, "directory"),
44335
+ t2.actions.openFolderFailed
44336
+ );
44337
+ }
44338
+ });
44339
+ }
44340
+ items.push({
44341
+ label: t2.actions.copyPath,
44342
+ title: t2.actions.copyPathTitle,
44343
+ onSelect: () => {
44344
+ void runAction(() => copyText(item.path), t2.actions.copyFailed);
44345
+ }
44346
+ });
44347
+ items.push({
44348
+ label: t2.open,
44349
+ title: t2.openTitle,
44350
+ disabled: !!busyPath,
44351
+ onSelect: () => {
44352
+ void openWorktree(item);
44353
+ }
44354
+ });
44355
+ }
44356
+ if (item.serverUrl) {
44357
+ items.push({
44358
+ label: t2.actions.copyServerUrl,
44359
+ title: t2.actions.copyServerUrlTitle(item.serverUrl),
44360
+ onSelect: () => {
44361
+ void runAction(() => copyText(item.serverUrl), t2.actions.copyFailed);
44362
+ }
44363
+ });
44364
+ }
44365
+ if (item.serverUrl && !item.current) {
44366
+ items.push({
44367
+ label: t2.actions.stopServer,
44368
+ title: t2.actions.stopServerTitle,
44369
+ disabled: !!busyPath,
44370
+ onSelect: () => {
44371
+ void stopWorktreeServer(item);
44372
+ }
44373
+ });
44374
+ }
44375
+ const merge = mergeCommand(item);
44376
+ if (merge) {
44377
+ items.push({
44378
+ label: t2.actions.copyMerge,
44379
+ title: t2.actions.copyMergeTitle(merge.base, merge.branch),
44380
+ onSelect: () => {
44381
+ void runAction(() => copyText(merge.command), t2.actions.copyFailed);
44382
+ }
44383
+ });
44384
+ }
44385
+ if (!item.current) {
44386
+ if (items.length) items.push({ kind: "separator" });
44387
+ items.push({
44388
+ label: t2.remove,
44389
+ title: t2.removeTitle,
44390
+ danger: true,
44391
+ disabled: !!busyPath,
44392
+ onSelect: () => {
44393
+ void removeWorktree(item);
44394
+ }
44395
+ });
44396
+ }
44397
+ return items;
44398
+ }
44399
+ function rowMenuButton(item) {
44400
+ const items = rowMenuItems(item);
44401
+ if (!items.length) return null;
44402
+ const t2 = text3();
44403
+ const button = el("button", "worktree-row-menu");
44404
+ button.type = "button";
44405
+ button.title = t2.actions.menuTitle;
44406
+ button.setAttribute("aria-label", t2.actions.menuFor(item.name));
44407
+ button.setAttribute("aria-haspopup", "menu");
44408
+ button.innerHTML = iconSvg("octicon-kebab-horizontal", KEBAB_16_PATH);
44409
+ button.addEventListener("click", (event) => {
44410
+ event.stopPropagation();
44411
+ showContextMenu(button, rowMenuItems(item));
44412
+ });
44413
+ return button;
44414
+ }
44415
+ function note(body, isError = false) {
44416
+ const node = el("div", "history-status", body);
44417
+ node.hidden = false;
44418
+ if (isError) {
44419
+ node.setAttribute("role", "alert");
44420
+ node.style.color = "var(--danger)";
44421
+ }
44422
+ return node;
44423
+ }
44424
+ function renderList() {
44425
+ if (!listPanel) return;
44426
+ const t2 = text3();
44427
+ listPanel.replaceChildren();
44428
+ const head = el("div", "history-head");
44429
+ head.appendChild(el("span", "history-title", t2.panes.worktrees));
44430
+ head.appendChild(
44431
+ headButton(t2.refresh, t2.refreshTitle, () => {
44432
+ void refresh();
44433
+ })
44434
+ );
44435
+ head.appendChild(
44436
+ headButton(t2.add, t2.addTitle, () => {
44437
+ void addWorktree();
44438
+ })
44439
+ );
44440
+ listPanel.appendChild(head);
44441
+ worktreeFilterInput.placeholder = t2.panes.filterWorktrees;
44442
+ const filterWrap = el("div", "history-filter-wrap");
44443
+ filterWrap.appendChild(worktreeFilterInput);
44444
+ listPanel.appendChild(filterWrap);
44445
+ if (data && !data.baseBranch) {
44446
+ listPanel.appendChild(note(t2.baseUnknown));
44447
+ }
44448
+ if (message) listPanel.appendChild(note(message, messageIsError));
44449
+ const overlaps = data?.overlaps || [];
44450
+ if (overlaps.length) {
44451
+ const banner = el("div", "history-banner");
44452
+ banner.hidden = false;
44453
+ banner.textContent = `${t2.overlaps.heading(overlaps.length)} — ${t2.overlaps.intro}`;
44454
+ banner.title = overlaps.map(
44455
+ (overlap) => t2.overlaps.entry(
44456
+ overlap.path,
44457
+ overlap.worktreeIds.map((id) => worktreeLabel(id))
44458
+ )
44459
+ ).join("\n");
44460
+ listPanel.appendChild(banner);
44461
+ }
44462
+ if (!data) {
44463
+ listPanel.appendChild(note(t2.loading));
44464
+ return;
44465
+ }
44466
+ if (!data.worktrees.length) {
44467
+ listPanel.appendChild(note(t2.empty));
44468
+ return;
44469
+ }
44470
+ const items = data.worktrees.filter(
44471
+ (item) => matches(item.name, worktreeFilter) || matches(item.branch, worktreeFilter) || matches(item.displayPath, worktreeFilter)
44472
+ );
44473
+ if (!items.length) {
44474
+ listPanel.appendChild(note(t2.panes.noWorktreeMatch));
44475
+ return;
44476
+ }
44477
+ const list2 = el("ol", "history-list");
44478
+ const selected = route()?.wt;
44479
+ for (const item of items) {
44480
+ const row = el("li", "history-item");
44481
+ row.dataset.wt = item.id;
44482
+ if (item.id === selected) row.classList.add("active");
44483
+ if (item.divergence?.mergeState === "conflict") {
44484
+ row.classList.add("worktree-conflict");
44485
+ }
44486
+ const head2 = el("span", "worktree-row-head");
44487
+ const subject = el("span", "subject", item.name);
44488
+ subject.title = item.path;
44489
+ head2.appendChild(subject);
44490
+ const badges = worktreeBadges(item);
44491
+ if (badges.length) {
44492
+ const wrap = el("span", "worktree-row-badges");
44493
+ badges.forEach((badge, index) => {
44494
+ if (index) wrap.appendChild(document.createTextNode(" · "));
44495
+ const node = el("span", "worktree-badge", badge.label);
44496
+ node.title = badge.title;
44497
+ wrap.appendChild(node);
44498
+ });
44499
+ head2.appendChild(wrap);
44500
+ }
44501
+ const menu = rowMenuButton(item);
44502
+ if (menu) head2.appendChild(menu);
44503
+ row.appendChild(head2);
44504
+ const pathLine = el("span", "worktree-row-path", item.path);
44505
+ pathLine.title = item.path;
44506
+ row.appendChild(pathLine);
44507
+ const meta = el("span", "meta2");
44508
+ const branchName = el("span", "sha", item.branch || t2.badges.detached);
44509
+ if (!item.branch) branchName.title = t2.badges.detachedTitle;
44510
+ meta.appendChild(branchName);
44511
+ meta.appendChild(
44512
+ el(
44513
+ "span",
44514
+ "author",
44515
+ item.fileCount ? t2.files.heading(item.fileCount) : t2.files.none
44516
+ )
44517
+ );
44518
+ if (item.lastCommit) {
44519
+ const parsed = Date.parse(item.lastCommit.when);
44520
+ const when = el(
44521
+ "span",
44522
+ "when",
44523
+ Number.isFinite(parsed) ? blameRelativeTime(Math.round(parsed / 1e3)) : item.lastCommit.when
44524
+ );
44525
+ when.title = Number.isFinite(parsed) ? `${item.lastCommit.subject}
44526
+ ${new Date(parsed).toLocaleString()}` : item.lastCommit.subject;
44527
+ meta.appendChild(when);
44528
+ }
44529
+ if (item.lastTouched) {
44530
+ const parsed = Date.parse(item.lastTouched);
44531
+ const when = el(
44532
+ "span",
44533
+ "when",
44534
+ Number.isFinite(parsed) ? t2.lastTouched(blameRelativeTime(Math.round(parsed / 1e3))) : t2.lastTouched(item.lastTouched)
44535
+ );
44536
+ when.title = Number.isFinite(parsed) ? new Date(parsed).toLocaleString() : item.lastTouched;
44537
+ meta.appendChild(when);
44538
+ }
44539
+ row.appendChild(meta);
44540
+ const second = el("span", "meta2");
44541
+ const summary = divergenceSummary(item);
44542
+ const summaryText = el("span", "author", summary);
44543
+ summaryText.title = summary;
44544
+ second.appendChild(summaryText);
44545
+ const merge = copyMergeButton(item);
44546
+ if (merge) second.appendChild(merge);
44547
+ row.appendChild(second);
44548
+ if (item.error) {
44549
+ const error2 = el("span", "meta2");
44550
+ const detail = el("span", "author", item.error);
44551
+ detail.style.color = "var(--danger)";
44552
+ error2.appendChild(detail);
44553
+ row.appendChild(error2);
44554
+ }
44555
+ list2.appendChild(row);
44556
+ }
44557
+ list2.addEventListener("click", (event) => {
44558
+ const row = event.target?.closest(
44559
+ ".history-item"
44560
+ );
44561
+ const id = row?.dataset.wt;
44562
+ if (!id) return;
44563
+ navigate({ wt: id, file: void 0, origin: void 0 });
44564
+ });
44565
+ listPanel.appendChild(list2);
44566
+ if (data.worktrees.length === 1) {
44567
+ listPanel.appendChild(note(t2.intro.listNote));
44568
+ }
44569
+ }
44570
+ function introCard() {
44571
+ const t2 = text3();
44572
+ const card = el("div", "worktree-intro");
44573
+ card.appendChild(el("p", "worktree-intro-title", t2.intro.cardTitle));
44574
+ card.appendChild(el("p", "", t2.intro.cardWhat));
44575
+ card.appendChild(el("p", "", t2.intro.cardWhy));
44576
+ card.appendChild(el("p", "", t2.intro.cardHow));
44577
+ const button = el("button", "gdp-btn gdp-btn-sm", t2.intro.cardButton);
44578
+ button.type = "button";
44579
+ button.addEventListener("click", () => {
44580
+ void addWorktree();
44581
+ });
44582
+ card.appendChild(button);
44583
+ return card;
44584
+ }
44585
+ function visibleFiles(item) {
44586
+ const hideTests = deps.getOptions().hideTests;
44587
+ return item.files.filter(
44588
+ (file) => matches(file.path, fileFilter) && !(hideTests && deps.isTestPath(file.path))
44589
+ );
44590
+ }
44591
+ function emptyTree() {
44592
+ return { dirs: /* @__PURE__ */ new Map(), files: [] };
44593
+ }
44594
+ function buildTree(files) {
44595
+ const root = emptyTree();
44596
+ for (const file of files) {
44597
+ const parts = file.path.split("/");
44598
+ let node = root;
44599
+ for (let i2 = 0; i2 < parts.length - 1; i2++) {
44600
+ const name = parts[i2];
44601
+ let child = node.dirs.get(name);
44602
+ if (!child) {
44603
+ child = emptyTree();
44604
+ node.dirs.set(name, child);
44605
+ }
44606
+ node = child;
44607
+ }
44608
+ node.files.push(file);
44609
+ }
44610
+ return root;
44611
+ }
44612
+ function collapseChain(name, node) {
44613
+ let label = name;
44614
+ let current = node;
44615
+ while (current.files.length === 0 && current.dirs.size === 1) {
44616
+ const [childName, child] = [...current.dirs.entries()][0];
44617
+ label = `${label}/${childName}`;
44618
+ current = child;
44619
+ }
44620
+ return [label, current];
44621
+ }
44622
+ function fileRow(item, file, depth, label) {
44623
+ const t2 = text3();
44624
+ const row = el("li", "tree-file");
44625
+ row.tabIndex = -1;
44626
+ row.dataset.path = file.path;
44627
+ row.dataset.key = fileKey(file);
44628
+ row.dataset.type = "blob";
44629
+ const currentRoute = route();
44630
+ if (currentRoute?.file === file.path && currentRoute.origin === file.origin) {
44631
+ row.classList.add("active");
44632
+ }
44633
+ row.style.setProperty("--lvl-pad", `${12 + depth * 14}px`);
44634
+ row.appendChild(el("span", "chev-spacer"));
44635
+ const badge = el("span", `badge ${file.status}`, file.status);
44636
+ badge.title = t2.files.statusTitles[file.status] || file.status;
44637
+ row.appendChild(badge);
44638
+ const name = el("span", "name", label);
44639
+ name.title = file.path;
44640
+ row.appendChild(name);
44641
+ const others = overlapOthers(item, file);
44642
+ if (others.length) {
44643
+ row.classList.add("worktree-overlap");
44644
+ const mark = el(
44645
+ "span",
44646
+ "stat worktree-overlap-count",
44647
+ String(others.length)
44648
+ );
44649
+ mark.title = t2.files.overlapTitle(others);
44650
+ row.appendChild(mark);
44651
+ name.title = `${file.path}
44652
+ ${t2.files.overlapTitle(others)}`;
44653
+ }
44654
+ return row;
44655
+ }
44656
+ function renderTreeInto(parent, item, node, depth) {
44657
+ for (const [rawName, rawChild] of node.dirs) {
44658
+ const [label, child] = collapseChain(rawName, rawChild);
44659
+ const li = el("li", "tree-dir");
44660
+ li.tabIndex = -1;
44661
+ li.dataset.type = "tree";
44662
+ li.style.setProperty("--lvl-pad", `${12 + depth * 14}px`);
44663
+ const chev = el("span", "chev");
44664
+ chev.innerHTML = chevronSvg();
44665
+ const icon = el("span", "dir-icon");
44666
+ icon.innerHTML = folderSvg(false);
44667
+ const name = el("span", "name", label);
44668
+ name.title = label;
44669
+ li.append(chev, icon, name);
44670
+ const toggle = (event) => {
44671
+ event.stopPropagation();
44672
+ const collapsed = li.classList.toggle("collapsed");
44673
+ icon.innerHTML = folderSvg(collapsed);
44674
+ };
44675
+ chev.addEventListener("click", toggle);
44676
+ icon.addEventListener("click", toggle);
44677
+ parent.appendChild(li);
44678
+ const children = el("ul", "tree-children");
44679
+ renderTreeInto(children, item, child, depth + 1);
44680
+ parent.appendChild(children);
44681
+ }
44682
+ for (const file of node.files) {
44683
+ const parts = file.path.split("/");
44684
+ parent.appendChild(
44685
+ fileRow(item, file, depth, parts[parts.length - 1] || file.path)
44686
+ );
44687
+ }
44688
+ }
44689
+ function renderFiles() {
44690
+ const t2 = text3();
44691
+ const list2 = document.getElementById("filelist");
44692
+ const title = document.querySelector(".sb-title");
44693
+ const totals = document.getElementById("totals");
44694
+ if (!list2) return;
44695
+ const tree = deps.getSidebarView() === "tree";
44696
+ list2.classList.toggle("tree", tree);
44697
+ for (const button of sidebarViewButtons()) {
44698
+ const active = button.dataset.view === (tree ? "tree" : "flat");
44699
+ button.classList.toggle("active", active);
44700
+ button.setAttribute("aria-pressed", String(active));
44701
+ }
44702
+ for (const button of document.querySelectorAll(
44703
+ ".sb-tree-action"
44704
+ )) {
44705
+ button.disabled = !tree;
44706
+ }
44707
+ if (title) title.textContent = t2.panes.files;
44708
+ const item = selectedWorktree();
44709
+ if (totals) {
44710
+ totals.textContent = item ? t2.files.heading(item.fileCount) : "";
44711
+ }
44712
+ list2.replaceChildren();
44713
+ if (!item) {
44714
+ list2.appendChild(note(t2.panes.selectWorktree));
44715
+ return;
44716
+ }
44717
+ if (!item.fileCount) {
44718
+ list2.appendChild(note(t2.files.none));
44719
+ return;
44720
+ }
44721
+ const shown = visibleFiles(item);
44722
+ if (!shown.length) {
44723
+ list2.appendChild(note(t2.panes.noFileMatch));
44724
+ return;
44725
+ }
44726
+ if (shown.some((entry) => overlapOthers(item, entry).length > 0)) {
44727
+ list2.appendChild(
44728
+ el("li", "worktree-overlap-legend", t2.intro.overlapLegend)
44729
+ );
44730
+ }
44731
+ for (const group of ["uncommitted", "committed"]) {
44732
+ const files = shown.filter((file) => file.origin === group);
44733
+ if (!files.length) continue;
44734
+ list2.appendChild(
44735
+ el(
44736
+ "li",
44737
+ "worktree-file-group",
44738
+ group === "uncommitted" ? t2.files.uncommitted : t2.files.committed
44739
+ )
44740
+ );
44741
+ if (tree) renderTreeInto(list2, item, buildTree(files), 0);
44742
+ else {
44743
+ for (const file of files) {
44744
+ list2.appendChild(fileRow(item, file, 0, file.path));
44745
+ }
44746
+ }
44747
+ }
44748
+ }
44749
+ function onFileListClick(event) {
44750
+ const row = event.target?.closest(
44751
+ ".tree-file"
44752
+ );
44753
+ const key = row?.dataset.key;
44754
+ if (!key) return;
44755
+ const shell = document.querySelector(
44756
+ `#diff .gdp-file-shell[data-key="${CSS.escape(key)}"]`
44757
+ );
44758
+ restoredRouteKey = key;
44759
+ shell?.scrollIntoView({ block: "start" });
44760
+ const [origin, ...rest] = key.split(":");
44761
+ navigate(
44762
+ {
44763
+ file: rest.join(":"),
44764
+ origin: origin === "committed" ? "committed" : "uncommitted"
44765
+ },
44766
+ true
44767
+ );
44768
+ }
44769
+ function diffKey(item) {
44770
+ const options = deps.getOptions();
44771
+ return [
44772
+ item?.path || "",
44773
+ options.layout,
44774
+ options.ignoreWs ? "w" : "",
44775
+ options.hideTests ? "t" : "",
44776
+ options.syntax ? "s" : "",
44777
+ fileFilter
44778
+ ].join("|");
44779
+ }
44780
+ async function loadShell(item, file, shell) {
44781
+ const seq = lifecycle;
44782
+ const renderKey = diffKey(item);
44783
+ const t2 = text3();
44784
+ const body = shell.querySelector(".gdp-shell-body");
44785
+ if (!body) return;
44786
+ const params = new URLSearchParams({
44787
+ path: item.path,
44788
+ file: file.path,
44789
+ origin: file.origin
44790
+ });
44791
+ if (file.status === "U") params.set("untracked", "1");
44792
+ if (deps.getOptions().ignoreWs) params.set("ignore_ws", "1");
44793
+ try {
44794
+ const res = await deps.trackLoad(
44795
+ fetch(`/_worktree/diff?${params.toString()}`).then(async (response) => {
44796
+ if (!response.ok) {
44797
+ throw new Error(await response.text() || `${response.status}`);
44798
+ }
44799
+ return await response.json();
44800
+ })
44801
+ );
44802
+ if (!isCurrent(seq) || !shell.isConnected || diffKey(item) !== renderKey) {
44803
+ return;
44804
+ }
44805
+ const options = deps.getOptions();
44806
+ const hljs = options.syntax ? await deps.loadHljs() : null;
44807
+ if (!isCurrent(seq) || !shell.isConnected || diffKey(item) !== renderKey) {
44808
+ return;
44809
+ }
44810
+ body.replaceChildren();
44811
+ if (!res.diff.trim()) {
44812
+ body.appendChild(el("div", "gdp-info", t2.panes.diffEmpty));
44813
+ } else {
44814
+ const head = shell.querySelector(".gdp-shell-header");
44815
+ if (head) head.style.display = "none";
44816
+ if (res.truncated) {
44817
+ body.appendChild(
44818
+ el(
44819
+ "div",
44820
+ "gdp-info",
44821
+ t2.panes.diffTruncated(res.renderedHunks, res.totalHunks)
44822
+ )
44823
+ );
44824
+ }
44825
+ const host = el("div", "");
44826
+ body.appendChild(host);
44827
+ const ui = new window.Diff2HtmlUI(
44828
+ host,
44829
+ res.diff,
44830
+ {
44831
+ drawFileList: false,
44832
+ matching: "lines",
44833
+ // topbar の 統合 / 分割。
44834
+ outputFormat: options.layout,
44835
+ synchronisedScroll: true,
44836
+ // topbar の「構文」。diff2html 自身に塗らせる (Diff ビューアは
44837
+ // 巨大ファイル対策で自前の idle 描画を持つが、こちらは 1 本ぶんの
44838
+ // 変更が対象なのでライブラリに任せる)。
44839
+ highlight: !!hljs,
44840
+ fileListToggle: false,
44841
+ fileContentToggle: false
44842
+ },
44843
+ hljs
44844
+ );
44845
+ ui.draw();
44846
+ }
44847
+ shell.classList.add("loaded");
44848
+ } catch (error2) {
44849
+ if (!isCurrent(seq) || !shell.isConnected) return;
44850
+ body.replaceChildren();
44851
+ const failure = el(
44852
+ "div",
44853
+ "gdp-info",
44854
+ error2 instanceof Error ? error2.message : t2.panes.diffFailed
44855
+ );
44856
+ failure.setAttribute("role", "alert");
44857
+ body.appendChild(failure);
44858
+ shell.classList.add("loaded");
44859
+ }
44860
+ }
44861
+ function renderDiffs() {
44862
+ const t2 = text3();
44863
+ const diff = document.getElementById("diff");
44864
+ if (!diff) return;
44865
+ const item = selectedWorktree();
44866
+ observer?.disconnect();
44867
+ observer = null;
44868
+ restoredRouteKey = null;
44869
+ diff.replaceChildren();
44870
+ if (!item) {
44871
+ if (data && data.worktrees.length === 1) {
44872
+ diff.appendChild(introCard());
44873
+ } else {
44874
+ diff.appendChild(el("div", "gdp-info", t2.panes.selectWorktree));
44875
+ }
44876
+ diffFor = "";
44877
+ return;
44878
+ }
44879
+ if (!item.fileCount) {
44880
+ const card = el("div", "gdp-info worktree-empty-diff");
44881
+ card.appendChild(el("p", "", t2.emptyDiff.title));
44882
+ card.appendChild(el("p", "", t2.emptyDiff.body(item.path)));
44883
+ if (!item.missing && !item.bare) {
44884
+ const buttons = el("span", "worktree-empty-diff-actions");
44885
+ const openFolder = openFolderButton(item, true);
44886
+ if (openFolder) buttons.appendChild(openFolder);
44887
+ buttons.appendChild(copyPathButton(item, true));
44888
+ card.appendChild(buttons);
44889
+ }
44890
+ diff.appendChild(card);
44891
+ diffFor = diffKey(item);
44892
+ return;
44893
+ }
44894
+ const files = visibleFiles(item);
44895
+ if (!files.length) {
44896
+ diff.appendChild(el("div", "gdp-info", t2.panes.noFileMatch));
44897
+ diffFor = diffKey(item);
44898
+ return;
44899
+ }
44900
+ const pending = /* @__PURE__ */ new Map();
44901
+ for (const file of files) {
44902
+ const shell = el("div", "gdp-file-shell");
44903
+ shell.dataset.path = file.path;
44904
+ shell.dataset.key = fileKey(file);
44905
+ shell.dataset.status = file.status;
44906
+ const head = el("div", "gdp-shell-header");
44907
+ head.appendChild(el("span", `status-pill ${file.status}`, file.status));
44908
+ head.appendChild(el("span", "path", file.path));
44909
+ const stats = el("span", "stats");
44910
+ stats.appendChild(el("span", "a", `+${file.additions}`));
44911
+ stats.appendChild(el("span", "d", `−${file.deletions}`));
44912
+ head.appendChild(stats);
44913
+ shell.appendChild(head);
44914
+ const body = el("div", "gdp-shell-body");
44915
+ body.appendChild(el("div", "gdp-info", t2.panes.diffLoading));
44916
+ shell.appendChild(body);
44917
+ diff.appendChild(shell);
44918
+ pending.set(shell, file);
44919
+ }
44920
+ diffFor = diffKey(item);
44921
+ if (typeof IntersectionObserver === "undefined") {
44922
+ for (const [shell, file] of pending) void loadShell(item, file, shell);
44923
+ return;
44924
+ }
44925
+ observer = new IntersectionObserver(
44926
+ (entries) => {
44927
+ for (const entry of entries) {
44928
+ if (!entry.isIntersecting) continue;
44929
+ const shell = entry.target;
44930
+ const file = pending.get(shell);
44931
+ if (!file) continue;
44932
+ pending.delete(shell);
44933
+ observer?.unobserve(shell);
44934
+ void loadShell(item, file, shell);
44935
+ }
44936
+ },
44937
+ { rootMargin: DIFF_PREFETCH_MARGIN }
44938
+ );
44939
+ for (const shell of pending.keys()) observer.observe(shell);
44940
+ }
44941
+ function render() {
44942
+ renderList();
44943
+ renderFiles();
44944
+ const item = selectedWorktree();
44945
+ if (diffKey(item) !== diffFor) renderDiffs();
44946
+ restoreRouteSelection();
44947
+ }
44948
+ function restoreRouteSelection() {
44949
+ const current = route();
44950
+ if (!current?.file || !current.origin) {
44951
+ restoredRouteKey = null;
44952
+ return;
44953
+ }
44954
+ const key = `${current.origin}:${current.file}`;
44955
+ if (key === restoredRouteKey) return;
44956
+ const shell = document.querySelector(
44957
+ `#diff .gdp-file-shell[data-key="${CSS.escape(key)}"]`
44958
+ );
44959
+ if (!shell) return;
44960
+ restoredRouteKey = key;
44961
+ shell.scrollIntoView({ block: "start" });
44962
+ }
44963
+ return {
44964
+ async enter() {
44965
+ if (deps.getRoute().screen !== "worktree") return;
44966
+ if (mounted) {
44967
+ render();
44968
+ return;
44969
+ }
44970
+ mount();
44971
+ await refresh();
44972
+ },
44973
+ suspend,
44974
+ reload() {
44975
+ if (!mounted) return this.enter();
44976
+ return refresh();
44977
+ },
44978
+ displayOptionsChanged() {
44979
+ if (!mounted) return;
44980
+ diffFor = null;
44981
+ render();
44982
+ },
44983
+ handleSse() {
44984
+ if (!mounted) return;
44985
+ if (sseTimer !== null) window.clearTimeout(sseTimer);
44986
+ sseTimer = window.setTimeout(() => {
44987
+ sseTimer = null;
44988
+ void refresh();
44989
+ }, SSE_REFRESH_DELAY_MS);
44990
+ },
44991
+ localize() {
44992
+ if (!mounted) return;
44993
+ diffFor = null;
44994
+ render();
44995
+ }
44996
+ };
44997
+ }
44998
+
43140
44999
  // web-src/app.ts
43141
45000
  window.GdpExpandLogic = GdpExpandLogic;
43142
45001
  (() => {
@@ -43310,12 +45169,12 @@ ${formatErrorDetail(error2)}`);
43310
45169
  }
43311
45170
  function setProjectBranch(branch) {
43312
45171
  PROJECT_BRANCH = branch;
43313
- const el = document.querySelector("#project-branch");
43314
- if (!el) return;
43315
- el.hidden = !branch;
43316
- const name = el.querySelector(".project-branch-name");
45172
+ const el2 = document.querySelector("#project-branch");
45173
+ if (!el2) return;
45174
+ el2.hidden = !branch;
45175
+ const name = el2.querySelector(".project-branch-name");
43317
45176
  if (name) name.textContent = branch;
43318
- el.title = branch ? `Current branch: ${branch}` : "";
45177
+ el2.title = branch ? `Current branch: ${branch}` : "";
43319
45178
  }
43320
45179
  function mergeLocalSettings(patch) {
43321
45180
  const next = { ...APP_SETTINGS };
@@ -44209,6 +46068,7 @@ ${error2.stack}` : ""}`
44209
46068
  history: "History",
44210
46069
  journal: "Work Log",
44211
46070
  database: "Datastores",
46071
+ worktree: "Worktrees",
44212
46072
  tools: "Tools",
44213
46073
  help: "Settings & Help"
44214
46074
  },
@@ -44544,6 +46404,7 @@ ${error2.stack}` : ""}`
44544
46404
  history: "履歴",
44545
46405
  journal: "ワークログ",
44546
46406
  database: "データストア",
46407
+ worktree: "作業ツリー",
44547
46408
  tools: "ツール",
44548
46409
  help: "設定・ヘルプ"
44549
46410
  },
@@ -44877,8 +46738,8 @@ ${error2.stack}` : ""}`
44877
46738
  return UI_TEXT[STATE.language];
44878
46739
  }
44879
46740
  function setElementText(selector, text3) {
44880
- const el = document.querySelector(selector);
44881
- if (el) el.textContent = text3;
46741
+ const el2 = document.querySelector(selector);
46742
+ if (el2) el2.textContent = text3;
44882
46743
  }
44883
46744
  function setButtonLabel(button, text3) {
44884
46745
  if (button) button.textContent = text3;
@@ -45064,6 +46925,7 @@ ${error2.stack}` : ""}`
45064
46925
  });
45065
46926
  relocalizeHistory?.();
45066
46927
  relocalizeJournal?.();
46928
+ relocalizeWorktree?.();
45067
46929
  relocalizeViewerSettings?.();
45068
46930
  SOURCE_VIEW.localize();
45069
46931
  setElementText(".annotation-panel-head strong", text3.annotations.title);
@@ -45097,6 +46959,7 @@ ${error2.stack}` : ""}`
45097
46959
  }
45098
46960
  let relocalizeHistory = null;
45099
46961
  let relocalizeJournal = null;
46962
+ let relocalizeWorktree = null;
45100
46963
  let relocalizeTools = null;
45101
46964
  let relocalizeViewerSettings = null;
45102
46965
  let relocalizeTerminal = null;
@@ -45125,14 +46988,14 @@ ${error2.stack}` : ""}`
45125
46988
  }
45126
46989
  }
45127
46990
  function setStatus(s2) {
45128
- const el = $("#status");
45129
- el.classList.remove("live", "refreshing", "error");
45130
- if (s2) el.classList.add(s2);
46991
+ const el2 = $("#status");
46992
+ el2.classList.remove("live", "refreshing", "error");
46993
+ if (s2) el2.classList.add(s2);
45131
46994
  const text3 = uiText();
45132
46995
  const label = s2 === "live" ? text3.global.statusLive : s2 === "refreshing" ? text3.global.statusLoading : s2 === "error" ? text3.global.statusError : text3.global.statusIdle;
45133
- const labelEl = el.querySelector(".status-label");
46996
+ const labelEl = el2.querySelector(".status-label");
45134
46997
  if (labelEl) labelEl.textContent = label;
45135
- el.setAttribute("aria-label", label);
46998
+ el2.setAttribute("aria-label", label);
45136
46999
  updateNetworkActivity();
45137
47000
  }
45138
47001
  function applyTheme() {
@@ -45196,7 +47059,8 @@ ${error2.stack}` : ""}`
45196
47059
  }
45197
47060
  function routeCanUseSyntaxHighlighter(route = STATE.route) {
45198
47061
  if (!STATE.syntaxHighlight) return false;
45199
- if (route.screen === "diff" || route.screen === "history") return true;
47062
+ if (route.screen === "diff" || route.screen === "history" || route.screen === "worktree")
47063
+ return true;
45200
47064
  return route.screen === "file" && sourceInternalPathKind(route.path) === null;
45201
47065
  }
45202
47066
  function ensureSyntaxHighlighterForRoute() {
@@ -45204,6 +47068,7 @@ ${error2.stack}` : ""}`
45204
47068
  loadSyntaxHighlighter().then((hljsRef) => {
45205
47069
  if (!hljsRef) return;
45206
47070
  rerenderLoadedDiffs();
47071
+ WORKTREE_VIEW?.displayOptionsChanged();
45207
47072
  });
45208
47073
  }
45209
47074
  function setLayout(layout, persist = true) {
@@ -45223,9 +47088,10 @@ ${error2.stack}` : ""}`
45223
47088
  }
45224
47089
  scheduleIdleHighlight(card, file);
45225
47090
  });
47091
+ WORKTREE_VIEW?.displayOptionsChanged();
45226
47092
  }
45227
- function setChevronIcon(el) {
45228
- el.innerHTML = '<svg class="octicon octicon-chevron-down" viewBox="0 0 12 12" width="12" height="12" fill="currentColor" aria-hidden="true"><path fill="currentColor" d="' + CHEVRON_DOWN_12_PATH + '"></path></svg>';
47093
+ function setChevronIcon(el2) {
47094
+ el2.innerHTML = '<svg class="octicon octicon-chevron-down" viewBox="0 0 12 12" width="12" height="12" fill="currentColor" aria-hidden="true"><path fill="currentColor" d="' + CHEVRON_DOWN_12_PATH + '"></path></svg>';
45229
47095
  }
45230
47096
  function scopeOmitSourceLabel() {
45231
47097
  return savedScopeOmitDirs() != null || savedScopeExcludeNames() != null ? uiText().settings.browserOverride : uiText().settings.serverDefault;
@@ -45506,6 +47372,7 @@ ${error2.stack}` : ""}`
45506
47372
  }
45507
47373
  let ANNOTATIONS_UI = null;
45508
47374
  let JOURNAL_VIEW = null;
47375
+ let WORKTREE_VIEW = null;
45509
47376
  function applyInlineAnnotations() {
45510
47377
  ANNOTATIONS_UI?.applyInlineAnnotations();
45511
47378
  }
@@ -45546,12 +47413,53 @@ ${error2.stack}` : ""}`
45546
47413
  );
45547
47414
  }
45548
47415
  }
47416
+ let fileRouteSignature = null;
47417
+ let fileRouteSignatureSeed = null;
47418
+ let fileRouteSignatureCheck = null;
47419
+ function fileRouteSignatureKey(route) {
47420
+ return `${route.view || "blob"}\0${route.path}\0${route.ref || "worktree"}`;
47421
+ }
47422
+ async function readFileRouteSignature(route) {
47423
+ const info = await REPO_VIEW.loadRawFileInfo({
47424
+ path: route.path,
47425
+ ref: route.ref || "worktree"
47426
+ });
47427
+ return rawFileInfoSignature(info);
47428
+ }
47429
+ function seedFileRouteSignature(route) {
47430
+ const key = fileRouteSignatureKey(route);
47431
+ const seed = trackLoad(readFileRouteSignature(route)).then((sig) => {
47432
+ if (fileRouteSignatureSeed === seed) fileRouteSignatureSeed = null;
47433
+ if (sig === null) return;
47434
+ fileRouteSignature = { key, sig };
47435
+ });
47436
+ fileRouteSignatureSeed = seed;
47437
+ }
47438
+ function refreshFileRouteIfChanged(route) {
47439
+ if (fileRouteSignatureCheck) return;
47440
+ const key = fileRouteSignatureKey(route);
47441
+ const pendingSeed = fileRouteSignatureSeed ?? Promise.resolve();
47442
+ fileRouteSignatureCheck = pendingSeed.then(() => trackLoad(readFileRouteSignature(route))).then((sig) => {
47443
+ fileRouteSignatureCheck = null;
47444
+ if (sig === null) return;
47445
+ const routeNow = STATE.route;
47446
+ if (routeNow.screen !== "file" || !isBlobOrBlameFileRoute(routeNow) || fileRouteSignatureKey(routeNow) !== key)
47447
+ return;
47448
+ if (fileSignatureUnchanged(fileRouteSignature, key, sig)) return;
47449
+ fileRouteSignature = { key, sig };
47450
+ dispatchFileRoute(routeNow, { refresh: true });
47451
+ });
47452
+ fileRouteSignatureCheck.catch(() => {
47453
+ fileRouteSignatureCheck = null;
47454
+ });
47455
+ }
45549
47456
  function dispatchFileRoute(route, options = {}) {
45550
47457
  if (route.view === "blob") {
45551
47458
  setStatus("live");
45552
47459
  removeFileHistoryShell2();
45553
47460
  BLAME_VIEW.removeBlamePage();
45554
47461
  applySourceRouteToShell(options);
47462
+ seedFileRouteSignature(route);
45555
47463
  return true;
45556
47464
  }
45557
47465
  if (route.view === "blame") {
@@ -45559,6 +47467,7 @@ ${error2.stack}` : ""}`
45559
47467
  cancelActiveSourceLoad("navigation");
45560
47468
  removeFileHistoryShell2();
45561
47469
  void BLAME_VIEW.renderBlamePage({ path: route.path, ref: route.ref });
47470
+ seedFileRouteSignature(route);
45562
47471
  return true;
45563
47472
  }
45564
47473
  if (route.view === "history") {
@@ -45608,6 +47517,9 @@ ${error2.stack}` : ""}`
45608
47517
  if (previousRoute.screen === "journal" && nextRoute.screen !== "journal") {
45609
47518
  JOURNAL_VIEW?.suspend();
45610
47519
  }
47520
+ if (previousRoute.screen === "worktree" && nextRoute.screen !== "worktree") {
47521
+ WORKTREE_VIEW?.suspend();
47522
+ }
45611
47523
  STATE.route = nextRoute;
45612
47524
  STATE.from = nextRoute.range.from;
45613
47525
  STATE.to = nextRoute.range.to;
@@ -45634,13 +47546,19 @@ ${error2.stack}` : ""}`
45634
47546
  removeStandaloneSource();
45635
47547
  void JOURNAL_VIEW?.enter();
45636
47548
  }
47549
+ if (nextRoute.screen === "worktree") {
47550
+ cancelActiveSourceLoad("navigation");
47551
+ setPageMode();
47552
+ removeStandaloneSource();
47553
+ void WORKTREE_VIEW?.enter();
47554
+ }
45637
47555
  }
45638
- function setQueryHistoryPanelOpen(open) {
47556
+ function setQueryHistoryPanelOpen(open2) {
45639
47557
  const panel = document.getElementById("query-history-panel");
45640
47558
  if (!panel) return;
45641
- panel.hidden = !open;
45642
- document.body.classList.toggle("query-history-panel-open", open);
45643
- if (open && ANNOTATIONS_UI) {
47559
+ panel.hidden = !open2;
47560
+ document.body.classList.toggle("query-history-panel-open", open2);
47561
+ if (open2 && ANNOTATIONS_UI) {
45644
47562
  ANNOTATIONS_UI.setAnnotationPanelOpen(false);
45645
47563
  }
45646
47564
  }
@@ -45679,6 +47597,18 @@ ${error2.stack}` : ""}`
45679
47597
  "gdp-journal-page",
45680
47598
  STATE.route.screen === "journal"
45681
47599
  );
47600
+ document.body.classList.toggle(
47601
+ "gdp-worktree-page",
47602
+ STATE.route.screen === "worktree"
47603
+ );
47604
+ document.body.toggleAttribute(
47605
+ "data-content-scrolls-when-docked",
47606
+ STATE.route.screen === "diff" || STATE.route.screen === "history" || STATE.route.screen === "repo" || STATE.route.screen === "file" || STATE.route.screen === "help" || STATE.route.screen === "worktree"
47607
+ );
47608
+ document.body.toggleAttribute(
47609
+ "data-history-list-panel",
47610
+ historyPanelRoute || STATE.route.screen === "worktree"
47611
+ );
45682
47612
  const repoTargetWrap = document.querySelector("#repo-target-wrap");
45683
47613
  if (!repoSidebarRoute && repoTargetWrap) {
45684
47614
  repoTargetWrap.hidden = true;
@@ -45753,6 +47683,14 @@ ${error2.stack}` : ""}`
45753
47683
  })
45754
47684
  );
45755
47685
  }
47686
+ if (link2.dataset.route === "worktree") {
47687
+ link2.href = withOverlayState(
47688
+ buildRoute({
47689
+ screen: "worktree",
47690
+ range: currentRange()
47691
+ })
47692
+ );
47693
+ }
45756
47694
  if (link2.dataset.route === "help") {
45757
47695
  link2.href = withOverlayState(
45758
47696
  buildRoute({
@@ -45785,7 +47723,9 @@ ${error2.stack}` : ""}`
45785
47723
  setTimeout(() => {
45786
47724
  button?.classList.remove("opened");
45787
47725
  }, 1200);
45788
- } catch {
47726
+ return true;
47727
+ } catch (error2) {
47728
+ console.error("[code-viewer] failed to open path in OS", error2);
45789
47729
  if (button) {
45790
47730
  button.classList.add("failed");
45791
47731
  button.title = "failed to open in OS";
@@ -45794,6 +47734,7 @@ ${error2.stack}` : ""}`
45794
47734
  button.title = oldTitle || "open in OS";
45795
47735
  }, 1600);
45796
47736
  }
47737
+ return false;
45797
47738
  } finally {
45798
47739
  if (button) button.disabled = false;
45799
47740
  }
@@ -46316,10 +48257,7 @@ ${error2.stack}` : ""}`
46316
48257
  moveActiveSidebarItem(e2.key === "ArrowDown" ? 1 : -1);
46317
48258
  } else if (e2.key === "Escape") {
46318
48259
  if (sbFilter.value) {
46319
- sbFilter.value = "";
46320
- syncSidebarFilterClearButton();
46321
- flushSidebarFilter();
46322
- applyFilter();
48260
+ clearSidebarFilter();
46323
48261
  } else {
46324
48262
  sbFilter.blur();
46325
48263
  }
@@ -46767,6 +48705,9 @@ ${error2.stack}` : ""}`
46767
48705
  setStatus("live");
46768
48706
  return Promise.resolve(null);
46769
48707
  }
48708
+ if (STATE.route.screen === "worktree") {
48709
+ return (WORKTREE_VIEW?.reload() ?? Promise.resolve()).then(() => null);
48710
+ }
46770
48711
  if (STATE.route.screen === "file" && !(isFileHistoryRoute(STATE.route) && activeHistoryPathFilter) && // load() is by definition a reload (topbar Reload button, ignore-ws
46771
48712
  // toggle, catch-up after SSE reconnect) - bypass the idempotent-mount
46772
48713
  // guard so the blob view actually refetches, matching the pre-guard
@@ -46833,6 +48774,8 @@ ${error2.stack}` : ""}`
46833
48774
  } else if (STATE.route.screen === "journal") {
46834
48775
  setStatus("live");
46835
48776
  void JOURNAL_VIEW?.enter();
48777
+ } else if (STATE.route.screen === "worktree") {
48778
+ void WORKTREE_VIEW?.enter();
46836
48779
  } else load();
46837
48780
  syncLineRefPill();
46838
48781
  syncDoctorSheetFromUrl();
@@ -46975,9 +48918,9 @@ ${error2.stack}` : ""}`
46975
48918
  function isDoctorOverlayOpen() {
46976
48919
  return parseDoctorOverlay(window.location.pathname, window.location.search);
46977
48920
  }
46978
- function updateUrlForDoctorOverlay(open) {
48921
+ function updateUrlForDoctorOverlay(open2) {
46979
48922
  const current = window.location.pathname + window.location.search;
46980
- const next = withDoctorOverlay(current, open);
48923
+ const next = withDoctorOverlay(current, open2);
46981
48924
  if (next !== current) {
46982
48925
  history.replaceState(history.state, "", next + window.location.hash);
46983
48926
  }
@@ -46996,9 +48939,9 @@ ${error2.stack}` : ""}`
46996
48939
  }
46997
48940
  function syncDoctorSheetFromUrl() {
46998
48941
  const shouldOpen = isDoctorOverlayOpen();
46999
- const open = DOCTOR_VIEW.isOpen();
47000
- if (shouldOpen && !open) void DOCTOR_VIEW.open();
47001
- else if (!shouldOpen && open) DOCTOR_VIEW.close();
48942
+ const open2 = DOCTOR_VIEW.isOpen();
48943
+ if (shouldOpen && !open2) void DOCTOR_VIEW.open();
48944
+ else if (!shouldOpen && open2) DOCTOR_VIEW.close();
47002
48945
  }
47003
48946
  document.getElementById("doctor-sheet-overlay")?.addEventListener("click", () => closeDoctorSheet());
47004
48947
  document.addEventListener("keydown", (event) => {
@@ -47030,10 +48973,10 @@ ${error2.stack}` : ""}`
47030
48973
  function syncAppPanel() {
47031
48974
  const tools = parseToolsOverlay(window.location.search) !== null;
47032
48975
  const terminal = parseTerminalOverlay(window.location.search) !== null;
47033
- const open = tools || terminal;
48976
+ const open2 = tools || terminal;
47034
48977
  const panel = document.getElementById("app-panel");
47035
48978
  if (panel) {
47036
- panel.classList.toggle("app-panel-open", open);
48979
+ panel.classList.toggle("app-panel-open", open2);
47037
48980
  }
47038
48981
  for (const [id, selected] of [
47039
48982
  ["#panel-tab-tools", tools],
@@ -47121,10 +49064,10 @@ ${error2.stack}` : ""}`
47121
49064
  }
47122
49065
  function syncToolsSheetFromUrl() {
47123
49066
  const tool = openToolsOverlay();
47124
- const open = TOOLS_VIEW.isOpen();
47125
- if (tool && (!open || TOOLS_VIEW.getActiveTool() !== tool))
49067
+ const open2 = TOOLS_VIEW.isOpen();
49068
+ if (tool && (!open2 || TOOLS_VIEW.getActiveTool() !== tool))
47126
49069
  void TOOLS_VIEW.open(tool);
47127
- else if (!tool && open) TOOLS_VIEW.close();
49070
+ else if (!tool && open2) TOOLS_VIEW.close();
47128
49071
  syncAppPanel();
47129
49072
  }
47130
49073
  const TERMINAL_VIEW = createTerminalView({
@@ -47167,11 +49110,11 @@ ${error2.stack}` : ""}`
47167
49110
  }
47168
49111
  function syncTerminalSheetFromUrl() {
47169
49112
  const state = parseTerminalOverlay(window.location.search);
47170
- const open = TERMINAL_VIEW.isOpen();
49113
+ const open2 = TERMINAL_VIEW.isOpen();
47171
49114
  const target = state === "open" ? null : state;
47172
- if (state && (!open || target && TERMINAL_VIEW.getActiveTarget() !== target)) {
49115
+ if (state && (!open2 || target && TERMINAL_VIEW.getActiveTarget() !== target)) {
47173
49116
  void TERMINAL_VIEW.open(target);
47174
- } else if (!state && open) {
49117
+ } else if (!state && open2) {
47175
49118
  TERMINAL_VIEW.close();
47176
49119
  }
47177
49120
  syncAppPanel();
@@ -47187,6 +49130,30 @@ ${error2.stack}` : ""}`
47187
49130
  setStatus
47188
49131
  });
47189
49132
  relocalizeJournal = () => JOURNAL_VIEW?.localize();
49133
+ WORKTREE_VIEW = createWorktreeView({
49134
+ getRoute: () => STATE.route,
49135
+ // topbar のトグルは Diff ビューアと同じものを使う。値の出所も同じ STATE。
49136
+ getOptions: () => ({
49137
+ layout: STATE.layout,
49138
+ ignoreWs: STATE.ignoreWs,
49139
+ hideTests: STATE.hideTests,
49140
+ syntax: STATE.syntaxHighlight
49141
+ }),
49142
+ isTestPath: isTestFilePath,
49143
+ getSidebarView: () => STATE.sbView,
49144
+ // ハイライタは遅延バンドル。差分を描く直前に読み込ませる。
49145
+ loadHljs: loadSyntaxHighlighter,
49146
+ setRoute,
49147
+ currentRange,
49148
+ trackLoad,
49149
+ getText: () => worktreeText(STATE.language),
49150
+ setPageMode,
49151
+ syncHeaderMenu,
49152
+ setStatus,
49153
+ createOpenPathButton,
49154
+ openPathInOs: (path, kind) => openPathInOs(path, kind)
49155
+ });
49156
+ relocalizeWorktree = () => WORKTREE_VIEW?.localize();
47190
49157
  const DATABASE_VIEW = createDatabaseView({
47191
49158
  setRoute,
47192
49159
  setPageMode,
@@ -47245,6 +49212,8 @@ ${error2.stack}` : ""}`
47245
49212
  DATABASE_VIEW.suspend();
47246
49213
  if (previousRoute.screen === "journal" && nextRoute.screen !== "journal")
47247
49214
  JOURNAL_VIEW?.suspend();
49215
+ if (previousRoute.screen === "worktree" && nextRoute.screen !== "worktree")
49216
+ WORKTREE_VIEW?.suspend();
47248
49217
  if (isHistoryPanelRoute(previousRoute) && !isHistoryPanelRoute(nextRoute))
47249
49218
  HISTORY_VIEW.leaveHistory();
47250
49219
  if (isHistoryPanelRoute(previousRoute) && !isHistoryPanelRoute(nextRoute))
@@ -47314,6 +49283,13 @@ ${error2.stack}` : ""}`
47314
49283
  setStatus("live");
47315
49284
  return;
47316
49285
  }
49286
+ if (STATE.route.screen === "worktree") {
49287
+ cancelActiveSourceLoad("navigation");
49288
+ setPageMode();
49289
+ removeStandaloneSource();
49290
+ void WORKTREE_VIEW?.enter();
49291
+ return;
49292
+ }
47317
49293
  if (STATE.route.screen !== "file") {
47318
49294
  cancelActiveSourceLoad("navigation");
47319
49295
  setPageMode();
@@ -47365,6 +49341,7 @@ ${error2.stack}` : ""}`
47365
49341
  ensureSyntaxHighlighterForRoute();
47366
49342
  } else {
47367
49343
  rerenderLoadedDiffs();
49344
+ WORKTREE_VIEW?.displayOptionsChanged();
47368
49345
  }
47369
49346
  }
47370
49347
  setHighlightButton(STATE.syntaxHighlight && getHljs() ? "loaded" : "idle");
@@ -47389,6 +49366,7 @@ ${error2.stack}` : ""}`
47389
49366
  else updateTreeDirVisibility();
47390
49367
  if (typeof applyViewedState === "function") applyViewedState();
47391
49368
  applyHideTestsToMeta();
49369
+ WORKTREE_VIEW?.displayOptionsChanged();
47392
49370
  }
47393
49371
  function visibleDiffMetaForBrief(meta) {
47394
49372
  if (!meta.totals) return meta;
@@ -47441,7 +49419,7 @@ ${error2.stack}` : ""}`
47441
49419
  getFiles: () => STATE.files,
47442
49420
  getRoute: () => STATE.route,
47443
49421
  getAnnotationPanelOpen: () => APP_SETTINGS.annotationPanelOpen === true,
47444
- setAnnotationPanelOpenState: (open) => patchSettings({ annotationPanelOpen: open }),
49422
+ setAnnotationPanelOpenState: (open2) => patchSettings({ annotationPanelOpen: open2 }),
47445
49423
  getAnnotationPanelWidth: () => APP_SETTINGS.annotationPanelWidth,
47446
49424
  setAnnotationPanelWidth: (width) => patchSettings({ annotationPanelWidth: width }),
47447
49425
  getAnnotationFollow: () => APP_SETTINGS.annotationFollow !== false,
@@ -47502,7 +49480,7 @@ ${error2.stack}` : ""}`
47502
49480
  $,
47503
49481
  getActiveSessionEntries: () => ANNOTATIONS_UI?.getActiveSessionEntries() ?? [],
47504
49482
  openAnnotationEntry: (id) => ANNOTATIONS_UI ? ANNOTATIONS_UI.openAnnotationEntry(id) : Promise.resolve(),
47505
- setAnnotationPanelOpen: (open) => ANNOTATIONS_UI?.setAnnotationPanelOpen(open),
49483
+ setAnnotationPanelOpen: (open2) => ANNOTATIONS_UI?.setAnnotationPanelOpen(open2),
47506
49484
  onAnnotationsChanged: (cb) => ANNOTATIONS_UI?.onAnnotationsChanged(cb),
47507
49485
  onAnnotationOpened: (cb) => ANNOTATIONS_UI?.onAnnotationOpened(cb),
47508
49486
  getActiveAnnotationId: () => ANNOTATIONS_UI ? ANNOTATIONS_UI.getActiveAnnotationId() : null,
@@ -47661,7 +49639,7 @@ ${error2.stack}` : ""}`
47661
49639
  if (isBlobOrBlameFileRoute(route)) {
47662
49640
  const viewingPath = route.path;
47663
49641
  if (viewingPath && !changedPathsCoverPath(paths, viewingPath)) return;
47664
- dispatchFileRoute(route, { refresh: true });
49642
+ refreshFileRouteIfChanged(route);
47665
49643
  return;
47666
49644
  }
47667
49645
  if (route.screen === "repo") {
@@ -47745,6 +49723,7 @@ ${error2.stack}` : ""}`
47745
49723
  }
47746
49724
  }
47747
49725
  if (isHistoryPanelRoute(STATE.route)) HISTORY_VIEW.notePossibleUpdate();
49726
+ if (STATE.route.screen === "worktree") WORKTREE_VIEW?.handleSse();
47748
49727
  scheduleSseLoad(paths);
47749
49728
  });
47750
49729
  es.addEventListener("watch-limit", (event) => {