@youtyan/code-viewer 0.1.17 → 0.1.19

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
@@ -21,6 +21,21 @@
21
21
  throw Error('Dynamic require of "' + x + '" is not supported');
22
22
  });
23
23
 
24
+ // web-src/catch-up.ts
25
+ function shouldCatchUpDiff(route) {
26
+ return route.screen !== "repo" && !(route.screen === "file" && route.view === "blob");
27
+ }
28
+ function createCatchUpGate(now, minIntervalMs) {
29
+ let lastForceAt = 0;
30
+ return function shouldRun() {
31
+ const current = now();
32
+ if (current - lastForceAt < minIntervalMs)
33
+ return false;
34
+ lastForceAt = current;
35
+ return true;
36
+ };
37
+ }
38
+
24
39
  // web-src/expand-logic.ts
25
40
  function initExpandState(prevHunkEndNew, hunkNewStart) {
26
41
  return {
@@ -78,20 +93,6 @@
78
93
  applyTrailingResult
79
94
  };
80
95
 
81
- // web-src/file-navigation.ts
82
- function nextVisibleFileIndex(currentIndex, itemCount, direction) {
83
- if (itemCount <= 0)
84
- return -1;
85
- if (currentIndex < 0)
86
- return direction > 0 ? 0 : itemCount - 1;
87
- return Math.max(0, Math.min(itemCount - 1, currentIndex + direction));
88
- }
89
-
90
- // web-src/file-path-copy.ts
91
- function filePathClipboardText(path) {
92
- return path || "";
93
- }
94
-
95
96
  // web-src/file-filter.ts
96
97
  function normalizeFileFilterQuery(value) {
97
98
  return (value || "").toLowerCase().trim();
@@ -131,6 +132,20 @@
131
132
  };
132
133
  }
133
134
 
135
+ // web-src/file-navigation.ts
136
+ function nextVisibleFileIndex(currentIndex, itemCount, direction) {
137
+ if (itemCount <= 0)
138
+ return -1;
139
+ if (currentIndex < 0)
140
+ return direction > 0 ? 0 : itemCount - 1;
141
+ return Math.max(0, Math.min(itemCount - 1, currentIndex + direction));
142
+ }
143
+
144
+ // web-src/file-path-copy.ts
145
+ function filePathClipboardText(path) {
146
+ return path || "";
147
+ }
148
+
134
149
  // web-src/focus-scope.ts
135
150
  function isEditableKeyTarget(target) {
136
151
  if (!target)
@@ -344,22 +359,58 @@
344
359
  if (isGlobPathQuery(query)) {
345
360
  return items.map((item) => {
346
361
  const match = globMatchPath(query, item.path);
347
- return match ? { item, score: match.score, ranges: match.ranges, mode: "glob" } : null;
362
+ return match ? {
363
+ item,
364
+ score: match.score,
365
+ ranges: match.ranges,
366
+ mode: "glob"
367
+ } : null;
348
368
  }).filter((item) => item !== null).sort((a, b) => b.score - a.score || a.item.path.localeCompare(b.item.path));
349
369
  }
350
- return rankFuzzyPaths(query, items).map((item) => ({ ...item, mode: "fuzzy" }));
370
+ return rankFuzzyPaths(query, items).map((item) => ({
371
+ ...item,
372
+ mode: "fuzzy"
373
+ }));
351
374
  }
352
375
 
353
376
  // web-src/keymap.ts
354
377
  var DEFAULT_KEY_BINDINGS = [
355
- { action: "open-file-palette", key: "k", ctrl: true, allowEditable: true, allowPaletteOpen: true },
356
- { action: "open-file-palette", key: "k", meta: true, allowEditable: true, allowPaletteOpen: true },
357
- { action: "open-grep-palette", key: "g", ctrl: true, allowEditable: true, allowPaletteOpen: true },
358
- { action: "open-grep-palette", key: "g", meta: true, allowEditable: true, allowPaletteOpen: true },
378
+ {
379
+ action: "open-file-palette",
380
+ key: "k",
381
+ ctrl: true,
382
+ allowEditable: true,
383
+ allowPaletteOpen: true
384
+ },
385
+ {
386
+ action: "open-file-palette",
387
+ key: "k",
388
+ meta: true,
389
+ allowEditable: true,
390
+ allowPaletteOpen: true
391
+ },
392
+ {
393
+ action: "open-grep-palette",
394
+ key: "g",
395
+ ctrl: true,
396
+ allowEditable: true,
397
+ allowPaletteOpen: true
398
+ },
399
+ {
400
+ action: "open-grep-palette",
401
+ key: "g",
402
+ meta: true,
403
+ allowEditable: true,
404
+ allowPaletteOpen: true
405
+ },
359
406
  { action: "focus-file-filter", key: "/" },
360
407
  { action: "focus-sidebar", key: "h", ctrl: true },
361
408
  { action: "focus-main", key: "l", ctrl: true },
362
- { action: "cancel-source-load", key: "escape", requires: { lightboxClosed: true } },
409
+ {
410
+ action: "cancel-source-load",
411
+ key: "escape",
412
+ requires: { lightboxClosed: true }
413
+ },
363
414
  { action: "open-sidebar-item", key: "enter", scope: "sidebar" },
364
415
  { action: "open-sidebar-item", key: "enter", scope: "global" },
365
416
  { action: "sidebar-next", key: "j", scope: "sidebar" },
@@ -384,12 +435,37 @@
384
435
  { action: "scroll-main-page-up", key: "pageup", scope: "global" },
385
436
  { action: "scroll-main-page-down", key: "pagedown", scope: "sidebar" },
386
437
  { action: "scroll-main-page-up", key: "pageup", scope: "sidebar" },
387
- { action: "scroll-main-page-down", key: "arrowdown", scope: "main", ctrl: true },
438
+ {
439
+ action: "scroll-main-page-down",
440
+ key: "arrowdown",
441
+ scope: "main",
442
+ ctrl: true
443
+ },
388
444
  { action: "scroll-main-page-up", key: "arrowup", scope: "main", ctrl: true },
389
- { action: "scroll-main-page-down", key: "arrowdown", scope: "global", ctrl: true },
390
- { action: "scroll-main-page-up", key: "arrowup", scope: "global", ctrl: true },
391
- { action: "scroll-main-page-down", key: "arrowdown", scope: "sidebar", ctrl: true },
392
- { action: "scroll-main-page-up", key: "arrowup", scope: "sidebar", ctrl: true },
445
+ {
446
+ action: "scroll-main-page-down",
447
+ key: "arrowdown",
448
+ scope: "global",
449
+ ctrl: true
450
+ },
451
+ {
452
+ action: "scroll-main-page-up",
453
+ key: "arrowup",
454
+ scope: "global",
455
+ ctrl: true
456
+ },
457
+ {
458
+ action: "scroll-main-page-down",
459
+ key: "arrowdown",
460
+ scope: "sidebar",
461
+ ctrl: true
462
+ },
463
+ {
464
+ action: "scroll-main-page-up",
465
+ key: "arrowup",
466
+ scope: "sidebar",
467
+ ctrl: true
468
+ },
393
469
  { action: "tab-preview", key: "p", scope: "main", pendingG: true },
394
470
  { action: "tab-code", key: "c", scope: "main", pendingG: true },
395
471
  { action: "goto-top", key: "g", pendingG: true },
@@ -433,147 +509,6 @@
433
509
  return null;
434
510
  }
435
511
 
