…
<pre class="line" id="ln">Making your line…</pre>
<button class="btn" id="cp" type="button">Copy</button> <button class="btn" id="nw" type="button">New line</button> <span class="small" id="ex"></span>
+<span class="sp-say" id="lsay" role="status"></span>
+<details id="diag" hidden><summary>Details for the designer</summary><pre class="line"></pre></details></div></div></li>
-<span class="sp-say" id="lsay" role="status"></span></div></div></li>
<li id="s3" class="col"><span class="n">3</span><div><b>Connected</b><div class="more" hidden><span id="st"><span class="dot"></span>Waiting for your AI…</span>
<p class="small">Then tell it what you want, in its own chat.</p></div></div></li>
…
// the line: POST /api/v2/onboard/setup {kind: "connect"} once P-1366 ships; until then the same invite the connect-ai block makes,
// read from Help:ConnectYourAi?connect=1 in this person's own session (the block makes the invite on that request)
-const viaBlock=async()=>{const r=await fetch("/topics/Help:ConnectYourAi?connect=1",{credentials:"same-origin",cache:"no-store"});
- if(!r.ok)throw new Error("the connect steps answered "+r.status);
- const doc=new DOMParser().parseFromString(await r.text(),"text/html");const blk=doc.querySelector('[data-block="connect-ai"]')||doc;
+const DIAG=[];const mask=t=>String(t||"").replace(/\/onboard\/[A-Za-z0-9_-]+/g,"/onboard/•••").replace(/d2t_[A-Za-z0-9_-]+/g,"d2t_•••");
+const lineIn=(html,where)=>{const doc=new DOMParser().parseFromString(html,"text/html");const blk=doc.querySelector('[data-block="connect-ai"]');
- let line="";const pick=sel=>blk.querySelectorAll(sel).forEach(el=>{const t=(el.getAttribute("data-copy")||el.value||el.textContent||"").trim();
+ let line="";const pick=sel=>(blk||doc).querySelectorAll(sel).forEach(el=>{const t=(el.getAttribute("data-copy")||el.value||el.textContent||"").trim();
if(!line&&/\/onboard\/[A-Za-z0-9_-]{8,}/.test(t)&&t.length<400)line=t});
- pick("[data-copy],textarea,input,pre,code");if(!line){const m=/You (?:are|'re) the [^.]+?\.aiputty\.com[^<]*?\/onboard\/[A-Za-z0-9_-]+[^<]*?follow it\./.exec(blk.textContent||"");if(m)line=m[0]}
- if(!line)throw new Error("no line in the connect steps");return {line,expires:new Date(Date.now()+10*60e3).toISOString()}};
+ pick("[data-copy],textarea,input,pre,code");if(!line){const m=/[^\n.]*\/onboard\/[A-Za-z0-9_-]{8,}[^\n]*/.exec((blk||doc.body||doc).textContent||"");if(m)line=m[0].trim()}
+ DIAG.push(where+": block "+(blk?"found":"missing")+", line "+(line?"found":"missing")+(blk?" · "+mask((blk.textContent||"").replace(/\s+/g," ").trim()).slice(0,300):""));return line};
+const viaBlock=async()=>{
+ // 1) the steps as d2 draws them for this person, asked for (?connect=1)
+ let r=await fetch("/topics/Help:ConnectYourAi?connect=1",{credentials:"same-origin",cache:"no-store"});DIAG.push("GET "+r.status+" "+mask(r.url));
+ let line=r.ok?lineIn(await r.text(),"GET"):"";
+ // 2) the block's Connect button: POST /ai/invite {project}, then the page it lands on
+ if(!line){r=await fetch("/ai/invite",{method:"POST",credentials:"same-origin",cache:"no-store",headers:{"Content-Type":"application/x-www-form-urlencoded"},
+ body:new URLSearchParams({project:PROJECT,next:"/topics/Help:ConnectYourAi?connect=1#connect-ai"})});
+ DIAG.push("POST "+r.status+" "+mask(r.url));if(r.ok)line=lineIn(await r.text(),"POST")}
+ if(!line){const d=document.getElementById("diag");d.hidden=false;d.querySelector("pre").textContent=DIAG.join("\n");throw new Error("no line in the connect steps")}
+ return {line,expires:new Date(Date.now()+10*60e3).toISOString()}};
const make=async()=>{if(busy)return;busy=true;$("#lsay").textContent="";$("#ln").textContent="Making your line…";
try{let r;try{r=await api("POST","/api/v2/onboard/setup",{project:PROJECT,kind:"connect"})}catch(e){if(!/E_ARG|kind/.test(String(e&&e.message||e)))throw e;r=await viaBlock()}
…