@youtyan/code-viewer 0.1.35 → 0.1.36
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 +165 -3
- package/package.json +2 -3
- package/web/app.js +14359 -13242
- package/web/index.html +20 -5
- package/web/style.css +203 -14
package/dist/code-viewer.js
CHANGED
|
@@ -197,6 +197,47 @@ function addAnnotationEntry(state, input, now, makeId = makeAnnotationId) {
|
|
|
197
197
|
created_session: createdSession
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
|
+
function renameAnnotationSession(state, id, title) {
|
|
201
|
+
const session = state.sessions.find((s) => s.id === id);
|
|
202
|
+
if (!session)
|
|
203
|
+
return { state, renamed: false };
|
|
204
|
+
const next = title.trim().slice(0, ANNOTATION_TITLE_MAX_CHARS) || "Untitled session";
|
|
205
|
+
return {
|
|
206
|
+
state: {
|
|
207
|
+
version: 1,
|
|
208
|
+
sessions: state.sessions.map((s) => s.id === id ? { ...s, title: next } : s)
|
|
209
|
+
},
|
|
210
|
+
renamed: true
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function updateAnnotationEntry(state, id, patch) {
|
|
214
|
+
const session = state.sessions.find((s) => s.entries.some((e) => e.id === id));
|
|
215
|
+
if (!session)
|
|
216
|
+
return { ok: false, error: "annotation not found" };
|
|
217
|
+
if (patch.body !== undefined) {
|
|
218
|
+
if (!patch.body.trim())
|
|
219
|
+
return { ok: false, error: "body is required" };
|
|
220
|
+
if (Buffer.byteLength(patch.body, "utf8") > ANNOTATION_BODY_MAX_BYTES)
|
|
221
|
+
return { ok: false, error: "body is too large" };
|
|
222
|
+
}
|
|
223
|
+
let updated = null;
|
|
224
|
+
const sessions = state.sessions.map((s) => s.id === session.id ? {
|
|
225
|
+
...s,
|
|
226
|
+
entries: s.entries.map((e) => {
|
|
227
|
+
if (e.id !== id)
|
|
228
|
+
return e;
|
|
229
|
+
updated = {
|
|
230
|
+
...e,
|
|
231
|
+
...patch.title !== undefined ? { title: patch.title.trim() || undefined } : {},
|
|
232
|
+
...patch.body !== undefined ? { body: patch.body } : {}
|
|
233
|
+
};
|
|
234
|
+
return updated;
|
|
235
|
+
})
|
|
236
|
+
} : s);
|
|
237
|
+
if (!updated)
|
|
238
|
+
return { ok: false, error: "annotation not found" };
|
|
239
|
+
return { ok: true, state: { version: 1, sessions }, entry: updated };
|
|
240
|
+
}
|
|
200
241
|
function deleteAnnotationById(state, id) {
|
|
201
242
|
for (const session of state.sessions) {
|
|
202
243
|
if (session.id === id) {
|
|
@@ -1211,6 +1252,41 @@ function parseAnnotateArgs(argv) {
|
|
|
1211
1252
|
}
|
|
1212
1253
|
};
|
|
1213
1254
|
}
|
|
1255
|
+
if (subcommand === "rename") {
|
|
1256
|
+
const id = rest[1];
|
|
1257
|
+
if (!id)
|
|
1258
|
+
return { ok: false, error: "rename requires a session id" };
|
|
1259
|
+
const title = options.get("--title");
|
|
1260
|
+
if (!title)
|
|
1261
|
+
return { ok: false, error: "rename requires --title <text>" };
|
|
1262
|
+
return {
|
|
1263
|
+
ok: true,
|
|
1264
|
+
args: { command: { kind: "rename", id, title }, cwd, server }
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
if (subcommand === "edit") {
|
|
1268
|
+
const id = rest[1];
|
|
1269
|
+
if (!id)
|
|
1270
|
+
return { ok: false, error: "edit requires an annotation id" };
|
|
1271
|
+
const body = options.get("--body");
|
|
1272
|
+
const bodyFile = options.get("--body-file");
|
|
1273
|
+
if (body !== undefined && bodyFile !== undefined)
|
|
1274
|
+
return { ok: false, error: "use either --body or --body-file" };
|
|
1275
|
+
return {
|
|
1276
|
+
ok: true,
|
|
1277
|
+
args: {
|
|
1278
|
+
command: {
|
|
1279
|
+
kind: "edit",
|
|
1280
|
+
id,
|
|
1281
|
+
title: options.get("--title"),
|
|
1282
|
+
body,
|
|
1283
|
+
bodyFile
|
|
1284
|
+
},
|
|
1285
|
+
cwd,
|
|
1286
|
+
server
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1214
1290
|
if (subcommand === "list") {
|
|
1215
1291
|
return {
|
|
1216
1292
|
ok: true,
|
|
@@ -1391,6 +1467,37 @@ async function runAnnotateCli(argv) {
|
|
|
1391
1467
|
printList(state);
|
|
1392
1468
|
return;
|
|
1393
1469
|
}
|
|
1470
|
+
if (command.kind === "rename") {
|
|
1471
|
+
await request(serverUrl, "POST", {
|
|
1472
|
+
action: "rename",
|
|
1473
|
+
id: command.id,
|
|
1474
|
+
title: command.title
|
|
1475
|
+
});
|
|
1476
|
+
console.log(`renamed session ${command.id} to "${command.title}"`);
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
if (command.kind === "edit") {
|
|
1480
|
+
let bodyText = command.body;
|
|
1481
|
+
if (command.bodyFile !== undefined)
|
|
1482
|
+
bodyText = readFileSync4(command.bodyFile, "utf8");
|
|
1483
|
+
if (bodyText === undefined) {
|
|
1484
|
+
const stdin = await readStdin();
|
|
1485
|
+
if (stdin.trim())
|
|
1486
|
+
bodyText = stdin;
|
|
1487
|
+
}
|
|
1488
|
+
if (bodyText === undefined && command.title === undefined) {
|
|
1489
|
+
console.error("edit requires --title, --body, --body-file, or stdin");
|
|
1490
|
+
process.exit(1);
|
|
1491
|
+
}
|
|
1492
|
+
const result = await request(serverUrl, "POST", {
|
|
1493
|
+
action: "update",
|
|
1494
|
+
id: command.id,
|
|
1495
|
+
title: command.title,
|
|
1496
|
+
body: bodyText
|
|
1497
|
+
});
|
|
1498
|
+
console.log(`updated annotation ${result.entry.id} (${result.entry.path}${formatLine(result.entry.line)})`);
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1394
1501
|
if (command.kind === "delete") {
|
|
1395
1502
|
const result = await request(serverUrl, "POST", {
|
|
1396
1503
|
action: "delete",
|
|
@@ -1421,6 +1528,9 @@ Usage:
|
|
|
1421
1528
|
code-viewer annotate add --file <path> [--line <n>|<n>-<m>]
|
|
1422
1529
|
[--from <ref>] [--to <ref>] [--title <text>] [--session <id>]
|
|
1423
1530
|
[--body <markdown> | --body-file <path>] (or pipe body via stdin)
|
|
1531
|
+
code-viewer annotate rename <session-id> --title <text>
|
|
1532
|
+
code-viewer annotate edit <id> [--title <text>]
|
|
1533
|
+
[--body <markdown> | --body-file <path>] (or pipe body via stdin)
|
|
1424
1534
|
code-viewer annotate list [--json]
|
|
1425
1535
|
code-viewer annotate delete <id>
|
|
1426
1536
|
code-viewer annotate clear
|
|
@@ -1488,6 +1598,20 @@ location and renders your explanation directly under the annotated lines.
|
|
|
1488
1598
|
- The human can share a walkthrough as a URL; one session = one shareable
|
|
1489
1599
|
walkthrough. Do not mix unrelated topics in one session.
|
|
1490
1600
|
|
|
1601
|
+
## Fixing mistakes and follow-ups
|
|
1602
|
+
|
|
1603
|
+
- The human may paste a reference block copied from the viewer that starts
|
|
1604
|
+
with "code-viewer のコード注釈について依頼があります" and lists the
|
|
1605
|
+
annotation id, location, and session, followed by their question.
|
|
1606
|
+
Read the current body first: code-viewer annotate list --json
|
|
1607
|
+
- Revise a wrong annotation IN PLACE (do not delete + re-add; the id and
|
|
1608
|
+
its position in the walkthrough are preserved):
|
|
1609
|
+
code-viewer annotate edit <id> --body "<corrected markdown>"
|
|
1610
|
+
(long bodies: --body-file <path> or pipe via stdin; --title also works)
|
|
1611
|
+
- Post a follow-up answer next to the original instead of replacing it:
|
|
1612
|
+
code-viewer annotate add --session <session-id> --file <path> --line <n> --title "回答: ..." --body "<markdown>"
|
|
1613
|
+
- Rename a session: code-viewer annotate rename <session-id> --title <text>
|
|
1614
|
+
|
|
1491
1615
|
## Cleanup
|
|
1492
1616
|
|
|
1493
1617
|
- delete <id> removes one annotation or a whole session by its id.
|
|
@@ -1500,7 +1624,7 @@ var init_annotate_cli = __esm(() => {
|
|
|
1500
1624
|
init_server_registry();
|
|
1501
1625
|
});
|
|
1502
1626
|
|
|
1503
|
-
// web-src/directory-name.ts
|
|
1627
|
+
// web-src/core/directory-name.ts
|
|
1504
1628
|
function normalizeNewDirectoryName(name) {
|
|
1505
1629
|
if (typeof name !== "string")
|
|
1506
1630
|
return null;
|
|
@@ -1517,7 +1641,7 @@ function normalizeNewDirectoryName(name) {
|
|
|
1517
1641
|
return trimmed;
|
|
1518
1642
|
}
|
|
1519
1643
|
|
|
1520
|
-
// web-src/routes.ts
|
|
1644
|
+
// web-src/core/routes.ts
|
|
1521
1645
|
var SPA_PATHS, APP_ENTRY_PATHS;
|
|
1522
1646
|
var init_routes = __esm(() => {
|
|
1523
1647
|
SPA_PATHS = ["/todif", "/todiff", "/file", "/help"];
|
|
@@ -3784,6 +3908,32 @@ async function handleAnnotations(req) {
|
|
|
3784
3908
|
}
|
|
3785
3909
|
return json({ ok: true, removed: result.removed });
|
|
3786
3910
|
}
|
|
3911
|
+
if (action === "rename") {
|
|
3912
|
+
const id = typeof body.id === "string" ? body.id : "";
|
|
3913
|
+
const title = typeof body.title === "string" ? body.title : "";
|
|
3914
|
+
if (!id)
|
|
3915
|
+
return text("invalid id", 400);
|
|
3916
|
+
const result = renameAnnotationSession(loadAnnotationsState(cwd), id, title);
|
|
3917
|
+
if (!result.renamed)
|
|
3918
|
+
return text("session not found", 404);
|
|
3919
|
+
saveAnnotationsState(cwd, result.state);
|
|
3920
|
+
annotationSse("update", id);
|
|
3921
|
+
return json({ ok: true });
|
|
3922
|
+
}
|
|
3923
|
+
if (action === "update") {
|
|
3924
|
+
const id = typeof body.id === "string" ? body.id : "";
|
|
3925
|
+
if (!id)
|
|
3926
|
+
return text("invalid id", 400);
|
|
3927
|
+
const result = updateAnnotationEntry(loadAnnotationsState(cwd), id, {
|
|
3928
|
+
title: typeof body.title === "string" ? body.title : undefined,
|
|
3929
|
+
body: typeof body.body === "string" ? body.body : undefined
|
|
3930
|
+
});
|
|
3931
|
+
if (result.ok === false)
|
|
3932
|
+
return text(result.error, 400);
|
|
3933
|
+
saveAnnotationsState(cwd, result.state);
|
|
3934
|
+
annotationSse("update", undefined, id);
|
|
3935
|
+
return json({ ok: true, entry: result.entry });
|
|
3936
|
+
}
|
|
3787
3937
|
if (action === "clear") {
|
|
3788
3938
|
saveAnnotationsState(cwd, emptyAnnotationsState());
|
|
3789
3939
|
annotationSse("clear");
|
|
@@ -3977,12 +4127,24 @@ data: ok
|
|
|
3977
4127
|
started_at: new Date().toISOString()
|
|
3978
4128
|
});
|
|
3979
4129
|
process.on("exit", () => removeServerRegistry(cwd, process.pid));
|
|
3980
|
-
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
4130
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
3981
4131
|
process.on(signal, () => {
|
|
3982
4132
|
removeServerRegistry(cwd, process.pid);
|
|
3983
4133
|
process.exit(0);
|
|
3984
4134
|
});
|
|
3985
4135
|
}
|
|
4136
|
+
if (process.env.CODE_VIEWER_DEV === "1") {
|
|
4137
|
+
const parentPid = process.ppid;
|
|
4138
|
+
setInterval(() => {
|
|
4139
|
+
try {
|
|
4140
|
+
process.kill(parentPid, 0);
|
|
4141
|
+
} catch {
|
|
4142
|
+
console.log("dev wrapper exited; shutting down preview server");
|
|
4143
|
+
removeServerRegistry(cwd, process.pid);
|
|
4144
|
+
process.exit(0);
|
|
4145
|
+
}
|
|
4146
|
+
}, 1000).unref();
|
|
4147
|
+
}
|
|
3986
4148
|
startDevAssetReload({
|
|
3987
4149
|
enabled: process.env.CODE_VIEWER_DEV === "1",
|
|
3988
4150
|
webRoot: WEB_ROOT,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youtyan/code-viewer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"description": "Local browser-based code and git diff viewer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -65,6 +65,5 @@
|
|
|
65
65
|
},
|
|
66
66
|
"engines": {
|
|
67
67
|
"node": ">=20.0.0"
|
|
68
|
-
}
|
|
69
|
-
"dependencies": {}
|
|
68
|
+
}
|
|
70
69
|
}
|