436
- // web-src/search-palette.ts
437
- var PALETTE_RESULT_LIMIT = 50;
438
- function limitPaletteResults(items) {
439
- return items.slice(0, PALETTE_RESULT_LIMIT);
440
- }
441
- function movePaletteSelection(index, count, direction) {
442
- if (count <= 0)
443
- return -1;
444
- if (index < 0)
445
- return direction > 0 ? 0 : count - 1;
446
- return (index + direction + count) % count;
447
- }
448
-
449
- // web-src/catch-up.ts
450
- function shouldCatchUpDiff(route) {
451
- return route.screen !== "repo" && !(route.screen === "file" && route.view === "blob");
452
- }
453
- function createCatchUpGate(now, minIntervalMs) {
454
- let lastForceAt = 0;
455
- return function shouldRun() {
456
- const current = now();
457
- if (current - lastForceAt < minIntervalMs)
458
- return false;
459
- lastForceAt = current;
460
- return true;
461
- };
462
- }
463
-
464
- // web-src/routes.ts
465
- function assertNever(value) {
466
- throw new Error("unhandled route: " + JSON.stringify(value));
467
- }
468
- function parseLegacyRange(value, fallback) {
469
- const raw = value || "";
470
- const sep = raw.indexOf("..");
471
- if (sep < 0)
472
- return fallback;
473
- return {
474
- from: raw.slice(0, sep) || fallback.from,
475
- to: raw.slice(sep + 2) || fallback.to
476
- };
477
- }
478
- function parseLineTarget(value) {
479
- const raw = value || "";
480
- const range = /^(\d+)-(\d+)$/.exec(raw);
481
- if (range) {
482
- const a = Number(range[1]);
483
- const b = Number(range[2]);
484
- const start = Math.min(a, b);
485
- const end = Math.max(a, b);
486
- if (start > 0)
487
- return { start, end };
488
- return;
489
- }
490
- const line = Number(raw);
491
- return Number.isInteger(line) && line > 0 ? line : undefined;
492
- }
493
- function formatLineTarget(line) {
494
- return typeof line === "number" ? String(line) : line.start + "-" + line.end;
495
- }
496
- function parseRoute(pathname, search, fallbackRange) {
497
- const params = new URLSearchParams(search);
498
- const legacyRange = parseLegacyRange(params.get("range"), fallbackRange);
499
- const range = {
500
- from: params.get("from") || legacyRange.from,
501
- to: params.get("to") || legacyRange.to
502
- };
503
- switch (pathname) {
504
- case "/":
505
- case "/index.html":
506
- return {
507
- screen: "repo",
508
- ref: params.get("ref") || params.get("target") || "worktree",
509
- path: params.get("path") || "",
510
- range
511
- };
512
- case "/todif":
513
- case "/todiff":
514
- return {
515
- screen: "diff",
516
- range,
517
- ...params.get("path") ? { path: params.get("path") || "" } : {},
518
- ...parseLineTarget(params.get("line")) ? { line: parseLineTarget(params.get("line")) } : {}
519
- };
520
- case "/file": {
521
- const path = params.get("path") || "";
522
- const target = params.get("target") || "";
523
- const ref = target || params.get("ref") || "worktree";
524
- const line = parseLineTarget(params.get("line"));
525
- if (!path)
526
- return { screen: "unknown", reason: "missing-path", rawPathname: pathname, rawSearch: search, range };
527
- return { screen: "file", path, ref, range, view: target ? "blob" : "detail", ...line ? { line } : {} };
528
- }
529
- case "/help":
530
- return {
531
- screen: "help",
532
- range,
533
- lang: params.get("lang") || "en",
534
- section: params.get("section") || "keybindings"
535
- };
536
- default:
537
- return { screen: "unknown", reason: "unknown-pathname", rawPathname: pathname, rawSearch: search, range };
538
- }
539
- }
540
- function buildRoute(route) {
541
- switch (route.screen) {
542
- case "repo": {
543
- const params = new URLSearchParams;
544
- if (route.ref && route.ref !== "worktree")
545
- params.set("ref", route.ref);
546
- if (route.path)
547
- params.set("path", route.path);
548
- const qs = params.toString();
549
- return "/" + (qs ? "?" + qs : "");
550
- }
551
- case "file":
552
- if (route.view === "blob") {
553
- return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(route.ref || "worktree") + (route.line ? "&line=" + encodeURIComponent(formatLineTarget(route.line)) : "");
554
- }
555
- 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)) : "");
556
- case "diff":
557
- 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)) : "");
558
- case "help": {
559
- const params = new URLSearchParams;
560
- if (route.lang && route.lang !== "en")
561
- params.set("lang", route.lang);
562
- if (route.section && route.section !== "keybindings")
563
- params.set("section", route.section);
564
- const qs = params.toString();
565
- return "/help" + (qs ? "?" + qs : "");
566
- }
567
- case "unknown":
568
- return "/todif?from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree");
569
- default:
570
- return assertNever(route);
571
- }
572
- }
573
- function buildRawFileUrl(target) {
574
- return "/_file?path=" + encodeURIComponent(target.path) + "&ref=" + encodeURIComponent(target.ref || "worktree");
575
- }
576
-
577
512
  // node_modules/markdown-it/lib/common/utils.mjs
578
513
  var exports_utils = {};
579
514
  __export(exports_utils, {
@@ -6155,6 +6090,138 @@
6155
6090
  md.core.ruler.after("inline", "footnote_tail", footnote_tail);
6156
6091
  }
6157
6092
 
6093
+ // web-src/routes.ts
6094
+ function assertNever(value) {
6095
+ throw new Error("unhandled route: " + JSON.stringify(value));
6096
+ }
6097
+ function parseLegacyRange(value, fallback) {
6098
+ const raw = value || "";
6099
+ const sep = raw.indexOf("..");
6100
+ if (sep < 0)
6101
+ return fallback;
6102
+ return {
6103
+ from: raw.slice(0, sep) || fallback.from,
6104
+ to: raw.slice(sep + 2) || fallback.to
6105
+ };
6106
+ }
6107
+ function parseLineTarget(value) {
6108
+ const raw = value || "";
6109
+ const range = /^(\d+)-(\d+)$/.exec(raw);
6110
+ if (range) {
6111
+ const a2 = Number(range[1]);
6112
+ const b2 = Number(range[2]);
6113
+ const start = Math.min(a2, b2);
6114
+ const end = Math.max(a2, b2);
6115
+ if (start > 0)
6116
+ return { start, end };
6117
+ return;
6118
+ }
6119
+ const line = Number(raw);
6120
+ return Number.isInteger(line) && line > 0 ? line : undefined;
6121
+ }
6122
+ function formatLineTarget(line) {
6123
+ return typeof line === "number" ? String(line) : line.start + "-" + line.end;
6124
+ }
6125
+ function parseRoute(pathname, search, fallbackRange) {
6126
+ const params = new URLSearchParams(search);
6127
+ const legacyRange = parseLegacyRange(params.get("range"), fallbackRange);
6128
+ const range = {
6129
+ from: params.get("from") || legacyRange.from,
6130
+ to: params.get("to") || legacyRange.to
6131
+ };
6132
+ switch (pathname) {
6133
+ case "/":
6134
+ case "/index.html":
6135
+ return {
6136
+ screen: "repo",
6137
+ ref: params.get("ref") || params.get("target") || "worktree",
6138
+ path: params.get("path") || "",
6139
+ range
6140
+ };
6141
+ case "/todif":
6142
+ case "/todiff":
6143
+ return {
6144
+ screen: "diff",
6145
+ range,
6146
+ ...params.get("path") ? { path: params.get("path") || "" } : {},
6147
+ ...parseLineTarget(params.get("line")) ? { line: parseLineTarget(params.get("line")) } : {}
6148
+ };
6149
+ case "/file": {
6150
+ const path = params.get("path") || "";
6151
+ const target = params.get("target") || "";
6152
+ const ref = target || params.get("ref") || "worktree";
6153
+ const line = parseLineTarget(params.get("line"));
6154
+ if (!path)
6155
+ return {
6156
+ screen: "unknown",
6157
+ reason: "missing-path",
6158
+ rawPathname: pathname,
6159
+ rawSearch: search,
6160
+ range
6161
+ };
6162
+ return {
6163
+ screen: "file",
6164
+ path,
6165
+ ref,
6166
+ range,
6167
+ view: target ? "blob" : "detail",
6168
+ ...line ? { line } : {}
6169
+ };
6170
+ }
6171
+ case "/help":
6172
+ return {
6173
+ screen: "help",
6174
+ range,
6175
+ lang: params.get("lang") || "en",
6176
+ section: params.get("section") || "keybindings"
6177
+ };
6178
+ default:
6179
+ return {
6180
+ screen: "unknown",
6181
+ reason: "unknown-pathname",
6182
+ rawPathname: pathname,
6183
+ rawSearch: search,
6184
+ range
6185
+ };
6186
+ }
6187
+ }
6188
+ function buildRoute(route) {
6189
+ switch (route.screen) {
6190
+ case "repo": {
6191
+ const params = new URLSearchParams;
6192
+ if (route.ref && route.ref !== "worktree")
6193
+ params.set("ref", route.ref);
6194
+ if (route.path)
6195
+ params.set("path", route.path);
6196
+ const qs = params.toString();
6197
+ return "/" + (qs ? "?" + qs : "");
6198
+ }
6199
+ case "file":
6200
+ if (route.view === "blob") {
6201
+ return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(route.ref || "worktree") + (route.line ? "&line=" + encodeURIComponent(formatLineTarget(route.line)) : "");
6202
+ }
6203
+ 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)) : "");
6204
+ case "diff":
6205
+ 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)) : "");
6206
+ case "help": {
6207
+ const params = new URLSearchParams;
6208
+ if (route.lang && route.lang !== "en")
6209
+ params.set("lang", route.lang);
6210
+ if (route.section && route.section !== "keybindings")
6211
+ params.set("section", route.section);
6212
+ const qs = params.toString();
6213
+ return "/help" + (qs ? "?" + qs : "");
6214
+ }
6215
+ case "unknown":
6216
+ return "/todif?from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree");
6217
+ default:
6218
+ return assertNever(route);
6219
+ }
6220
+ }
6221
+ function buildRawFileUrl(target) {
6222
+ return "/_file?path=" + encodeURIComponent(target.path) + "&ref=" + encodeURIComponent(target.ref || "worktree");
6223
+ }
6224
+
6158
6225
  // web-src/markdown-preview.ts
