@youtyan/code-viewer 0.13.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/package.json +1 -1
- package/web/app.js +150 -21
package/package.json
CHANGED
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;
|
|
@@ -25714,6 +25725,18 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
25714
25725
|
(f2) => `${fileKey2(f2)}\0${f2.path}\0${f2.old_path || ""}\0${f2.status || "M"}`
|
|
25715
25726
|
).join("\n");
|
|
25716
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
|
+
}
|
|
25717
25740
|
function computeCardSignature(f2) {
|
|
25718
25741
|
return [
|
|
25719
25742
|
fileKey2(f2),
|
|
@@ -25725,12 +25748,18 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
25725
25748
|
f2.size_class || "small",
|
|
25726
25749
|
f2.force_layout || "",
|
|
25727
25750
|
f2.highlight ? 1 : 0,
|
|
25728
|
-
f2.load_url,
|
|
25729
|
-
f2.preview_url
|
|
25751
|
+
stripLoadUrlGeneration(f2.load_url),
|
|
25752
|
+
f2.preview_url ? stripLoadUrlGeneration(f2.preview_url) : "",
|
|
25730
25753
|
f2.estimated_height_px || 0,
|
|
25731
25754
|
f2.untracked ? 1 : 0
|
|
25732
25755
|
].join("\0");
|
|
25733
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
|
+
}
|
|
25734
25763
|
function ensureLazyObserver() {
|
|
25735
25764
|
if (!lazyObserver) {
|
|
25736
25765
|
lazyObserver = new IntersectionObserver(
|
|
@@ -25738,8 +25767,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
25738
25767
|
for (const entry of entries) {
|
|
25739
25768
|
if (!entry.isIntersecting) continue;
|
|
25740
25769
|
const card = entry.target;
|
|
25741
|
-
if (
|
|
25742
|
-
continue;
|
|
25770
|
+
if (!cardNeedsLoad(card)) continue;
|
|
25743
25771
|
const f2 = card._file || STATE.files.find((x) => x.path === card.dataset.path);
|
|
25744
25772
|
if (f2) enqueueLoad(f2, card, 0);
|
|
25745
25773
|
}
|
|
@@ -25761,15 +25789,27 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
25761
25789
|
return false;
|
|
25762
25790
|
if (!changedPathsCoverPath(changedPaths, file.path)) return false;
|
|
25763
25791
|
card.dataset.reqId = String(++CLIENT_REQ_SEQ);
|
|
25792
|
+
delete card.dataset.stale;
|
|
25793
|
+
delete card.dataset.staleLoading;
|
|
25764
25794
|
card.style.minHeight = `${measuredHeight ?? card.offsetHeight}px`;
|
|
25765
25795
|
card.classList.remove("loaded", "loading", "error");
|
|
25766
25796
|
card.classList.add("pending");
|
|
25767
25797
|
card._diffData = null;
|
|
25798
|
+
card._loadedSig = null;
|
|
25799
|
+
card._loadedSigUrl = null;
|
|
25768
25800
|
const indicator = card.querySelector(".loading-indicator");
|
|
25769
25801
|
if (indicator) indicator.hidden = false;
|
|
25770
25802
|
activatePendingCard(card, file);
|
|
25771
25803
|
return true;
|
|
25772
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
|
+
}
|
|
25773
25813
|
function updateSidebarStats(files) {
|
|
25774
25814
|
for (const f2 of files) {
|
|
25775
25815
|
const li = document.querySelector(
|
|
@@ -25800,8 +25840,12 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
25800
25840
|
delete card.dataset.manualRendered;
|
|
25801
25841
|
delete card.dataset.manualLoad;
|
|
25802
25842
|
delete card.dataset.manualMode;
|
|
25843
|
+
delete card.dataset.stale;
|
|
25844
|
+
delete card.dataset.staleLoading;
|
|
25803
25845
|
card.style.minHeight = `${file.estimated_height_px || 80}px`;
|
|
25804
25846
|
card._diffData = null;
|
|
25847
|
+
card._loadedSig = null;
|
|
25848
|
+
card._loadedSigUrl = null;
|
|
25805
25849
|
card._file = file;
|
|
25806
25850
|
}
|
|
25807
25851
|
function renderShell(meta, changedPaths) {
|
|
@@ -25877,23 +25921,37 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
25877
25921
|
activatePendingCard(card, f2);
|
|
25878
25922
|
invalidatedCards++;
|
|
25879
25923
|
sidebarNeedsStatsUpdate = true;
|
|
25880
|
-
} else {
|
|
25924
|
+
} else if (sigChanged) {
|
|
25881
25925
|
const stats = card.querySelector(".gdp-shell-header .stats");
|
|
25882
25926
|
if (stats) {
|
|
25883
25927
|
stats.innerHTML = '<span class="a">+' + (f2.additions || 0) + '</span><span class="d">−' + (f2.deletions || 0) + "</span>";
|
|
25884
25928
|
}
|
|
25885
25929
|
card._file = f2;
|
|
25886
|
-
const didInvalidate =
|
|
25930
|
+
const didInvalidate = invalidateLoadedCard(
|
|
25887
25931
|
card,
|
|
25888
25932
|
f2,
|
|
25889
|
-
|
|
25933
|
+
null,
|
|
25890
25934
|
measuredHeights.get(key)
|
|
25891
25935
|
);
|
|
25892
25936
|
if (didInvalidate) invalidatedCards++;
|
|
25893
|
-
else if (
|
|
25937
|
+
else if (card.classList.contains("pending")) {
|
|
25894
25938
|
activatePendingCard(card, f2);
|
|
25895
25939
|
}
|
|
25896
|
-
|
|
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
|
+
}
|
|
25897
25955
|
}
|
|
25898
25956
|
}
|
|
25899
25957
|
if (sidebarNeedsStatsUpdate && canUpdateSidebar)
|
|
@@ -25988,8 +26046,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
25988
26046
|
entries.forEach((entry) => {
|
|
25989
26047
|
if (!entry.isIntersecting) return;
|
|
25990
26048
|
const card = entry.target;
|
|
25991
|
-
if (
|
|
25992
|
-
return;
|
|
26049
|
+
if (!cardNeedsLoad(card)) return;
|
|
25993
26050
|
const f2 = card._file || STATE.files.find((x) => x.path === card.dataset.path);
|
|
25994
26051
|
if (!f2) return;
|
|
25995
26052
|
enqueueLoad(f2, card, 0);
|
|
@@ -26030,8 +26087,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
26030
26087
|
while (ACTIVE_LOADS < MAX_PARALLEL && LOAD_QUEUE.length) {
|
|
26031
26088
|
const item = LOAD_QUEUE.shift();
|
|
26032
26089
|
if (item.epoch !== LOAD_EPOCH) continue;
|
|
26033
|
-
if (
|
|
26034
|
-
continue;
|
|
26090
|
+
if (!cardNeedsLoad(item.card)) continue;
|
|
26035
26091
|
ACTIVE_LOADS++;
|
|
26036
26092
|
loadFile(item.file, item.card).finally(() => {
|
|
26037
26093
|
if (item.epoch === LOAD_EPOCH) {
|
|
@@ -26126,20 +26182,29 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
26126
26182
|
});
|
|
26127
26183
|
}
|
|
26128
26184
|
function loadFile(file, card, urlOverride, options) {
|
|
26129
|
-
card.classList.
|
|
26130
|
-
card.classList.add("loading");
|
|
26131
|
-
if (lazyObserver) lazyObserver.unobserve(card);
|
|
26185
|
+
const silent = card.classList.contains("loaded") && card.dataset.stale === "1";
|
|
26132
26186
|
const indicator = card.querySelector(".loading-indicator");
|
|
26133
|
-
if (
|
|
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);
|
|
26134
26195
|
const url = urlOverride || (card.dataset.manualMode === "full" ? file.load_url : file.preview_url || file.load_url);
|
|
26135
26196
|
const myGen = getServerGeneration();
|
|
26136
26197
|
const myReq = ++CLIENT_REQ_SEQ;
|
|
26137
26198
|
card.dataset.reqId = String(myReq);
|
|
26138
26199
|
const retryStale = () => {
|
|
26139
26200
|
if (String(myReq) !== card.dataset.reqId) return;
|
|
26140
|
-
|
|
26141
|
-
|
|
26142
|
-
|
|
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
|
+
}
|
|
26143
26208
|
const fresh = card._file || STATE.files.find((x) => x.path === card.dataset.path);
|
|
26144
26209
|
if (fresh && card.isConnected) enqueueLoad(fresh, card, 0);
|
|
26145
26210
|
};
|
|
@@ -26158,11 +26223,32 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
26158
26223
|
retryStale();
|
|
26159
26224
|
return;
|
|
26160
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
|
+
}
|
|
26161
26235
|
if (!options?.immediate) await nextIdle();
|
|
26162
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;
|
|
26163
26243
|
renderFile(file, data, card);
|
|
26164
26244
|
}).catch((error2) => {
|
|
26165
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
|
+
}
|
|
26166
26252
|
console.error("[code-viewer] failed to load diff", error2);
|
|
26167
26253
|
card.classList.remove("loading");
|
|
26168
26254
|
card.classList.add("error");
|
|
@@ -36086,6 +36172,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36086
36172
|
async function loadRawFileInfo(target) {
|
|
36087
36173
|
try {
|
|
36088
36174
|
const res = await fetch(buildRawFileUrl(target), { method: "HEAD" });
|
|
36175
|
+
if (res.status === 404) return { missing: true };
|
|
36089
36176
|
if (!res.ok) return {};
|
|
36090
36177
|
const rawSize = res.headers.get("content-length");
|
|
36091
36178
|
const size = rawSize == null ? NaN : Number(rawSize);
|
|
@@ -47326,12 +47413,53 @@ ${error2.stack}` : ""}`
|
|
|
47326
47413
|
);
|
|
47327
47414
|
}
|
|
47328
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
|
+
}
|
|
47329
47456
|
function dispatchFileRoute(route, options = {}) {
|
|
47330
47457
|
if (route.view === "blob") {
|
|
47331
47458
|
setStatus("live");
|
|
47332
47459
|
removeFileHistoryShell2();
|
|
47333
47460
|
BLAME_VIEW.removeBlamePage();
|
|
47334
47461
|
applySourceRouteToShell(options);
|
|
47462
|
+
seedFileRouteSignature(route);
|
|
47335
47463
|
return true;
|
|
47336
47464
|
}
|
|
47337
47465
|
if (route.view === "blame") {
|
|
@@ -47339,6 +47467,7 @@ ${error2.stack}` : ""}`
|
|
|
47339
47467
|
cancelActiveSourceLoad("navigation");
|
|
47340
47468
|
removeFileHistoryShell2();
|
|
47341
47469
|
void BLAME_VIEW.renderBlamePage({ path: route.path, ref: route.ref });
|
|
47470
|
+
seedFileRouteSignature(route);
|
|
47342
47471
|
return true;
|
|
47343
47472
|
}
|
|
47344
47473
|
if (route.view === "history") {
|
|
@@ -49510,7 +49639,7 @@ ${error2.stack}` : ""}`
|
|
|
49510
49639
|
if (isBlobOrBlameFileRoute(route)) {
|
|
49511
49640
|
const viewingPath = route.path;
|
|
49512
49641
|
if (viewingPath && !changedPathsCoverPath(paths, viewingPath)) return;
|
|
49513
|
-
|
|
49642
|
+
refreshFileRouteIfChanged(route);
|
|
49514
49643
|
return;
|
|
49515
49644
|
}
|
|
49516
49645
|
if (route.screen === "repo") {
|