@a9n-shoji/rvw 0.4.0 → 0.4.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/CHANGELOG.md +9 -0
- package/README.md +4 -0
- package/dist/cli.mjs +159 -33
- package/dist/cli.mjs.map +3 -3
- package/dist/web/assets/DocumentViewer-cnnF6CLV.js +1420 -0
- package/dist/web/assets/StructureViewer-B0fkdcLj.js +5 -0
- package/dist/web/assets/{CommentThread-dsad9VaW.js → WalkthroughPanel-BGgWu0bA.js} +1 -1
- package/dist/web/assets/{WalkthroughViewer-c3XUP0RH.js → WalkthroughViewer-DdPfdT2-.js} +4 -4
- package/dist/web/assets/index-B3zKWMdE.css +1 -0
- package/dist/web/assets/index-qmzlYGCz.js +11 -0
- package/dist/web/index.html +3 -3
- package/package.json +1 -1
- package/dist/web/assets/DocumentViewer-CPq2FCqU.js +0 -1414
- package/dist/web/assets/StructureViewer-CyDDQwHl.js +0 -1
- package/dist/web/assets/index-CWOtOTHG.js +0 -11
- package/dist/web/assets/index-D_e3Zku8.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.4.1] - 2026-09-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- repository fileから、そのfileをNode sourceとして参照するStructureをrename-awareに逆引きし、対応Nodeへ
|
|
13
|
+
閲覧状態を維持したまま移動できる機能
|
|
14
|
+
- Structure全体を現在のNode配置のままSVGまたは2倍基準のPNGへエクスポートする機能。focus、近傍、
|
|
15
|
+
viewportにかかわらず全Node、全Relation、全Edge labelを含み、画面の閲覧状態を変更しない
|
|
16
|
+
|
|
8
17
|
## [0.4.0] - 2026-09-01
|
|
9
18
|
|
|
10
19
|
### Added
|
package/README.md
CHANGED
|
@@ -181,6 +181,10 @@ viewerではfocusがある時に1-hop / 2-hopへ絞り、Allでは全Node / Edge
|
|
|
181
181
|
保存しません。同じsubjectの更新は同じURIを完全置換し、存続するIDの位置を保ちます。別subjectは新しい
|
|
182
182
|
Structureとしてpublishします。
|
|
183
183
|
|
|
184
|
+
Structure headerの`Export`から、現在のNode配置を保った図全体をstandalone SVGまたは2倍基準のPNGとして
|
|
185
|
+
保存できます。focusや1-hop / 2-hopで画面上に絞り込んでいても、出力には全Node、全Relation、全Edge labelが
|
|
186
|
+
含まれ、pan / zoomや選択状態は持ち込みません。
|
|
187
|
+
|
|
184
188
|
## Codex / Claude Code Skills
|
|
185
189
|
|
|
186
190
|
アプリ本体からローカルSkillをインストールします。一度のinstallで、コメント処理用の`rvw`、
|
package/dist/cli.mjs
CHANGED
|
@@ -20187,7 +20187,7 @@ import path5 from "node:path";
|
|
|
20187
20187
|
import { TextDecoder as TextDecoder2 } from "node:util";
|
|
20188
20188
|
|
|
20189
20189
|
// src/shared/constants.ts
|
|
20190
|
-
var APP_VERSION = "0.4.
|
|
20190
|
+
var APP_VERSION = "0.4.1";
|
|
20191
20191
|
var PROTOCOL_VERSION = 4;
|
|
20192
20192
|
var VIEWER_ID_HEADER = "x-rvw-viewer-id";
|
|
20193
20193
|
var VIEWER_OPEN_LEASE_HEADER = "x-rvw-viewer-open-lease";
|
|
@@ -28477,6 +28477,34 @@ function structureSourceAnchor(structure, locator) {
|
|
|
28477
28477
|
const edge = structure.edges.find((candidate) => candidate.id === locator.edgeId);
|
|
28478
28478
|
return edge?.anchors[locator.anchorIndex] ?? null;
|
|
28479
28479
|
}
|
|
28480
|
+
function compareStableIds(left, right) {
|
|
28481
|
+
if (left === right) return 0;
|
|
28482
|
+
return left < right ? -1 : 1;
|
|
28483
|
+
}
|
|
28484
|
+
function closestMatchingStructureNode(structure, matchingNodeIds) {
|
|
28485
|
+
if (matchingNodeIds.size === 0) return null;
|
|
28486
|
+
const distances = /* @__PURE__ */ new Map([[structure.originNodeId, 0]]);
|
|
28487
|
+
const neighbors = new Map(structure.nodes.map((node2) => [node2.id, /* @__PURE__ */ new Set()]));
|
|
28488
|
+
for (const edge of structure.edges) {
|
|
28489
|
+
neighbors.get(edge.from)?.add(edge.to);
|
|
28490
|
+
neighbors.get(edge.to)?.add(edge.from);
|
|
28491
|
+
}
|
|
28492
|
+
const queue = [structure.originNodeId];
|
|
28493
|
+
for (let index2 = 0; index2 < queue.length; index2 += 1) {
|
|
28494
|
+
const nodeId = queue[index2];
|
|
28495
|
+
const distance = distances.get(nodeId);
|
|
28496
|
+
for (const neighbor of neighbors.get(nodeId) ?? []) {
|
|
28497
|
+
if (distances.has(neighbor)) continue;
|
|
28498
|
+
distances.set(neighbor, distance + 1);
|
|
28499
|
+
queue.push(neighbor);
|
|
28500
|
+
}
|
|
28501
|
+
}
|
|
28502
|
+
return structure.nodes.filter((node2) => matchingNodeIds.has(node2.id)).sort((left, right) => {
|
|
28503
|
+
const leftDistance = distances.get(left.id) ?? Number.POSITIVE_INFINITY;
|
|
28504
|
+
const rightDistance = distances.get(right.id) ?? Number.POSITIVE_INFINITY;
|
|
28505
|
+
return leftDistance - rightDistance || compareStableIds(left.id, right.id);
|
|
28506
|
+
})[0] ?? null;
|
|
28507
|
+
}
|
|
28480
28508
|
|
|
28481
28509
|
// src/domain/source-excerpt.ts
|
|
28482
28510
|
var SOURCE_EXCERPT_CONTEXT_LINES = 20;
|
|
@@ -41445,6 +41473,18 @@ function assertLinePair(startLine, endLine) {
|
|
|
41445
41473
|
}
|
|
41446
41474
|
var codeReferenceIdPattern = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
41447
41475
|
var walkthroughDiagramNodePattern = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
41476
|
+
function structureSummary(structure) {
|
|
41477
|
+
return {
|
|
41478
|
+
id: structure.id,
|
|
41479
|
+
ref: structure.ref,
|
|
41480
|
+
pullRequestId: structure.pullRequestId,
|
|
41481
|
+
sourceOid: structure.sourceOid,
|
|
41482
|
+
title: structure.title,
|
|
41483
|
+
scope: structure.scope,
|
|
41484
|
+
createdAt: structure.createdAt,
|
|
41485
|
+
updatedAt: structure.updatedAt
|
|
41486
|
+
};
|
|
41487
|
+
}
|
|
41448
41488
|
var mermaidIdentifierPattern = /[A-Za-z][A-Za-z0-9_-]{0,63}/g;
|
|
41449
41489
|
var mermaidEdgePattern = /[<|o*x]*[-.=~]{2,}[|o*x>]*/g;
|
|
41450
41490
|
var mermaidClassShorthandPattern = /:::[A-Za-z][A-Za-z0-9_-]*(?:,[A-Za-z][A-Za-z0-9_-]*)*/g;
|
|
@@ -42509,6 +42549,71 @@ var RvwService = class {
|
|
|
42509
42549
|
this.getPullRequest(pullRequestId);
|
|
42510
42550
|
return this.database.listStructures(pullRequestId);
|
|
42511
42551
|
}
|
|
42552
|
+
async listFileStructureReferences(pullRequestId, targetSourceOid, targetPath) {
|
|
42553
|
+
const pullRequest = this.getPullRequest(pullRequestId);
|
|
42554
|
+
assertCodeReferencePath(targetPath);
|
|
42555
|
+
const structures = this.database.listStructures(pullRequestId).flatMap((summary) => {
|
|
42556
|
+
const structure = this.database.getStructure(summary.id);
|
|
42557
|
+
return structure?.pullRequestId === pullRequestId ? [structure] : [];
|
|
42558
|
+
});
|
|
42559
|
+
await this.assertCommitAvailable(pullRequest, targetSourceOid);
|
|
42560
|
+
const targetPaths = new Set(
|
|
42561
|
+
(await this.git.tree(pullRequest.localRepositoryPath, targetSourceOid)).map(
|
|
42562
|
+
(entry) => entry.path
|
|
42563
|
+
)
|
|
42564
|
+
);
|
|
42565
|
+
if (!targetPaths.has(targetPath)) return [];
|
|
42566
|
+
const successorIndexes = /* @__PURE__ */ new Map();
|
|
42567
|
+
const successorIndex = (sourceOid) => {
|
|
42568
|
+
const key2 = JSON.stringify([sourceOid, targetSourceOid]);
|
|
42569
|
+
const cached2 = successorIndexes.get(key2);
|
|
42570
|
+
if (cached2) return cached2;
|
|
42571
|
+
const index2 = this.git.changedFilesWithCopies(pullRequest.localRepositoryPath, sourceOid, targetSourceOid).then((changes) => {
|
|
42572
|
+
const successors = /* @__PURE__ */ new Map();
|
|
42573
|
+
for (const candidate of changes) {
|
|
42574
|
+
if (!candidate.status.startsWith("R") && !candidate.status.startsWith("C") || candidate.oldPath === null || candidate.newPath === null) {
|
|
42575
|
+
continue;
|
|
42576
|
+
}
|
|
42577
|
+
const paths = successors.get(candidate.oldPath) ?? /* @__PURE__ */ new Set();
|
|
42578
|
+
paths.add(candidate.newPath);
|
|
42579
|
+
successors.set(candidate.oldPath, paths);
|
|
42580
|
+
}
|
|
42581
|
+
return successors;
|
|
42582
|
+
});
|
|
42583
|
+
successorIndexes.set(key2, index2);
|
|
42584
|
+
return index2;
|
|
42585
|
+
};
|
|
42586
|
+
const resolvePath = (sourceOid, sourcePath) => {
|
|
42587
|
+
if (targetPaths.has(sourcePath)) return Promise.resolve(sourcePath);
|
|
42588
|
+
if (sourceOid === targetSourceOid) return Promise.resolve(null);
|
|
42589
|
+
return successorIndex(sourceOid).then((index2) => {
|
|
42590
|
+
const successors = index2.get(sourcePath);
|
|
42591
|
+
if (successors?.size !== 1) return null;
|
|
42592
|
+
const successor = [...successors][0];
|
|
42593
|
+
return targetPaths.has(successor) ? successor : null;
|
|
42594
|
+
});
|
|
42595
|
+
};
|
|
42596
|
+
const references = [];
|
|
42597
|
+
for (const structure of structures) {
|
|
42598
|
+
const matchingNodeIds = /* @__PURE__ */ new Set();
|
|
42599
|
+
await Promise.all(
|
|
42600
|
+
structure.nodes.map(async (node2) => {
|
|
42601
|
+
if (!node2.anchor) return;
|
|
42602
|
+
const resolvedPath = await resolvePath(structure.sourceOid, node2.anchor.path);
|
|
42603
|
+
if (resolvedPath === targetPath) matchingNodeIds.add(node2.id);
|
|
42604
|
+
})
|
|
42605
|
+
);
|
|
42606
|
+
const targetNode = closestMatchingStructureNode(structure, matchingNodeIds);
|
|
42607
|
+
if (!targetNode) continue;
|
|
42608
|
+
references.push({
|
|
42609
|
+
structure: structureSummary(structure),
|
|
42610
|
+
targetNodeId: targetNode.id,
|
|
42611
|
+
targetNodeLabel: targetNode.label,
|
|
42612
|
+
matchingNodeCount: matchingNodeIds.size
|
|
42613
|
+
});
|
|
42614
|
+
}
|
|
42615
|
+
return references;
|
|
42616
|
+
}
|
|
42512
42617
|
listStructuresByReference(reference) {
|
|
42513
42618
|
const pullRequest = this.resolveStoredPullRequest(reference);
|
|
42514
42619
|
return { pullRequest, structures: this.database.listStructures(pullRequest.id) };
|
|
@@ -42867,38 +42972,15 @@ var RvwService = class {
|
|
|
42867
42972
|
path: reference.path
|
|
42868
42973
|
});
|
|
42869
42974
|
const latestHeadOid = pullRequest.latestHeadOid;
|
|
42870
|
-
|
|
42871
|
-
|
|
42872
|
-
|
|
42873
|
-
|
|
42874
|
-
|
|
42875
|
-
|
|
42876
|
-
|
|
42877
|
-
|
|
42878
|
-
|
|
42879
|
-
path: reference.path
|
|
42880
|
-
});
|
|
42881
|
-
if (latestDocument.availability === "missing") {
|
|
42882
|
-
const successorPaths = [
|
|
42883
|
-
...new Set(
|
|
42884
|
-
(await this.git.changedFilesWithCopies(
|
|
42885
|
-
pullRequest.localRepositoryPath,
|
|
42886
|
-
sourceOid,
|
|
42887
|
-
latestHeadOid
|
|
42888
|
-
)).filter(
|
|
42889
|
-
(candidate) => (candidate.status.startsWith("R") || candidate.status.startsWith("C")) && candidate.oldPath === reference.path && candidate.newPath !== null
|
|
42890
|
-
).map((candidate) => candidate.newPath)
|
|
42891
|
-
)
|
|
42892
|
-
];
|
|
42893
|
-
latestPath = successorPaths.length === 1 ? successorPaths[0] : null;
|
|
42894
|
-
latestDocument = latestPath === null ? null : await this.getDocument({
|
|
42895
|
-
kind: "repository-file",
|
|
42896
|
-
pullRequestId: pullRequest.id,
|
|
42897
|
-
sourceOid: latestHeadOid,
|
|
42898
|
-
path: latestPath
|
|
42899
|
-
});
|
|
42900
|
-
}
|
|
42901
|
-
}
|
|
42975
|
+
const resolvedLatestFile = await this.resolveSourceFileAtCommit(
|
|
42976
|
+
pullRequest,
|
|
42977
|
+
sourceOid,
|
|
42978
|
+
reference.path,
|
|
42979
|
+
latestHeadOid,
|
|
42980
|
+
sourceDocument
|
|
42981
|
+
);
|
|
42982
|
+
const latestPath = resolvedLatestFile?.path ?? null;
|
|
42983
|
+
const latestDocument = resolvedLatestFile?.document ?? null;
|
|
42902
42984
|
const latestFileExists = latestDocument !== null && latestDocument.availability !== "missing";
|
|
42903
42985
|
const latestFileDisplayable = latestDocument?.availability === "available";
|
|
42904
42986
|
const mappedRange = reference.startLine === null || reference.endLine === null ? latestFileDisplayable ? null : void 0 : sourceDocument.availability === "available" && latestDocument?.availability === "available" ? mapUnchangedLineRange(
|
|
@@ -42955,6 +43037,38 @@ var RvwService = class {
|
|
|
42955
43037
|
document: sourceDocument
|
|
42956
43038
|
};
|
|
42957
43039
|
}
|
|
43040
|
+
async resolveSourceFileAtCommit(pullRequest, sourceOid, sourcePath, targetSourceOid, sourceDocument) {
|
|
43041
|
+
const directDocument = sourceOid === targetSourceOid ? sourceDocument : await this.getDocument({
|
|
43042
|
+
kind: "repository-file",
|
|
43043
|
+
pullRequestId: pullRequest.id,
|
|
43044
|
+
sourceOid: targetSourceOid,
|
|
43045
|
+
path: sourcePath
|
|
43046
|
+
});
|
|
43047
|
+
if (directDocument.availability !== "missing") {
|
|
43048
|
+
return { path: sourcePath, document: directDocument };
|
|
43049
|
+
}
|
|
43050
|
+
if (sourceOid === targetSourceOid) return null;
|
|
43051
|
+
const successorPaths = [
|
|
43052
|
+
...new Set(
|
|
43053
|
+
(await this.git.changedFilesWithCopies(
|
|
43054
|
+
pullRequest.localRepositoryPath,
|
|
43055
|
+
sourceOid,
|
|
43056
|
+
targetSourceOid
|
|
43057
|
+
)).filter(
|
|
43058
|
+
(candidate) => (candidate.status.startsWith("R") || candidate.status.startsWith("C")) && candidate.oldPath === sourcePath && candidate.newPath !== null
|
|
43059
|
+
).map((candidate) => candidate.newPath)
|
|
43060
|
+
)
|
|
43061
|
+
];
|
|
43062
|
+
if (successorPaths.length !== 1) return null;
|
|
43063
|
+
const successorPath = successorPaths[0];
|
|
43064
|
+
const successorDocument = await this.getDocument({
|
|
43065
|
+
kind: "repository-file",
|
|
43066
|
+
pullRequestId: pullRequest.id,
|
|
43067
|
+
sourceOid: targetSourceOid,
|
|
43068
|
+
path: successorPath
|
|
43069
|
+
});
|
|
43070
|
+
return successorDocument.availability === "missing" ? null : { path: successorPath, document: successorDocument };
|
|
43071
|
+
}
|
|
42958
43072
|
getWalkthroughByUri(uri) {
|
|
42959
43073
|
const walkthrough = this.database.getWalkthrough(parseWalkthroughUri(uri));
|
|
42960
43074
|
if (!walkthrough) {
|
|
@@ -47916,6 +48030,18 @@ function createApp(service, options) {
|
|
|
47916
48030
|
structures: service.listStructures(context.req.param("id"))
|
|
47917
48031
|
})
|
|
47918
48032
|
);
|
|
48033
|
+
app.get("/api/pull-requests/:id/structure-references", async (context) => {
|
|
48034
|
+
const sourceOid = oidQuery(context.req.query("sourceOid"), "sourceOid");
|
|
48035
|
+
const filePath = requiredQuery(context.req.query("path"), "path");
|
|
48036
|
+
return context.json({
|
|
48037
|
+
ok: true,
|
|
48038
|
+
references: await service.listFileStructureReferences(
|
|
48039
|
+
context.req.param("id"),
|
|
48040
|
+
sourceOid,
|
|
48041
|
+
filePath
|
|
48042
|
+
)
|
|
48043
|
+
});
|
|
48044
|
+
});
|
|
47919
48045
|
app.get(
|
|
47920
48046
|
"/api/pull-requests/:id/structures/:structureId",
|
|
47921
48047
|
(context) => context.json({
|