6159
6226
  var mermaidPromise = null;
6160
6227
  var mermaidInitialized = false;
@@ -6231,7 +6298,10 @@
6231
6298
  }
6232
6299
  function resolveRepoRelative(currentPath, requestedPath) {
6233
6300
  const base2 = currentPath.split("/").slice(0, -1);
6234
- const parts = [...requestedPath.startsWith("/") ? [] : base2, ...requestedPath.split("/")].filter((part) => part && part !== ".");
6301
+ const parts = [
6302
+ ...requestedPath.startsWith("/") ? [] : base2,
6303
+ ...requestedPath.split("/")
6304
+ ].filter((part) => part && part !== ".");
6235
6305
  const resolved = [];
6236
6306
  for (const part of parts) {
6237
6307
  if (part === "..") {
@@ -6453,7 +6523,10 @@
6453
6523
  const toc = root.querySelector(".gdp-markdown-toc");
6454
6524
  if (!toc)
6455
6525
  return;
6456
- const entries = Array.from(toc.querySelectorAll("a[data-target]")).map((link2) => ({ link: link2, target: root.querySelector("#" + CSS.escape(link2.dataset.target || "")) })).filter((entry) => !!entry.target);
6526
+ const entries = Array.from(toc.querySelectorAll("a[data-target]")).map((link2) => ({
6527
+ link: link2,
6528
+ target: root.querySelector("#" + CSS.escape(link2.dataset.target || ""))
6529
+ })).filter((entry) => !!entry.target);
6457
6530
  if (!entries.length)
6458
6531
  return;
6459
6532
  toc.addEventListener("click", (e2) => {
@@ -6498,7 +6571,10 @@
6498
6571
  if (!raf)
6499
6572
  raf = requestAnimationFrame(update);
6500
6573
  };
6501
- window.addEventListener("scroll", schedule, { passive: true, signal: controller.signal });
6574
+ window.addEventListener("scroll", schedule, {
6575
+ passive: true,
6576
+ signal: controller.signal
6577
+ });
6502
6578
  window.addEventListener("resize", schedule, { signal: controller.signal });
6503
6579
  setTimeout(() => {
6504
6580
  if (!root.isConnected)
@@ -6550,7 +6626,11 @@
6550
6626
  const typed = mod;
6551
6627
  const mermaid = typed.default;
6552
6628
  if (!mermaidInitialized) {
6553
- mermaid.initialize({ startOnLoad: false, securityLevel: "strict", theme: "default" });
6629
+ mermaid.initialize({
6630
+ startOnLoad: false,
6631
+ securityLevel: "strict",
6632
+ theme: "default"
6633
+ });
6554
6634
  mermaidInitialized = true;
6555
6635
  }
6556
6636
  return mermaid;
@@ -6728,6 +6808,19 @@
6728
6808
  return { width: rect.width || 800, height: rect.height || 600 };
6729
6809
  }
6730
6810
 
6811
+ // web-src/search-palette.ts
6812
+ var PALETTE_RESULT_LIMIT = 50;
6813
+ function limitPaletteResults(items) {
6814
+ return items.slice(0, PALETTE_RESULT_LIMIT);
6815
+ }
6816
+ function movePaletteSelection(index, count, direction) {
6817
+ if (count <= 0)
6818
+ return -1;
6819
+ if (index < 0)
6820
+ return direction > 0 ? 0 : count - 1;
6821
+ return (index + direction + count) % count;
6822
+ }
6823
+
6731
6824
  // web-src/ws-highlight.ts
6732
6825
  function isWhitespaceOnlyInlineHighlight(text2) {
6733
6826
  return !!text2 && !/\S/.test(text2);
@@ -6844,10 +6937,41 @@
6844
6937
  title: "Keyboard Shortcuts",
6845
6938
  intro: "Use these shortcuts to move between panels and navigate files without leaving the keyboard.",
6846
6939
  groups: [
6847
- { title: "Global", rows: [["Ctrl+K", "Open file palette"], ["Ctrl+G", "Open grep palette"], ["/", "Focus file filter"], ["t", "Toggle theme"]] },
6848
- { title: "Panels", rows: [["Ctrl+H", "Focus sidebar"], ["Ctrl+L", "Focus main panel"]] },
6849
- { title: "Sidebar", rows: [["j / k", "Move selection down / up"], ["Ctrl+D / Ctrl+U", "Move selection by half a page"], ["gg / Shift+G", "Move to top / bottom"], ["Enter", "Open selected item"], ["h / l", "Collapse / expand directory"]] },
6850
- { title: "Main Panel", rows: [["j / k", "Move code cursor down / up"], ["Ctrl+D / Ctrl+U", "Move code cursor by half a page"], ["gg / Shift+G", "Move code cursor to top / bottom"], ["gp / gc", "Switch to Preview / Code tab"]] }
6940
+ {
6941
+ title: "Global",
6942
+ rows: [
6943
+ ["Ctrl+K", "Open file palette"],
6944
+ ["Ctrl+G", "Open grep palette"],
6945
+ ["/", "Focus file filter"],
6946
+ ["t", "Toggle theme"]
6947
+ ]
6948
+ },
6949
+ {
6950
+ title: "Panels",
6951
+ rows: [
6952
+ ["Ctrl+H", "Focus sidebar"],
6953
+ ["Ctrl+L", "Focus main panel"]
6954
+ ]
6955
+ },
6956
+ {
6957
+ title: "Sidebar",
6958
+ rows: [
6959
+ ["j / k", "Move selection down / up"],
6960
+ ["Ctrl+D / Ctrl+U", "Move selection by half a page"],
6961
+ ["gg / Shift+G", "Move to top / bottom"],
6962
+ ["Enter", "Open selected item"],
6963
+ ["h / l", "Collapse / expand directory"]
6964
+ ]
6965
+ },
6966
+ {
6967
+ title: "Main Panel",
6968
+ rows: [
6969
+ ["j / k", "Move code cursor down / up"],
6970
+ ["Ctrl+D / Ctrl+U", "Move code cursor by half a page"],
6971
+ ["gg / Shift+G", "Move code cursor to top / bottom"],
6972
+ ["gp / gc", "Switch to Preview / Code tab"]
6973
+ ]
6974
+ }
6851
6975
  ]
6852
6976
  }
6853
6977
  }
@@ -6861,10 +6985,41 @@
6861
6985
  title: "キーバインド",
6862
6986
  intro: "キーボードだけでパネル移動、ファイル選択、スクロールを行うためのショートカットです。",
6863
6987
  groups: [
6864
- { title: "グローバル", rows: [["Ctrl+K", "ファイルパレットを開く"], ["Ctrl+G", "grep パレットを開く"], ["/", "ファイルフィルターへフォーカス"], ["t", "テーマ切り替え"]] },
6865
- { title: "パネル", rows: [["Ctrl+H", "サイドバーへフォーカス"], ["Ctrl+L", "メインパネルへフォーカス"]] },
6866
- { title: "サイドバー", rows: [["j / k", "選択を下 / 上へ移動"], ["Ctrl+D / Ctrl+U", "半ページ分選択を移動"], ["gg / Shift+G", "先頭 / 末尾へ移動"], ["Enter", "選択項目を開く"], ["h / l", "ディレクトリを閉じる / 開く"]] },
6867
- { title: "メインパネル", rows: [["j / k", "コードカーソルを下 / 上へ移動"], ["Ctrl+D / Ctrl+U", "コードカーソルを半ページ分移動"], ["gg / Shift+G", "コードカーソルを先頭 / 末尾へ移動"], ["gp / gc", "Preview / Code タブへ切り替え"]] }
6988
+ {
6989
+ title: "グローバル",
6990
+ rows: [
6991
+ ["Ctrl+K", "ファイルパレットを開く"],
6992
+ ["Ctrl+G", "grep パレットを開く"],
6993
+ ["/", "ファイルフィルターへフォーカス"],
6994
+ ["t", "テーマ切り替え"]
6995
+ ]
6996
+ },
6997
+ {
6998
+ title: "パネル",
6999
+ rows: [
7000
+ ["Ctrl+H", "サイドバーへフォーカス"],
7001
+ ["Ctrl+L", "メインパネルへフォーカス"]
7002
+ ]
7003
+ },
7004
+ {
7005
+ title: "サイドバー",
7006
+ rows: [
7007
+ ["j / k", "選択を下 / 上へ移動"],
7008
+ ["Ctrl+D / Ctrl+U", "半ページ分選択を移動"],
7009
+ ["gg / Shift+G", "先頭 / 末尾へ移動"],
7010
+ ["Enter", "選択項目を開く"],
7011
+ ["h / l", "ディレクトリを閉じる / 開く"]
7012
+ ]
7013
+ },
7014
+ {
7015
+ title: "メインパネル",
7016
+ rows: [
7017
+ ["j / k", "コードカーソルを下 / 上へ移動"],
7018
+ ["Ctrl+D / Ctrl+U", "コードカーソルを半ページ分移動"],
7019
+ ["gg / Shift+G", "コードカーソルを先頭 / 末尾へ移動"],
7020
+ ["gp / gc", "Preview / Code タブへ切り替え"]
7021
+ ]
7022
+ }
6868
7023
  ]
6869
7024
  }
6870
7025
  }
@@ -6915,7 +7070,10 @@
6915
7070
  }
6916
7071
  function resetSourceCursorForTarget(target, totalLines) {
6917
7072
  const routeLine = lineTargetStart(currentSourceLineTarget(target));
6918
- SOURCE_CURSOR = { target, line: Math.max(1, Math.min(totalLines, routeLine || 1)) };
7073
+ SOURCE_CURSOR = {
7074
+ target,
7075
+ line: Math.max(1, Math.min(totalLines, routeLine || 1))
7076
+ };
6919
7077
  }
6920
7078
  function scrollSourceCursorIntoView(cursor, edge = "nearest") {
6921
7079
  const scroller = findMainScrollTarget();
@@ -7004,7 +7162,10 @@
7004
7162
  return;
7005
7163
  const target = findMainScrollTarget();
7006
7164
  if (target) {
7007
- target.scrollTo({ top: edge === "top" ? 0 : target.scrollHeight, behavior: "auto" });
7165
+ target.scrollTo({
7166
+ top: edge === "top" ? 0 : target.scrollHeight,
7167
+ behavior: "auto"
7168
+ });
7008
7169
  return;
7009
7170
  }
7010
7171
  const top = edge === "top" ? 0 : Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
@@ -7033,7 +7194,9 @@
7033
7194
  }
7034
7195
  function normalizeScopeOmitDirs(value) {
7035
7196
  const raw = Array.isArray(value) ? value : value.split(/[\n,]+/);
7036
- return [...new Set(raw.map((item) => item.trim()).filter((item) => item && item.length <= 64 && !item.includes("/") && !item.includes("\\") && item !== "." && item !== ".." && item !== ".git"))].slice(0, 100).sort((a2, b2) => a2.localeCompare(b2));
7197
+ return [
7198
+ ...new Set(raw.map((item) => item.trim()).filter((item) => item && item.length <= 64 && !item.includes("/") && !item.includes("\\") && item !== "." && item !== ".." && item !== ".git"))
7199
+ ].slice(0, 100).sort((a2, b2) => a2.localeCompare(b2));
7037
7200
  }
7038
7201
  function scopeOmitDirsStorageKey() {
7039
7202
  return SCOPE_OMIT_DIRS_STORAGE_KEY_PREFIX + (PROJECT_NAME || "default");
@@ -7494,7 +7657,14 @@
7494
7657
  refreshRepositoryTreeAfterSettings();
7495
7658
  }
7496
7659
  function buildTree(files) {
7497
- const root = { name: "", dirs: {}, files: [], path: "", minOrder: Infinity, explicit: true };
7660
+ const root = {
7661
+ name: "",
7662
+ dirs: {},
7663
+ files: [],
7664
+ path: "",
7665
+ minOrder: Infinity,
7666
+ explicit: true
7667
+ };
7498
7668
  for (const f2 of files) {
7499
7669
  const parts = f2.path.split("/");
7500
7670
  let node = root;
@@ -7504,7 +7674,13 @@
7504
7674
  const p2 = parts[i2];
7505
7675
  acc = acc ? acc + "/" + p2 : p2;
7506
7676
  if (!node.dirs[p2]) {
7507
- node.dirs[p2] = { name: p2, dirs: {}, files: [], path: acc, minOrder: Infinity };
7677
+ node.dirs[p2] = {
7678
+ name: p2,
7679
+ dirs: {},
7680
+ files: [],
7681
+ path: acc,
7682
+ minOrder: Infinity
7683
+ };
7508
7684
  }
7509
7685
  node = node.dirs[p2];
7510
7686
  if (typeof f2.order === "number" && f2.order < node.minOrder)
@@ -7544,7 +7720,11 @@
7544
7720
  items.push({ kind: "dir", sortKey: d2.minOrder, dir: d2 });
7545
7721
  }
7546
7722
  for (const f2 of node.files) {
7547
- items.push({ kind: "file", sortKey: f2.order != null ? f2.order : Infinity, file: f2 });
7723
+ items.push({
7724
+ kind: "file",
7725
+ sortKey: f2.order != null ? f2.order : Infinity,
7726
+ file: f2
7727
+ });
7548
7728
  }
7549
7729
  items.sort((a2, b2) => a2.sortKey - b2.sortKey);
7550
7730
  for (const item of items) {
@@ -7665,7 +7845,9 @@
7665
7845
  scheduleMainSurfaceFocus();
7666
7846
  });
7667
7847
  if (!onFileClick)
7668
- li.addEventListener("mouseenter", () => prefetchByPath(f2.path), { passive: true });
7848
+ li.addEventListener("mouseenter", () => prefetchByPath(f2.path), {
7849
+ passive: true
7850
+ });
7669
7851
  ul.appendChild(li);
7670
7852
  }
