@youtyan/code-viewer 0.1.18 → 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/dist/code-viewer.js +172 -18
- package/package.json +2 -2
- package/web/app.js +165 -26
package/dist/code-viewer.js
CHANGED
|
@@ -82,7 +82,13 @@ function startDevAssetReload(options) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// web-src/server/git.ts
|
|
85
|
-
import {
|
|
85
|
+
import {
|
|
86
|
+
existsSync,
|
|
87
|
+
lstatSync as lstatSync2,
|
|
88
|
+
readdirSync,
|
|
89
|
+
readFileSync
|
|
90
|
+
} from "node:fs";
|
|
91
|
+
import { join as join2 } from "node:path";
|
|
86
92
|
|
|
87
93
|
// web-src/server/runtime.ts
|
|
88
94
|
import { spawn, spawnSync } from "node:child_process";
|
|
@@ -235,9 +241,11 @@ async function writeWebResponse(res, response) {
|
|
|
235
241
|
}
|
|
236
242
|
|
|
237
243
|
// web-src/server/git.ts
|
|
238
|
-
import { join as join2 } from "node:path";
|
|
239
244
|
var WORKTREE_RECURSIVE_DEPTH_LIMIT = 32;
|
|
240
245
|
var WORKTREE_RECURSIVE_ENTRY_LIMIT = 50000;
|
|
246
|
+
var DEFAULT_REF_COMMIT_LIMIT = 100;
|
|
247
|
+
var MAX_REF_COMMIT_LIMIT = 500;
|
|
248
|
+
var COMMIT_FORMAT = "%H%x00%s%x00%an%x00%aI";
|
|
241
249
|
var DEFAULT_WORKTREE_OMIT_DIR_NAMES = [
|
|
242
250
|
"node_modules",
|
|
243
251
|
".venv",
|
|
@@ -291,11 +299,19 @@ function catFileBlobStream(oid, cwd) {
|
|
|
291
299
|
}
|
|
292
300
|
function objectSize(ref, path, cwd) {
|
|
293
301
|
const res = run(["git", "cat-file", "-s", `${ref}:${path}`], cwd);
|
|
294
|
-
return {
|
|
302
|
+
return {
|
|
303
|
+
code: res.code,
|
|
304
|
+
size: Number(res.stdout.trim()) || 0,
|
|
305
|
+
stderr: res.stderr
|
|
306
|
+
};
|
|
295
307
|
}
|
|
296
308
|
function objectByteSize(oid, cwd) {
|
|
297
309
|
const res = run(["git", "cat-file", "-s", oid], cwd);
|
|
298
|
-
return {
|
|
310
|
+
return {
|
|
311
|
+
code: res.code,
|
|
312
|
+
size: Number(res.stdout.trim()) || 0,
|
|
313
|
+
stderr: res.stderr
|
|
314
|
+
};
|
|
299
315
|
}
|
|
300
316
|
function objectId(ref, path, cwd) {
|
|
301
317
|
const res = run(["git", "rev-parse", "--verify", `${ref}:${path}`], cwd);
|
|
@@ -316,7 +332,12 @@ function verifyTreeRef(ref, cwd) {
|
|
|
316
332
|
return res.code === 0;
|
|
317
333
|
}
|
|
318
334
|
function refs(cwd) {
|
|
319
|
-
const out = {
|
|
335
|
+
const out = {
|
|
336
|
+
branches: [],
|
|
337
|
+
tags: [],
|
|
338
|
+
commits: [],
|
|
339
|
+
current: ""
|
|
340
|
+
};
|
|
320
341
|
const branches = run([
|
|
321
342
|
"git",
|
|
322
343
|
"for-each-ref",
|
|
@@ -329,17 +350,104 @@ function refs(cwd) {
|
|
|
329
350
|
out.branches = branches.stdout.split(`
|
|
330
351
|
`).filter((line) => line && line !== "origin/HEAD");
|
|
331
352
|
}
|
|
332
|
-
const tags = run([
|
|
353
|
+
const tags = run([
|
|
354
|
+
"git",
|
|
355
|
+
"for-each-ref",
|
|
356
|
+
"--sort=-creatordate",
|
|
357
|
+
"--format=%(refname:short)",
|
|
358
|
+
"refs/tags"
|
|
359
|
+
], cwd);
|
|
333
360
|
if (tags.code === 0)
|
|
334
361
|
out.tags = tags.stdout.split(`
|
|
335
362
|
`).filter(Boolean);
|
|
336
|
-
|
|
337
|
-
if (commits.code === 0)
|
|
338
|
-
out.commits = commits.stdout.split(`
|
|
339
|
-
`).filter(Boolean);
|
|
363
|
+
out.commits = refCommits(cwd, "", DEFAULT_REF_COMMIT_LIMIT);
|
|
340
364
|
out.current = currentBranch(cwd) || "";
|
|
341
365
|
return out;
|
|
342
366
|
}
|
|
367
|
+
function clampCommitLimit(max) {
|
|
368
|
+
return Math.max(1, Math.min(max, MAX_REF_COMMIT_LIMIT));
|
|
369
|
+
}
|
|
370
|
+
function parseCommitLog(stdout) {
|
|
371
|
+
const parts = stdout.split("\x00");
|
|
372
|
+
const commits = [];
|
|
373
|
+
for (let index = 0;index < parts.length; ) {
|
|
374
|
+
if (!parts[index]) {
|
|
375
|
+
index++;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
const sha = parts[index++] || "";
|
|
379
|
+
const subject = parts[index++] || "";
|
|
380
|
+
const author = parts[index++] || "";
|
|
381
|
+
const when = parts[index++] || "";
|
|
382
|
+
if (sha)
|
|
383
|
+
commits.push({ sha, subject, author, when });
|
|
384
|
+
}
|
|
385
|
+
return commits;
|
|
386
|
+
}
|
|
387
|
+
function commitLogArgs(limit) {
|
|
388
|
+
return [
|
|
389
|
+
"git",
|
|
390
|
+
"log",
|
|
391
|
+
"--all",
|
|
392
|
+
"-z",
|
|
393
|
+
`--max-count=${limit}`,
|
|
394
|
+
`--format=${COMMIT_FORMAT}`
|
|
395
|
+
];
|
|
396
|
+
}
|
|
397
|
+
function mergeCommitResults(limit, ...groups) {
|
|
398
|
+
const seen = new Set;
|
|
399
|
+
const merged = [];
|
|
400
|
+
for (const commits of groups) {
|
|
401
|
+
for (const commit of commits) {
|
|
402
|
+
if (!commit.sha || seen.has(commit.sha))
|
|
403
|
+
continue;
|
|
404
|
+
seen.add(commit.sha);
|
|
405
|
+
merged.push(commit);
|
|
406
|
+
if (merged.length >= limit)
|
|
407
|
+
return merged;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return merged;
|
|
411
|
+
}
|
|
412
|
+
function runCommitLog(cwd, args) {
|
|
413
|
+
const commits = run(args, cwd);
|
|
414
|
+
return commits.code === 0 ? parseCommitLog(commits.stdout) : [];
|
|
415
|
+
}
|
|
416
|
+
function refCommits(cwd, query = "", max = DEFAULT_REF_COMMIT_LIMIT) {
|
|
417
|
+
const limit = clampCommitLimit(max);
|
|
418
|
+
const trimmed = query.trim().slice(0, 200).replace(/\0/g, "");
|
|
419
|
+
const hashMatches = [];
|
|
420
|
+
if (/^[0-9a-f]{4,40}$/i.test(trimmed)) {
|
|
421
|
+
const verified = run(["git", "rev-parse", "--verify", `${trimmed}^{commit}`], cwd);
|
|
422
|
+
const single = run([
|
|
423
|
+
"git",
|
|
424
|
+
"log",
|
|
425
|
+
"-z",
|
|
426
|
+
"-1",
|
|
427
|
+
`--format=${COMMIT_FORMAT}`,
|
|
428
|
+
verified.code === 0 && verified.stdout.trim() ? verified.stdout.trim() : trimmed
|
|
429
|
+
], cwd);
|
|
430
|
+
if (single.code === 0 && single.stdout.trim()) {
|
|
431
|
+
hashMatches.push(...parseCommitLog(single.stdout));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (!trimmed) {
|
|
435
|
+
return runCommitLog(cwd, commitLogArgs(limit));
|
|
436
|
+
}
|
|
437
|
+
const subjectMatches = runCommitLog(cwd, [
|
|
438
|
+
...commitLogArgs(limit),
|
|
439
|
+
"--regexp-ignore-case",
|
|
440
|
+
"--fixed-strings",
|
|
441
|
+
`--grep=${trimmed}`
|
|
442
|
+
]);
|
|
443
|
+
const authorMatches = runCommitLog(cwd, [
|
|
444
|
+
...commitLogArgs(limit),
|
|
445
|
+
"--regexp-ignore-case",
|
|
446
|
+
"--fixed-strings",
|
|
447
|
+
`--author=${trimmed}`
|
|
448
|
+
]);
|
|
449
|
+
return mergeCommitResults(limit, hashMatches, subjectMatches, authorMatches);
|
|
450
|
+
}
|
|
343
451
|
function nameStatus(args, cwd) {
|
|
344
452
|
const res = run([
|
|
345
453
|
"git",
|
|
@@ -366,7 +474,12 @@ function nameStatus(args, cwd) {
|
|
|
366
474
|
const oldPath = parts[i++] || "";
|
|
367
475
|
const path = parts[i++] || "";
|
|
368
476
|
if (path)
|
|
369
|
-
files.push({
|
|
477
|
+
files.push({
|
|
478
|
+
status: kind,
|
|
479
|
+
old_path: oldPath,
|
|
480
|
+
path,
|
|
481
|
+
similarity: Number(status.slice(1)) || undefined
|
|
482
|
+
});
|
|
370
483
|
} else {
|
|
371
484
|
const path = parts[i++] || "";
|
|
372
485
|
if (path)
|
|
@@ -512,7 +625,11 @@ function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_
|
|
|
512
625
|
continue;
|
|
513
626
|
walk(full, entryPath, depth + 1);
|
|
514
627
|
} else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
515
|
-
if (!pushRecursiveEntry({
|
|
628
|
+
if (!pushRecursiveEntry({
|
|
629
|
+
name: entry.name,
|
|
630
|
+
path: entryPath,
|
|
631
|
+
type: "blob"
|
|
632
|
+
}))
|
|
516
633
|
return;
|
|
517
634
|
}
|
|
518
635
|
}
|
|
@@ -545,7 +662,11 @@ function gitTreeEntries(ref, path, cwd, recursive) {
|
|
|
545
662
|
if (!match)
|
|
546
663
|
return null;
|
|
547
664
|
const entryPath = match[2];
|
|
548
|
-
return {
|
|
665
|
+
return {
|
|
666
|
+
name: entryPath.split("/").pop() || entryPath,
|
|
667
|
+
path: entryPath,
|
|
668
|
+
type: match[1]
|
|
669
|
+
};
|
|
549
670
|
}).filter((entry) => !!entry);
|
|
550
671
|
if (recursive)
|
|
551
672
|
entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -566,7 +687,11 @@ function worktreeFiles(cwd) {
|
|
|
566
687
|
function listTree(ref, path, cwd, options = {}) {
|
|
567
688
|
const base = normalizeTreePath(path);
|
|
568
689
|
if (ref === "worktree") {
|
|
569
|
-
return {
|
|
690
|
+
return {
|
|
691
|
+
code: 0,
|
|
692
|
+
entries: worktreeFilesystemEntries(cwd, base, !!options.recursive, options.omitDirNames),
|
|
693
|
+
stderr: ""
|
|
694
|
+
};
|
|
570
695
|
}
|
|
571
696
|
const direct = gitTreeEntries(ref, base, cwd, false);
|
|
572
697
|
if (direct.code !== 0 || !options.recursive)
|
|
@@ -574,7 +699,11 @@ function listTree(ref, path, cwd, options = {}) {
|
|
|
574
699
|
const recursive = gitTreeEntries(ref, base, cwd, true);
|
|
575
700
|
if (recursive.code !== 0)
|
|
576
701
|
return recursive;
|
|
577
|
-
return {
|
|
702
|
+
return {
|
|
703
|
+
code: 0,
|
|
704
|
+
entries: combineDirectAndRecursiveFiles(direct.entries, recursive.entries),
|
|
705
|
+
stderr: ""
|
|
706
|
+
};
|
|
578
707
|
}
|
|
579
708
|
function untrackedMeta(cwd) {
|
|
580
709
|
return untracked(cwd).map((path) => {
|
|
@@ -589,7 +718,14 @@ function untrackedMeta(cwd) {
|
|
|
589
718
|
lines = data.toString("utf8").split(`
|
|
590
719
|
`).length - 1;
|
|
591
720
|
}
|
|
592
|
-
return {
|
|
721
|
+
return {
|
|
722
|
+
path,
|
|
723
|
+
status: "A",
|
|
724
|
+
additions: binary ? 0 : lines,
|
|
725
|
+
deletions: 0,
|
|
726
|
+
binary,
|
|
727
|
+
untracked: true
|
|
728
|
+
};
|
|
593
729
|
});
|
|
594
730
|
}
|
|
595
731
|
function fileMeta(args, cwd, includeUntracked = false) {
|
|
@@ -990,7 +1126,12 @@ function buildFileSearchList(ref, generation, entries) {
|
|
|
990
1126
|
}
|
|
991
1127
|
function buildRgArgs(query, max, paths, regex = false, omitDirNames = []) {
|
|
992
1128
|
const safePaths = paths.length ? paths : ["."];
|
|
993
|
-
const omitGlobs = omitDirNames.flatMap((name) => [
|
|
1129
|
+
const omitGlobs = omitDirNames.flatMap((name) => [
|
|
1130
|
+
"--glob",
|
|
1131
|
+
`!${name}/**`,
|
|
1132
|
+
"--glob",
|
|
1133
|
+
`!**/${name}/**`
|
|
1134
|
+
]);
|
|
994
1135
|
const args = [
|
|
995
1136
|
"rg",
|
|
996
1137
|
"--no-config",
|
|
@@ -1029,7 +1170,12 @@ function parseRgOutput(stdout, max, omitDirNames = []) {
|
|
|
1029
1170
|
const preview = parsed[4];
|
|
1030
1171
|
if (!path || !lineNo || !column || isSkippableSearchPath(path, omitDirNames))
|
|
1031
1172
|
continue;
|
|
1032
|
-
matches.push({
|
|
1173
|
+
matches.push({
|
|
1174
|
+
path,
|
|
1175
|
+
line: lineNo,
|
|
1176
|
+
column,
|
|
1177
|
+
preview: preview.slice(0, 500)
|
|
1178
|
+
});
|
|
1033
1179
|
}
|
|
1034
1180
|
return matches;
|
|
1035
1181
|
}
|
|
@@ -1728,6 +1874,12 @@ function handleGrep(url) {
|
|
|
1728
1874
|
return text("invalid target", 400);
|
|
1729
1875
|
return json(grepTreeRef(ref, query, max, paths, regex, omitDirNames));
|
|
1730
1876
|
}
|
|
1877
|
+
function handleRefCommits(url) {
|
|
1878
|
+
const query = url.searchParams.get("q") || "";
|
|
1879
|
+
const parsedMax = Number(url.searchParams.get("max") || "");
|
|
1880
|
+
const max = Number.isFinite(parsedMax) && parsedMax > 0 ? parsedMax : undefined;
|
|
1881
|
+
return json({ commits: refCommits(cwd, query, max) });
|
|
1882
|
+
}
|
|
1731
1883
|
function handleFileDiff(url) {
|
|
1732
1884
|
const path = url.searchParams.get("path") || "";
|
|
1733
1885
|
if (!safePath(path))
|
|
@@ -2300,6 +2452,8 @@ var server = await startServer({
|
|
|
2300
2452
|
return handleFiles(url);
|
|
2301
2453
|
if (url.pathname === "/_grep")
|
|
2302
2454
|
return handleGrep(url);
|
|
2455
|
+
if (url.pathname === "/_commits")
|
|
2456
|
+
return handleRefCommits(url);
|
|
2303
2457
|
if (url.pathname === "/file_diff")
|
|
2304
2458
|
return handleFileDiff(url);
|
|
2305
2459
|
if (url.pathname === "/file_range")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youtyan/code-viewer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"description": "Local browser-based code and git diff viewer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"check": "bun run typecheck",
|
|
33
33
|
"typecheck": "tsc --noEmit",
|
|
34
34
|
"check:bundle": "bun build --target=browser --format=iife --outfile=/tmp/code-viewer-app.js web-src/app.ts && cmp /tmp/code-viewer-app.js web/app.js && bun build --target=browser --format=esm --outfile=/tmp/code-viewer-mermaid.js web-src/mermaid-entry.ts && cmp /tmp/code-viewer-mermaid.js web/mermaid.js && bun build --target=browser --format=esm --outfile=/tmp/code-viewer-shiki.js web-src/shiki-entry.ts && cmp /tmp/code-viewer-shiki.js web/shiki.js",
|
|
35
|
-
"check:format": "biome format
|
|
35
|
+
"check:format": "biome format .",
|
|
36
36
|
"dev": "bun run web-src/server/dev.ts",
|
|
37
37
|
"preview": "bun run web-src/server/dev.ts",
|
|
38
38
|
"preview:raw": "bun run web-src/server/preview.ts",
|
package/web/app.js
CHANGED
|
@@ -359,22 +359,58 @@
|
|
|
359
359
|
if (isGlobPathQuery(query)) {
|
|
360
360
|
return items.map((item) => {
|
|
361
361
|
const match = globMatchPath(query, item.path);
|
|
362
|
-
return match ? {
|
|
362
|
+
return match ? {
|
|
363
|
+
item,
|
|
364
|
+
score: match.score,
|
|
365
|
+
ranges: match.ranges,
|
|
366
|
+
mode: "glob"
|
|
367
|
+
} : null;
|
|
363
368
|
}).filter((item) => item !== null).sort((a, b) => b.score - a.score || a.item.path.localeCompare(b.item.path));
|
|
364
369
|
}
|
|
365
|
-
return rankFuzzyPaths(query, items).map((item) => ({
|
|
370
|
+
return rankFuzzyPaths(query, items).map((item) => ({
|
|
371
|
+
...item,
|
|
372
|
+
mode: "fuzzy"
|
|
373
|
+
}));
|
|
366
374
|
}
|
|
367
375
|
|
|
368
376
|
// web-src/keymap.ts
|
|
369
377
|
var DEFAULT_KEY_BINDINGS = [
|
|
370
|
-
{
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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
|
+
},
|
|
374
406
|
{ action: "focus-file-filter", key: "/" },
|
|
375
407
|
{ action: "focus-sidebar", key: "h", ctrl: true },
|
|
376
408
|
{ action: "focus-main", key: "l", ctrl: true },
|
|
377
|
-
{
|
|
409
|
+
{
|
|
410
|
+
action: "cancel-source-load",
|
|
411
|
+
key: "escape",
|
|
412
|
+
requires: { lightboxClosed: true }
|
|
413
|
+
},
|
|
378
414
|
{ action: "open-sidebar-item", key: "enter", scope: "sidebar" },
|
|
379
415
|
{ action: "open-sidebar-item", key: "enter", scope: "global" },
|
|
380
416
|
{ action: "sidebar-next", key: "j", scope: "sidebar" },
|
|
@@ -399,12 +435,37 @@
|
|
|
399
435
|
{ action: "scroll-main-page-up", key: "pageup", scope: "global" },
|
|
400
436
|
{ action: "scroll-main-page-down", key: "pagedown", scope: "sidebar" },
|
|
401
437
|
{ action: "scroll-main-page-up", key: "pageup", scope: "sidebar" },
|
|
402
|
-
{
|
|
438
|
+
{
|
|
439
|
+
action: "scroll-main-page-down",
|
|
440
|
+
key: "arrowdown",
|
|
441
|
+
scope: "main",
|
|
442
|
+
ctrl: true
|
|
443
|
+
},
|
|
403
444
|
{ action: "scroll-main-page-up", key: "arrowup", scope: "main", ctrl: true },
|
|
404
|
-
{
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
+
},
|
|
408
469
|
{ action: "tab-preview", key: "p", scope: "main", pendingG: true },
|
|
409
470
|
{ action: "tab-code", key: "c", scope: "main", pendingG: true },
|
|
410
471
|
{ action: "goto-top", key: "g", pendingG: true },
|
|
@@ -6091,8 +6152,21 @@
|
|
|
6091
6152
|
const ref = target || params.get("ref") || "worktree";
|
|
6092
6153
|
const line = parseLineTarget(params.get("line"));
|
|
6093
6154
|
if (!path)
|
|
6094
|
-
return {
|
|
6095
|
-
|
|
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
|
+
};
|
|
6096
6170
|
}
|
|
6097
6171
|
case "/help":
|
|
6098
6172
|
return {
|
|
@@ -6102,7 +6176,13 @@
|
|
|
6102
6176
|
section: params.get("section") || "keybindings"
|
|
6103
6177
|
};
|
|
6104
6178
|
default:
|
|
6105
|
-
return {
|
|
6179
|
+
return {
|
|
6180
|
+
screen: "unknown",
|
|
6181
|
+
reason: "unknown-pathname",
|
|
6182
|
+
rawPathname: pathname,
|
|
6183
|
+
rawSearch: search,
|
|
6184
|
+
range
|
|
6185
|
+
};
|
|
6106
6186
|
}
|
|
6107
6187
|
}
|
|
6108
6188
|
function buildRoute(route) {
|
|
@@ -6218,7 +6298,10 @@
|
|
|
6218
6298
|
}
|
|
6219
6299
|
function resolveRepoRelative(currentPath, requestedPath) {
|
|
6220
6300
|
const base2 = currentPath.split("/").slice(0, -1);
|
|
6221
|
-
const parts = [
|
|
6301
|
+
const parts = [
|
|
6302
|
+
...requestedPath.startsWith("/") ? [] : base2,
|
|
6303
|
+
...requestedPath.split("/")
|
|
6304
|
+
].filter((part) => part && part !== ".");
|
|
6222
6305
|
const resolved = [];
|
|
6223
6306
|
for (const part of parts) {
|
|
6224
6307
|
if (part === "..") {
|
|
@@ -6440,7 +6523,10 @@
|
|
|
6440
6523
|
const toc = root.querySelector(".gdp-markdown-toc");
|
|
6441
6524
|
if (!toc)
|
|
6442
6525
|
return;
|
|
6443
|
-
const entries = Array.from(toc.querySelectorAll("a[data-target]")).map((link2) => ({
|
|
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);
|
|
6444
6530
|
if (!entries.length)
|
|
6445
6531
|
return;
|
|
6446
6532
|
toc.addEventListener("click", (e2) => {
|
|
@@ -6485,7 +6571,10 @@
|
|
|
6485
6571
|
if (!raf)
|
|
6486
6572
|
raf = requestAnimationFrame(update);
|
|
6487
6573
|
};
|
|
6488
|
-
window.addEventListener("scroll", schedule, {
|
|
6574
|
+
window.addEventListener("scroll", schedule, {
|
|
6575
|
+
passive: true,
|
|
6576
|
+
signal: controller.signal
|
|
6577
|
+
});
|
|
6489
6578
|
window.addEventListener("resize", schedule, { signal: controller.signal });
|
|
6490
6579
|
setTimeout(() => {
|
|
6491
6580
|
if (!root.isConnected)
|
|
@@ -6537,7 +6626,11 @@
|
|
|
6537
6626
|
const typed = mod;
|
|
6538
6627
|
const mermaid = typed.default;
|
|
6539
6628
|
if (!mermaidInitialized) {
|
|
6540
|
-
mermaid.initialize({
|
|
6629
|
+
mermaid.initialize({
|
|
6630
|
+
startOnLoad: false,
|
|
6631
|
+
securityLevel: "strict",
|
|
6632
|
+
theme: "default"
|
|
6633
|
+
});
|
|
6541
6634
|
mermaidInitialized = true;
|
|
6542
6635
|
}
|
|
6543
6636
|
return mermaid;
|
|
@@ -12438,20 +12531,58 @@
|
|
|
12438
12531
|
}
|
|
12439
12532
|
fetchRefs();
|
|
12440
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
|
+
}
|
|
12441
12566
|
function buildPopBody(query) {
|
|
12442
12567
|
const q = (query || "").toLowerCase().trim();
|
|
12443
12568
|
const m = (s2) => !q || String(s2).toLowerCase().includes(q);
|
|
12444
12569
|
const html = [];
|
|
12445
12570
|
if (popTab === "commits") {
|
|
12446
|
-
|
|
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}`));
|
|
12447
12578
|
if (!commits.length) {
|
|
12448
12579
|
html.push('<div class="rp-empty">no commits</div>');
|
|
12449
12580
|
}
|
|
12450
|
-
for (const
|
|
12451
|
-
|
|
12452
|
-
if (!sha)
|
|
12581
|
+
for (const commit of commits) {
|
|
12582
|
+
if (!commit.sha)
|
|
12453
12583
|
continue;
|
|
12454
|
-
|
|
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>");
|
|
12455
12586
|
}
|
|
12456
12587
|
} else if (popTab === "branches") {
|
|
12457
12588
|
const branches = (REFS.branches || []).filter(m);
|
|
@@ -12502,6 +12633,8 @@
|
|
|
12502
12633
|
function openPopover(input) {
|
|
12503
12634
|
popTarget = input;
|
|
12504
12635
|
popSearch.value = "";
|
|
12636
|
+
if (popTab === "commits")
|
|
12637
|
+
scheduleCommitSearch("");
|
|
12505
12638
|
buildPopBody("");
|
|
12506
12639
|
const cur = (input.value || "").trim();
|
|
12507
12640
|
popover.querySelectorAll(".rp-chip").forEach((c2) => {
|
|
@@ -12539,13 +12672,17 @@
|
|
|
12539
12672
|
});
|
|
12540
12673
|
renderStandaloneSource({ path: STATE.route.path, ref });
|
|
12541
12674
|
});
|
|
12542
|
-
popSearch.addEventListener("input", () =>
|
|
12675
|
+
popSearch.addEventListener("input", () => {
|
|
12676
|
+
if (popTab === "commits")
|
|
12677
|
+
scheduleCommitSearch(popSearch.value);
|
|
12678
|
+
buildPopBody(popSearch.value);
|
|
12679
|
+
});
|
|
12543
12680
|
popSearch.addEventListener("keydown", (e2) => {
|
|
12544
12681
|
if (e2.key === "Escape") {
|
|
12545
12682
|
closePopover();
|
|
12546
12683
|
}
|
|
12547
12684
|
if (e2.key === "Enter") {
|
|
12548
|
-
const first = popBody.querySelector(".rp-item");
|
|
12685
|
+
const first = popBody.querySelector(".rp-item-commit, .rp-item-ref");
|
|
12549
12686
|
if (first)
|
|
12550
12687
|
first.click();
|
|
12551
12688
|
}
|
|
@@ -12568,6 +12705,8 @@
|
|
|
12568
12705
|
t2.addEventListener("click", () => {
|
|
12569
12706
|
popTab = t2.dataset.tab || "commits";
|
|
12570
12707
|
popover.querySelectorAll(".rp-tab").forEach((b2) => b2.classList.toggle("active", b2 === t2));
|
|
12708
|
+
if (popTab === "commits")
|
|
12709
|
+
scheduleCommitSearch(popSearch.value);
|
|
12571
12710
|
buildPopBody(popSearch.value);
|
|
12572
12711
|
});
|
|
12573
12712
|
});
|