snatching amp's walkthrough for my own purposes mwhahaha traverse.dunkirk.sh/diagram/6121f05c-a5ef-4ecf-8ffc-02534c5e767c
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: nicer ui

+274 -139
+274 -139
src/template.ts
··· 52 52 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 53 53 background: var(--bg); 54 54 color: var(--text); 55 - height: 100vh; 56 - display: flex; 57 - flex-direction: column; 58 - overflow: hidden; 55 + min-height: 100vh; 59 56 } 60 57 61 58 .summary-bar { ··· 64 61 border-bottom: 1px solid var(--border); 65 62 font-size: 14px; 66 63 color: var(--text-muted); 67 - flex-shrink: 0; 68 64 display: flex; 69 65 align-items: center; 70 66 gap: 8px; ··· 78 74 letter-spacing: 0.05em; 79 75 } 80 76 81 - .main { 82 - display: flex; 83 - flex: 1; 84 - overflow: hidden; 85 - } 86 - 87 - .diagram-container { 88 - flex: 1; 89 - overflow: auto; 77 + .diagram-section { 90 78 display: flex; 91 79 align-items: center; 92 80 justify-content: center; 93 - padding: 32px; 94 - min-width: 0; 81 + padding: 24px; 82 + border: 1px solid var(--border); 83 + border-radius: 8px; 95 84 } 96 85 97 - .diagram-container pre.mermaid { 86 + .diagram-section pre.mermaid { 98 87 width: 100%; 99 88 } 100 89 101 - .diagram-container svg { 90 + .diagram-section svg { 91 + max-height: none !important; 92 + height: auto !important; 102 93 width: 100%; 103 - height: auto; 94 + } 95 + 96 + /* ── Force theme colors on all Mermaid elements ── */ 97 + 98 + /* Global mermaid label overrides */ 99 + .diagram-section .label { 100 + font-family: inherit; 101 + color: var(--text) !important; 102 + } 103 + .diagram-section .label text, 104 + .diagram-section .label span { 105 + fill: var(--text) !important; 106 + color: var(--text) !important; 107 + } 108 + .diagram-section .cluster-label text, 109 + .diagram-section .cluster-label span, 110 + .diagram-section .cluster-label span p { 111 + fill: var(--text) !important; 112 + color: var(--text) !important; 113 + background-color: transparent !important; 114 + } 115 + 116 + /* Flowchart nodes */ 117 + .diagram-section .node rect, 118 + .diagram-section .node circle, 119 + .diagram-section .node ellipse, 120 + .diagram-section .node polygon, 121 + .diagram-section .node path, 122 + .diagram-section .node .label-container, 123 + .diagram-section .node .label-container path, 124 + .diagram-section .node g path { 125 + fill: var(--bg) !important; 126 + stroke: var(--text) !important; 127 + } 128 + .diagram-section .node .label, 129 + .diagram-section .node .nodeLabel, 130 + .diagram-section .node text, 131 + .diagram-section .node foreignObject { 132 + color: var(--text) !important; 133 + fill: var(--text) !important; 134 + } 135 + .diagram-section .node foreignObject div, 136 + .diagram-section .node foreignObject span, 137 + .diagram-section .node foreignObject p { 138 + color: var(--text) !important; 139 + } 140 + 141 + /* Edge labels */ 142 + .diagram-section .edgeLabel, 143 + .diagram-section .edgeLabel span, 144 + .diagram-section .edgeLabel div, 145 + .diagram-section .edgeLabel p, 146 + .diagram-section .edgeLabel foreignObject, 147 + .diagram-section .edgeLabel foreignObject *, 148 + .diagram-section .edgeLabel text, 149 + .diagram-section .edgeLabel tspan { 150 + color: var(--text) !important; 151 + fill: var(--text) !important; 152 + } 153 + .diagram-section .edgeLabel, 154 + .diagram-section .edgeLabel p, 155 + .diagram-section .edgeLabel span { 156 + background-color: var(--bg) !important; 157 + } 158 + .diagram-section .edgeLabel rect, 159 + .diagram-section .edgeLabel .labelBkg { 160 + fill: var(--bg) !important; 161 + opacity: 1 !important; 162 + } 163 + 164 + /* Edge paths and arrows */ 165 + .diagram-section .edgePath path, 166 + .diagram-section .flowchart-link, 167 + .diagram-section path.path, 168 + .diagram-section .edge-pattern-solid, 169 + .diagram-section .edge-pattern-dotted, 170 + .diagram-section .edge-pattern-dashed { 171 + stroke: var(--text) !important; 172 + } 173 + .diagram-section marker path, 174 + .diagram-section .arrowheadPath, 175 + .diagram-section .arrowMarkerAbs path { 176 + fill: var(--text) !important; 177 + stroke: var(--text) !important; 178 + } 179 + 180 + /* Subgraph/cluster styling */ 181 + .diagram-section .cluster rect, 182 + .diagram-section .cluster-label, 183 + .diagram-section g.cluster > rect { 184 + fill: var(--bg-panel) !important; 185 + stroke: var(--text) !important; 186 + } 187 + .diagram-section .cluster text, 188 + .diagram-section .cluster-label text, 189 + .diagram-section .cluster .nodeLabel { 190 + fill: var(--text) !important; 191 + color: var(--text) !important; 192 + } 193 + 194 + /* ── Sequence diagram overrides ── */ 195 + .diagram-section rect.actor { 196 + fill: var(--bg) !important; 197 + stroke: var(--text) !important; 198 + } 199 + .diagram-section text.actor, 200 + .diagram-section text.actor tspan, 201 + .diagram-section .actor > tspan { 202 + fill: var(--text) !important; 203 + stroke: none !important; 204 + } 205 + .diagram-section .actor-man circle, 206 + .diagram-section .actor-man line { 207 + fill: var(--bg) !important; 208 + stroke: var(--text) !important; 209 + } 210 + .diagram-section line.actor-line, 211 + .diagram-section .actor-line { 212 + stroke: var(--text) !important; 213 + } 214 + .diagram-section .sequenceNumber { 215 + fill: var(--bg) !important; 216 + } 217 + .diagram-section .messageLine0, 218 + .diagram-section .messageLine1 { 219 + stroke: var(--text) !important; 220 + } 221 + .diagram-section .messageText { 222 + fill: var(--text) !important; 223 + } 224 + .diagram-section .activation0, 225 + .diagram-section .activation1, 226 + .diagram-section .activation2 { 227 + fill: var(--code-bg) !important; 228 + stroke: var(--text) !important; 229 + } 230 + .diagram-section .labelBox { 231 + fill: var(--bg-panel) !important; 232 + stroke: var(--text) !important; 233 + } 234 + .diagram-section .labelText, 235 + .diagram-section .loopText { 236 + fill: var(--text) !important; 104 237 } 105 238 106 - /* Make clickable nodes interactive */ 107 - .diagram-container .node { cursor: pointer; } 108 - .diagram-container .node:hover rect, 109 - .diagram-container .node:hover polygon, 110 - .diagram-container .node:hover circle, 111 - .diagram-container .node:hover .basic { 112 - filter: brightness(0.92); 239 + /* ── ERD diagram overrides ── */ 240 + .diagram-section .entityBox { 241 + fill: var(--bg-panel) !important; 242 + } 243 + .diagram-section .row-rect-odd path, 244 + .diagram-section .row-rect-even path { 245 + fill: var(--bg) !important; 246 + } 247 + .diagram-section .row-rect-even path { 248 + fill: var(--code-bg) !important; 249 + } 250 + .diagram-section .relationshipLine path { 251 + stroke: var(--text) !important; 252 + } 253 + .diagram-section .relationshipLabel { 254 + fill: var(--text) !important; 113 255 } 114 256 115 - .node.selected rect, 116 - .node.selected polygon, 117 - .node.selected circle, 118 - .node.selected .basic { 119 - stroke: var(--accent) !important; 120 - stroke-width: 2.5px !important; 257 + /* ── Node interaction ── */ 258 + .diagram-section .node { cursor: pointer; } 259 + 260 + .diagram-section .node:hover :is(rect, circle, ellipse, polygon, path) { 261 + filter: brightness(1.12) !important; 121 262 } 122 263 123 - .detail-panel { 124 - width: 420px; 125 - flex-shrink: 0; 126 - border-left: 1px solid var(--border); 127 - background: var(--bg-panel); 128 - display: none; 129 - flex-direction: column; 130 - overflow: hidden; 264 + .diagram-section .node.selected :is(rect, circle, ellipse, polygon, path) { 265 + filter: brightness(1.2) !important; 266 + stroke: var(--text-muted) !important; 267 + stroke-width: 1.5px !important; 131 268 } 132 269 133 - .detail-panel.open { 134 - display: flex; 270 + /* Edge hover */ 271 + .diagram-section .node:hover ~ .edgePath path, 272 + .diagram-section .edgePath:hover path { 273 + stroke-width: 3px; 274 + filter: brightness(1.2); 135 275 } 136 276 137 - @media (max-width: 1300px) { 138 - .main { 139 - flex-direction: column; 140 - } 277 + /* Highlight pulse animation */ 278 + .diagram-section .node.highlighted :is(rect, circle, ellipse, polygon, path) { 279 + animation: node-highlight-pulse 0.5s ease-in-out 3; 280 + } 281 + @keyframes node-highlight-pulse { 282 + 0%, 100% { filter: brightness(1); } 283 + 50% { filter: brightness(1.3) drop-shadow(0 0 8px var(--text)); } 284 + } 141 285 142 - .detail-panel { 143 - width: 100%; 144 - border-left: none; 145 - border-top: 1px solid var(--border); 146 - max-height: 50vh; 147 - } 286 + /* ERD: don't apply hover/selected effects to individual row cells */ 287 + .diagram-section .erDiagram .node:hover :is(.row-rect-odd, .row-rect-even) :is(path, rect, polygon), 288 + .diagram-section .erDiagram .node.selected :is(.row-rect-odd, .row-rect-even) :is(path, rect, polygon) { 289 + filter: none !important; 290 + stroke: none !important; 291 + stroke-width: 0 !important; 148 292 } 149 293 150 - .detail-header { 151 - padding: 16px 20px; 152 - border-bottom: 1px solid var(--border); 153 - display: flex; 154 - align-items: center; 155 - justify-content: space-between; 156 - flex-shrink: 0; 294 + /* ── Content wrap ── */ 295 + .content-wrap { 296 + max-width: 720px; 297 + margin: 0 auto; 298 + padding: 32px 20px; 157 299 } 158 300 159 - .detail-header h2 { 160 - font-size: 16px; 301 + /* ── Detail section ── */ 302 + .content-summary { 303 + font-size: 20px; 161 304 font-weight: 600; 305 + padding: 24px 0 0; 162 306 } 163 307 164 - .close-btn { 165 - background: none; 166 - border: none; 167 - color: var(--text-muted); 168 - cursor: pointer; 169 - padding: 4px; 170 - border-radius: 4px; 171 - font-size: 18px; 172 - line-height: 1; 173 - transition: color 0.15s; 308 + .node-card { 309 + padding: 24px 0; 310 + border-top: 1px solid var(--border); 174 311 } 175 312 176 - .close-btn:hover { color: var(--text); } 313 + .node-card:first-child { 314 + border-top: none; 315 + } 177 316 178 - .detail-body { 179 - flex: 1; 180 - overflow-y: auto; 181 - padding: 20px; 317 + .node-card h3 { 318 + font-size: 16px; 319 + font-weight: 600; 320 + margin-bottom: 12px; 182 321 } 183 322 184 - .detail-body .description { 323 + .node-card .description { 185 324 font-size: 14px; 186 325 line-height: 1.65; 187 326 } 188 327 189 - .detail-body .description h1, 190 - .detail-body .description h2, 191 - .detail-body .description h3 { 328 + .node-card .description h1, 329 + .node-card .description h2, 330 + .node-card .description h3 { 192 331 margin-top: 16px; 193 332 margin-bottom: 8px; 194 333 } 195 334 196 - .detail-body .description h1 { font-size: 18px; } 197 - .detail-body .description h2 { font-size: 16px; } 198 - .detail-body .description h3 { font-size: 14px; } 335 + .node-card .description h1 { font-size: 18px; } 336 + .node-card .description h2 { font-size: 16px; } 337 + .node-card .description h3 { font-size: 14px; } 199 338 200 - .detail-body .description p { margin-bottom: 12px; } 339 + .node-card .description p { margin-bottom: 12px; } 201 340 202 - .detail-body .description code { 341 + .node-card .description code { 203 342 background: var(--code-bg); 204 343 padding: 2px 5px; 205 344 border-radius: 3px; 206 345 font-size: 13px; 207 346 } 208 347 209 - .detail-body .description pre { 348 + .node-card .description pre { 210 349 background: var(--code-bg); 211 350 padding: 12px; 212 351 border-radius: 6px; ··· 214 353 margin-bottom: 12px; 215 354 } 216 355 217 - .detail-body .description pre code { 356 + .node-card .description pre code { 218 357 background: none; 219 358 padding: 0; 359 + line-height: 1.625 !important; 220 360 } 221 361 222 - .detail-body .description ul, 223 - .detail-body .description ol { 362 + .node-card .description ul, 363 + .node-card .description ol { 224 364 margin-bottom: 12px; 225 365 padding-left: 20px; 226 366 } 227 367 228 - .detail-body .description li { margin-bottom: 4px; } 368 + .node-card .description li { margin-bottom: 4px; } 229 369 230 370 .section-label { 231 371 font-size: 11px; ··· 267 407 font-size: 13px; 268 408 line-height: 1.5; 269 409 } 270 - 271 - /* empty state */ 272 - .empty-hint { 273 - color: var(--text-muted); 274 - font-size: 13px; 275 - text-align: center; 276 - padding: 40px 20px; 277 - } 278 410 </style> 279 411 </head> 280 412 <body> ··· 283 415 <span>${escapeHTML(diagram.summary)}</span> 284 416 </div> 285 417 286 - <div class="main"> 287 - <div class="diagram-container"> 418 + <div class="content-wrap"> 419 + <div class="diagram-section"> 288 420 <pre class="mermaid">${escapeHTML(diagram.code)}</pre> 289 421 </div> 290 422 291 - <div class="detail-panel" id="detail-panel"> 292 - <div class="detail-header"> 293 - <h2 id="detail-title">Select a node</h2> 294 - <button class="close-btn" id="close-btn" aria-label="Close panel">&times;</button> 295 - </div> 296 - <div class="detail-body" id="detail-body"> 297 - <div class="empty-hint">Click a node in the diagram to view details.</div> 298 - </div> 299 - </div> 423 + <div id="detail-section"></div> 300 424 </div> 301 425 302 426 <script type="module"> ··· 313 437 window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", initTheme); 314 438 315 439 async function init() { 316 - const dark = window.matchMedia("(prefers-color-scheme: dark)").matches; 317 440 await mermaid.initialize({ 318 441 startOnLoad: true, 319 - theme: dark ? "dark" : "default", 320 - flowchart: { useMaxWidth: false, htmlLabels: true, curve: "basis" }, 442 + theme: "base", 443 + flowchart: { useMaxWidth: false, htmlLabels: true, curve: "monotoneY", nodeSpacing: 50, rankSpacing: 50 }, 321 444 securityLevel: "loose", 322 445 }); 323 446 ··· 328 451 requestAnimationFrame(() => { 329 452 fitDiagram(); 330 453 attachClickHandlers(); 454 + renderAllNodes(); 331 455 }); 332 456 333 457 window.addEventListener("resize", fitDiagram); 334 458 } 335 459 336 460 function fitDiagram() { 337 - const svg = document.querySelector(".diagram-container svg"); 461 + const svg = document.querySelector(".diagram-section svg"); 338 462 if (!svg) return; 339 463 340 464 // Read the intrinsic size mermaid rendered ··· 347 471 } 348 472 349 473 function attachClickHandlers() { 350 - const svg = document.querySelector(".diagram-container svg"); 474 + const svg = document.querySelector(".diagram-section svg"); 351 475 if (!svg) return; 352 476 353 477 const nodeIds = Object.keys(DIAGRAM_DATA.nodes); ··· 380 504 381 505 // Click outside to deselect 382 506 document.addEventListener("click", (e) => { 383 - if (!e.target.closest(".detail-panel") && !e.target.closest(".node")) { 507 + if (!e.target.closest(".detail-section") && !e.target.closest(".node")) { 384 508 deselectAll(); 385 509 } 386 510 }); ··· 388 512 389 513 let selectedEl = null; 390 514 391 - function selectNode(nodeId, el) { 392 - const meta = DIAGRAM_DATA.nodes[nodeId]; 393 - if (!meta) return; 394 - 395 - // Update selection styling 396 - if (selectedEl) selectedEl.classList.remove("selected"); 397 - el.classList.add("selected"); 398 - selectedEl = el; 399 - 400 - // Update panel 401 - const panel = document.getElementById("detail-panel"); 402 - const title = document.getElementById("detail-title"); 403 - const body = document.getElementById("detail-body"); 404 - 405 - title.textContent = meta.title; 406 - 407 - let html = '<div class="description">' + marked.parse(meta.description) + "</div>"; 515 + function renderNodeCard(nodeId, meta) { 516 + let html = '<div class="node-card" data-card-id="' + escapeAttr(nodeId) + '">'; 517 + html += '<h3>' + escapeText(meta.title) + '</h3>'; 518 + html += '<div class="description">' + marked.parse(meta.description) + "</div>"; 408 519 409 520 if (meta.links && meta.links.length > 0) { 410 521 html += '<div class="section-label">Related Files</div>'; ··· 420 531 html += '<div class="code-snippet"><pre><code>' + escapeText(meta.codeSnippet) + "</code></pre></div>"; 421 532 } 422 533 423 - body.innerHTML = html; 534 + html += '</div>'; 535 + return html; 536 + } 537 + 538 + function renderAllNodes() { 539 + const section = document.getElementById("detail-section"); 540 + let html = '<h2 class="content-summary">' + escapeText(DIAGRAM_DATA.summary) + '</h2>'; 541 + for (const [nodeId, meta] of Object.entries(DIAGRAM_DATA.nodes)) { 542 + html += renderNodeCard(nodeId, meta); 543 + } 544 + section.innerHTML = html; 545 + highlightAll(section); 546 + } 424 547 425 - // Highlight code blocks 426 - body.querySelectorAll("pre code").forEach(block => { 548 + function highlightAll(container) { 549 + container.querySelectorAll("pre code").forEach(block => { 427 550 hljs.highlightElement(block); 428 551 }); 552 + } 429 553 430 - panel.classList.add("open"); 554 + function selectNode(nodeId, el) { 555 + const meta = DIAGRAM_DATA.nodes[nodeId]; 556 + if (!meta) return; 557 + 558 + if (selectedEl) selectedEl.classList.remove("selected"); 559 + el.classList.add("selected"); 560 + selectedEl = el; 561 + 562 + const section = document.getElementById("detail-section"); 563 + section.innerHTML = renderNodeCard(nodeId, meta); 564 + highlightAll(section); 565 + 566 + section.scrollIntoView({ behavior: "smooth", block: "start" }); 431 567 } 432 568 433 569 function deselectAll() { ··· 435 571 selectedEl.classList.remove("selected"); 436 572 selectedEl = null; 437 573 } 438 - document.getElementById("detail-panel").classList.remove("open"); 574 + renderAllNodes(); 575 + window.scrollTo({ top: 0, behavior: "smooth" }); 439 576 } 440 - 441 - document.getElementById("close-btn").addEventListener("click", deselectAll); 442 577 443 578 function escapeText(s) { 444 579 const d = document.createElement("div");