@youtyan/code-viewer 0.8.6 → 0.8.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/README.md +13 -3
- package/dist/code-viewer.js +45 -24
- package/package.json +1 -1
- package/web/app.js +719 -286
- package/web/index.html +10 -9
- package/web/style.css +1548 -366
package/README.md
CHANGED
|
@@ -157,6 +157,12 @@ view's row component, so line numbers, drag-selection of `line=` ranges,
|
|
|
157
157
|
syntax highlighting and the Viewer Settings code font size all match the
|
|
158
158
|
Code tab.
|
|
159
159
|
|
|
160
|
+
When the repository remote is hosted on GitHub, repository and file headers
|
|
161
|
+
include an **Open on GitHub** action. Selecting source lines opens a compact
|
|
162
|
+
action bar for copying the AI reference, opening the exact line range on
|
|
163
|
+
GitHub, or copying that GitHub URL. Markdown and HTML diff cards also expose a
|
|
164
|
+
direct **Preview** shortcut beside the regular file view action.
|
|
165
|
+
|
|
160
166
|
Very large text files use a virtualized source viewer. Only visible rows are
|
|
161
167
|
rendered, and the page includes controls to copy the full file or reopen it in
|
|
162
168
|
the full non-virtual view.
|
|
@@ -213,9 +219,11 @@ root:
|
|
|
213
219
|
- `db-snapshots.sqlite` — datastore snapshots and diff blobs.
|
|
214
220
|
- `datastore-connections.json` — saved non-secret datastore connection settings.
|
|
215
221
|
|
|
216
|
-
The `.code-viewer` directory is tool-
|
|
217
|
-
tree,
|
|
218
|
-
|
|
222
|
+
The `.code-viewer` directory is tool-managed. It appears in the repository
|
|
223
|
+
tree, and its text files can be inspected in the Code view, but it remains
|
|
224
|
+
excluded from repository searches and diffs. Treat the files as diagnostic
|
|
225
|
+
state rather than hand-edited configuration. Add `.code-viewer/` to
|
|
226
|
+
`.gitignore` if you do not want to share its contents through git.
|
|
219
227
|
|
|
220
228
|
## Datastore Viewer
|
|
221
229
|
|
|
@@ -803,6 +811,8 @@ state is saved per project).
|
|
|
803
811
|
|
|
804
812
|
A copy button on each annotation produces a paste-ready prompt block that
|
|
805
813
|
references the annotation by URL for sharing back into the originating agent.
|
|
814
|
+
The panel-open state, selected session, and selected annotation are also kept
|
|
815
|
+
in the URL, so a reload or shared link restores the same walkthrough context.
|
|
806
816
|
|
|
807
817
|
The `annotate` subcommand talks to the running server for the repository
|
|
808
818
|
(discovered via `~/.cache/code-viewer/servers/`); start one with
|
package/dist/code-viewer.js
CHANGED
|
@@ -196,7 +196,7 @@ function buildRoute(route) {
|
|
|
196
196
|
}
|
|
197
197
|
case "journal": {
|
|
198
198
|
const params = new URLSearchParams;
|
|
199
|
-
if (route.tab && route.tab !== "
|
|
199
|
+
if (route.tab && route.tab !== "tasks")
|
|
200
200
|
params.set("tab", route.tab);
|
|
201
201
|
if (route.date)
|
|
202
202
|
params.set("date", route.date);
|
|
@@ -1449,18 +1449,13 @@ function resolveGitArgs(args) {
|
|
|
1449
1449
|
return [commandForExternal("git"), ...args.slice(1)];
|
|
1450
1450
|
}
|
|
1451
1451
|
function gitFailureMessage(res, fallback) {
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
return
|
|
1452
|
+
const message = isCommandNotFoundResult("git", res) ? commandNotFoundDetail("git") : res.stderr?.trim() || fallback;
|
|
1453
|
+
console.error(`[code-viewer] ${fallback} (git exit ${res.code}): ${message}`);
|
|
1454
|
+
return message;
|
|
1455
1455
|
}
|
|
1456
1456
|
function gitFailureResult(res, fallback) {
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
}
|
|
1460
|
-
return {
|
|
1461
|
-
error: commandNotFoundDetail("git"),
|
|
1462
|
-
status: 503
|
|
1463
|
-
};
|
|
1457
|
+
const error = gitFailureMessage(res, fallback);
|
|
1458
|
+
return isCommandNotFoundResult("git", res) ? { error, status: 503 } : { error };
|
|
1464
1459
|
}
|
|
1465
1460
|
function runGitRefLookup(args, cwd) {
|
|
1466
1461
|
const res = run(args, cwd);
|
|
@@ -2534,10 +2529,19 @@ async function untrackedMetaAsync(cwd) {
|
|
|
2534
2529
|
return results.filter((meta) => meta !== null);
|
|
2535
2530
|
}
|
|
2536
2531
|
async function fileMetaResultAsync(args, cwd, includeUntracked = false) {
|
|
2537
|
-
|
|
2532
|
+
let ns;
|
|
2533
|
+
let nm;
|
|
2534
|
+
if (args.includes("--")) {
|
|
2535
|
+
[ns, nm] = await Promise.all([
|
|
2536
|
+
nameStatusResultAsync(args, cwd),
|
|
2537
|
+
numstatZResultAsync(args, cwd)
|
|
2538
|
+
]);
|
|
2539
|
+
} else {
|
|
2540
|
+
ns = await nameStatusResultAsync(args, cwd);
|
|
2541
|
+
nm = await numstatZResultAsync(args, cwd);
|
|
2542
|
+
}
|
|
2538
2543
|
if (ns.error)
|
|
2539
2544
|
return { files: [], error: ns.error };
|
|
2540
|
-
const nm = await numstatZResultAsync(args, cwd);
|
|
2541
2545
|
if (nm.error)
|
|
2542
2546
|
return { files: [], error: nm.error };
|
|
2543
2547
|
const byPath = new Map(nm.files.map((file) => [file.path, file]));
|
|
@@ -15054,6 +15058,7 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15054
15058
|
const initialScanAsync = options.initialScanMode === "async" || (!options.watch || options.watch === nodeWatch) && !options.readdirSync;
|
|
15055
15059
|
const initialScanQueue = [];
|
|
15056
15060
|
let initialScanTimer = null;
|
|
15061
|
+
let processingInitialScan = false;
|
|
15057
15062
|
const pendingPathInspections = new Map;
|
|
15058
15063
|
let pathInspectionTimer = null;
|
|
15059
15064
|
let timer = null;
|
|
@@ -15141,8 +15146,14 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15141
15146
|
return;
|
|
15142
15147
|
}
|
|
15143
15148
|
const next = initialScanQueue.shift();
|
|
15144
|
-
if (next)
|
|
15145
|
-
|
|
15149
|
+
if (next) {
|
|
15150
|
+
processingInitialScan = true;
|
|
15151
|
+
try {
|
|
15152
|
+
watchDirectory(next, true);
|
|
15153
|
+
} finally {
|
|
15154
|
+
processingInitialScan = false;
|
|
15155
|
+
}
|
|
15156
|
+
}
|
|
15146
15157
|
if (watchers.size >= maxWatchedDirectories) {
|
|
15147
15158
|
reportWatchLimit();
|
|
15148
15159
|
initialScanQueue.length = 0;
|
|
@@ -15160,7 +15171,7 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15160
15171
|
if (children.length > remaining)
|
|
15161
15172
|
reportWatchLimit();
|
|
15162
15173
|
initialScanQueue.push(...children.slice(0, remaining));
|
|
15163
|
-
if (!initialScanTimer)
|
|
15174
|
+
if (!initialScanTimer && !processingInitialScan)
|
|
15164
15175
|
initialScanTimer = setTimer(processInitialScanQueue, 5000);
|
|
15165
15176
|
};
|
|
15166
15177
|
const processChangedPath = (changed, fullChangedPath) => {
|
|
@@ -24819,7 +24830,7 @@ function buildQuery(params) {
|
|
|
24819
24830
|
const s = q.toString();
|
|
24820
24831
|
return s ? `?${s}` : "";
|
|
24821
24832
|
}
|
|
24822
|
-
function fileToMeta(file, range, extraQs) {
|
|
24833
|
+
function fileToMeta(file, range, extraQs, responseGeneration) {
|
|
24823
24834
|
const sizeClass = classify(file);
|
|
24824
24835
|
const q = {
|
|
24825
24836
|
path: file.path,
|
|
@@ -24827,6 +24838,7 @@ function fileToMeta(file, range, extraQs) {
|
|
|
24827
24838
|
status: file.status,
|
|
24828
24839
|
from: range.from,
|
|
24829
24840
|
to: range.to,
|
|
24841
|
+
generation: responseGeneration,
|
|
24830
24842
|
...extraQs
|
|
24831
24843
|
};
|
|
24832
24844
|
if (file.untracked)
|
|
@@ -24865,7 +24877,7 @@ async function computePayload(extras, range, pathFilter = "", responseGeneration
|
|
|
24865
24877
|
};
|
|
24866
24878
|
}
|
|
24867
24879
|
const { args, refs } = buildRangeArgs(range);
|
|
24868
|
-
const fullArgs = [...extras, ...args];
|
|
24880
|
+
const fullArgs = pathFilter ? [...extras, ...args, "--", pathFilter] : [...extras, ...args];
|
|
24869
24881
|
const metaResult = await fileMetaResultAsync(fullArgs, cwd, false);
|
|
24870
24882
|
const files = metaResult.files;
|
|
24871
24883
|
if (!metaResult.error && includeUntracked(range, refs)) {
|
|
@@ -24883,7 +24895,7 @@ async function computePayload(extras, range, pathFilter = "", responseGeneration
|
|
|
24883
24895
|
if (e === "--ignore-blank-lines")
|
|
24884
24896
|
extraQs.ignore_blank = "1";
|
|
24885
24897
|
}
|
|
24886
|
-
const meta = filteredFiles.map((file) => fileToMeta(file, range, extraQs));
|
|
24898
|
+
const meta = filteredFiles.map((file) => fileToMeta(file, range, extraQs, responseGeneration));
|
|
24887
24899
|
const totals = meta.reduce((acc, file) => {
|
|
24888
24900
|
acc.additions += file.additions || 0;
|
|
24889
24901
|
acc.deletions += file.deletions || 0;
|
|
@@ -24902,7 +24914,7 @@ async function computePayload(extras, range, pathFilter = "", responseGeneration
|
|
|
24902
24914
|
};
|
|
24903
24915
|
}
|
|
24904
24916
|
async function handleDiffJson(url) {
|
|
24905
|
-
|
|
24917
|
+
let responseGeneration = generation;
|
|
24906
24918
|
const extras = [];
|
|
24907
24919
|
if (url.searchParams.get("ignore_ws") === "1")
|
|
24908
24920
|
extras.push("-w");
|
|
@@ -24928,9 +24940,15 @@ async function handleDiffJson(url) {
|
|
|
24928
24940
|
const requestSequence = ++diffMetaRequestSequence;
|
|
24929
24941
|
latestDiffMetaRequest.set(key, requestSequence);
|
|
24930
24942
|
try {
|
|
24931
|
-
|
|
24932
|
-
if (latestDiffMetaRequest.get(key) !== requestSequence
|
|
24943
|
+
let payload = await computePayload(extras, range, path, responseGeneration);
|
|
24944
|
+
if (latestDiffMetaRequest.get(key) !== requestSequence)
|
|
24933
24945
|
return json2(payload);
|
|
24946
|
+
if (responseGeneration !== generation) {
|
|
24947
|
+
responseGeneration = generation;
|
|
24948
|
+
payload = await computePayload(extras, range, path, responseGeneration);
|
|
24949
|
+
if (latestDiffMetaRequest.get(key) !== requestSequence || responseGeneration !== generation)
|
|
24950
|
+
return json2(payload);
|
|
24951
|
+
}
|
|
24934
24952
|
const sig = JSON.stringify({ ...payload, generation: undefined });
|
|
24935
24953
|
if (noCache && (!cached || cached.sig !== sig)) {
|
|
24936
24954
|
generation++;
|
|
@@ -25399,7 +25417,7 @@ async function handleFileBlame(url) {
|
|
|
25399
25417
|
return json2({ ...result, base, ref, generation: responseGeneration });
|
|
25400
25418
|
}
|
|
25401
25419
|
async function handleFileDiff(url) {
|
|
25402
|
-
const
|
|
25420
|
+
const serverGenerationAtRequest = generation;
|
|
25403
25421
|
const path = url.searchParams.get("path") || "";
|
|
25404
25422
|
if (!safePath(path))
|
|
25405
25423
|
return text("invalid path", 400);
|
|
@@ -25413,6 +25431,9 @@ async function handleFileDiff(url) {
|
|
|
25413
25431
|
from: url.searchParams.get("from") || "",
|
|
25414
25432
|
to: url.searchParams.get("to") || ""
|
|
25415
25433
|
};
|
|
25434
|
+
const requestedGeneration = Number(url.searchParams.get("generation"));
|
|
25435
|
+
const usesWorktree = !range.from || range.from === "worktree" || !range.to || range.to === "worktree";
|
|
25436
|
+
const responseGeneration = !usesWorktree && Number.isSafeInteger(requestedGeneration) && requestedGeneration > 0 ? requestedGeneration : serverGenerationAtRequest;
|
|
25416
25437
|
if (isSameWorktreeRange(range)) {
|
|
25417
25438
|
return json2({
|
|
25418
25439
|
path,
|
|
@@ -25466,7 +25487,7 @@ async function handleFileDiff(url) {
|
|
|
25466
25487
|
errStatus = res.status ?? 500;
|
|
25467
25488
|
}
|
|
25468
25489
|
}
|
|
25469
|
-
if (!errText &&
|
|
25490
|
+
if (!errText && serverGenerationAtRequest === generation)
|
|
25470
25491
|
setTimedCacheEntry(fileCache, cacheKey, { diffText });
|
|
25471
25492
|
}
|
|
25472
25493
|
if (errStatus)
|