7671
7853
  }
@@ -7698,7 +7880,9 @@
7698
7880
  scheduleMainSurfaceFocus();
7699
7881
  });
7700
7882
  if (!onFileClick)
7701
- li.addEventListener("mouseenter", () => prefetchByPath(f2.path), { passive: true });
7883
+ li.addEventListener("mouseenter", () => prefetchByPath(f2.path), {
7884
+ passive: true
7885
+ });
7702
7886
  ul.appendChild(li);
7703
7887
  });
7704
7888
  }
@@ -7998,7 +8182,13 @@
7998
8182
  });
7999
8183
  }
8000
8184
  function escapeHtml2(s2) {
8001
- return String(s2 == null ? "" : s2).replace(/[&<>"']/g, (c2) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c2]);
8185
+ return String(s2 == null ? "" : s2).replace(/[&<>"']/g, (c2) => ({
8186
+ "&": "&amp;",
8187
+ "<": "&lt;",
8188
+ ">": "&gt;",
8189
+ '"': "&quot;",
8190
+ "'": "&#39;"
8191
+ })[c2]);
8002
8192
  }
8003
8193
  function sourceTargetsEqual(a2, b2) {
8004
8194
  return !!a2 && !!b2 && a2.path === b2.path && a2.ref === b2.ref;
@@ -8030,7 +8220,10 @@
8030
8220
  return { path: file.path, ref };
8031
8221
  }
8032
8222
  function currentRange() {
8033
- return { from: STATE.from || DEFAULT_RANGE.from, to: STATE.to || DEFAULT_RANGE.to };
8223
+ return {
8224
+ from: STATE.from || DEFAULT_RANGE.from,
8225
+ to: STATE.to || DEFAULT_RANGE.to
8226
+ };
8034
8227
  }
8035
8228
  function sourceTargetFromRoute() {
8036
8229
  return STATE.route.screen === "file" ? { path: STATE.route.path, ref: STATE.route.ref } : null;
@@ -8053,7 +8246,12 @@
8053
8246
  STATE.repoRef = nextRoute.ref || "worktree";
8054
8247
  }
8055
8248
  const url = buildRoute(nextRoute);
8056
- const state = nextRoute.screen === "file" ? { screen: "file", path: nextRoute.path, ref: nextRoute.ref, view: nextRoute.view || "detail" } : { view: nextRoute.screen };
8249
+ const state = nextRoute.screen === "file" ? {
8250
+ screen: "file",
8251
+ path: nextRoute.path,
8252
+ ref: nextRoute.ref,
8253
+ view: nextRoute.view || "detail"
8254
+ } : { view: nextRoute.screen };
8057
8255
  if (replace2)
8058
8256
  history.replaceState(state, "", url);
8059
8257
  else
@@ -8074,13 +8272,23 @@
8074
8272
  link2.classList.toggle("active", active);
8075
8273
  link2.setAttribute("aria-current", active ? "page" : "false");
8076
8274
  if (link2.dataset.route === "repo") {
8077
- link2.href = buildRoute({ screen: "repo", ref: STATE.repoRef || "worktree", path: "", range: currentRange() });
8275
+ link2.href = buildRoute({
8276
+ screen: "repo",
8277
+ ref: STATE.repoRef || "worktree",
8278
+ path: "",
8279
+ range: currentRange()
8280
+ });
8078
8281
  }
8079
8282
  if (link2.dataset.route === "diff") {
8080
8283
  link2.href = buildRoute({ screen: "diff", range: currentRange() });
8081
8284
  }
8082
8285
  if (link2.dataset.route === "help") {
8083
- link2.href = buildRoute({ screen: "help", lang: helpLanguageFromRoute(), section: helpSectionFromRoute(), range: currentRange() });
8286
+ link2.href = buildRoute({
8287
+ screen: "help",
8288
+ lang: helpLanguageFromRoute(),
8289
+ section: helpSectionFromRoute(),
8290
+ range: currentRange()
8291
+ });
8084
8292
  }
8085
8293
  });
