@taciturnaxolotl/traverse 0.1.2 → 0.1.4
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 +5 -6
- package/fonts/inter-latin-400-normal.woff +0 -0
- package/fonts/inter-latin-700-normal.woff +0 -0
- package/package.json +2 -2
- package/src/index.ts +33 -5
- package/src/og.ts +109 -1
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# traverse
|
|
2
2
|
|
|
3
|
-
[](https://traverse.dunkirk.sh/diagram/6121f05c-a5ef-4ecf-8ffc-02534c5e767c)
|
|
4
|
+
|
|
5
|
+
> [!NOTE]
|
|
6
|
+
> The canonical repo for this is hosted on tangled over at [`dunkirk.sh/traverse`](https://tangled.org/@dunkirk.sh/traverse)
|
|
4
7
|
|
|
5
8
|
One of my favorite features about [amp](https://ampcode.com) is their walkthrough feature. It runs a sub agent which goes and breaks your repo into parts and then sends it up to amp's services to get rendered into a nice web page! I got curious and ended up dumping the tool prompt for both the walkthrough subagent and the tool prompt that generates the diagram.
|
|
6
9
|
|
|
@@ -43,8 +46,6 @@ For other agents its the same JSON config typically.
|
|
|
43
46
|
|
|
44
47
|
## Config
|
|
45
48
|
|
|
46
|
-
### json config
|
|
47
|
-
|
|
48
49
|
On macos edit/create `~/Library/Application Support/traverse/config.json`. If you are on Linux then `~/.config/traverse/config.json` (or `$XDG_CONFIG_HOME/traverse/config.json`)
|
|
49
50
|
|
|
50
51
|
```json
|
|
@@ -55,7 +56,7 @@ On macos edit/create `~/Library/Application Support/traverse/config.json`. If yo
|
|
|
55
56
|
}
|
|
56
57
|
```
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
alteratively or suplementally you can use env vars to define the same options:
|
|
59
60
|
|
|
60
61
|
| var | default | description |
|
|
61
62
|
| -------------------- | ----------------------------- | ------------------------------------------ |
|
|
@@ -64,8 +65,6 @@ On macos edit/create `~/Library/Application Support/traverse/config.json`. If yo
|
|
|
64
65
|
| `TRAVERSE_SHARE_URL` | `https://traverse.dunkirk.sh` | share server url |
|
|
65
66
|
| `TRAVERSE_DATA_DIR` | platform default | sqlite db location |
|
|
66
67
|
|
|
67
|
-
The canonical repo for this is hosted on tangled over at [`dunkirk.sh/traverse`](https://tangled.org/@dunkirk.sh/traverse)
|
|
68
|
-
|
|
69
68
|
<p align="center">
|
|
70
69
|
<img src="https://raw.githubusercontent.com/taciturnaxolotl/carriage/main/.github/images/line-break.svg" />
|
|
71
70
|
</p>
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taciturnaxolotl/traverse",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Interactive code walkthrough diagrams via MCP",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"src",
|
|
12
12
|
"bin",
|
|
13
|
+
"fonts",
|
|
13
14
|
"icon.svg"
|
|
14
15
|
],
|
|
15
16
|
"scripts": {
|
|
@@ -23,7 +24,6 @@
|
|
|
23
24
|
"typescript": "^5"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
26
|
-
"@fontsource/inter": "^5.2.8",
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
28
28
|
"@resvg/resvg-wasm": "^2.6.2",
|
|
29
29
|
"satori": "^0.19.1"
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { z } from "zod/v4";
|
|
|
4
4
|
import { generateViewerHTML } from "./template.ts";
|
|
5
5
|
import type { WalkthroughDiagram } from "./types.ts";
|
|
6
6
|
import { initDb, loadAllDiagrams, saveDiagram, deleteDiagramFromDb, generateId, getSharedUrl, saveSharedUrl } from "./storage.ts";
|
|
7
|
-
import { generateOgImage } from "./og.ts";
|
|
7
|
+
import { generateOgImage, generateIndexOgImage } from "./og.ts";
|
|
8
8
|
import { loadConfig } from "./config.ts";
|
|
9
9
|
|
|
10
10
|
const config = loadConfig();
|
|
@@ -153,11 +153,22 @@ try {
|
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
// Index OG image
|
|
157
|
+
if (url.pathname === "/og.png") {
|
|
158
|
+
const png = await generateIndexOgImage(MODE, diagrams.size);
|
|
159
|
+
return new Response(png, {
|
|
160
|
+
headers: {
|
|
161
|
+
"Content-Type": "image/png",
|
|
162
|
+
"Cache-Control": "public, max-age=3600",
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
156
167
|
// List available diagrams
|
|
157
168
|
if (url.pathname === "/") {
|
|
158
169
|
const html = MODE === "server"
|
|
159
|
-
? generateServerIndexHTML(diagrams.size, VERSION)
|
|
160
|
-
: generateLocalIndexHTML(diagrams, VERSION);
|
|
170
|
+
? generateServerIndexHTML(diagrams.size, VERSION, url.origin)
|
|
171
|
+
: generateLocalIndexHTML(diagrams, VERSION, url.origin);
|
|
161
172
|
return new Response(html, {
|
|
162
173
|
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
163
174
|
});
|
|
@@ -305,7 +316,7 @@ function generate404HTML(title: string, message: string): string {
|
|
|
305
316
|
</html>`;
|
|
306
317
|
}
|
|
307
318
|
|
|
308
|
-
function generateLocalIndexHTML(diagrams: Map<string, WalkthroughDiagram>, gitHash: string): string {
|
|
319
|
+
function generateLocalIndexHTML(diagrams: Map<string, WalkthroughDiagram>, gitHash: string, baseUrl: string): string {
|
|
309
320
|
const items = [...diagrams.entries()]
|
|
310
321
|
.map(
|
|
311
322
|
([id, d]) => {
|
|
@@ -346,6 +357,7 @@ function generateLocalIndexHTML(diagrams: Map<string, WalkthroughDiagram>, gitHa
|
|
|
346
357
|
</div>`
|
|
347
358
|
: `<div class="diagram-list">${items}</div>`;
|
|
348
359
|
|
|
360
|
+
const diagramCount = diagrams.size;
|
|
349
361
|
return `<!DOCTYPE html>
|
|
350
362
|
<html lang="en">
|
|
351
363
|
<head>
|
|
@@ -353,6 +365,14 @@ function generateLocalIndexHTML(diagrams: Map<string, WalkthroughDiagram>, gitHa
|
|
|
353
365
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
354
366
|
<title>Traverse</title>
|
|
355
367
|
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
|
|
368
|
+
<meta property="og:type" content="website" />
|
|
369
|
+
<meta property="og:title" content="Traverse" />
|
|
370
|
+
<meta property="og:description" content="Interactive code walkthrough diagrams. ${diagramCount} diagram${diagramCount !== 1 ? "s" : ""}." />
|
|
371
|
+
<meta property="og:image" content="${escapeHTML(baseUrl)}/og.png" />
|
|
372
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
373
|
+
<meta name="twitter:title" content="Traverse" />
|
|
374
|
+
<meta name="twitter:description" content="Interactive code walkthrough diagrams." />
|
|
375
|
+
<meta name="twitter:image" content="${escapeHTML(baseUrl)}/og.png" />
|
|
356
376
|
<style>
|
|
357
377
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
358
378
|
:root {
|
|
@@ -511,7 +531,7 @@ function generateLocalIndexHTML(diagrams: Map<string, WalkthroughDiagram>, gitHa
|
|
|
511
531
|
</html>`;
|
|
512
532
|
}
|
|
513
533
|
|
|
514
|
-
function generateServerIndexHTML(diagramCount: number, gitHash: string): string {
|
|
534
|
+
function generateServerIndexHTML(diagramCount: number, gitHash: string, baseUrl: string): string {
|
|
515
535
|
return `<!DOCTYPE html>
|
|
516
536
|
<html lang="en">
|
|
517
537
|
<head>
|
|
@@ -519,6 +539,14 @@ function generateServerIndexHTML(diagramCount: number, gitHash: string): string
|
|
|
519
539
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
520
540
|
<title>Traverse</title>
|
|
521
541
|
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
|
|
542
|
+
<meta property="og:type" content="website" />
|
|
543
|
+
<meta property="og:title" content="Traverse" />
|
|
544
|
+
<meta property="og:description" content="Interactive code walkthrough diagrams, shareable with anyone. ${diagramCount} diagram${diagramCount !== 1 ? "s" : ""} shared." />
|
|
545
|
+
<meta property="og:image" content="${escapeHTML(baseUrl)}/og.png" />
|
|
546
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
547
|
+
<meta name="twitter:title" content="Traverse" />
|
|
548
|
+
<meta name="twitter:description" content="Interactive code walkthrough diagrams, shareable with anyone." />
|
|
549
|
+
<meta name="twitter:image" content="${escapeHTML(baseUrl)}/og.png" />
|
|
522
550
|
<style>
|
|
523
551
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
524
552
|
:root {
|
package/src/og.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { initWasm, Resvg } from "@resvg/resvg-wasm";
|
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
|
|
5
5
|
// Load Inter font files (woff, not woff2 — satori doesn't support woff2)
|
|
6
|
-
const fontsDir = join(import.meta.dir, "../
|
|
6
|
+
const fontsDir = join(import.meta.dir, "../fonts");
|
|
7
7
|
const [interRegular, interBold] = await Promise.all([
|
|
8
8
|
Bun.file(join(fontsDir, "inter-latin-400-normal.woff")).arrayBuffer(),
|
|
9
9
|
Bun.file(join(fontsDir, "inter-latin-700-normal.woff")).arrayBuffer(),
|
|
@@ -16,6 +16,114 @@ await initWasm(Bun.file(wasmPath).arrayBuffer());
|
|
|
16
16
|
// Cache generated images by diagram ID
|
|
17
17
|
const cache = new Map<string, Buffer>();
|
|
18
18
|
|
|
19
|
+
export async function generateIndexOgImage(
|
|
20
|
+
mode: "local" | "server",
|
|
21
|
+
diagramCount: number,
|
|
22
|
+
): Promise<Buffer> {
|
|
23
|
+
const cacheKey = `__index_${mode}_${diagramCount}`;
|
|
24
|
+
const cached = cache.get(cacheKey);
|
|
25
|
+
if (cached) return cached;
|
|
26
|
+
|
|
27
|
+
const subtitle = mode === "server"
|
|
28
|
+
? `${diagramCount} diagram${diagramCount !== 1 ? "s" : ""} shared`
|
|
29
|
+
: `${diagramCount} diagram${diagramCount !== 1 ? "s" : ""}`;
|
|
30
|
+
|
|
31
|
+
const tagline = mode === "server"
|
|
32
|
+
? "Interactive code walkthrough diagrams, shareable with anyone."
|
|
33
|
+
: "Interactive code walkthrough diagrams";
|
|
34
|
+
|
|
35
|
+
const svg = await satori(
|
|
36
|
+
{
|
|
37
|
+
type: "div",
|
|
38
|
+
props: {
|
|
39
|
+
style: {
|
|
40
|
+
width: "100%",
|
|
41
|
+
height: "100%",
|
|
42
|
+
display: "flex",
|
|
43
|
+
flexDirection: "column",
|
|
44
|
+
justifyContent: "center",
|
|
45
|
+
alignItems: "center",
|
|
46
|
+
padding: "60px",
|
|
47
|
+
backgroundColor: "#0a0a0a",
|
|
48
|
+
color: "#e5e5e5",
|
|
49
|
+
fontFamily: "Inter",
|
|
50
|
+
},
|
|
51
|
+
children: [
|
|
52
|
+
{
|
|
53
|
+
type: "div",
|
|
54
|
+
props: {
|
|
55
|
+
style: {
|
|
56
|
+
display: "flex",
|
|
57
|
+
flexDirection: "column",
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
gap: "20px",
|
|
60
|
+
},
|
|
61
|
+
children: [
|
|
62
|
+
{
|
|
63
|
+
type: "div",
|
|
64
|
+
props: {
|
|
65
|
+
style: {
|
|
66
|
+
fontSize: "80px",
|
|
67
|
+
fontWeight: 700,
|
|
68
|
+
color: "#e5e5e5",
|
|
69
|
+
},
|
|
70
|
+
children: "Traverse",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: "div",
|
|
75
|
+
props: {
|
|
76
|
+
style: {
|
|
77
|
+
fontSize: "32px",
|
|
78
|
+
color: "#a3a3a3",
|
|
79
|
+
textAlign: "center",
|
|
80
|
+
},
|
|
81
|
+
children: tagline,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: "div",
|
|
86
|
+
props: {
|
|
87
|
+
style: {
|
|
88
|
+
display: "flex",
|
|
89
|
+
alignItems: "center",
|
|
90
|
+
gap: "8px",
|
|
91
|
+
marginTop: "16px",
|
|
92
|
+
fontSize: "24px",
|
|
93
|
+
color: "#a3a3a3",
|
|
94
|
+
backgroundColor: "#1c1c1e",
|
|
95
|
+
padding: "10px 24px",
|
|
96
|
+
borderRadius: "8px",
|
|
97
|
+
border: "1px solid #262626",
|
|
98
|
+
},
|
|
99
|
+
children: subtitle,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
width: 1200,
|
|
110
|
+
height: 630,
|
|
111
|
+
fonts: [
|
|
112
|
+
{ name: "Inter", data: interRegular, weight: 400, style: "normal" as const },
|
|
113
|
+
{ name: "Inter", data: interBold, weight: 700, style: "normal" as const },
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const resvg = new Resvg(svg, {
|
|
119
|
+
fitTo: { mode: "width", value: 1200 },
|
|
120
|
+
});
|
|
121
|
+
const png = Buffer.from(resvg.render().asPng());
|
|
122
|
+
|
|
123
|
+
cache.set(cacheKey, png);
|
|
124
|
+
return png;
|
|
125
|
+
}
|
|
126
|
+
|
|
19
127
|
export async function generateOgImage(
|
|
20
128
|
id: string,
|
|
21
129
|
summary: string,
|