…
const D2=window.d2||window.D2||{},PARAMS=Object.assign({},window.d2params||{},D2.params||{});
const URLQ=()=>new URLSearchParams(location.search);
+// every call is relative: the project the page is opened on (its SpecUi, brain, pipeline and pages; base d2's Page:/App:
+// topics read from any project, P-1377). ?spec= / ?pages= name another project only where the server allows it.
+const SPEC=URLQ().get("spec")||PARAMS.spec||"",PAGES=URLQ().get("pages")||PARAMS.pages||"";
-// d2's own journeys and features live on d2spec, its pages on base d2; a project of its own passes ?spec=&pages= (both its id)
-const SPEC=URLQ().get("spec")||PARAMS.spec||"d2spec",PAGES=URLQ().get("pages")||PARAMS.pages||"d2";
const api=(m,u,b)=>D2.api?Promise.resolve(D2.api(m,u,b)):Promise.reject(new Error("E_BRIDGE: this page runs inside d2 (no d2.api here)"));
-const inP=(u,p)=>u+(u.includes("?")?"&":"?")+"project="+encodeURIComponent(p);
+const inP=(u,p)=>p?u+(u.includes("?")?"&":"?")+"project="+encodeURIComponent(p):u;
const rawOf=j=>typeof j==="string"?j:(j&&(j.text||j.body||j.markdown))||"";
const $=s=>document.querySelector(s),$$=s=>[...document.querySelectorAll(s)];
…
return out.slice(0,12)}
async function linksOf(n){if(n in LINKS)return LINKS[n];if(n.startsWith("/")){LINKS[n]=null;return null}
- const t=await topicText(n,[PAGES,SPEC]);if(!t){LINKS[n]=null;return null}
+ const t=await topicText(n,[...new Set([PAGES,SPEC])]);if(!t){LINKS[n]=null;return null}
const l=linksIn(n,t);LINKS[n]=l;if(!l.length&&/```home-button/.test(t))SCRIPT[n]="a home-button with no target";else if(/\bd2\.go\(\s*[A-Za-z_$]/.test(t)||(!l.length&&/```embedhtml|<script/.test(t)))SCRIPT[n]="next is set in script";return l}
async function load(){
…
J=parseJourneys(specUi);
PAGELIST=[...new Set([...BUILTIN,...names,...J.flatMap(j=>j.ids.map(i=>st[i].page)).filter(Boolean)])];
- const there="https://"+SPEC+"."+location.hostname.split(".").slice(-2).join(".")+location.pathname+location.search;
- $("#jstatus").innerHTML=`${J.length} specified journey${J.length==1?"":"s"} · ${FEATS.length} features · ${PAGELIST.length} pages · journeys and features from ${esc(SPEC)}, pages from ${esc(PAGES)}`+
- (!FEATS.length&&!location.hostname.startsWith(SPEC+".")?` · <b>No features here:</b> the brain is read on its own project; <a href="${esc(there)}">open this page on ${esc(SPEC)}</a>`:"")}
+ const base=location.hostname.split(".").length<=2,there="https://d2spec."+location.hostname.split(".").slice(-2).join(".")+location.pathname+location.search;
+ $("#jstatus").innerHTML=`${J.length} specified journey${J.length==1?"":"s"} · ${FEATS.length} features · ${PAGELIST.length} pages · from ${esc(SPEC||"this project")}`+
+ (!FEATS.length?(base&&!SPEC?` · <b>No features here:</b> d2's own spec lives on d2spec; <a href="${esc(there)}">open this page there</a>`:` · <b>No features yet:</b> this project's Spec has no feature sections`):"")}
// ---- the journey fence (SpecUi 2.36): journey <id> "<name>" { … }, steps, if/elseif/else { }, wait, done
…
const sh=short(s.page);s.k=s.page?TK.filter(t=>{const x=(t.title||"")+" "+(t.summary||"");return (sh.length>3&&x.includes(sh))||s.codes.some(c=>x.includes(c))}).slice(0,6).map(t=>[t.id,t.title,t.status]):[]}
async function enrichFromPage(id){const s=st[id];if(!s.page||s.page.startsWith("/")||s.pageRead)return;s.pageRead=true;
- const t=await topicText(s.page,[PAGES,SPEC]);const more=[...new Set((t.match(/\^[a-z][a-z0-9-]*-\d+/gi)||[]).map(c=>c.slice(1)))].filter(c=>!s.codes.includes(c)&&FB[c.replace(/-\d+$/,"")]);
+ const t=await topicText(s.page,[...new Set([PAGES,SPEC])]);const more=[...new Set((t.match(/\^[a-z][a-z0-9-]*-\d+/gi)||[]).map(c=>c.slice(1)))].filter(c=>!s.codes.includes(c)&&FB[c.replace(/-\d+$/,"")]);
if(more.length){s.codes=s.codes.concat(more.slice(0,8));enrich(id);draw()}}
…