8086
8294
  }
@@ -8119,7 +8327,12 @@
8119
8327
  langSelect.appendChild(option);
8120
8328
  });
8121
8329
  langSelect.addEventListener("change", () => {
8122
- setRoute({ screen: "help", lang: langSelect.value, section, range: currentRange() });
8330
+ setRoute({
8331
+ screen: "help",
8332
+ lang: langSelect.value,
8333
+ section,
8334
+ range: currentRange()
8335
+ });
8123
8336
  setPageMode();
8124
8337
  renderHelpPage();
8125
8338
  syncHeaderMenu();
@@ -8135,7 +8348,12 @@
8135
8348
  button.className = helpSection === section ? "active" : "";
8136
8349
  button.textContent = content.sections[helpSection].nav;
8137
8350
  button.addEventListener("click", () => {
8138
- setRoute({ screen: "help", lang, section: helpSection, range: currentRange() });
8351
+ setRoute({
8352
+ screen: "help",
8353
+ lang,
8354
+ section: helpSection,
8355
+ range: currentRange()
8356
+ });
8139
8357
  renderHelpPage();
8140
8358
  syncHeaderMenu();
8141
8359
  });
@@ -8265,7 +8483,10 @@
8265
8483
  try {
8266
8484
  const res = await fetch("/_open_path", {
8267
8485
  method: "POST",
8268
- headers: { "Content-Type": "application/json", "X-Code-Viewer-Action": "1" },
8486
+ headers: {
8487
+ "Content-Type": "application/json",
8488
+ "X-Code-Viewer-Action": "1"
8489
+ },
8269
8490
  body: JSON.stringify({ path, kind })
8270
8491
  });
8271
8492
  if (!res.ok)
@@ -8370,7 +8591,12 @@
8370
8591
  return dropPanel;
8371
8592
  }
8372
8593
  function repoRoute(ref, path) {
8373
- return { screen: "repo", ref: ref || "worktree", path, range: currentRange() };
8594
+ return {
8595
+ screen: "repo",
8596
+ ref: ref || "worktree",
8597
+ path,
8598
+ range: currentRange()
8599
+ };
8374
8600
  }
8375
8601
  function wireRefSelectorInput(input, onPick) {
8376
8602
  const wrap = input.closest("[data-ref-selector]");
@@ -8516,7 +8742,13 @@
8516
8742
  setRoute(repoRoute(meta.ref, entry.path));
8517
8743
  loadRepo();
8518
8744
  } else if (entry.type === "blob") {
8519
- setRoute({ screen: "file", path: entry.path, ref: meta.ref, view: "blob", range: currentRange() });
8745
+ setRoute({
8746
+ screen: "file",
8747
+ path: entry.path,
8748
+ ref: meta.ref,
8749
+ view: "blob",
8750
+ range: currentRange()
8751
+ });
8520
8752
  renderStandaloneSource({ path: entry.path, ref: meta.ref });
8521
8753
  }
8522
8754
  });
@@ -8553,7 +8785,13 @@
8553
8785
  wrapper.appendChild(await renderMarkdownPreview(meta.readme.text, { path: meta.readme.path, ref: meta.ref }, {
8554
8786
  syntaxHighlight: STATE.syntaxHighlight,
8555
8787
  onNavigateMarkdown: (path, ref) => {
8556
- setRoute({ screen: "file", path, ref, view: "blob", range: currentRange() });
8788
+ setRoute({
8789
+ screen: "file",
8790
+ path,
8791
+ ref,
8792
+ view: "blob",
8793
+ range: currentRange()
8794
+ });
8557
8795
  renderStandaloneSource({ path, ref });
8558
8796
  }
8559
8797
  }));
@@ -8608,7 +8846,13 @@
8608
8846
  loadRepo();
8609
8847
  return;
8610
8848
  }
8611
- setRoute({ screen: "file", path: file.path, ref: normalizedRef, view: "blob", range: currentRange() });
8849
+ setRoute({
8850
+ screen: "file",
8851
+ path: file.path,
8852
+ ref: normalizedRef,
8853
+ view: "blob",
8854
+ range: currentRange()
8855
+ });
8612
8856
  renderStandaloneSource({ path: file.path, ref: normalizedRef });
