@taciturnaxolotl/traverse 0.1.6 → 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 +30 -29
- package/src/index.ts +13 -1
- package/src/og.ts +22 -28
- package/src/template.ts +11 -2
- package/src/types.ts +1 -0
- package/vendor/index_bg.wasm +0 -0
package/package.json
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
2
|
+
"name": "@taciturnaxolotl/traverse",
|
|
3
|
+
"version": "0.1.8",
|
|
4
|
+
"description": "Interactive code walkthrough diagrams via MCP",
|
|
5
|
+
"module": "src/index.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"traverse": "bin/traverse.cjs"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"bin",
|
|
13
|
+
"fonts",
|
|
14
|
+
"vendor",
|
|
15
|
+
"icon.svg"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"start": "bun run src/index.ts",
|
|
19
|
+
"dev": "bun --hot run src/index.ts"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/bun": "latest"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"typescript": "^5"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
29
|
+
"@resvg/resvg-wasm": "^2.6.2",
|
|
30
|
+
"satori": "^0.19.1"
|
|
31
|
+
}
|
|
31
32
|
}
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,16 @@ const VERSION = await Bun.$`git rev-parse --short HEAD`.text().then(s => s.trim(
|
|
|
14
14
|
const pkg = await Bun.file(import.meta.dir + "/../package.json").json();
|
|
15
15
|
return `v${pkg.version}`;
|
|
16
16
|
});
|
|
17
|
+
const GITHUB_REPO = await Bun.$`git remote get-url origin`.text().then(url => {
|
|
18
|
+
url = url.trim();
|
|
19
|
+
// Convert SSH URLs: git@github.com:user/repo.git -> https://github.com/user/repo
|
|
20
|
+
const sshMatch = url.match(/^git@github\.com:(.+?)(?:\.git)?$/);
|
|
21
|
+
if (sshMatch) return `https://github.com/${sshMatch[1]}`;
|
|
22
|
+
// Convert HTTPS URLs: https://github.com/user/repo.git -> https://github.com/user/repo
|
|
23
|
+
const httpsMatch = url.match(/^https:\/\/github\.com\/(.+?)(?:\.git)?$/);
|
|
24
|
+
if (httpsMatch) return `https://github.com/${httpsMatch[1]}`;
|
|
25
|
+
return "";
|
|
26
|
+
}).catch(() => "");
|
|
17
27
|
initDb();
|
|
18
28
|
|
|
19
29
|
// Load persisted diagrams
|
|
@@ -119,6 +129,7 @@ try {
|
|
|
119
129
|
code: body.code,
|
|
120
130
|
summary: body.summary,
|
|
121
131
|
nodes: body.nodes,
|
|
132
|
+
githubRepo: body.githubRepo || undefined,
|
|
122
133
|
createdAt: new Date().toISOString(),
|
|
123
134
|
};
|
|
124
135
|
diagrams.set(id, diagram);
|
|
@@ -230,7 +241,7 @@ Then build the diagram:
|
|
|
230
241
|
const res = await fetch(`http://localhost:${PORT}/api/diagrams`, {
|
|
231
242
|
method: "POST",
|
|
232
243
|
headers: { "Content-Type": "application/json" },
|
|
233
|
-
body: JSON.stringify({ code, summary, nodes }),
|
|
244
|
+
body: JSON.stringify({ code, summary, nodes, githubRepo: GITHUB_REPO || undefined }),
|
|
234
245
|
});
|
|
235
246
|
if (!res.ok) {
|
|
236
247
|
return {
|
|
@@ -245,6 +256,7 @@ Then build the diagram:
|
|
|
245
256
|
code,
|
|
246
257
|
summary,
|
|
247
258
|
nodes,
|
|
259
|
+
githubRepo: GITHUB_REPO || undefined,
|
|
248
260
|
createdAt: new Date().toISOString(),
|
|
249
261
|
};
|
|
250
262
|
diagrams.set(id, diagram);
|
package/src/og.ts
CHANGED
|
@@ -10,38 +10,32 @@ const [interRegular, interBold] = await Promise.all([
|
|
|
10
10
|
]);
|
|
11
11
|
|
|
12
12
|
// Initialize resvg-wasm
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Fallback to manual path resolution if import.meta.resolve fails
|
|
24
|
-
const possiblePaths = [
|
|
25
|
-
join(import.meta.dir, "../node_modules/@resvg/resvg-wasm/index_bg.wasm"),
|
|
26
|
-
join(import.meta.dir, "../../@resvg/resvg-wasm/index_bg.wasm"),
|
|
27
|
-
join(import.meta.dir, "../../../@resvg/resvg-wasm/index_bg.wasm"),
|
|
28
|
-
];
|
|
13
|
+
// Try bundled WASM first, then fall back to node_modules
|
|
14
|
+
const possiblePaths = [
|
|
15
|
+
// Bundled WASM file (most reliable)
|
|
16
|
+
join(import.meta.dir, "../vendor/index_bg.wasm"),
|
|
17
|
+
// Development: node_modules is a sibling of src
|
|
18
|
+
join(import.meta.dir, "../node_modules/@resvg/resvg-wasm/index_bg.wasm"),
|
|
19
|
+
// Installed: in parent node_modules
|
|
20
|
+
join(import.meta.dir, "../../@resvg/resvg-wasm/index_bg.wasm"),
|
|
21
|
+
join(import.meta.dir, "../../../@resvg/resvg-wasm/index_bg.wasm"),
|
|
22
|
+
];
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
let wasmPath: string | undefined;
|
|
25
|
+
for (const path of possiblePaths) {
|
|
26
|
+
if (await Bun.file(path).exists()) {
|
|
27
|
+
wasmPath = path;
|
|
28
|
+
break;
|
|
35
29
|
}
|
|
30
|
+
}
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
console.error(`\nCurrent directory: ${import.meta.dir}`);
|
|
43
|
-
throw new Error("Could not find @resvg/resvg-wasm WASM file");
|
|
32
|
+
if (!wasmPath) {
|
|
33
|
+
console.error("Failed to find @resvg/resvg-wasm WASM file. Tried:");
|
|
34
|
+
for (const path of possiblePaths) {
|
|
35
|
+
console.error(` - ${path}`);
|
|
44
36
|
}
|
|
37
|
+
console.error(`\nCurrent directory: ${import.meta.dir}`);
|
|
38
|
+
throw new Error("Could not find @resvg/resvg-wasm WASM file");
|
|
45
39
|
}
|
|
46
40
|
|
|
47
41
|
await initWasm(Bun.file(wasmPath).arrayBuffer());
|
package/src/template.ts
CHANGED
|
@@ -654,6 +654,7 @@ export function generateViewerHTML(diagram: WalkthroughDiagram, gitHash: string
|
|
|
654
654
|
|
|
655
655
|
const DIAGRAM_DATA = ${diagramJSON};
|
|
656
656
|
const PROJECT_ROOT = ${JSON.stringify(projectRoot)};
|
|
657
|
+
const GITHUB_REPO = ${JSON.stringify(diagram.githubRepo || "")};
|
|
657
658
|
const SHARE_SERVER_URL = ${JSON.stringify(shareServerUrl)};
|
|
658
659
|
const DIAGRAM_ID = ${JSON.stringify(diagramId)};
|
|
659
660
|
const VIEWER_MODE = ${JSON.stringify(mode)};
|
|
@@ -814,7 +815,8 @@ export function generateViewerHTML(diagram: WalkthroughDiagram, gitHash: string
|
|
|
814
815
|
html += '<ul class="links-list">';
|
|
815
816
|
meta.links.forEach(link => {
|
|
816
817
|
const href = buildFileUrl(link.label, link.url);
|
|
817
|
-
|
|
818
|
+
const target = GITHUB_REPO ? ' target="_blank" rel="noopener"' : '';
|
|
819
|
+
html += '<li><a href="' + escapeAttr(href) + '"' + target + '>' + escapeText(link.label) + "</a></li>";
|
|
818
820
|
});
|
|
819
821
|
html += "</ul>";
|
|
820
822
|
}
|
|
@@ -921,8 +923,15 @@ export function generateViewerHTML(diagram: WalkthroughDiagram, gitHash: string
|
|
|
921
923
|
|
|
922
924
|
function buildFileUrl(label, url) {
|
|
923
925
|
// Parse line number from label like "src/index.ts:56-59" or "src/index.ts:56"
|
|
924
|
-
const lineMatch = label.match(/:(\d+)
|
|
926
|
+
const lineMatch = label.match(/:(\d+)(?:-(\d+))?/);
|
|
925
927
|
const line = lineMatch ? lineMatch[1] : "1";
|
|
928
|
+
if (GITHUB_REPO) {
|
|
929
|
+
// Build GitHub link: https://github.com/user/repo/blob/HEAD/path#L1-L5
|
|
930
|
+
const lineAnchor = lineMatch && lineMatch[2]
|
|
931
|
+
? "#L" + lineMatch[1] + "-L" + lineMatch[2]
|
|
932
|
+
: "#L" + line;
|
|
933
|
+
return GITHUB_REPO + "/blob/HEAD/" + url + lineAnchor;
|
|
934
|
+
}
|
|
926
935
|
const filePath = PROJECT_ROOT + "/" + url;
|
|
927
936
|
return "vscode://file/" + filePath + ":" + line;
|
|
928
937
|
}
|
package/src/types.ts
CHANGED
|
Binary file
|