@youtyan/code-viewer 0.1.7 → 0.1.8
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 +39 -16
- package/web-src/server/dev-assets.ts +37 -0
- package/web-src/server/dev.ts +6 -1
- package/web-src/server/preview.ts +10 -5
- package/web-src/server/runtime.d.ts +6 -0
- package/web-src/types.ts +0 -4
package/package.json
CHANGED
package/web/app.js
CHANGED
|
@@ -109,6 +109,21 @@
|
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
// web-src/catch-up.ts
|
|
113
|
+
function shouldCatchUpDiff(route) {
|
|
114
|
+
return route.screen !== "repo" && !(route.screen === "file" && route.view === "blob");
|
|
115
|
+
}
|
|
116
|
+
function createCatchUpGate(now, minIntervalMs) {
|
|
117
|
+
let lastForceAt = 0;
|
|
118
|
+
return function shouldRun() {
|
|
119
|
+
const current = now();
|
|
120
|
+
if (current - lastForceAt < minIntervalMs)
|
|
121
|
+
return false;
|
|
122
|
+
lastForceAt = current;
|
|
123
|
+
return true;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
112
127
|
// web-src/routes.ts
|
|
113
128
|
function assertNever(value) {
|
|
114
129
|
throw new Error("unhandled route: " + JSON.stringify(value));
|
|
@@ -2686,7 +2701,7 @@
|
|
|
2686
2701
|
syncHeaderMenu();
|
|
2687
2702
|
}).catch(() => setStatus("error"));
|
|
2688
2703
|
}
|
|
2689
|
-
function load() {
|
|
2704
|
+
function load(options = {}) {
|
|
2690
2705
|
if (STATE.route.screen === "repo")
|
|
2691
2706
|
return loadRepo();
|
|
2692
2707
|
setStatus("refreshing");
|
|
@@ -2697,6 +2712,8 @@
|
|
|
2697
2712
|
params.set("from", STATE.from);
|
|
2698
2713
|
if (STATE.to)
|
|
2699
2714
|
params.set("to", STATE.to);
|
|
2715
|
+
if (options.force)
|
|
2716
|
+
params.set("nocache", "1");
|
|
2700
2717
|
const url = "/diff.json" + (params.toString() ? "?" + params.toString() : "");
|
|
2701
2718
|
return trackLoad(fetch(url).then((r) => r.json())).then((data) => {
|
|
2702
2719
|
renderShell(data);
|
|
@@ -3033,24 +3050,30 @@
|
|
|
3033
3050
|
}, 350);
|
|
3034
3051
|
}
|
|
3035
3052
|
const es = new EventSource("/events");
|
|
3053
|
+
const catchUpGate = createCatchUpGate(() => Date.now(), 1000);
|
|
3054
|
+
let openedOnce = false;
|
|
3036
3055
|
es.addEventListener("update", () => scheduleSseLoad());
|
|
3037
3056
|
es.addEventListener("reload", () => location.reload());
|
|
3038
3057
|
es.addEventListener("error", () => setStatus("error"));
|
|
3039
|
-
es.addEventListener("open", () =>
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3058
|
+
es.addEventListener("open", () => {
|
|
3059
|
+
setStatus("live");
|
|
3060
|
+
if (!openedOnce) {
|
|
3061
|
+
openedOnce = true;
|
|
3062
|
+
return;
|
|
3063
|
+
}
|
|
3064
|
+
catchUpDiff();
|
|
3065
|
+
});
|
|
3066
|
+
function catchUpDiff() {
|
|
3067
|
+
if (!shouldCatchUpDiff(STATE.route))
|
|
3068
|
+
return;
|
|
3069
|
+
if (!catchUpGate())
|
|
3070
|
+
return;
|
|
3071
|
+
load({ force: true });
|
|
3052
3072
|
}
|
|
3053
|
-
|
|
3054
|
-
|
|
3073
|
+
document.addEventListener("visibilitychange", () => {
|
|
3074
|
+
if (!document.hidden)
|
|
3075
|
+
catchUpDiff();
|
|
3076
|
+
});
|
|
3077
|
+
window.addEventListener("focus", catchUpDiff);
|
|
3055
3078
|
})();
|
|
3056
3079
|
})();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
|
|
3
|
+
export type WatchFn = (
|
|
4
|
+
path: string,
|
|
5
|
+
options: { persistent?: boolean },
|
|
6
|
+
listener: (eventType: string, filename: string | Buffer | null) => void,
|
|
7
|
+
) => unknown;
|
|
8
|
+
|
|
9
|
+
type DevAssetReloadOptions = {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
webRoot: string;
|
|
12
|
+
watchedFiles: readonly string[];
|
|
13
|
+
watch: WatchFn;
|
|
14
|
+
sendReload: () => void;
|
|
15
|
+
setTimeoutFn?: typeof setTimeout;
|
|
16
|
+
clearTimeoutFn?: typeof clearTimeout;
|
|
17
|
+
debounceMs?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function startDevAssetReload(options: DevAssetReloadOptions): boolean {
|
|
21
|
+
if (!options.enabled) return false;
|
|
22
|
+
const watched = new Set(options.watchedFiles);
|
|
23
|
+
const setTimer = options.setTimeoutFn || setTimeout;
|
|
24
|
+
const clearTimer = options.clearTimeoutFn || clearTimeout;
|
|
25
|
+
const debounceMs = options.debounceMs ?? 150;
|
|
26
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
27
|
+
|
|
28
|
+
options.watch(options.webRoot, { persistent: false }, (_event, filename) => {
|
|
29
|
+
if (!filename || !watched.has(basename(filename.toString()))) return;
|
|
30
|
+
if (timer) clearTimer(timer);
|
|
31
|
+
timer = setTimer(() => {
|
|
32
|
+
timer = null;
|
|
33
|
+
options.sendReload();
|
|
34
|
+
}, debounceMs);
|
|
35
|
+
});
|
|
36
|
+
return true;
|
|
37
|
+
}
|
package/web-src/server/dev.ts
CHANGED
|
@@ -56,7 +56,12 @@ function startServer() {
|
|
|
56
56
|
firstStart = false;
|
|
57
57
|
server = Bun.spawn([
|
|
58
58
|
'bun', 'run', 'web-src/server/preview.ts', ...args,
|
|
59
|
-
], {
|
|
59
|
+
], {
|
|
60
|
+
cwd: ROOT,
|
|
61
|
+
stdout: 'inherit',
|
|
62
|
+
stderr: 'inherit',
|
|
63
|
+
env: { ...process.env, CODE_VIEWER_DEV: '1' },
|
|
64
|
+
}) as ChildProcess;
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
async function restartServer() {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import { existsSync, readFileSync, realpathSync, statSync } from 'node:fs';
|
|
3
|
+
import { existsSync, readFileSync, realpathSync, statSync, watch } from 'node:fs';
|
|
4
4
|
import { basename, extname, join, normalize, relative } from 'node:path';
|
|
5
5
|
import { APP_ENTRY_PATHS, SPA_PATHS } from '../routes';
|
|
6
6
|
import type { DiffMeta, FileDiffResponse, FileMeta, FileRangeResponse, RepoTreeResponse } from '../types';
|
|
7
7
|
import { cacheFresh, type TimedCacheEntry } from './cache';
|
|
8
|
+
import { startDevAssetReload } from './dev-assets';
|
|
8
9
|
import * as git from './git';
|
|
9
10
|
import { isSameWorktreeRange } from './range';
|
|
10
11
|
|
|
@@ -458,10 +459,6 @@ const server = Bun.serve({
|
|
|
458
459
|
if (url.pathname === '/file_range') return handleFileRange(url);
|
|
459
460
|
if (url.pathname === '/_file') return handleRawFile(url);
|
|
460
461
|
if (url.pathname === '/_refs') return json(git.refs(cwd));
|
|
461
|
-
if (url.pathname === '/_asset_version') {
|
|
462
|
-
const version = Math.max(...WATCHED_ASSET_FILES.map((name) => statSync(join(WEB_ROOT, name)).mtimeMs));
|
|
463
|
-
return json({ version });
|
|
464
|
-
}
|
|
465
462
|
if (url.pathname === '/refresh' && req.method === 'POST') {
|
|
466
463
|
generation++;
|
|
467
464
|
fileCache.clear();
|
|
@@ -502,5 +499,13 @@ const server = Bun.serve({
|
|
|
502
499
|
},
|
|
503
500
|
});
|
|
504
501
|
|
|
502
|
+
startDevAssetReload({
|
|
503
|
+
enabled: process.env.CODE_VIEWER_DEV === '1',
|
|
504
|
+
webRoot: WEB_ROOT,
|
|
505
|
+
watchedFiles: WATCHED_ASSET_FILES,
|
|
506
|
+
watch,
|
|
507
|
+
sendReload: () => sendSse('reload'),
|
|
508
|
+
});
|
|
509
|
+
|
|
505
510
|
console.log(`GDP_LISTEN_URL=http://127.0.0.1:${server.port}/`);
|
|
506
511
|
console.log(`git-diff-preview serving ${cwd}`);
|
|
@@ -17,6 +17,7 @@ declare const Bun: {
|
|
|
17
17
|
|
|
18
18
|
declare const process: {
|
|
19
19
|
argv: string[];
|
|
20
|
+
env: Record<string, string | undefined>;
|
|
20
21
|
cwd(): string;
|
|
21
22
|
platform: 'darwin' | 'win32' | string;
|
|
22
23
|
on(event: 'SIGINT' | 'SIGTERM', listener: () => void): void;
|
|
@@ -34,6 +35,11 @@ declare module 'node:fs' {
|
|
|
34
35
|
export function readFileSync(path: string, encoding: BufferEncoding): string;
|
|
35
36
|
export function realpathSync(path: string): string;
|
|
36
37
|
export function statSync(path: string): { mtimeMs: number };
|
|
38
|
+
export function watch(
|
|
39
|
+
path: string,
|
|
40
|
+
options: { persistent?: boolean },
|
|
41
|
+
listener: (eventType: string, filename: string | Buffer | null) => void,
|
|
42
|
+
): unknown;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
declare module 'node:path' {
|