8613
8857
  });
8614
8858
  REPO_SIDEBAR_REF = normalizedRef;
@@ -8668,7 +8912,9 @@
8668
8912
  }, { rootMargin: "1200px 0px 1600px 0px" });
8669
8913
  document.querySelectorAll(".gdp-file-shell.pending").forEach((c2) => lazyObserver.observe(c2));
8670
8914
  }
8671
- window.addEventListener("scroll", () => enqueueInitialLoads(), { passive: true });
8915
+ window.addEventListener("scroll", () => enqueueInitialLoads(), {
8916
+ passive: true
8917
+ });
8672
8918
  window.addEventListener("resize", () => {
8673
8919
  enqueueInitialLoads();
8674
8920
  syncSidebarHeaderHeight();
@@ -8757,7 +9003,12 @@
8757
9003
  openFileBtn.title = "Open this file in the virtualized source viewer";
8758
9004
  openFileBtn.addEventListener("click", () => {
8759
9005
  const target = fileSourceTarget(file);
8760
- setRoute({ screen: "file", path: target.path, ref: target.ref, range: currentRange() });
9006
+ setRoute({
9007
+ screen: "file",
9008
+ path: target.path,
9009
+ ref: target.ref,
9010
+ range: currentRange()
9011
+ });
8761
9012
  applySourceRouteToShell();
8762
9013
  });
8763
9014
  const fullBtn = document.createElement("button");
@@ -8916,7 +9167,11 @@
8916
9167
  if (!info)
8917
9168
  return;
8918
9169
  const txt = (info.textContent || "").trim();
8919
- arr.push({ tr, info, hunk: parseHunkHeader(txt) });
9170
+ arr.push({
9171
+ tr,
9172
+ info,
9173
+ hunk: parseHunkHeader(txt)
9174
+ });
8920
9175
  });
8921
9176
  perTable.push(arr);
8922
9177
  });
@@ -9161,7 +9416,13 @@
9161
9416
  });
9162
9417
  };
9163
9418
  rows.forEach((row) => {
9164
- row.ln.appendChild(createExpandStack([{ direction: "down", title: "Show more lines", onClick: fetchAndInsert }]));
9419
+ row.ln.appendChild(createExpandStack([
9420
+ {
9421
+ direction: "down",
9422
+ title: "Show more lines",
9423
+ onClick: fetchAndInsert
9424
+ }
9425
+ ]));
9165
9426
  });
9166
9427
  syncExpandRowHeights(rows.map((row) => row.tr), rows[0].tr);
9167
9428
  }
@@ -9309,7 +9570,23 @@
9309
9570
  card.appendChild(view);
9310
9571
  }
9311
9572
  function isPreviewableSource(path) {
9312
- return /\.(md|markdown|mdown|mkdn|mdx)$/i.test(path);
9573
+ return /\.(md|markdown|mdown|mkdn|mdx|html|htm)$/i.test(path);
9574
+ }
9575
+ function sourcePreviewKind(path) {
9576
+ if (/\.(md|markdown|mdown|mkdn|mdx)$/i.test(path))
9577
+ return "markdown";
9578
+ if (/\.(html|htm)$/i.test(path))
9579
+ return "html";
9580
+ return null;
9581
+ }
9582
+ function renderHtmlPreview(target, html) {
9583
+ const preview = document.createElement("div");
9584
+ preview.className = "gdp-html-preview";
9585
+ const frame = document.createElement("iframe");
9586
+ frame.title = target.path + " preview";
9587
+ frame.srcdoc = html;
9588
+ preview.appendChild(frame);
9589
+ return preview;
9313
9590
  }
