This repository has no description
0

Configure Feed

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

feat: strata page with graph, summary, synthesis, nodes + edges

Layout: summary + graph at top, synthesis below, then themes,
relationships, tensions, open questions, and full node/edge lists
at the bottom.

๐Ÿ‘พ Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta Code <noreply@letta.com>

author
nandi
co-author
Letta Code
date (May 13, 2026, 5:41 AM UTC) commit f7d2e3da parent 221079b7
+138 -36
+47 -25
src/frontend/app.ts
··· 517 517 <div class="island-card" data-island-id="${escHtml(island.id)}"> 518 518 <canvas class="island-graph" data-island-id="${escHtml(island.id)}" width="400" height="260"></canvas> 519 519 <div class="island-card-footer"> 520 + <span class="island-size">${island.vertices.length} nodes &middot; ${island.edges.length} edges</span> 520 521 <p class="island-summary">${escHtml(summary)}</p> 521 522 <button class="${strataBtnClass}" data-island-id="${escHtml(island.id)}">${strataBtnLabel}</button> 522 523 </div> ··· 628 629 if (!content) return; 629 630 const s = island.strata!; 630 631 632 + const summary = island.summary || summarizeIsland(island); 633 + 631 634 // Themes 632 635 const themesHtml = s.themes.length > 0 633 636 ? `<div class="theme-tags">${s.themes.map(t => `<span class="theme-tag">${escHtml(t)}</span>`).join("")}</div>` ··· 665 668 <div class="strata-page"> 666 669 <a href="/" class="back-link">&larr; Explore</a> 667 670 <h2>Strata</h2> 671 + <p class="island-summary-large">${escHtml(summary)}</p> 668 672 <p class="island-meta">${island.vertices.length} vertices &middot; ${island.edges.length} edges</p> 669 673 674 + <canvas class="island-graph-large" id="strata-graph" width="900" height="500"></canvas> 675 + 676 + <div class="strata-section"><h3>Synthesis</h3><div class="synthesis-text">${escHtml(s.synthesis)}</div></div> 677 + 670 678 ${themesHtml ? `<div class="strata-section"><h3>Themes</h3>${themesHtml}</div>` : ""} 671 679 672 680 ${relsHtml ? `<div class="strata-section"><h3>Relationships</h3>${relsHtml}</div>` : ""} 673 681 674 682 ${tensionsHtml} 675 683 ${questionsHtml} 676 - ${synthesisHtml} 684 + 685 + <div class="strata-section"> 686 + <h3>Nodes</h3> 687 + <div class="strata-nodes">${island.vertices.map(v => { 688 + const meta = island.vertexMeta[v]; 689 + const title = meta?.title || truncateUri(v); 690 + const desc = meta?.description; 691 + return `<div class="strata-node"> 692 + <span class="strata-node-type">${escHtml(meta?.type || "unknown")}</span> 693 + <a href="${escHtml(v)}" class="strata-node-title" target="_blank" rel="noopener">${escHtml(title)}</a> 694 + ${desc ? `<span class="strata-node-desc">${escHtml(desc.slice(0, 120))}</span>` : ""} 695 + </div>`; 696 + }).join("")}</div> 697 + </div> 698 + 699 + <div class="strata-section"> 700 + <h3>Edges</h3> 701 + <div class="strata-edges">${island.edges.map(e => { 702 + const sourceMeta = island.vertexMeta[e.source]; 703 + const targetMeta = island.vertexMeta[e.target]; 704 + const sourceTitle = sourceMeta?.title || truncateUri(e.source); 705 + const targetTitle = targetMeta?.title || truncateUri(e.target); 706 + const type = connectionTypeLabel(e.connectionType); 707 + const icon = connectionTypeIcon(e.connectionType); 708 + return `<div class="strata-edge"> 709 + <span class="strata-edge-source">${escHtml(sourceTitle)}</span> 710 + <span class="strata-edge-type">${icon} ${escHtml(type)}</span> 711 + <span class="strata-edge-target">${escHtml(targetTitle)}</span> 712 + ${e.note ? `<span class="strata-edge-note">${escHtml(e.note)}</span>` : ""} 713 + ${e.handle ? `<span class="strata-edge-handle">@${escHtml(e.handle)}</span>` : ""} 714 + </div>`; 715 + }).join("")}</div> 716 + </div> 677 717 </div> 678 718 `; 719 + 720 + // Initialize the force graph 721 + const canvas = document.getElementById("strata-graph") as HTMLCanvasElement; 722 + if (canvas) { 723 + new ForceGraph(canvas, island); 724 + } 679 725 } 680 726 681 727 // โ”€โ”€ Connect page โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ··· 838 884 // โ”€โ”€ Init โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 839 885 840 886 document.addEventListener("DOMContentLoaded", () => { 841 - const searchInput = document.getElementById("search-input") as HTMLInputElement; 842 - const searchBtn = document.getElementById("search-btn"); 843 - 844 - searchBtn?.addEventListener("click", () => { 845 - const q = searchInput?.value.trim(); 846 - if (q) { 847 - window.location.href = `/island?id=${encodeURIComponent(q)}`; 848 - } 849 - }); 850 - 851 - searchInput?.addEventListener("keydown", (e: KeyboardEvent) => { 852 - if (e.key === "Enter") { 853 - const q = searchInput.value.trim(); 854 - if (q) { 855 - window.location.href = `/island?id=${encodeURIComponent(q)}`; 856 - } 857 - } 858 - }); 859 - 860 - document.getElementById("login-btn")?.addEventListener("click", () => { 861 - alert("OAuth login not yet implemented. Use your PDS directly to create connection records."); 862 - }); 863 - 864 887 route(); 865 - initAuth(); 866 888 }); 867 889 868 890 window.addEventListener("popstate", route);
+91 -11
src/landing-page.ts
··· 951 951 color: #fff; 952 952 border-color: var(--purple); 953 953 } 954 + .island-summary-large { 955 + font-size: 1.0625rem; 956 + line-height: 1.5; 957 + color: var(--text); 958 + margin-bottom: 0.5rem; 959 + } 960 + .strata-nodes { 961 + display: flex; 962 + flex-direction: column; 963 + gap: 0.25rem; 964 + } 965 + .strata-node { 966 + display: flex; 967 + align-items: baseline; 968 + gap: 0.5rem; 969 + padding: 0.25rem 0; 970 + font-size: 0.8125rem; 971 + border-bottom: 1px solid var(--border); 972 + } 973 + .strata-node:last-child { border-bottom: none; } 974 + .strata-node-type { 975 + font-size: 0.625rem; 976 + padding: 0.0625rem 0.375rem; 977 + border-radius: 8px; 978 + background: var(--border); 979 + color: var(--text-muted); 980 + flex-shrink: 0; 981 + text-transform: uppercase; 982 + } 983 + .strata-node-title { 984 + color: var(--accent); 985 + text-decoration: none; 986 + font-weight: 500; 987 + overflow: hidden; 988 + text-overflow: ellipsis; 989 + white-space: nowrap; 990 + } 991 + .strata-node-title:hover { text-decoration: underline; } 992 + .strata-node-desc { 993 + color: var(--text-muted); 994 + font-size: 0.75rem; 995 + overflow: hidden; 996 + text-overflow: ellipsis; 997 + white-space: nowrap; 998 + flex: 1; 999 + min-width: 0; 1000 + } 1001 + .strata-edges { 1002 + display: flex; 1003 + flex-direction: column; 1004 + gap: 0.25rem; 1005 + } 1006 + .strata-edge { 1007 + display: flex; 1008 + align-items: center; 1009 + gap: 0.5rem; 1010 + flex-wrap: wrap; 1011 + padding: 0.25rem 0; 1012 + font-size: 0.8125rem; 1013 + border-bottom: 1px solid var(--border); 1014 + } 1015 + .strata-edge:last-child { border-bottom: none; } 1016 + .strata-edge-source, .strata-edge-target { 1017 + color: var(--accent); 1018 + overflow: hidden; 1019 + text-overflow: ellipsis; 1020 + white-space: nowrap; 1021 + max-width: 200px; 1022 + } 1023 + .strata-edge-type { 1024 + font-size: 0.6875rem; 1025 + padding: 0.0625rem 0.375rem; 1026 + border-radius: 8px; 1027 + background: var(--border); 1028 + color: var(--text); 1029 + flex-shrink: 0; 1030 + } 1031 + .strata-edge-note { 1032 + color: var(--text-muted); 1033 + font-size: 0.6875rem; 1034 + overflow: hidden; 1035 + text-overflow: ellipsis; 1036 + white-space: nowrap; 1037 + flex: 1; 1038 + min-width: 80px; 1039 + } 1040 + .strata-edge-handle { 1041 + color: var(--text-muted); 1042 + font-size: 0.625rem; 1043 + flex-shrink: 0; 1044 + } 954 1045 955 1046 /* โ”€โ”€ Island card footer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ 956 1047 .island-card-footer { ··· 987 1078 <body> 988 1079 <header> 989 1080 <h1>Stigmergic</h1> 990 - <nav> 991 - <a href="/">Explore</a> 992 - <a href="/connect">Connect</a> 993 - <span id="auth-status" class="visible"></span> 994 - <button id="login-btn">Sign in</button> 995 - </nav> 996 1081 </header> 997 1082 <main> 998 - <div class="search-bar"> 999 - <input type="text" id="search-input" placeholder="Enter a URL or AT URI to explore..."> 1000 - <button id="search-btn">Go</button> 1001 - </div> 1002 - <div class="search-hint">Islands are connected components in the knowledge graph. Each island is a cluster of linked URLs.</div> 1003 1083 <div id="page-content"></div> 1004 1084 </main> 1005 1085 <script src="/app.js"></script>