This repository has no description
0

Configure Feed

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

fix: add relationships section back to strata page

Was dropped in the edit-paradigm rewrite. Now rendered as editable
items with entity linking (Semble pages) and arrow styling.

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

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

author
nandi
co-author
Letta Code
date (May 14, 2026, 5:06 AM UTC) commit bff0e35b parent 9b0a4f22
+49 -1
+49 -1
src/frontend/app.ts
··· 698 698 </div> 699 699 </div> 700 700 701 + ${s.relationships.length > 0 ? `<div class="strata-section"> 702 + <h3>Relationships <span class="edit-hint" title="Click to edit">&#9998;</span></h3> 703 + <div class="editable-list" data-field="relationships"> 704 + ${s.relationships.map(r => { 705 + // Link entity names to their Semble pages 706 + let styled = escHtml(r); 707 + const titleToUri = new Map<string, string>(); 708 + for (const uri of island.vertices!) { 709 + const m = island.vertexMeta![uri]; 710 + const title = m?.title; 711 + if (title && title.length > 3 && !title.startsWith("http") && !title.startsWith("at://")) { 712 + titleToUri.set(title, uri); 713 + } 714 + } 715 + const sortedTitles = [...titleToUri.entries()].sort((a, b) => b[0].length - a[0].length); 716 + for (const [title, uri] of sortedTitles) { 717 + const escaped = escHtml(title); 718 + if (styled.includes(escaped)) { 719 + const href = `https://semble.so/url/${encodeURIComponent(uri)}`; 720 + styled = styled.replace(escaped, `<a href="${href}" target="_blank" rel="noopener" class="entity-link">${escaped}</a>`); 721 + } 722 + } 723 + for (const [title, uri] of sortedTitles) { 724 + const href = `https://semble.so/url/${encodeURIComponent(uri)}`; 725 + const shortNames = [ 726 + title.split(/[:โ€”โ€“\-]/)[0].trim(), 727 + title.split("(")[0].trim(), 728 + ].filter(n => n.length > 3 && n.length < title.length); 729 + for (const shortName of shortNames) { 730 + const escaped = escHtml(shortName); 731 + if (styled.includes(escaped) && !styled.includes(`>${escaped}<`)) { 732 + styled = styled.replace(escaped, `<a href="${href}" target="_blank" rel="noopener" class="entity-link">${escaped}</a>`); 733 + } 734 + } 735 + } 736 + styled = styled 737 + .replace(/โ†’/g, '<span class="arrow arrow-forward">โ†’</span>') 738 + .replace(/โ†/g, '<span class="arrow arrow-back">โ†</span>') 739 + .replace(/โ†”/g, '<span class="arrow arrow-bidi">โ†”</span>') 740 + .replace(/โ‡/g, '<span class="arrow arrow-blocked">โ‡</span>'); 741 + return `<div class="strata-rel-line editable" contenteditable="true">${styled}</div>`; 742 + }).join("")} 743 + <div class="list-add" data-field="relationships">+ Add relationship</div> 744 + </div> 745 + </div>` : ""} 746 + 701 747 <div class="strata-section"> 702 748 <h3>Tensions <span class="edit-hint" title="Click to edit">&#9998;</span></h3> 703 749 <div class="editable-list" data-field="tensions"> ··· 794 840 btn.addEventListener("click", () => { 795 841 const field = (btn as HTMLElement).dataset.field!; 796 842 const div = document.createElement("div"); 797 - div.className = field === "tensions" ? "analysis-tension editable" : "analysis-question editable"; 843 + div.className = field === "tensions" ? "analysis-tension editable" : field === "relationships" ? "strata-rel-line editable" : "analysis-question editable"; 798 844 div.setAttribute("contenteditable", "true"); 799 845 div.textContent = "New item..."; 800 846 div.addEventListener("input", markDirty); ··· 811 857 const themeTags = editContainer.querySelectorAll('.theme-tag'); 812 858 const tensionEls = editContainer.querySelectorAll('[data-field="tensions"] .editable'); 813 859 const questionEls = editContainer.querySelectorAll('[data-field="openQuestions"] .editable'); 860 + const relEls = editContainer.querySelectorAll('[data-field="relationships"] .editable'); 814 861 815 862 return { 816 863 title: titleEl?.textContent?.trim() || "", ··· 818 865 themes: Array.from(themeTags).map(t => t.getAttribute("data-value") || t.textContent?.replace("ร—", "").trim() || ""), 819 866 tensions: Array.from(tensionEls).map(t => t.textContent?.trim() || "").filter(Boolean), 820 867 openQuestions: Array.from(questionEls).map(q => q.textContent?.trim() || "").filter(Boolean), 868 + relationships: Array.from(relEls).map(r => r.textContent?.trim() || "").filter(Boolean), 821 869 }; 822 870 } 823 871