9314
9591
  const EXT_TO_LANG = {
9315
9592
  js: "javascript",
@@ -9753,6 +10030,7 @@
9753
10030
  if (signal?.aborted)
9754
10031
  return false;
9755
10032
  const previewable = isPreviewableSource(target.path);
10033
+ const previewKind = sourcePreviewKind(target.path);
9756
10034
  const tabsHost = card.querySelector(".gdp-file-detail-tabs");
9757
10035
  if (usesVirtualSource) {
9758
10036
  const virtualCode = renderVirtualSource(target, textValue, lines, hljsRef, lang);
@@ -9762,11 +10040,17 @@
9762
10040
  tabsHost.hidden = false;
9763
10041
  tabsHost.replaceChildren(tabs2);
9764
10042
  }
9765
- const preview = await renderMarkdownPreview(textValue, target, {
10043
+ const preview = previewKind === "html" ? renderHtmlPreview(target, textValue) : await renderMarkdownPreview(textValue, target, {
9766
10044
  syntaxHighlight: STATE.syntaxHighlight,
9767
10045
  signal,
9768
10046
  onNavigateMarkdown: (path, ref) => {
9769
- setRoute({ screen: "file", path, ref, view: "blob", range: currentRange() });
10047
+ setRoute({
10048
+ screen: "file",
10049
+ path,
10050
+ ref,
10051
+ view: "blob",
10052
+ range: currentRange()
10053
+ });
9770
10054
  renderStandaloneSource({ path, ref });
9771
10055
  }
9772
10056
  });
@@ -9848,11 +10132,17 @@
9848
10132
  tabsHost.replaceChildren(tabs);
9849
10133
  }
9850
10134
  if (previewable) {
9851
- const preview = await renderMarkdownPreview(textValue, target, {
10135
+ const preview = previewKind === "html" ? renderHtmlPreview(target, textValue) : await renderMarkdownPreview(textValue, target, {
9852
10136
  syntaxHighlight: STATE.syntaxHighlight,
9853
10137
  signal,
9854
10138
  onNavigateMarkdown: (path, ref) => {
9855
- setRoute({ screen: "file", path, ref, view: "blob", range: currentRange() });
10139
+ setRoute({
10140
+ screen: "file",
10141
+ path,
10142
+ ref,
10143
+ view: "blob",
10144
+ range: currentRange()
10145
+ });
9856
10146
  renderStandaloneSource({ path, ref });
9857
10147
  }
9858
10148
  });
@@ -10229,7 +10519,10 @@
10229
10519
  const activeRange = search?.activeRange() || null;
10230
10520
  if (appendVirtualSourceLineCode(code2, line, searchQuery, activeRange, index + 1)) {} else if (hljsRef && hljsRef.highlight && lang && line.length <= VIRTUAL_SOURCE_HIGHLIGHT_MAX_LINE_LENGTH && (!hljsRef.getLanguage || hljsRef.getLanguage(lang))) {
10231
10521
  try {
10232
- code2.innerHTML = hljsRef.highlight(line, { language: lang, ignoreIllegals: true }).value;
10522
+ code2.innerHTML = hljsRef.highlight(line, {
10523
+ language: lang,
10524
+ ignoreIllegals: true
10525
+ }).value;
10233
10526
  code2.classList.add("hljs");
10234
10527
  } catch {
10235
10528
  code2.textContent = line;
@@ -10391,7 +10684,10 @@
10391
10684
  code2.textContent = "";
10392
10685
  } else if (appendVirtualSourceLineCode(code2, line, search?.query() || "", search?.activeRange() || null, lineNumber)) {} else if (hljsRef && hljsRef.highlight && lang && line.length <= VIRTUAL_SOURCE_HIGHLIGHT_MAX_LINE_LENGTH && (!hljsRef.getLanguage || hljsRef.getLanguage(lang))) {
10393
10686
  try {
10394
- code2.innerHTML = hljsRef.highlight(line, { language: lang, ignoreIllegals: true }).value;
10687
+ code2.innerHTML = hljsRef.highlight(line, {
10688
+ language: lang,
10689
+ ignoreIllegals: true
10690
+ }).value;
10395
10691
  code2.classList.add("hljs");
10396
10692
  } catch {
10397
10693
  code2.textContent = line;
@@ -10420,7 +10716,9 @@
10420
10716
  let done = false;
10421
10717
  while (!done && matches.length < 5000) {
10422
10718
  const endLine = startLine + VIRTUAL_SOURCE_PAGE_SIZE - 1;
10423
- const data = await trackLoad(fetch(buildFileRangeUrl(target, startLine, endLine), { signal: matchSignal }).then((res) => {
10719
+ const data = await trackLoad(fetch(buildFileRangeUrl(target, startLine, endLine), {
10720
+ signal: matchSignal
10721
+ }).then((res) => {
10424
10722
  if (!res.ok)
10425
10723
  throw new Error("file range failed");
10426
10724
  return res.json();
@@ -10431,7 +10729,11 @@
10431
10729
  const lineNumber = data.start + index;
10432
10730
  lines.set(lineNumber, lineValue);
10433
10731
  for (const range of virtualSourceSearchRanges(lineValue, query)) {
10434
- matches.push({ line: lineNumber, start: range.start, end: range.end });
10732
+ matches.push({
10733
+ line: lineNumber,
10734
+ start: range.start,
10735
+ end: range.end
10736
+ });
10435
10737
  if (matches.length >= 5000)
10436
10738
  break;
10437
10739
  }
@@ -10491,7 +10793,9 @@
10491
10793
  const hljsRef = STATE.syntaxHighlight ? await loadSyntaxHighlighter() : null;
10492
10794
  if (signal?.aborted)
10493
10795
  return false;
10494
- const initial = await trackLoad(fetch(buildFileRangeUrl(target, initialStart, initialEnd), { signal }).then((res) => res.ok ? res.json() : null));
10796
+ const initial = await trackLoad(fetch(buildFileRangeUrl(target, initialStart, initialEnd), {
10797
+ signal
10798
+ }).then((res) => res.ok ? res.json() : null));
10495
10799
  if (!initial)
10496
10800
  return false;
10497
10801
  if (signal?.aborted)
@@ -10942,7 +11246,12 @@
10942
11246
  viewFile.addEventListener("click", (e2) => {
10943
11247
  e2.stopPropagation();
10944
11248
  const target = fileSourceTarget(file);
10945
- setRoute({ screen: "file", path: target.path, ref: target.ref, range: currentRange() });
11249
+ setRoute({
11250
+ screen: "file",
11251
+ path: target.path,
11252
+ ref: target.ref,
11253
+ range: currentRange()
11254
+ });
10946
11255
  applySourceRouteToShell();
10947
11256
  });
10948
11257
  header.appendChild(viewFile);
@@ -11053,7 +11362,10 @@
11053
11362
  if (text2.length === 0)
11054
11363
  return;
11055
11364
  try {
11056
- s2.innerHTML = hljsRef.highlight(text2, { language: lang, ignoreIllegals: true }).value;
11365
+ s2.innerHTML = hljsRef.highlight(text2, {
11366
+ language: lang,
11367
+ ignoreIllegals: true
11368
+ }).value;
11057
11369
  if (!s2.classList.contains("hljs"))
11058
11370
  s2.classList.add("hljs");
11059
11371
  } catch (_) {}
@@ -11084,7 +11396,10 @@
11084
11396
  if (text2.length === 0)
11085
11397
  continue;
11086
11398
  try {
11087
- s2.innerHTML = hljsRef.highlight(text2, { language: lang, ignoreIllegals: true }).value;
11399
+ s2.innerHTML = hljsRef.highlight(text2, {
11400
+ language: lang,
11401
+ ignoreIllegals: true
11402
+ }).value;
11088
11403
  if (!s2.classList.contains("hljs"))
11089
11404
  s2.classList.add("hljs");
11090
11405
  } catch (_) {}
@@ -11729,7 +12044,17 @@
11729
12044
  if (!query.trim()) {
11730
12045
  const base2 = source === "diff" ? state.diffSnapshot.map((file) => {
11731
12046
  const target = fileSourceTarget(file);
11732
- return { kind: "file", path: file.path, old_path: file.old_path, displayPath: file.path, ref: paletteRef(source), targetPath: target.path, targetRef: target.ref, source, ranges: [] };
12047
+ return {
12048
+ kind: "file",
12049
+ path: file.path,
12050
+ old_path: file.old_path,
12051
+ displayPath: file.path,
12052
+ ref: paletteRef(source),
12053
+ targetPath: target.path,
12054
+ targetRef: target.ref,
12055
+ source,
12056
+ ranges: []
12057
+ };
11733
12058
  }) : [];
11734
12059
  state.items = limitPaletteResults(base2);
11735
12060
  state.selected = state.items.length ? 0 : -1;
@@ -11795,7 +12120,9 @@
11795
12120
  }
11796
12121
  const controller = new AbortController;
11797
12122
  state.controller = controller;
11798
- trackLoad(fetch("/_grep?" + params.toString(), { signal: controller.signal }).then((r2) => {
12123
+ trackLoad(fetch("/_grep?" + params.toString(), {
12124
+ signal: controller.signal
12125
+ }).then((r2) => {
11799
12126
  if (!r2.ok)
11800
12127
  throw new Error("grep failed");
11801
12128
  return r2.json();
@@ -11839,22 +12166,45 @@
11839
12166
  if (item.kind === "file") {
11840
12167
  if (item.source === "diff") {
11841
12168
  if (STATE.route.screen === "file") {
11842
- setRoute({ screen: "file", path: item.targetPath || item.path, ref: item.targetRef || item.ref, range: currentRange() });
12169
+ setRoute({
12170
+ screen: "file",
12171
+ path: item.targetPath || item.path,
12172
+ ref: item.targetRef || item.ref,
12173
+ range: currentRange()
12174
+ });
11843
12175
  applySourceRouteToShell();
11844
12176
  } else {
11845
12177
  scrollToFile(item.path);
11846
12178
  }
11847
12179
  } else {
11848
- setRoute({ screen: "file", path: item.path, ref: item.ref, view: "blob", range: currentRange() });
12180
+ setRoute({
12181
+ screen: "file",
12182
+ path: item.path,
12183
+ ref: item.ref,
12184
+ view: "blob",
12185
+ range: currentRange()
12186
+ });
11849
12187
  renderStandaloneSource({ path: item.path, ref: item.ref });
11850
12188
  }
11851
12189
  return;
11852
12190
  }
11853
12191
  if (item.source === "diff") {
11854
- setRoute({ screen: "diff", range: currentRange(), path: item.path, line: item.line });
12192
+ setRoute({
12193
+ screen: "diff",
12194
+ range: currentRange(),
12195
+ path: item.path,
12196
+ line: item.line
12197
+ });
11855
12198
  scrollToFile(item.path, item.line);
11856
12199
  } else {
11857
- setRoute({ screen: "file", path: item.path, ref: item.ref, view: "blob", line: item.line, range: currentRange() });
12200
+ setRoute({
12201
+ screen: "file",
12202
+ path: item.path,
12203
+ ref: item.ref,
12204
+ view: "blob",
12205
+ line: item.line,
12206
+ range: currentRange()
12207
+ });
11858
12208
  renderStandaloneSource({ path: item.path, ref: item.ref });
11859
12209
  }
11860
12210
  }
@@ -11991,7 +12341,10 @@
11991
12341
  else if (scope === "sidebar")
11992
12342
  moveActiveSidebarToEdge(edge);
11993
12343
  else
11994
- window.scrollTo({ top: edge === "top" ? 0 : Math.max(document.documentElement.scrollHeight, document.body.scrollHeight), behavior: "auto" });
12344
+ window.scrollTo({
12345
+ top: edge === "top" ? 0 : Math.max(document.documentElement.scrollHeight, document.body.scrollHeight),
12346
+ behavior: "auto"
12347
+ });
11995
12348
  return true;
11996
12349
  }
11997
12350
  if (action === "layout-unified") {
@@ -12041,7 +12394,9 @@
12041
12394
  function handleVirtualSourcePagingKeydown(e2) {
12042
12395
  handleVirtualSourcePagingKey(e2, e2.target);
12043
12396
  }
12044
- document.addEventListener("keydown", handleVirtualSourcePagingKeydown, { capture: true });
12397
+ document.addEventListener("keydown", handleVirtualSourcePagingKeydown, {
12398
+ capture: true
12399
+ });
12045
12400
  document.addEventListener("keydown", (e2) => {
12046
12401
  if (e2.__gdpVirtualSourcePagingHandled)
12047
12402
  return;
@@ -12145,7 +12500,12 @@
12145
12500
  if (STATE.route.screen === "file") {
12146
12501
  setRoute({ screen: "file", path: STATE.route.path, ref: STATE.route.ref, range }, true);
12147
12502
  } else if (STATE.route.screen === "help") {
12148
- setRoute({ screen: "help", lang: helpLanguageFromRoute(), section: helpSectionFromRoute(), range }, true);
12503
+ setRoute({
12504
+ screen: "help",
12505
+ lang: helpLanguageFromRoute(),
12506
+ section: helpSectionFromRoute(),
12507
+ range
12508
+ }, true);
12149
12509
  renderHelpPage();
12150
12510
  } else {
12151
12511
  setRoute({ screen: "diff", range }, true);
@@ -12154,7 +12514,12 @@
12154
12514
  }
12155
12515
  syncRefInputs();
12156
12516
  syncHeaderMenu();
12157
- const REFS = { branches: [], tags: [], commits: [], current: "" };
12517
+ const REFS = {
12518
+ branches: [],
12519
+ tags: [],
12520
+ commits: [],
12521
+ current: ""
12522
+ };
12158
12523
  const popover = $("#ref-popover");
12159
12524
  const popBody = popover.querySelector(".rp-body");
12160
12525
  const popSearch = popover.querySelector(".rp-search");
@@ -12166,20 +12531,58 @@
12166
12531
  }
12167
12532
  fetchRefs();
12168
12533
  let popTab = "commits";
12534
+ let commitSearchTimer = null;
12535
+ let commitSearchSeq = 0;
12536
+ let commitSearchAbort = null;
12537
+ let commitSearchLoading = false;
12538
+ function fetchCommitRefs(query) {
12539
+ const seq = ++commitSearchSeq;
12540
+ if (commitSearchAbort)
12541
+ commitSearchAbort.abort();
12542
+ commitSearchAbort = new AbortController;
12543
+ const url = "/_commits?max=100&q=" + encodeURIComponent((query || "").trim());
12544
+ return fetch(url, { signal: commitSearchAbort.signal }).then((r2) => r2.json()).then((refs) => {
12545
+ if (seq !== commitSearchSeq)
12546
+ return;
12547
+ commitSearchLoading = false;
12548
+ REFS.commits = refs.commits || [];
12549
+ if (!popover.hidden && popTab === "commits") {
12550
+ buildPopBody(popSearch.value);
12551
+ }
12552
+ }).catch(() => {
12553
+ if (seq === commitSearchSeq)
12554
+ commitSearchLoading = false;
12555
+ });
12556
+ }
12557
+ function scheduleCommitSearch(query) {
12558
+ if (commitSearchTimer)
12559
+ clearTimeout(commitSearchTimer);
12560
+ commitSearchLoading = true;
12561
+ commitSearchTimer = setTimeout(() => {
12562
+ commitSearchTimer = null;
12563
+ fetchCommitRefs(query);
12564
+ }, 150);
12565
+ }
12169
12566
  function buildPopBody(query) {
12170
12567
  const q = (query || "").toLowerCase().trim();
12171
12568
  const m = (s2) => !q || String(s2).toLowerCase().includes(q);
12172
12569
  const html = [];
12173
12570
  if (popTab === "commits") {
12174
- const commits = (REFS.commits || []).filter((c2) => m(c2));
12571
+ if (commitSearchLoading) {
12572
+ html.push('<div class="rp-empty">loading commits...</div>');
12573
+ popBody.innerHTML = html.join("");
12574
+ highlightCurrentInPopover();
12575
+ return;
12576
+ }
12577
+ const commits = (REFS.commits || []).filter((commit) => m(`${commit.sha} ${commit.subject} ${commit.author}`));
12175
12578
  if (!commits.length) {
12176
12579
  html.push('<div class="rp-empty">no commits</div>');
12177
12580
  }
12178
- for (const c2 of commits) {
12179
- const [sha, subject, author, when] = c2.split("\t");
12180
- if (!sha)
12581
+ for (const commit of commits) {
12582
+ if (!commit.sha)
12181
12583
  continue;
12182
- html.push('<div class="rp-item-commit" data-val="' + escapeAttr(sha) + '"><div class="row1"><span class="sha">' + escapeHtml2(sha) + '</span><span class="subject" title="' + escapeAttr(subject || "") + '">' + escapeHtml2(subject || "") + '</span></div><div class="row2"><span class="author">' + escapeHtml2(author || "") + '</span><span class="when">' + escapeHtml2(when || "") + "</span></div></div>");
12584
+ const shortSha = commit.sha.slice(0, 7);
12585
+ html.push('<div class="rp-item-commit" data-val="' + escapeAttr(commit.sha) + '"><div class="row1"><span class="sha">' + escapeHtml2(shortSha) + '</span><span class="subject" title="' + escapeAttr(commit.subject || "") + '">' + escapeHtml2(commit.subject || "") + '</span></div><div class="row2"><span class="author">' + escapeHtml2(commit.author || "") + '</span><span class="when">' + escapeHtml2(commit.when || "") + "</span></div></div>");
12183
12586
  }
12184
12587
  } else if (popTab === "branches") {
12185
12588
  const branches = (REFS.branches || []).filter(m);
@@ -12230,6 +12633,8 @@
12230
12633
  function openPopover(input) {
12231
12634
  popTarget = input;
12232
12635
  popSearch.value = "";
12636
+ if (popTab === "commits")
12637
+ scheduleCommitSearch("");
12233
12638
  buildPopBody("");
12234
12639
  const cur = (input.value || "").trim();
12235
12640
  popover.querySelectorAll(".rp-chip").forEach((c2) => {
@@ -12258,16 +12663,26 @@
12258
12663
  wireRefSelectorInput($("#repo-target"), (ref) => {
12259
12664
  if (STATE.route.screen !== "file")
12260
12665
  return;
12261
- setRoute({ screen: "file", path: STATE.route.path, ref, view: "blob", range: currentRange() });
12666
+ setRoute({
12667
+ screen: "file",
12668
+ path: STATE.route.path,
12669
+ ref,
12670
+ view: "blob",
12671
+ range: currentRange()
12672
+ });
12262
12673
  renderStandaloneSource({ path: STATE.route.path, ref });
12263
12674
  });
12264
- popSearch.addEventListener("input", () => buildPopBody(popSearch.value));
12675
+ popSearch.addEventListener("input", () => {
12676
+ if (popTab === "commits")
12677
+ scheduleCommitSearch(popSearch.value);
12678
+ buildPopBody(popSearch.value);
12679
+ });
12265
12680
  popSearch.addEventListener("keydown", (e2) => {
12266
12681
  if (e2.key === "Escape") {
12267
12682
  closePopover();
12268
12683
  }
12269
12684
  if (e2.key === "Enter") {
12270
- const first = popBody.querySelector(".rp-item");
12685
+ const first = popBody.querySelector(".rp-item-commit, .rp-item-ref");
12271
12686
  if (first)
12272
12687
  first.click();
12273
12688
  }
@@ -12290,6 +12705,8 @@
12290
12705
  t2.addEventListener("click", () => {
12291
12706
  popTab = t2.dataset.tab || "commits";
12292
12707
  popover.querySelectorAll(".rp-tab").forEach((b2) => b2.classList.toggle("active", b2 === t2));
12708
+ if (popTab === "commits")
12709
+ scheduleCommitSearch(popSearch.value);
12293
12710
  buildPopBody(popSearch.value);
12294
12711
  });
12295
12712
  });