austere-axi 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -135,7 +135,7 @@ npx -y austere-axi plan.html # validate and open the page for review
135
135
  npx -y austere-axi poll plan.html # wait until the reviewer presses Send
136
136
  ```
137
137
 
138
- `--help` prints the file skeleton and the full loop.
138
+ `--help` prints the file skeleton and the loop (about 900 tokens); `help all` adds the rarer commands and the environment. The agent does not look at the page itself: the reviewer judges how it looks, and render failures come back through `poll`.
139
139
 
140
140
  ## How it works
141
141
 
@@ -143,7 +143,7 @@ npx -y austere-axi poll plan.html # wait until the reviewer presses Send
143
143
  agent writes plan.html ──► austere-axi plan.html ──► local server + browser review
144
144
  │ reviewer annotates, picks options,
145
145
  │ types, presses Send
146
- agent edits statements ◄── austere-axi poll plan.html ◄─┘ (long-poll returns the batch)
146
+ agent patches changes ◄── austere-axi poll plan.html ◄─┘ (long-poll returns the batch)
147
147
  │
148
148
  └─► page updates in place and flashes the changed statements
149
149
  ```
@@ -170,7 +170,7 @@ austere-axi close <file> close the review: no more notes until reopen
170
170
  austere-axi reopen <file> reopen an ended or closed review
171
171
  austere-axi login [file] single-use link that signs a browser in as you (SSH, --no-open)
172
172
  austere-axi feedback <file> feedback no agent has answered yet [--all] [--peek]
173
- austere-axi check <file> validate the program (no browser) [--fix]
173
+ austere-axi check <file> validate the program (no browser) [--fix] [--render: headless Chrome, text report]
174
174
  austere-axi patch <file> merge statements from stdin into the program (reports dropped unreferenced ones)
175
175
  austere-axi spec [group|pack...] component reference (~5.8k tokens; groups narrow it)
176
176
  austere-axi components ... list | search | show | add | remove | new | build
@@ -183,7 +183,7 @@ austere-axi server --persist run the server in the foreground and never idle
183
183
  austere-axi mcp MCP server on stdio (see MCP)
184
184
  ```
185
185
 
186
- Output is compact YAML for agents. Each `poll` item includes its statement and line, so the agent does not re-read the file.
186
+ Output is compact YAML for agents. Each `poll` item has its statement and that statement's line and source, so the agent does not read the file again. Agents revise with `patch` only: they send the changed statements, never the whole file.
187
187
 
188
188
  ### Session rules
189
189
 
@@ -195,6 +195,7 @@ Output is compact YAML for agents. Each `poll` item includes its statement and l
195
195
  - A review the user ended or closed opens again only with `--reopen`.
196
196
  - **Tabs closed:** `poll` returns `browser_disconnected` 10 s after the last review tab closes, or 5 min after a dropped connection (for example, a locked phone). The review stays open, so later notes reach the next `poll`.
197
197
  - `feedback <file>` returns notes no agent has answered, from any day. Reading them acknowledges them; `reply` marks them answered.
198
+ - **Render failures reach the agent without a click:** bad Mermaid syntax, an unknown component or a component that throws. The owner's open review page sends them as a note from "Render check", once per page revision, and `poll` returns them as `render_error` items. With no review tab open, nothing renders, so nothing is sent. `check --render <file>` renders the page in headless Chrome (an installed Chrome, Chromium or Edge, or `CHROME_PATH`) at 1280 and 390 px and prints render errors and the statements that make the page scroll sideways, as text, with no screenshot. Agents run it only when the user asks for a check.
198
199
 
199
200
  ## Components and packs
200
201
 
@@ -361,6 +362,7 @@ Outputs: `ok` (`true` when every page passed) and `artifact-url`.
361
362
  | `AUSTERE_AXI_IDLE_TIMEOUT_MS` | `1800000` | idle shutdown; `0` disables |
362
363
  | `AUSTERE_AXI_PERSIST` | | `1`: never idle out, as `server --persist` |
363
364
  | `AUSTERE_AXI_NPM_REGISTRY` | `npm_config_registry`, else npmjs | where `components add` gets packs |
365
+ | `CHROME_PATH` | an installed Chrome, Chromium or Edge | the browser `check --render` uses |
364
366
 
365
367
  ## Security
366
368
 
package/dist/austere.js CHANGED
@@ -1607,11 +1607,11 @@ dataLane = Group("DATA STORES WITH A HEADING LONGER THAN THE COLUMN IS WIDE", [c
1607
1607
  <button type="button" data-act="queue" class="primary">Queue</button>
1608
1608
  </span>
1609
1609
  </div>
1610
- </div>`,document.body.appendChild(r);let n=r.querySelector(".austere-hover"),o=r.querySelector(".austere-hover-label"),i=r.querySelector(".austere-selbtn"),l=r.querySelector(".austere-pop"),s=r.querySelector(".austere-pop-path"),u=r.querySelector(".austere-pop-quote"),d=r.querySelector("textarea"),p=null,f=null,m=0,x=null,v=J=>J instanceof Element&&!!J.closest(`[${M9}]`),w=J=>J instanceof Element&&J.closest("[data-oui-id]")||null;function b(J,se,ue=4){J.style.left=`${se.left-ue}px`,J.style.top=`${se.top-ue}px`,J.style.width=`${se.width+ue*2}px`,J.style.height=`${se.height+ue*2}px`}function k(J){if(!J){n.hidden=!0;return}b(n,LX(J)),o.textContent=`${J.getAttribute("data-oui-id")} \xB7 ${J.getAttribute("data-oui-c")}`,n.hidden=!1}function I(J){a!=="annotate"||!l.hidden||(x=J.target,!m&&(m=requestAnimationFrame(()=>{m=0,!v(x)&&k(w(x))})))}function L(){let J=window.getSelection();return!!J&&!J.isCollapsed&&J.toString().trim().length>0}function _(J){if(a!=="annotate"||v(J.target))return;if(!l.hidden){J.preventDefault(),J.stopPropagation();return}if(L())return;let se=J.target;if(!(se instanceof Element)||!J.altKey&&se.closest(u$a))return;let ue=w(se);ue&&(J.preventDefault(),J.stopPropagation(),T({target:ue,el:se,point:{x:J.clientX,y:J.clientY}}))}function M(){if(a!=="annotate"||!l.hidden)return;let J=window.getSelection();if(!J||J.isCollapsed||!J.rangeCount){i.hidden=!0,f=null;return}let se=J.toString().trim(),ue=J.getRangeAt(0),Y=ue.commonAncestorContainer,Z=Y instanceof Element?Y:Y.parentElement,ne=w(Z);if(!se||!Z||!ne||v(Z)){i.hidden=!0,f=null;return}let me=ue.getBoundingClientRect();f={target:ne,el:Z,selection:kv(se,600),point:{x:me.left,y:me.bottom}},i.hidden=!1;let ve=i.offsetWidth||140;i.style.left=`${Math.max(8,Math.min(window.innerWidth-ve-8,me.left+me.width/2-ve/2))}px`,i.style.top=`${Math.max(8,Math.min(window.innerHeight-40,me.bottom+8))}px`}let A=0;function D(){clearTimeout(A),A=window.setTimeout(M,120)}i.addEventListener("mousedown",J=>J.preventDefault()),i.addEventListener("click",J=>{J.preventDefault(),J.stopPropagation(),f&&T(f),i.hidden=!0});function E(){let J=p;J&&(s.textContent="",J.path.forEach((se,ue)=>{let Y=document.createElement("button");if(Y.type="button",Y.className="austere-chip"+(se.id===J.targetId?" active":""),Y.textContent=se.id,Y.title=se.component,Y.addEventListener("click",()=>{J.targetId=se.id,J.component=se.component,E(),d.focus()}),s.appendChild(Y),ue<J.path.length-1){let Z=document.createElement("span");Z.className="austere-chip-sep",Z.textContent="\u203A",s.appendChild(Z)}}))}function T({target:J,el:se,selection:ue,point:Y}){let Z=d$a(J),ne=p$a(J,se),me=CEt(J,se),ve;if(!ue){let at=me||se;ve=kv((at instanceof HTMLElement?at.innerText:void 0)||at.textContent,200)||void 0,me&&ve===ne?.part&&(ve=void 0)}let Ie=_Et(J);p={targetId:Ie.id,component:Ie.component,path:Z,selection:ue||void 0,excerpt:ve,detail:ne},E();let Ze=ue||ve||ne?.part||"";u.textContent=kv(Ze,220),u.hidden=!Ze,d.value="",n.hidden=!0,i.hidden=!0,k(J),n.classList.add("locked"),l.hidden=!1,O(Y),d.focus()}function O(J){if(window.innerWidth<640){l.classList.add("sheet"),l.style.left=l.style.top="";return}l.classList.remove("sheet");let se=l.offsetWidth||360,ue=l.offsetHeight||200,Y=J.x+12,Z=J.y+12;Y+se>window.innerWidth-8&&(Y=Math.max(8,J.x-se-12)),Z+ue>window.innerHeight-8&&(Z=Math.max(8,J.y-ue-12)),l.style.left=`${Y}px`,l.style.top=`${Z}px`}function $(){l.hidden=!0,n.classList.remove("locked"),n.hidden=!0,p=null}function P(){let J=d.value.trim();if(!p)return;if(!J&&!p.selection){d.focus(),d.classList.add("shake"),setTimeout(()=>d.classList.remove("shake"),400);return}let se={kind:"annotation",target:p.targetId,component:p.component,path:p.path.map(Y=>Y.id),note:J};p.selection&&(se.selection=p.selection),p.excerpt&&!p.selection&&(se.excerpt=p.excerpt),p.detail&&p.targetId===p.path[p.path.length-1].id&&(se.detail=p.detail),e({type:"annotate",item:se});let ue=p.targetId;$(),window.getSelection()?.removeAllRanges(),re([ue],"queued")}l.addEventListener("click",J=>{let se=J.target instanceof Element?J.target.closest("[data-act]")?.getAttribute("data-act"):void 0;se==="cancel"&&$(),se==="queue"&&P()}),d.addEventListener("keydown",J=>{J.key==="Enter"&&!J.shiftKey&&!J.isComposing?(J.preventDefault(),P()):J.key==="Escape"&&(J.preventDefault(),d.value.trim()||$())}),document.addEventListener("mousemove",I,{passive:!0}),document.addEventListener("click",_,!0),document.addEventListener("mouseup",D),document.addEventListener("keyup",J=>{(J.shiftKey||J.key.startsWith("Arrow"))&&D()}),document.addEventListener("selectionchange",()=>{"ontouchstart"in window&&D()}),document.addEventListener("keydown",J=>{(J.metaKey||J.ctrlKey)&&(J.key==="i"||J.key==="I")&&(J.preventDefault(),e({type:"toggle-mode"}))}),window.addEventListener("scroll",()=>{l.hidden&&(n.hidden=!0,i.hidden=!0)},{passive:!0}),document.addEventListener("mouseleave",()=>{l.hidden&&(n.hidden=!0)});function q(J,se,ue,Y=new Set){if(Y.has(J))return[];Y.add(J);let Z=document.querySelectorAll(`[data-oui-id="${CSS.escape(J)}"]`);return Z.length?Array.from(Z):!se||!ue?[]:ue(se,J).flatMap(ne=>q(ne,se,ue,Y))}function H(J,se,ue){let Y=new Set;for(let Z of J)for(let ne of q(Z,se,ue))Y.add(ne);return Array.from(Y).filter(Z=>!Array.from(Y).some(ne=>ne!==Z&&ne.contains(Z)))}function W(J,se){let ue=document.createElement("div");return ue.setAttribute(M9,""),ue.className=se,ue.style.left=`${J.left+window.scrollX-4}px`,ue.style.top=`${J.top+window.scrollY-4}px`,ue.style.width=`${J.width+8}px`,ue.style.height=`${J.height+8}px`,ue}function X(J){if(getComputedStyle(J).display!=="contents")return J.getBoundingClientRect();let[se,ue,Y,Z]=[1/0,1/0,-1/0,-1/0],ne=ve=>{!ve.width&&!ve.height||([se,ue,Y,Z]=[Math.min(se,ve.left),Math.min(ue,ve.top),Math.max(Y,ve.right),Math.max(Z,ve.bottom)])},me=ve=>{for(let Ie of ve.childNodes)if(Ie instanceof Element)getComputedStyle(Ie).display==="contents"?me(Ie):ne(Ie.getBoundingClientRect());else if(Ie.nodeType===Node.TEXT_NODE&&Ie.textContent?.trim()){let Ze=document.createRange();Ze.selectNodeContents(Ie),ne(Ze.getBoundingClientRect())}};return me(J),{left:se,top:ue,right:Y,bottom:Z}}function ae(J){let{left:se,top:ue,right:Y,bottom:Z}=X(J);for(let ne=J.parentElement;ne&&ne!==document.body;ne=ne.parentElement){let me=getComputedStyle(ne),ve=me.overflowX!=="visible",Ie=me.overflowY!=="visible";if(!ve&&!Ie||me.display==="contents")continue;let Ze=ne.getBoundingClientRect();ve&&([se,Y]=[Math.max(se,Ze.left),Math.min(Y,Ze.right)]),Ie&&([ue,Z]=[Math.max(ue,Ze.top),Math.min(Z,Ze.bottom)])}return Y>se&&Z>ue?{left:se,top:ue,width:Y-se,height:Z-ue}:null}function re(J,se="changed",ue,Y){let Z=H(J,ue,Y);for(let ne of Z){let me=ae(ne);if(!me)continue;let ve=W(me,`austere-flash ${se}`);document.body.appendChild(ve),setTimeout(()=>ve.remove(),2400)}return Z}function F(J,se,ue){let Y=q(J,se,ue);if(!Y.length)return!1;let Z=LX(Y[0]),ne=Z.top+window.scrollY-Math.max(24,(window.innerHeight-Z.height)/3);return window.scrollTo({top:Math.max(0,ne),behavior:"smooth"}),setTimeout(()=>re([J],"changed",se,ue),350),!0}let le=document.createElement("div");le.setAttribute(M9,""),r.appendChild(le);let he=null,U=0;function Be(){U=0;let[J,se]=he&&t?t():[],ue=[],Y=[];for(let Z of he?H(he,J,se):[]){let ne=ae(Z);ne&&(ue.push(Z),Y.push(ne))}return le.replaceChildren(...Y.map(Z=>W(Z,"austere-mark"))),ue}let Fe=()=>{he&&!U&&(U=requestAnimationFrame(Be))};window.addEventListener("resize",Fe,{passive:!0}),new ResizeObserver(Fe).observe(document.body),new MutationObserver(J=>{J.some(se=>!v(se.target.nodeType===1?se.target:se.target.parentElement))&&Fe()}).observe(document.body,{childList:!0,subtree:!0,characterData:!0,attributes:!0,attributeFilter:["class","style","hidden","open"]}),document.addEventListener("scroll",J=>{J.target!==document&&Fe()},{capture:!0,passive:!0});function Oe(J,se=!1){he=J.length?J:null;let ue=Be();if(se&&ue.length){let Y=ue.map(LX);if(!Y.some(Z=>Z.bottom>0&&Z.top<window.innerHeight)){let Z=Y[0],ne=Z.top+window.scrollY-Math.max(24,(window.innerHeight-Z.height)/3);window.scrollTo({top:Math.max(0,ne),behavior:"smooth"})}}return ue.length}return{setMode(J){a=J==="annotate"?"annotate":"explore",document.documentElement.classList.toggle("austere-annotating",a==="annotate"),a!=="annotate"&&($(),i.hidden=!0,n.hidden=!0)},flash:re,reveal:F,highlight:Oe}}var NEt={mermaid:{name:"@austere-axi/pack-mermaid",version:"0.6.0",components:["Mermaid"]},shadcn:{name:"@austere-axi/pack-shadcn",version:"0.6.0",components:["Progress","Avatar","AvatarGroup","Tooltip","HoverCard","Breadcrumb","BreadcrumbItem","Kbd","Skeleton","Calendar","Timeline","TimelineItem"]}};function f$a(e){let t=e.lastIndexOf("@");return t<=0?{name:e,version:""}:{name:e.slice(0,t),version:e.slice(t+1)}}var m$a=/^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,h$a=/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;function TEt(e){let t=[];for(let a of String(e||"").split(",")){let[r,n]=a.trim().split(/\s+/);if(!r)continue;let{name:o,version:i}=f$a(r);!m$a.test(o)||!h$a.test(i)||t.some(l=>l.name===o)||t.push(n&&/^sha(256|384|512)-[A-Za-z0-9+/=]+$/.test(n)?{name:o,version:i,integrity:n}:{name:o,version:i})}return t}var zEt="https://cdn.jsdelivr.net/npm";function REt(e=document){let t=e.querySelector('meta[name="austere-packs"]');return t?TEt(t.getAttribute("content")||""):[]}function EEt(e,t){let a=[...t];for(let[r,n]of Object.entries(NEt))a.some(o=>o.name===n.name)||n.components.some(o=>new RegExp(`\\b${o}\\s*\\(`).test(e))&&a.push({name:n.name,version:n.version,bundled:r});return a}function MX(){return/\/__austere\/$/.test(Lg)}function g$a(e){if(e.bundled)return{src:j4(`packs/${e.bundled}/pack.js`)||`${zEt}/austere-axi@${e.version}/dist/packs/${e.bundled}/pack.js`};if(MX()){let t=e.integrity?`?integrity=${encodeURIComponent(e.integrity)}`:"";return{src:`${Lg}installed-packs/${encodeURIComponent(`${e.name}@${e.version}`)}/pack.js${t}`}}return{src:`${zEt}/${e.name}@${e.version}/dist/pack.js`,integrity:e.integrity}}function y$a(e,t){return new Promise((a,r)=>{let n=document.createElement("script");n.src=e,n.async=!0,t&&(n.integrity=t,n.crossOrigin="anonymous"),n.onload=()=>a(),n.onerror=()=>r(new Error(`could not load ${e}`)),document.head.appendChild(n)})}async function DEt(e,{onError:t}={}){let a=e.filter(r=>!xs().some(n=>n.name===r.name));return await Promise.all(a.map(r=>{let{src:n,integrity:o}=g$a(r);return y$a(n,o).then(()=>{xs().some(i=>i.name===r.name)||t?.(r,new Error(`${n} loaded but did not register pack ${r.name}`))},i=>t?.(r,i))})),xs()}function OEt(e){for(let t of e){if(!t.css)continue;let a=`austere-pack-css-${t.name.replace(/[^a-z0-9]/gi,"-")}`;if(document.getElementById(a))continue;let r=document.createElement("style");r.id=a,r.textContent=t.css,document.head.appendChild(r)}}var x$a=/[A-Za-z_$]/;function v$a(e){let t=[],a=String(e??""),r=0,n=1,o=a.length;for(;r<o;){let i=a[r];if(i===`
1610
+ </div>`,document.body.appendChild(r);let n=r.querySelector(".austere-hover"),o=r.querySelector(".austere-hover-label"),i=r.querySelector(".austere-selbtn"),l=r.querySelector(".austere-pop"),s=r.querySelector(".austere-pop-path"),u=r.querySelector(".austere-pop-quote"),d=r.querySelector("textarea"),p=null,f=null,m=0,x=null,v=J=>J instanceof Element&&!!J.closest(`[${M9}]`),w=J=>J instanceof Element&&J.closest("[data-oui-id]")||null;function b(J,se,ue=4){J.style.left=`${se.left-ue}px`,J.style.top=`${se.top-ue}px`,J.style.width=`${se.width+ue*2}px`,J.style.height=`${se.height+ue*2}px`}function k(J){if(!J){n.hidden=!0;return}b(n,LX(J)),o.textContent=`${J.getAttribute("data-oui-id")} \xB7 ${J.getAttribute("data-oui-c")}`,n.hidden=!1}function I(J){a!=="annotate"||!l.hidden||(x=J.target,!m&&(m=requestAnimationFrame(()=>{m=0,!v(x)&&k(w(x))})))}function L(){let J=window.getSelection();return!!J&&!J.isCollapsed&&J.toString().trim().length>0}function _(J){if(a!=="annotate"||v(J.target))return;if(!l.hidden){J.preventDefault(),J.stopPropagation();return}if(L())return;let se=J.target;if(!(se instanceof Element)||!J.altKey&&se.closest(u$a))return;let ue=w(se);ue&&(J.preventDefault(),J.stopPropagation(),T({target:ue,el:se,point:{x:J.clientX,y:J.clientY}}))}function M(){if(a!=="annotate"||!l.hidden)return;let J=window.getSelection();if(!J||J.isCollapsed||!J.rangeCount){i.hidden=!0,f=null;return}let se=J.toString().trim(),ue=J.getRangeAt(0),Y=ue.commonAncestorContainer,Z=Y instanceof Element?Y:Y.parentElement,ne=w(Z);if(!se||!Z||!ne||v(Z)){i.hidden=!0,f=null;return}let me=ue.getBoundingClientRect();f={target:ne,el:Z,selection:kv(se,600),point:{x:me.left,y:me.bottom}},i.hidden=!1;let ve=i.offsetWidth||140;i.style.left=`${Math.max(8,Math.min(window.innerWidth-ve-8,me.left+me.width/2-ve/2))}px`,i.style.top=`${Math.max(8,Math.min(window.innerHeight-40,me.bottom+8))}px`}let A=0;function D(){clearTimeout(A),A=window.setTimeout(M,120)}i.addEventListener("mousedown",J=>J.preventDefault()),i.addEventListener("click",J=>{J.preventDefault(),J.stopPropagation(),f&&T(f),i.hidden=!0});function E(){let J=p;J&&(s.textContent="",J.path.forEach((se,ue)=>{let Y=document.createElement("button");if(Y.type="button",Y.className="austere-chip"+(se.id===J.targetId?" active":""),Y.textContent=se.id,Y.title=se.component,Y.addEventListener("click",()=>{J.targetId=se.id,J.component=se.component,E(),d.focus()}),s.appendChild(Y),ue<J.path.length-1){let Z=document.createElement("span");Z.className="austere-chip-sep",Z.textContent="\u203A",s.appendChild(Z)}}))}function T({target:J,el:se,selection:ue,point:Y}){let Z=d$a(J),ne=p$a(J,se),me=CEt(J,se),ve;if(!ue){let at=me||se;ve=kv((at instanceof HTMLElement?at.innerText:void 0)||at.textContent,200)||void 0,me&&ve===ne?.part&&(ve=void 0)}let Ie=_Et(J);p={targetId:Ie.id,component:Ie.component,path:Z,selection:ue||void 0,excerpt:ve,detail:ne},E();let Ze=ue||ve||ne?.part||"";u.textContent=kv(Ze,220),u.hidden=!Ze,d.value="",n.hidden=!0,i.hidden=!0,k(J),n.classList.add("locked"),l.hidden=!1,O(Y),d.focus()}function O(J){if(window.innerWidth<640){l.classList.add("sheet"),l.style.left=l.style.top="";return}l.classList.remove("sheet");let se=l.offsetWidth||360,ue=l.offsetHeight||200,Y=J.x+12,Z=J.y+12;Y+se>window.innerWidth-8&&(Y=Math.max(8,J.x-se-12)),Z+ue>window.innerHeight-8&&(Z=Math.max(8,J.y-ue-12)),l.style.left=`${Y}px`,l.style.top=`${Z}px`}function $(){l.hidden=!0,n.classList.remove("locked"),n.hidden=!0,p=null}function P(){let J=d.value.trim();if(!p)return;if(!J&&!p.selection){d.focus(),d.classList.add("shake"),setTimeout(()=>d.classList.remove("shake"),400);return}let se={kind:"annotation",target:p.targetId,component:p.component,path:p.path.map(Y=>Y.id),note:J};p.selection&&(se.selection=p.selection),p.excerpt&&!p.selection&&(se.excerpt=p.excerpt),p.detail&&p.targetId===p.path[p.path.length-1].id&&(se.detail=p.detail),e({type:"annotate",item:se});let ue=p.targetId;$(),window.getSelection()?.removeAllRanges(),re([ue],"queued")}l.addEventListener("click",J=>{let se=J.target instanceof Element?J.target.closest("[data-act]")?.getAttribute("data-act"):void 0;se==="cancel"&&$(),se==="queue"&&P()}),d.addEventListener("keydown",J=>{J.key==="Enter"&&!J.shiftKey&&!J.isComposing?(J.preventDefault(),P()):J.key==="Escape"&&(J.preventDefault(),d.value.trim()||$())}),document.addEventListener("mousemove",I,{passive:!0}),document.addEventListener("click",_,!0),document.addEventListener("mouseup",D),document.addEventListener("keyup",J=>{(J.shiftKey||J.key.startsWith("Arrow"))&&D()}),document.addEventListener("selectionchange",()=>{"ontouchstart"in window&&D()}),document.addEventListener("keydown",J=>{(J.metaKey||J.ctrlKey)&&(J.key==="i"||J.key==="I")&&(J.preventDefault(),e({type:"toggle-mode"}))}),window.addEventListener("scroll",()=>{l.hidden&&(n.hidden=!0,i.hidden=!0)},{passive:!0}),document.addEventListener("mouseleave",()=>{l.hidden&&(n.hidden=!0)});function q(J,se,ue,Y=new Set){if(Y.has(J))return[];Y.add(J);let Z=document.querySelectorAll(`[data-oui-id="${CSS.escape(J)}"]`);return Z.length?Array.from(Z):!se||!ue?[]:ue(se,J).flatMap(ne=>q(ne,se,ue,Y))}function H(J,se,ue){let Y=new Set;for(let Z of J)for(let ne of q(Z,se,ue))Y.add(ne);return Array.from(Y).filter(Z=>!Array.from(Y).some(ne=>ne!==Z&&ne.contains(Z)))}function W(J,se){let ue=document.createElement("div");return ue.setAttribute(M9,""),ue.className=se,ue.style.left=`${J.left+window.scrollX-4}px`,ue.style.top=`${J.top+window.scrollY-4}px`,ue.style.width=`${J.width+8}px`,ue.style.height=`${J.height+8}px`,ue}function X(J){if(getComputedStyle(J).display!=="contents")return J.getBoundingClientRect();let[se,ue,Y,Z]=[1/0,1/0,-1/0,-1/0],ne=ve=>{!ve.width&&!ve.height||([se,ue,Y,Z]=[Math.min(se,ve.left),Math.min(ue,ve.top),Math.max(Y,ve.right),Math.max(Z,ve.bottom)])},me=ve=>{for(let Ie of ve.childNodes)if(Ie instanceof Element)getComputedStyle(Ie).display==="contents"?me(Ie):ne(Ie.getBoundingClientRect());else if(Ie.nodeType===Node.TEXT_NODE&&Ie.textContent?.trim()){let Ze=document.createRange();Ze.selectNodeContents(Ie),ne(Ze.getBoundingClientRect())}};return me(J),{left:se,top:ue,right:Y,bottom:Z}}function ae(J){let{left:se,top:ue,right:Y,bottom:Z}=X(J);for(let ne=J.parentElement;ne&&ne!==document.body;ne=ne.parentElement){let me=getComputedStyle(ne),ve=me.overflowX!=="visible",Ie=me.overflowY!=="visible";if(!ve&&!Ie||me.display==="contents")continue;let Ze=ne.getBoundingClientRect();ve&&([se,Y]=[Math.max(se,Ze.left),Math.min(Y,Ze.right)]),Ie&&([ue,Z]=[Math.max(ue,Ze.top),Math.min(Z,Ze.bottom)])}return Y>se&&Z>ue?{left:se,top:ue,width:Y-se,height:Z-ue}:null}function re(J,se="changed",ue,Y){let Z=H(J,ue,Y);for(let ne of Z){let me=ae(ne);if(!me)continue;let ve=W(me,`austere-flash ${se}`);document.body.appendChild(ve),setTimeout(()=>ve.remove(),2400)}return Z}function F(J,se,ue){let Y=q(J,se,ue);if(!Y.length)return!1;let Z=LX(Y[0]),ne=Z.top+window.scrollY-Math.max(24,(window.innerHeight-Z.height)/3);return window.scrollTo({top:Math.max(0,ne),behavior:"smooth"}),setTimeout(()=>re([J],"changed",se,ue),350),!0}let le=document.createElement("div");le.setAttribute(M9,""),r.appendChild(le);let he=null,U=0;function Be(){U=0;let[J,se]=he&&t?t():[],ue=[],Y=[];for(let Z of he?H(he,J,se):[]){let ne=ae(Z);ne&&(ue.push(Z),Y.push(ne))}return le.replaceChildren(...Y.map(Z=>W(Z,"austere-mark"))),ue}let Fe=()=>{he&&!U&&(U=requestAnimationFrame(Be))};window.addEventListener("resize",Fe,{passive:!0}),new ResizeObserver(Fe).observe(document.body),new MutationObserver(J=>{J.some(se=>!v(se.target.nodeType===1?se.target:se.target.parentElement))&&Fe()}).observe(document.body,{childList:!0,subtree:!0,characterData:!0,attributes:!0,attributeFilter:["class","style","hidden","open"]}),document.addEventListener("scroll",J=>{J.target!==document&&Fe()},{capture:!0,passive:!0});function Oe(J,se=!1){he=J.length?J:null;let ue=Be();if(se&&ue.length){let Y=ue.map(LX);if(!Y.some(Z=>Z.bottom>0&&Z.top<window.innerHeight)){let Z=Y[0],ne=Z.top+window.scrollY-Math.max(24,(window.innerHeight-Z.height)/3);window.scrollTo({top:Math.max(0,ne),behavior:"smooth"})}}return ue.length}return{setMode(J){a=J==="annotate"?"annotate":"explore",document.documentElement.classList.toggle("austere-annotating",a==="annotate"),a!=="annotate"&&($(),i.hidden=!0,n.hidden=!0)},flash:re,reveal:F,highlight:Oe}}var NEt={mermaid:{name:"@austere-axi/pack-mermaid",version:"0.7.1",components:["Mermaid"]},shadcn:{name:"@austere-axi/pack-shadcn",version:"0.7.1",components:["Progress","Avatar","AvatarGroup","Tooltip","HoverCard","Breadcrumb","BreadcrumbItem","Kbd","Skeleton","Calendar","Timeline","TimelineItem"]}};function f$a(e){let t=e.lastIndexOf("@");return t<=0?{name:e,version:""}:{name:e.slice(0,t),version:e.slice(t+1)}}var m$a=/^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,h$a=/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;function TEt(e){let t=[];for(let a of String(e||"").split(",")){let[r,n]=a.trim().split(/\s+/);if(!r)continue;let{name:o,version:i}=f$a(r);!m$a.test(o)||!h$a.test(i)||t.some(l=>l.name===o)||t.push(n&&/^sha(256|384|512)-[A-Za-z0-9+/=]+$/.test(n)?{name:o,version:i,integrity:n}:{name:o,version:i})}return t}var zEt="https://cdn.jsdelivr.net/npm";function REt(e=document){let t=e.querySelector('meta[name="austere-packs"]');return t?TEt(t.getAttribute("content")||""):[]}function EEt(e,t){let a=[...t];for(let[r,n]of Object.entries(NEt))a.some(o=>o.name===n.name)||n.components.some(o=>new RegExp(`\\b${o}\\s*\\(`).test(e))&&a.push({name:n.name,version:n.version,bundled:r});return a}function MX(){return/\/__austere\/$/.test(Lg)}function g$a(e){if(e.bundled)return{src:j4(`packs/${e.bundled}/pack.js`)||`${zEt}/austere-axi@${e.version}/dist/packs/${e.bundled}/pack.js`};if(MX()){let t=e.integrity?`?integrity=${encodeURIComponent(e.integrity)}`:"";return{src:`${Lg}installed-packs/${encodeURIComponent(`${e.name}@${e.version}`)}/pack.js${t}`}}return{src:`${zEt}/${e.name}@${e.version}/dist/pack.js`,integrity:e.integrity}}function y$a(e,t){return new Promise((a,r)=>{let n=document.createElement("script");n.src=e,n.async=!0,t&&(n.integrity=t,n.crossOrigin="anonymous"),n.onload=()=>a(),n.onerror=()=>r(new Error(`could not load ${e}`)),document.head.appendChild(n)})}async function DEt(e,{onError:t}={}){let a=e.filter(r=>!xs().some(n=>n.name===r.name));return await Promise.all(a.map(r=>{let{src:n,integrity:o}=g$a(r);return y$a(n,o).then(()=>{xs().some(i=>i.name===r.name)||t?.(r,new Error(`${n} loaded but did not register pack ${r.name}`))},i=>t?.(r,i))})),xs()}function OEt(e){for(let t of e){if(!t.css)continue;let a=`austere-pack-css-${t.name.replace(/[^a-z0-9]/gi,"-")}`;if(document.getElementById(a))continue;let r=document.createElement("style");r.id=a,r.textContent=t.css,document.head.appendChild(r)}}var x$a=/[A-Za-z_$]/;function v$a(e){let t=[],a=String(e??""),r=0,n=1,o=a.length;for(;r<o;){let i=a[r];if(i===`
1611
1611
  `){n++,r++;continue}if(i===" "||i===" "||i==="\r"){r++;continue}if(i==="#"||i==="/"&&a[r+1]==="/"){for(;r<o&&a[r]!==`
1612
1612
  `;)r++;continue}let l=r,s=n,u=0,d=null,p=!1;for(;r<o;){let x=a[r];if(d){p?p=!1:x==="\\"?p=!0:x===d&&(d=null),x===`
1613
1613
  `&&n++,r++;continue}if(x==='"'||x==="'"){d=x,r++;continue}if(x==="#"||x==="/"&&a[r+1]==="/"){for(;r<o&&a[r]!==`
1614
1614
  `;)r++;continue}if(x==="("||x==="["||x==="{")u++;else if(x===")"||x==="]"||x==="}")u=Math.max(0,u-1);else if(x===`
1615
- `){if(u===0&&!b$a(a,r+1))break;n++}r++}let f=a.slice(l,r).replace(/\s+$/,""),m=/^\s*([A-Za-z_$][\w$]*)\s*=/.exec(f);m&&x$a.test(m[1][0])&&t.push({id:m[1],text:f,line:s,endLine:n})}return t}function b$a(e,t){let a=t;for(;a<e.length&&(e[a]===" "||e[a]===" ");)a++;return e[a]==="?"||e[a]===":"}function ah(e){let t=new Map;for(let a of v$a(e))t.set(a.id,a);return t}function $Et(e,t){let a=ah(e),r=ah(t),n=[];for(let[o,i]of r){let l=a.get(o);(!l||PEt(l.text)!==PEt(i.text))&&n.push(o)}return n}function _9(e,t){let a=new RegExp(`(^|[^\\w$])${w$a(t)}(?![\\w$])`),r=[];for(let[n,o]of e){if(n===t)continue;let i=o.text.slice(o.text.indexOf("=")+1);a.test(k$a(i))&&r.push(n)}return r}function k$a(e){return e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/gs,'""')}function PEt(e){return e.replace(/\s+/g," ").trim()}function w$a(e){return e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&")}var Iv=oe(kt(),1),AX="0.6.0",I$a='script[type="text/openui"], script[type="text/openui-lang"]';function S$a(){if(document.getElementById("austere-styles"))return;let e=document.createElement("style");if(e.id="austere-styles",e.textContent=[Noe,_oe,Coe,Aoe].join(`
1615
+ `){if(u===0&&!b$a(a,r+1))break;n++}r++}let f=a.slice(l,r).replace(/\s+$/,""),m=/^\s*([A-Za-z_$][\w$]*)\s*=/.exec(f);m&&x$a.test(m[1][0])&&t.push({id:m[1],text:f,line:s,endLine:n})}return t}function b$a(e,t){let a=t;for(;a<e.length&&(e[a]===" "||e[a]===" ");)a++;return e[a]==="?"||e[a]===":"}function ah(e){let t=new Map;for(let a of v$a(e))t.set(a.id,a);return t}function $Et(e,t){let a=ah(e),r=ah(t),n=[];for(let[o,i]of r){let l=a.get(o);(!l||PEt(l.text)!==PEt(i.text))&&n.push(o)}return n}function _9(e,t){let a=new RegExp(`(^|[^\\w$])${w$a(t)}(?![\\w$])`),r=[];for(let[n,o]of e){if(n===t)continue;let i=o.text.slice(o.text.indexOf("=")+1);a.test(k$a(i))&&r.push(n)}return r}function k$a(e){return e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/gs,'""')}function PEt(e){return e.replace(/\s+/g," ").trim()}function w$a(e){return e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&")}var Iv=oe(kt(),1),AX="0.7.1",I$a='script[type="text/openui"], script[type="text/openui-lang"]';function S$a(){if(document.getElementById("austere-styles"))return;let e=document.createElement("style");if(e.id="austere-styles",e.textContent=[Noe,_oe,Coe,Aoe].join(`
1616
1616
  `),document.head.appendChild(e),!document.querySelector('meta[name="viewport"]')){let t=document.createElement("meta");t.name="viewport",t.content="width=device-width, initial-scale=1",document.head.appendChild(t)}document.documentElement.classList.add("austere"),document.documentElement.style.colorScheme="dark"}function jEt(e){if(!e||typeof e!="object")return;let t={},a=Object.entries(e);for(let[r,n]of a)if(!r.startsWith("$"))if(n&&typeof n=="object"&&!Array.isArray(n))if("value"in n&&Object.keys(n).length<=3)t[r]=n.value;else{let o=jEt(n);o&&Object.keys(o).length&&(t[r]=o)}else t[r]=n;return Object.keys(t).length?t:void 0}function L$a(e){return!!e&&typeof e=="object"&&"austere"in e&&e.austere===1&&"type"in e&&typeof e.type=="string"}function M$a(){let e=window.parent&&window.parent!==window,t=new Map,a=r=>{e&&window.parent.postMessage({austere:1,...r},"*")};return window.addEventListener("message",r=>{if(r.source!==window.parent)return;let n=r.data;L$a(n)&&t.get(n.type)?.forEach(o=>o(n))}),{framed:e,connected:!1,send:a,on(r,n){let o=n,i=t.get(r)??new Set;return t.set(r,i),i.add(o),()=>{i.delete(o)}}}}function HEt(e,t){e.framed?e.send({type:"open-url",url:t}):window.open(t,"_blank","noopener")}function _X(e){let t=document.createElement("div");t.className="austere-toast",t.setAttribute("data-austere-ui",""),t.textContent=e,document.body.appendChild(t),setTimeout(()=>t.classList.add("out"),2600),setTimeout(()=>t.remove(),3100)}function _$a(e){let t=[e.message];return e.form&&t.push(`form: ${JSON.stringify(e.form)}`),e.context!==void 0&&t.push(`context: ${JSON.stringify(e.context)}`),t.join(`
1617
1617
  `)}var CX=null,UEt=new Set,wv=bv,BEt="";function C$a(e,t){UEt.add(e.name);let a=!e.bundled&&MX()?" (`austere-axi check <file>` says why)":"";Ec({source:"pack",component:e.name,message:`pack ${e.name}@${e.version}: ${t.message}${a}`})}function A$a(){let e=xs(),t=e.map(a=>`${a.name}@${a.version}`).join(",");if(t===BEt)return wv;BEt=t,OEt(e);try{wv=e.length?SX(e.flatMap(a=>a.components),{onSkip:(a,r)=>Ec({source:"pack",component:a.name,message:r})}):bv}catch(a){Ec({source:"pack",message:`packs could not be added (${a.message}); rendering without them`}),wv=bv}return window.__austere.library=wv,wv}async function VEt(e){CX||(CX=REt());let t=EEt(e,CX).filter(a=>!UEt.has(a.name));return t.some(a=>!xs().some(r=>r.name===a.name))&&await DEt(t,{onError:C$a}),A$a()}function N$a({programRef:e,initialLibrary:t,bridge:a,annotator:r,initialState:n}){let[o,i]=(0,$i.useState)({program:e.current,library:t}),l=o.program,s=(0,$i.useRef)(null);(0,$i.useEffect)(()=>a.on("source",async({program:f})=>{if(typeof f!="string"||f===e.current)return;let m=$Et(e.current,f);e.current=f,r?.highlight([]);let x=await VEt(f);e.current===f&&(s.current=m,i({program:f,library:x}))}),[a]),(0,$i.useEffect)(()=>a.on("reveal",({id:f})=>{r?.reveal(f,ah(e.current),_9)}),[a,r]),(0,$i.useEffect)(()=>{let f=s.current;if(!f)return;s.current=null;let m=setTimeout(()=>{let x=ah(e.current),v=f.filter(w=>w!=="root");r?.flash(v,"changed",x,_9),a.send({type:"changes",ids:v})},120);return()=>clearTimeout(m)},[l,r,a]);let u=(0,$i.useCallback)(f=>{if(f.type==="open_url"){let v=f.params?.url;typeof v=="string"&&/^(https?:|mailto:)/i.test(v)&&HEt(a,v);return}if(f.type!=="continue_conversation")return;let m={kind:"action",message:f.humanFriendlyMessage||"(button)"},x=jEt(f.formState);if(x&&(m.form=x),f.params?.context!==void 0&&(m.context=f.params.context),a.connected)a.send({type:"action",item:m}),_X("Queued for the agent \u2014 press Send when ready");else{let v=_$a(m);navigator.clipboard?.writeText(v).then(()=>_X("Copied \u2014 paste it to your agent"),()=>_X(v))}},[a]),d=(0,$i.useCallback)(f=>{a.send({type:"errors",errors:(f||[]).map(m=>({source:m.source,code:m.code,statementId:m.statementId,message:m.message,hint:m.hint}))})},[a]),p=(0,$i.useCallback)(f=>a.send({type:"state",state:f}),[a]);return(0,Iv.jsx)(Moe,{mode:"dark",children:(0,Iv.jsx)("main",{className:"austere-page",children:(0,Iv.jsx)(wg,{response:l,library:o.library,isStreaming:!1,onAction:u,onError:d,onStateUpdate:p,initialState:n})})})}function FEt(){S$a();let t=document.querySelector(I$a)?.textContent??"",a=document.createElement("div");a.id="austere-root",document.body.appendChild(a);let r=M$a(),n={current:t},o=null,i=null,l=VEt(t),s=f=>i||(i=l.then(m=>(0,qEt.createRoot)(a).render((0,Iv.jsx)(N$a,{programRef:n,initialLibrary:m,bridge:r,annotator:o,initialState:f}))));if(!r.framed){s(void 0);return}let u={program:null,map:new Map},p=AEt({post:f=>r.send(f),lookup:()=>(u.program!==n.current&&(u={program:n.current,map:ah(n.current)}),[u.map,_9])});o=p,r.on("highlight",({ids:f,scroll:m})=>{p.highlight(Array.isArray(f)?f.filter(x=>typeof x=="string"):[],!!m)}),document.addEventListener("click",f=>{if(f.defaultPrevented||f.button!==0)return;let m=f.target instanceof Element?f.target.closest("a[href]"):null;if(!(m instanceof HTMLAnchorElement)||!/^(https?|mailto):/i.test(m.href)||m.protocol===location.protocol&&m.host===location.host)return;let x=(m.getAttribute("target")||"").toLowerCase();!x||x==="_self"||(f.preventDefault(),HEt(r,m.href))}),iee(f=>r.send({type:"runtime-error",error:f})),r.on("init",async f=>{if(r.connected=!0,p.setMode(f.mode),await s(f.state||void 0),f.scrollY>0){let m=0,x=()=>{window.scrollTo(0,f.scrollY),Math.abs(window.scrollY-f.scrollY)>2&&++m<20&&setTimeout(x,50)};setTimeout(x,30)}r.send({type:"ready",title:document.title,version:AX})}),r.on("mode",f=>p.setMode(f.mode)),window.addEventListener("scroll",()=>r.send({type:"scroll",y:window.scrollY}),{passive:!0}),r.send({type:"hello",title:document.title,version:AX}),setTimeout(()=>s(void 0),600)}window.__austere={version:AX,library:bv,packs:xs};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",FEt,{once:!0}):FEt();})();
package/dist/catalog.json CHANGED
@@ -1 +1 @@
1
- {"format":1,"pack":"core","version":"0.6.0","groups":[{"name":"Layout","notes":["- A page is root = Stack([...sections], \"column\", \"l\"). Each section is a Card([CardHeader(title, subtitle), ...content]).","- Side by side: Stack([a, b], \"row\", \"m\", \"stretch\", \"start\", true). Cards in a row share the width.","- Tabs for alternative views, Accordion for detail the reader may skip, Steps for ordered procedures. Several blocks in one Card: label each with InlineHeader.","- Card children must match its list; anything else (Accordion, Tag, a Callout row) can sit in a Stack inside the Card, or directly in root's Stack."]},{"name":"Content","notes":["- MarkDownRenderer for prose, bullet lists and inline code; one statement per paragraph group so each is annotatable.","- Callout for the one thing the reader must not miss: a risk, a decision needed, a caveat.","- CodeBlock for a whole snippet. Image src may be a path relative to the artifact file.","- Architecture for systems: services, stores, queues, trust boundaries. Name every Node, Edge and Group. Over ~15 nodes, use lanes or split the diagram.","- lanes: for systems split by owner, repo or tier. Groups are the columns, left to right as listed; put every node in one, in reading order (plain groups for a heading without a frame). About 4 columns fit a laptop; wider ones scroll (check warns). Phones get the lanes stacked as bands. Pin row only to put boxes side by side or line them up.","- Diff for code changes: show the hunks that matter, not whole files. Hunk counts are recomputed, so only start lines matter.","- Mermaid for sequences, state machines, ER, gantt and quick flowcharts."]},{"name":"Data","notes":["- Table is COLUMN-oriented: Table([Col(\"Option\", names), Col(\"Effort\", efforts), Col(\"Risk\", risks)]). Col data may mix strings and components, e.g. [\"n/a\", Tag(\"high\", null, \"sm\", \"danger\")]. Cells are plain text (no markdown) and wrap; Col type \"number\" for figures.","- EntityList for key/value facts; OverviewCardBlock + OverviewCardItem + MetricIndicatorInline for a KPI row.","- EditableTable when the reviewer should correct or fill in values; edits arrive with the next @ToAssistant from its Form.","- Icon names are lucide kebab-case (circle-check, triangle-alert, git-branch)."]},{"name":"Charts","notes":["- Charts take column arrays: BarChart(labels, [Series(\"p50\", [..]), Series(\"p99\", [..])], \"grouped\", \"Week\", \"ms\").","- PieChart(labels, values) with plain number arrays."]},{"name":"Forms","notes":["- Form(name, buttons, fields). Define each FormControl as its own statement. Never nest a Form in a Form.","- To ask the reviewer to decide: OptionCards or RadioGroup inside a Form whose button is Button(\"Send decision\", @ToAssistant(\"Decision: rollout strategy\"), \"primary\"). The values arrive with that message as form: {<formName>: {<fieldName>: value}}; no context needed. Reviewers can always annotate and message, so add a notes field only when it belongs with the decision.","- OptionCards(\"pick\", \"single\", [OptionCard(\"a\", \"Option A\", \"why\", Icon(\"zap\")), OptionCard(\"b\", \"Option B\")], {required: true}, \"a\") — the last argument pre-selects.","- rules: {required: true, minLength: 10, ...}; the renderer shows the messages."]},{"name":"Buttons","notes":["- @ToAssistant(message, context?) queues a message to the agent; @OpenUrl(url) opens a link. A Button with no action sends its label.","- Use buttons for follow-ups the reviewer can pick in one click: \"Go with option B\", \"Split this into two PRs\"."]}],"components":[{"name":"Card","group":"Layout","signature":"Card(children: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Tabs | Stack | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[], variant?: \"card\" | \"sunk\" | \"clear\", direction?: \"row\" | \"column\", gap?: \"none\" | \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"2xl\", align?: \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\", justify?: \"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\", wrap?: boolean)","description":"Styled container. variant: \"card\" (default, elevated) | \"sunk\" (recessed) | \"clear\" (transparent). Always full width. Accepts all Stack flex params (default: direction \"column\"). Cards flex to share space in row/wrap layouts.","tier":"core","pack":"core","examples":[]},{"name":"CardHeader","group":"Layout","signature":"CardHeader(title?: string, subtitle?: string)","description":"Header with optional title and subtitle","tier":"core","pack":"core","examples":[]},{"name":"TextContent","group":"Content","signature":"TextContent(text: string, size?: \"small\" | \"default\" | \"large\" | \"small-heavy\" | \"large-heavy\")","description":"Text block. Supports markdown. Optional size: \"small\" | \"default\" | \"large\" | \"small-heavy\" | \"large-heavy\".","tier":"core","pack":"core","examples":[]},{"name":"MarkDownRenderer","group":"Content","signature":"MarkDownRenderer(textMarkdown: string, variant?: \"clear\" | \"card\" | \"sunk\")","description":"Renders markdown text with optional container variant","tier":"core","pack":"core","examples":[]},{"name":"Callout","group":"Content","signature":"Callout(variant: \"info\" | \"warning\" | \"error\" | \"success\" | \"neutral\", title: string, description: string, visible?: $binding<boolean>)","description":"Callout banner. Optional visible is a reactive $boolean — auto-dismisses after 3s by setting $visible to false.","tier":"core","pack":"core","examples":[]},{"name":"Image","group":"Content","signature":"Image(alt: string, src?: string)","description":"Image with alt text and optional URL","tier":"core","pack":"core","examples":[]},{"name":"CodeBlock","group":"Content","signature":"CodeBlock(language: string, codeString: string)","description":"Syntax-highlighted code block","tier":"core","pack":"core","examples":[]},{"name":"InlineHeader","group":"Layout","signature":"InlineHeader(heading: string, description?: string)","description":"Compact section heading with an optional one-line description, for use inside cards.","tier":"core","pack":"core","examples":[]},{"name":"Table","group":"Data","signature":"Table(columns: Col[])","description":"Data table — column-oriented. Each Col holds its own data array.","tier":"core","pack":"core","examples":[]},{"name":"Col","group":"Data","signature":"Col(label: string, data: any, type?: \"string\" | \"number\" | \"action\")","description":"Column definition — holds label + data array","tier":"core","pack":"core","examples":[]},{"name":"EditableTable","group":"Data","signature":"EditableTable(name?: string, columns?: {type: \"text\" | \"number\" | \"date-single\" | \"select\" | \"url\", key?: string, header?: string, width?: number, options?: {value: string, label: string}[]}[], data?: {id: string, values: (string | number)[]}[])","description":"Spreadsheet-like table whose cells the user can edit inline (text, number, url, date, select columns); edits are saved back as a form field","tier":"extended","pack":"core","examples":[]},{"name":"BarChart","group":"Charts","signature":"BarChart(labels: string[], series: Series[], variant?: \"grouped\" | \"stacked\", xLabel?: string, yLabel?: string, height?: number)","description":"Vertical bars; use for comparing values across categories with one or more series","tier":"core","pack":"core","examples":[]},{"name":"LineChart","group":"Charts","signature":"LineChart(labels: string[], series: Series[], variant?: \"linear\" | \"natural\" | \"step\", xLabel?: string, yLabel?: string, height?: number)","description":"Lines over categories; use for trends and continuous data over time","tier":"core","pack":"core","examples":[]},{"name":"AreaChart","group":"Charts","signature":"AreaChart(labels: string[], series: Series[], variant?: \"linear\" | \"natural\" | \"step\", xLabel?: string, yLabel?: string, height?: number)","description":"Filled area under lines; use for cumulative totals or volume trends over time","tier":"extended","pack":"core","examples":[]},{"name":"HorizontalBarChart","group":"Charts","signature":"HorizontalBarChart(labels: string[], series: Series[], variant?: \"grouped\" | \"stacked\", xLabel?: string, yLabel?: string)","description":"Horizontal bars; prefer when category labels are long or for ranked lists","tier":"extended","pack":"core","examples":[]},{"name":"Series","group":"Charts","signature":"Series(category: string, values: number[])","description":"One data series","tier":"core","pack":"core","examples":[]},{"name":"PieChart","group":"Charts","signature":"PieChart(labels: string[], values: number[], variant?: \"pie\" | \"donut\", appearance?: \"circular\" | \"semiCircular\")","description":"Circular slices; use plucked arrays: PieChart(data.categories, data.values)","tier":"core","pack":"core","examples":[]},{"name":"ScatterChart","group":"Charts","signature":"ScatterChart(datasets: ScatterSeries[], xLabel?: string, yLabel?: string)","description":"X/Y scatter plot; use for correlations, distributions, and clustering","tier":"extended","pack":"core","examples":[]},{"name":"ScatterSeries","group":"Charts","signature":"ScatterSeries(name: string, points: Point[])","description":"Named dataset","tier":"extended","pack":"core","examples":[]},{"name":"Point","group":"Charts","signature":"Point(x: number, y: number, z?: number)","description":"Data point with numeric coordinates","tier":"extended","pack":"core","examples":[]},{"name":"Form","group":"Forms","signature":"Form(name: string, buttons: Buttons, fields?: FormControl[])","description":"Form container with fields and explicit action buttons","tier":"core","pack":"core","examples":[]},{"name":"FormControl","group":"Forms","signature":"FormControl(label: string, input: Input | TextArea | Select | Slider | CheckBoxGroup | RadioGroup | Chips | OptionCards, hint?: string)","description":"Field with label, input component, and optional hint text","tier":"core","pack":"core","examples":[]},{"name":"Label","group":"Forms","signature":"Label(text: string)","description":"Text label","tier":"extended","pack":"core","examples":[]},{"name":"Input","group":"Forms","signature":"Input(name: string, placeholder?: string, type?: \"text\" | \"email\" | \"password\" | \"number\" | \"url\", rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"TextArea","group":"Forms","signature":"TextArea(name: string, placeholder?: string, rows?: number, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"Select","group":"Forms","signature":"Select(name: string, items: SelectItem[], placeholder?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>, size?: \"small\" | \"medium\" | \"large\")","description":"","tier":"core","pack":"core","examples":[]},{"name":"SelectItem","group":"Forms","signature":"SelectItem(value: string, label: string)","description":"Option for Select","tier":"core","pack":"core","examples":[]},{"name":"Slider","group":"Forms","signature":"Slider(name: string, variant: \"continuous\" | \"discrete\", min: number, max: number, step?: number, defaultValue?: number[], label?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<number[]>)","description":"Numeric slider input; supports continuous and discrete (stepped) variants","tier":"extended","pack":"core","examples":[]},{"name":"CheckBoxGroup","group":"Forms","signature":"CheckBoxGroup(name: string, items: CheckBoxItem[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<Record<string, boolean>>)","description":"","tier":"extended","pack":"core","examples":[]},{"name":"CheckBoxItem","group":"Forms","signature":"CheckBoxItem(label: string, description: string, name: string, defaultChecked?: boolean)","description":"","tier":"extended","pack":"core","examples":[]},{"name":"RadioGroup","group":"Forms","signature":"RadioGroup(name: string, items: RadioItem[], defaultValue?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"RadioItem","group":"Forms","signature":"RadioItem(label: string, description: string, value: string)","description":"","tier":"core","pack":"core","examples":[]},{"name":"SwitchGroup","group":"Forms","signature":"SwitchGroup(name: string, items: SwitchItem[], variant?: \"clear\" | \"card\" | \"sunk\", value?: $binding<Record<string, boolean>>)","description":"Group of switch toggles","tier":"extended","pack":"core","examples":[]},{"name":"SwitchItem","group":"Forms","signature":"SwitchItem(label?: string, description?: string, name: string, defaultChecked?: boolean)","description":"Individual switch toggle","tier":"extended","pack":"core","examples":[]},{"name":"ChipItem","group":"Forms","signature":"ChipItem(value: string, label: string, icon?: Icon, disabled?: boolean)","description":"A single selectable chip inside a Chips group, with a value, label and optional icon.","tier":"extended","pack":"core","examples":[]},{"name":"Chips","group":"Forms","signature":"Chips(name: string, type?: \"single\" | \"multiple\", items?: ChipItem[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, defaultValue?: string | string[])","description":"A form field of compact selectable chips for choosing one or many short options; the selection is stored under `name`.","tier":"extended","pack":"core","examples":[]},{"name":"OptionCard","group":"Forms","signature":"OptionCard(value: string, title: string, subtitle?: string, topContent?: Icon | Image, disabled?: boolean)","description":"A single selectable card inside an OptionCards group, with a value, title, optional subtitle and an optional Icon or Image on top.","tier":"core","pack":"core","examples":[]},{"name":"OptionCards","group":"Forms","signature":"OptionCards(name: string, type?: \"single\" | \"multiple\", items?: OptionCard[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, defaultValue?: string | string[])","description":"A form field of selectable cards (title, optional subtitle, optional icon or image) laid out in a responsive grid for choosing one or many options; the selection is stored under `name`.","tier":"core","pack":"core","examples":[]},{"name":"Button","group":"Buttons","signature":"Button(label: string, action?: ActionExpression, variant?: \"primary\" | \"secondary\" | \"tertiary\", type?: \"normal\" | \"destructive\", size?: \"extra-small\" | \"small\" | \"medium\" | \"large\")","description":"Clickable button","tier":"core","pack":"core","examples":[]},{"name":"Buttons","group":"Buttons","signature":"Buttons(buttons: Button[], direction?: \"row\" | \"column\")","description":"Group of Button components. direction: \"row\" (default) | \"column\".","tier":"core","pack":"core","examples":[]},{"name":"Stack","group":"Layout","signature":"Stack(children: any[], direction?: \"row\" | \"column\", gap?: \"none\" | \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"2xl\", align?: \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\", justify?: \"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\", wrap?: boolean)","description":"Flex container. direction: \"row\"|\"column\" (default \"column\"). gap: \"none\"|\"xs\"|\"s\"|\"m\"|\"l\"|\"xl\"|\"2xl\" (default \"m\"). align: \"start\"|\"center\"|\"end\"|\"stretch\"|\"baseline\". justify: \"start\"|\"center\"|\"end\"|\"between\"|\"around\"|\"evenly\".","tier":"core","pack":"core","examples":[]},{"name":"Tabs","group":"Layout","signature":"Tabs(items: TabItem[])","description":"Tabbed container","tier":"core","pack":"core","examples":[]},{"name":"TabItem","group":"Layout","signature":"TabItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[])","description":"value is unique id, trigger is tab label, content is array of components","tier":"core","pack":"core","examples":[]},{"name":"Accordion","group":"Layout","signature":"Accordion(items: AccordionItem[])","description":"Collapsible sections","tier":"core","pack":"core","examples":[]},{"name":"AccordionItem","group":"Layout","signature":"AccordionItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[])","description":"value is unique id, trigger is section title","tier":"core","pack":"core","examples":[]},{"name":"Steps","group":"Layout","signature":"Steps(items: StepsItem[])","description":"Step-by-step guide","tier":"core","pack":"core","examples":[]},{"name":"StepsItem","group":"Layout","signature":"StepsItem(title: string, details: string)","description":"title and details text for one step","tier":"core","pack":"core","examples":[]},{"name":"Separator","group":"Layout","signature":"Separator(orientation?: \"horizontal\" | \"vertical\", decorative?: boolean)","description":"Visual divider between content sections","tier":"core","pack":"core","examples":[]},{"name":"TagBlock","group":"Data","signature":"TagBlock(tags: string[], size?: \"sm\" | \"md\" | \"lg\")","description":"tags is an array of strings; optional size sm | md | lg","tier":"extended","pack":"core","examples":[]},{"name":"Tag","group":"Data","signature":"Tag(text: string, icon?: Icon, size?: \"sm\" | \"md\" | \"lg\", variant?: \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\")","description":"Styled tag/badge with optional Icon and variant","tier":"core","pack":"core","examples":[]},{"name":"Icon","group":"Data","signature":"Icon(name: string, category?: string)","description":"A lucide icon by kebab-case name (e.g. 'circle-check'). Optional category picks a topical fallback when the name doesn't resolve.","tier":"core","pack":"core","examples":[]},{"name":"EntityList","group":"Data","signature":"EntityList(rows?: {left: string, right: string, rightVariant?: \"text\" | \"number\"}[], size?: \"small\" | \"default\", header?: {left: string, right: string, rightVariant?: \"text\" | \"number\"}, footer?: {left: string, right: string, rightVariant?: \"text\" | \"number\"})","description":"Two-column key/value rows (left label, right value). size 'default' supports optional header and footer rows; rightVariant 'number' uses tabular numbers.","tier":"core","pack":"core","examples":[]},{"name":"Text","group":"Data","signature":"Text(variant?: \"text\" | \"number\", value: string, subtext?: string, subtextVariant?: \"text\" | \"number\" | \"metric\", size?: \"xs\" | \"sm\" | \"md\" | \"lg\")","description":"Plain text line with optional subtext. variant 'number' uses tabular number styling; subtextVariant 'metric' colors a leading +/- subtext green/red.","tier":"core","pack":"core","examples":[]},{"name":"BoldText","group":"Data","signature":"BoldText(variant?: \"text\" | \"number\", value: string, subtext?: string, subtextVariant?: \"text\" | \"number\" | \"metric\", size?: \"xs\" | \"sm\" | \"md\" | \"lg\")","description":"Emphasized (bold) text line with optional subtext. variant 'number' uses tabular number styling; subtextVariant 'metric' colors a leading +/- subtext green/red.","tier":"extended","pack":"core","examples":[]},{"name":"IconText","group":"Data","signature":"IconText(icon: Icon, iconVariant?: \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\" | \"inverted\" | \"filled\" | \"soft\", iconSize?: \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"sm\" | \"md\" | \"lg\", title: string, subtitle?: string, bold?: boolean, layout?: \"horizontal\" | \"vertical\")","description":"An icon badge with a title and optional subtitle, laid out horizontally or vertically. iconVariant sets the badge color.","tier":"extended","pack":"core","examples":[]},{"name":"MetricIndicatorInline","group":"Data","signature":"MetricIndicatorInline(value: string, subtext?: string, trend?: {direction: \"up\" | \"down\", value: number})","description":"Headline metric value with an optional +/- percentage trend and subtext, all on one line.","tier":"core","pack":"core","examples":[]},{"name":"OverviewCardItem","group":"Data","signature":"OverviewCardItem(id?: string, top: IconText | Text, bottom?: MetricIndicatorInline)","description":"One overview card: a heading slot at the top (IconText, ImageText or Text) and an optional MetricIndicatorInline at the bottom.","tier":"core","pack":"core","examples":[]},{"name":"OverviewCardBlock","group":"Data","signature":"OverviewCardBlock(items: OverviewCardItem[], layout?: \"grid\" | \"carousel\", responsive?: boolean, action?: ActionExpression, gap?: number | string)","description":"A grid or horizontal carousel of compact overview cards, each with a heading (icon/image/text) on top and an inline metric below; optionally clickable with a shared action.","tier":"core","pack":"core","examples":[]},{"name":"Architecture","group":"Content","signature":"Architecture(nodes: Node[], edges: Edge[], groups?: Group[], direction?: \"auto\" | \"right\" | \"down\" | \"lanes\")","description":"System diagram laid out automatically: write no coordinates. Every Node, Edge and Group is its own statement and click target. direction auto (default) runs right when that fits the page legibly, else down; lanes makes each top-level Group a column.","tier":"core","pack":"core","examples":["platformMap = Architecture([browser, edge, api, sessions, cache], [toEdge, toApi, readSessions, readCache], [vpc])\nbrowser = Node(\"Browser\", \"external\")\nedge = Node(\"Edge worker\", \"cloud\", \"Cloudflare\")\napi = Node(\"API\", \"backend\", \"Node :8080\")\nsessions = Node(\"Sessions\", \"database\", \"Postgres\", \"new\")\ncache = Node(\"Redis\", \"database\", \"read-through\")\ntoEdge = Edge(browser, edge, \"HTTPS\", \"emphasis\")\ntoApi = Edge(edge, api)\nreadSessions = Edge(api, sessions, \"SQL\")\nreadCache = Edge(api, cache, \"cache\", \"dashed\")\nvpc = Group(\"Platform VPC\", [api, sessions, cache])","ownership = Architecture([web, ordersApi, ledger, ordersDb, billingJob], [placeOrder, writeOrder, readOrders, postLedger], [clients, ordersRepo, billingRepo], \"lanes\")\nweb = Node(\"Web app\", \"frontend\")\nordersApi = Node(\"Orders API\", \"backend\", \"Node :8080\")\nordersDb = Node(\"orders table\", \"database\", \"Postgres\", null, \"success\")\nbillingJob = Node(\"Nightly billing\", \"backend\", \"cron 02:00\")\nledger = Node(\"Ledger\", \"database\", \"append-only\", null, \"info\")\nplaceOrder = Edge(web, ordersApi, \"POST /orders\")\nwriteOrder = Edge(ordersApi, ordersDb, \"insert\")\nreadOrders = Edge(billingJob, ordersDb, \"read unbilled\", null, \"danger\")\npostLedger = Edge(billingJob, ledger, \"post\")\nclients = Group(\"CLIENTS\", [web], \"plain\")\nordersRepo = Group(\"orders-service\", [ordersApi, ordersDb])\nbillingRepo = Group(\"billing\", [billingJob, ledger])"]},{"name":"Node","group":"Content","signature":"Node(label: string, kind?: \"frontend\" | \"backend\" | \"database\" | \"messagebus\" | \"cloud\" | \"security\" | \"external\", detail?: string, tag?: string, tone?: \"info\" | \"success\" | \"warning\" | \"danger\" | \"strong\", row?: number)","description":"A box in an Architecture. kind sets icon and color (default backend); detail is monospace, up to 2 lines (\\n breaks); tag is a small chip such as \"new\"; tone tints it (danger = risk, strong = where it ends); row pins it to a row (1, 2, ...) in lanes.","tier":"core","pack":"core","examples":[]},{"name":"Edge","group":"Content","signature":"Edge(from: Node, to: Node, label?: string, style?: \"solid\" | \"dashed\" | \"emphasis\" | \"security\", tone?: \"info\" | \"success\" | \"warning\" | \"danger\")","description":"An arrow between two Node statements (pass their names, not strings). style: dashed for async, emphasis for the main path, security for auth or trust flows. tone colors it, e.g. danger for a risky write, success for a read.","tier":"core","pack":"core","examples":[]},{"name":"Group","group":"Content","signature":"Group(label: string, members: Node[], kind?: \"region\" | \"security\" | \"plain\")","description":"A boundary around Node statements (a VPC, a cluster, a trust zone); members still sit where their edges put them. A group whose members are a subset of another's is drawn inside it; kind security draws it red, plain draws only its name.","tier":"core","pack":"core","examples":[]},{"name":"Diff","group":"Content","signature":"Diff(patch: string, title?: string)","description":"Unified diff (git diff output, one or more files) with line numbers. patch is a raw multi-line string. Clicked lines reach the agent as file:+line.","tier":"core","pack":"core","examples":["retryPatch = Diff(\"diff --git a/src/retry.ts b/src/retry.ts\n--- a/src/retry.ts\n+++ b/src/retry.ts\n@@ -10,2 +10,3 @@\n- for (let i = 0; i < 3; i++) {\n+ for (let i = 0; i < MAX_ATTEMPTS; i++) {\n+ if (i > 0) await sleep(backoff(i));\")"]},{"name":"FileTree","group":"Data","signature":"FileTree(entries: string[])","description":"File tree, one path per entry, optionally prefixed with a git status code (A new, M modified, D deleted, R old -> new) and suffixed with \" # note\". Folders are inferred. Clicked files and folders reach the agent by path.","tier":"core","pack":"core","examples":["changedFiles = FileTree([\"M src/session/store.ts # read-through to Postgres\", \"A src/session/pg-store.ts\", \"A migrations/0042_sessions.sql # table + expiry index\", \"R src/session/redis.ts -> src/session/redis-cache.ts\", \"D src/session/legacy.ts\", \"M src/config.ts # SESSIONS_PG flag\"])"]},{"name":"Mermaid","group":"Content","signature":"Mermaid(source: string, caption?: string)","description":"Diagram from Mermaid source (flowchart, sequence, state, class, ER, gantt). source is a raw multi-line string. Keep node labels to a few words; clicked nodes reach the agent by label.","tier":"core","pack":"@austere-axi/pack-mermaid","examples":["requestPath = Mermaid(\"flowchart LR\n client[Client] --> api[API]\n api --> pg[(Postgres)]\", \"Request path\")"]},{"name":"Progress","group":"Data","signature":"Progress(value: number, label?: string, max?: number)","description":"Progress bar. value counts toward max (default 100); label names what is progressing. Shows a percentage.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["migration = Progress(64, \"Rows migrated\")"]},{"name":"Avatar","group":"Data","signature":"Avatar(name: string, src?: string, size?: \"md\" | \"lg\")","description":"Round avatar for a person. Shows the initials of name; src is an optional https image URL.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["owner = Avatar(\"Ada Lovelace\")"]},{"name":"AvatarGroup","group":"Data","signature":"AvatarGroup(avatars: Avatar[], max?: number)","description":"Overlapping Avatars, for reviewers or owners. max (default 4) shows the rest as +N.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["team = AvatarGroup([ada, grace, linus], 2)\nada = Avatar(\"Ada Lovelace\")\ngrace = Avatar(\"Grace Hopper\")\nlinus = Avatar(\"Linus Torvalds\")"]},{"name":"Tooltip","group":"Content","signature":"Tooltip(text: string, tip: string)","description":"Inline text with a short hint on hover or focus (a term and its definition). Keep tip to one line.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["term = Tooltip(\"idempotent\", \"Safe to run twice with the same result.\")"]},{"name":"HoverCard","group":"Content","signature":"HoverCard(text: string, title: string, body?: string)","description":"Inline text that shows a small card (title and a line or two of body) on hover or focus, e.g. a person or a service.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["svc = HoverCard(\"billing-api\", \"billing-api\", \"Owns invoices and refunds. On call: payments team.\")"]},{"name":"Breadcrumb","group":"Content","signature":"Breadcrumb(items: BreadcrumbItem[])","description":"Path trail such as Repo / Package / File, from BreadcrumbItems. Each item is a click target.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["trail = Breadcrumb([repo, pkg, file])\nrepo = BreadcrumbItem(\"austere-axi\")\npkg = BreadcrumbItem(\"packs\")\nfile = BreadcrumbItem(\"shadcn\")"]},{"name":"BreadcrumbItem","group":"Content","signature":"BreadcrumbItem(label: string, href?: string)","description":"One step of a Breadcrumb. href is optional; the last item is the current page.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":[]},{"name":"Kbd","group":"Content","signature":"Kbd(keys: string)","description":"Keyboard shortcut. keys joins keys with \"+\", e.g. \"Ctrl+K\"; each key is drawn as a key cap.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["shortcut = Kbd(\"Ctrl+Shift+P\")"]},{"name":"Skeleton","group":"Content","signature":"Skeleton(lines?: number, shape?: \"lines\" | \"avatar\")","description":"Grey placeholder block for content not yet written or loaded (default 3 lines). shape avatar adds a round head.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["pending = Skeleton(3, \"avatar\")"]},{"name":"Calendar","group":"Data","signature":"Calendar(month: string, highlights?: string[])","description":"Read-only month grid (not a date picker). month is \"YYYY-MM\"; highlights lists \"YYYY-MM-DD\" or \"YYYY-MM-DD label\" entries to mark, e.g. a freeze or a release. Marked days are click targets.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["window = Calendar(\"2026-11\", [\"2026-11-04 Code freeze\", \"2026-11-12 Release\", \"2026-11-13\"])"]},{"name":"Timeline","group":"Content","signature":"Timeline(items: TimelineItem[])","description":"Vertical history or schedule of TimelineItems, oldest first. Each item is a click target.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["history = Timeline([found, fixed, rollout])\nfound = TimelineItem(\"Bug found\", \"Oct 28\", \"Retries double-charge on timeout.\")\nfixed = TimelineItem(\"Fix merged\", \"Oct 29\", \"Idempotency key added.\", \"current\")\nrollout = TimelineItem(\"Rollout\", \"Nov 3\", \"\", \"upcoming\")"]},{"name":"TimelineItem","group":"Content","signature":"TimelineItem(title: string, date?: string, detail?: string, state?: \"done\" | \"current\" | \"upcoming\")","description":"One event in a Timeline. state: done (default), current or upcoming.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":[]}]}
1
+ {"format":1,"pack":"core","version":"0.7.1","groups":[{"name":"Layout","notes":["- A page is root = Stack([...sections], \"column\", \"l\"). Each section is a Card([CardHeader(title, subtitle), ...content]).","- Side by side: Stack([a, b], \"row\", \"m\", \"stretch\", \"start\", true). Cards in a row share the width.","- Tabs for alternative views, Accordion for detail the reader may skip, Steps for ordered procedures. Several blocks in one Card: label each with InlineHeader.","- Card children must match its list; anything else (Accordion, Tag, a Callout row) can sit in a Stack inside the Card, or directly in root's Stack."]},{"name":"Content","notes":["- MarkDownRenderer for prose, bullet lists and inline code; one statement per paragraph group so each is annotatable.","- Callout for the one thing the reader must not miss: a risk, a decision needed, a caveat.","- CodeBlock for a whole snippet. Image src may be a path relative to the artifact file.","- Architecture for systems: services, stores, queues, trust boundaries. Name every Node, Edge and Group. Over ~15 nodes, use lanes or split the diagram.","- lanes: for systems split by owner, repo or tier. Groups are the columns, left to right as listed; put every node in one, in reading order (plain groups for a heading without a frame). About 4 columns fit a laptop; wider ones scroll (check warns). Phones get the lanes stacked as bands. Pin row only to put boxes side by side or line them up.","- Diff for code changes: show the hunks that matter, not whole files. Hunk counts are recomputed, so only start lines matter.","- Mermaid for sequences, state machines, ER, gantt and quick flowcharts."]},{"name":"Data","notes":["- Table is COLUMN-oriented: Table([Col(\"Option\", names), Col(\"Effort\", efforts), Col(\"Risk\", risks)]). Col data may mix strings and components, e.g. [\"n/a\", Tag(\"high\", null, \"sm\", \"danger\")]. Cells are plain text (no markdown) and wrap; Col type \"number\" for figures.","- EntityList for key/value facts; OverviewCardBlock + OverviewCardItem + MetricIndicatorInline for a KPI row.","- EditableTable when the reviewer should correct or fill in values; edits arrive with the next @ToAssistant from its Form.","- Icon names are lucide kebab-case (circle-check, triangle-alert, git-branch)."]},{"name":"Charts","notes":["- Charts take column arrays: BarChart(labels, [Series(\"p50\", [..]), Series(\"p99\", [..])], \"grouped\", \"Week\", \"ms\").","- PieChart(labels, values) with plain number arrays."]},{"name":"Forms","notes":["- Form(name, buttons, fields). Define each FormControl as its own statement. Never nest a Form in a Form.","- To ask the reviewer to decide: OptionCards or RadioGroup inside a Form whose button is Button(\"Send decision\", @ToAssistant(\"Decision: rollout strategy\"), \"primary\"). The values arrive with that message as form: {<formName>: {<fieldName>: value}}; no context needed. Reviewers can always annotate and message, so add a notes field only when it belongs with the decision.","- OptionCards(\"pick\", \"single\", [OptionCard(\"a\", \"Option A\", \"why\", Icon(\"zap\")), OptionCard(\"b\", \"Option B\")], {required: true}, \"a\") — the last argument pre-selects.","- rules: {required: true, minLength: 10, ...}; the renderer shows the messages."]},{"name":"Buttons","notes":["- @ToAssistant(message, context?) queues a message to the agent; @OpenUrl(url) opens a link. A Button with no action sends its label.","- Use buttons for follow-ups the reviewer can pick in one click: \"Go with option B\", \"Split this into two PRs\"."]}],"components":[{"name":"Card","group":"Layout","signature":"Card(children: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Tabs | Stack | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[], variant?: \"card\" | \"sunk\" | \"clear\", direction?: \"row\" | \"column\", gap?: \"none\" | \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"2xl\", align?: \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\", justify?: \"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\", wrap?: boolean)","description":"Styled container. variant: \"card\" (default, elevated) | \"sunk\" (recessed) | \"clear\" (transparent). Always full width. Accepts all Stack flex params (default: direction \"column\"). Cards flex to share space in row/wrap layouts.","tier":"core","pack":"core","examples":[]},{"name":"CardHeader","group":"Layout","signature":"CardHeader(title?: string, subtitle?: string)","description":"Header with optional title and subtitle","tier":"core","pack":"core","examples":[]},{"name":"TextContent","group":"Content","signature":"TextContent(text: string, size?: \"small\" | \"default\" | \"large\" | \"small-heavy\" | \"large-heavy\")","description":"Text block. Supports markdown. Optional size: \"small\" | \"default\" | \"large\" | \"small-heavy\" | \"large-heavy\".","tier":"core","pack":"core","examples":[]},{"name":"MarkDownRenderer","group":"Content","signature":"MarkDownRenderer(textMarkdown: string, variant?: \"clear\" | \"card\" | \"sunk\")","description":"Renders markdown text with optional container variant","tier":"core","pack":"core","examples":[]},{"name":"Callout","group":"Content","signature":"Callout(variant: \"info\" | \"warning\" | \"error\" | \"success\" | \"neutral\", title: string, description: string, visible?: $binding<boolean>)","description":"Callout banner. Optional visible is a reactive $boolean — auto-dismisses after 3s by setting $visible to false.","tier":"core","pack":"core","examples":[]},{"name":"Image","group":"Content","signature":"Image(alt: string, src?: string)","description":"Image with alt text and optional URL","tier":"core","pack":"core","examples":[]},{"name":"CodeBlock","group":"Content","signature":"CodeBlock(language: string, codeString: string)","description":"Syntax-highlighted code block","tier":"core","pack":"core","examples":[]},{"name":"InlineHeader","group":"Layout","signature":"InlineHeader(heading: string, description?: string)","description":"Compact section heading with an optional one-line description, for use inside cards.","tier":"core","pack":"core","examples":[]},{"name":"Table","group":"Data","signature":"Table(columns: Col[])","description":"Data table — column-oriented. Each Col holds its own data array.","tier":"core","pack":"core","examples":[]},{"name":"Col","group":"Data","signature":"Col(label: string, data: any, type?: \"string\" | \"number\" | \"action\")","description":"Column definition — holds label + data array","tier":"core","pack":"core","examples":[]},{"name":"EditableTable","group":"Data","signature":"EditableTable(name?: string, columns?: {type: \"text\" | \"number\" | \"date-single\" | \"select\" | \"url\", key?: string, header?: string, width?: number, options?: {value: string, label: string}[]}[], data?: {id: string, values: (string | number)[]}[])","description":"Spreadsheet-like table whose cells the user can edit inline (text, number, url, date, select columns); edits are saved back as a form field","tier":"extended","pack":"core","examples":[]},{"name":"BarChart","group":"Charts","signature":"BarChart(labels: string[], series: Series[], variant?: \"grouped\" | \"stacked\", xLabel?: string, yLabel?: string, height?: number)","description":"Vertical bars; use for comparing values across categories with one or more series","tier":"core","pack":"core","examples":[]},{"name":"LineChart","group":"Charts","signature":"LineChart(labels: string[], series: Series[], variant?: \"linear\" | \"natural\" | \"step\", xLabel?: string, yLabel?: string, height?: number)","description":"Lines over categories; use for trends and continuous data over time","tier":"core","pack":"core","examples":[]},{"name":"AreaChart","group":"Charts","signature":"AreaChart(labels: string[], series: Series[], variant?: \"linear\" | \"natural\" | \"step\", xLabel?: string, yLabel?: string, height?: number)","description":"Filled area under lines; use for cumulative totals or volume trends over time","tier":"extended","pack":"core","examples":[]},{"name":"HorizontalBarChart","group":"Charts","signature":"HorizontalBarChart(labels: string[], series: Series[], variant?: \"grouped\" | \"stacked\", xLabel?: string, yLabel?: string)","description":"Horizontal bars; prefer when category labels are long or for ranked lists","tier":"extended","pack":"core","examples":[]},{"name":"Series","group":"Charts","signature":"Series(category: string, values: number[])","description":"One data series","tier":"core","pack":"core","examples":[]},{"name":"PieChart","group":"Charts","signature":"PieChart(labels: string[], values: number[], variant?: \"pie\" | \"donut\", appearance?: \"circular\" | \"semiCircular\")","description":"Circular slices; use plucked arrays: PieChart(data.categories, data.values)","tier":"core","pack":"core","examples":[]},{"name":"ScatterChart","group":"Charts","signature":"ScatterChart(datasets: ScatterSeries[], xLabel?: string, yLabel?: string)","description":"X/Y scatter plot; use for correlations, distributions, and clustering","tier":"extended","pack":"core","examples":[]},{"name":"ScatterSeries","group":"Charts","signature":"ScatterSeries(name: string, points: Point[])","description":"Named dataset","tier":"extended","pack":"core","examples":[]},{"name":"Point","group":"Charts","signature":"Point(x: number, y: number, z?: number)","description":"Data point with numeric coordinates","tier":"extended","pack":"core","examples":[]},{"name":"Form","group":"Forms","signature":"Form(name: string, buttons: Buttons, fields?: FormControl[])","description":"Form container with fields and explicit action buttons","tier":"core","pack":"core","examples":[]},{"name":"FormControl","group":"Forms","signature":"FormControl(label: string, input: Input | TextArea | Select | Slider | CheckBoxGroup | RadioGroup | Chips | OptionCards, hint?: string)","description":"Field with label, input component, and optional hint text","tier":"core","pack":"core","examples":[]},{"name":"Label","group":"Forms","signature":"Label(text: string)","description":"Text label","tier":"extended","pack":"core","examples":[]},{"name":"Input","group":"Forms","signature":"Input(name: string, placeholder?: string, type?: \"text\" | \"email\" | \"password\" | \"number\" | \"url\", rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"TextArea","group":"Forms","signature":"TextArea(name: string, placeholder?: string, rows?: number, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"Select","group":"Forms","signature":"Select(name: string, items: SelectItem[], placeholder?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>, size?: \"small\" | \"medium\" | \"large\")","description":"","tier":"core","pack":"core","examples":[]},{"name":"SelectItem","group":"Forms","signature":"SelectItem(value: string, label: string)","description":"Option for Select","tier":"core","pack":"core","examples":[]},{"name":"Slider","group":"Forms","signature":"Slider(name: string, variant: \"continuous\" | \"discrete\", min: number, max: number, step?: number, defaultValue?: number[], label?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<number[]>)","description":"Numeric slider input; supports continuous and discrete (stepped) variants","tier":"extended","pack":"core","examples":[]},{"name":"CheckBoxGroup","group":"Forms","signature":"CheckBoxGroup(name: string, items: CheckBoxItem[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<Record<string, boolean>>)","description":"","tier":"extended","pack":"core","examples":[]},{"name":"CheckBoxItem","group":"Forms","signature":"CheckBoxItem(label: string, description: string, name: string, defaultChecked?: boolean)","description":"","tier":"extended","pack":"core","examples":[]},{"name":"RadioGroup","group":"Forms","signature":"RadioGroup(name: string, items: RadioItem[], defaultValue?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"RadioItem","group":"Forms","signature":"RadioItem(label: string, description: string, value: string)","description":"","tier":"core","pack":"core","examples":[]},{"name":"SwitchGroup","group":"Forms","signature":"SwitchGroup(name: string, items: SwitchItem[], variant?: \"clear\" | \"card\" | \"sunk\", value?: $binding<Record<string, boolean>>)","description":"Group of switch toggles","tier":"extended","pack":"core","examples":[]},{"name":"SwitchItem","group":"Forms","signature":"SwitchItem(label?: string, description?: string, name: string, defaultChecked?: boolean)","description":"Individual switch toggle","tier":"extended","pack":"core","examples":[]},{"name":"ChipItem","group":"Forms","signature":"ChipItem(value: string, label: string, icon?: Icon, disabled?: boolean)","description":"A single selectable chip inside a Chips group, with a value, label and optional icon.","tier":"extended","pack":"core","examples":[]},{"name":"Chips","group":"Forms","signature":"Chips(name: string, type?: \"single\" | \"multiple\", items?: ChipItem[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, defaultValue?: string | string[])","description":"A form field of compact selectable chips for choosing one or many short options; the selection is stored under `name`.","tier":"extended","pack":"core","examples":[]},{"name":"OptionCard","group":"Forms","signature":"OptionCard(value: string, title: string, subtitle?: string, topContent?: Icon | Image, disabled?: boolean)","description":"A single selectable card inside an OptionCards group, with a value, title, optional subtitle and an optional Icon or Image on top.","tier":"core","pack":"core","examples":[]},{"name":"OptionCards","group":"Forms","signature":"OptionCards(name: string, type?: \"single\" | \"multiple\", items?: OptionCard[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, defaultValue?: string | string[])","description":"A form field of selectable cards (title, optional subtitle, optional icon or image) laid out in a responsive grid for choosing one or many options; the selection is stored under `name`.","tier":"core","pack":"core","examples":[]},{"name":"Button","group":"Buttons","signature":"Button(label: string, action?: ActionExpression, variant?: \"primary\" | \"secondary\" | \"tertiary\", type?: \"normal\" | \"destructive\", size?: \"extra-small\" | \"small\" | \"medium\" | \"large\")","description":"Clickable button","tier":"core","pack":"core","examples":[]},{"name":"Buttons","group":"Buttons","signature":"Buttons(buttons: Button[], direction?: \"row\" | \"column\")","description":"Group of Button components. direction: \"row\" (default) | \"column\".","tier":"core","pack":"core","examples":[]},{"name":"Stack","group":"Layout","signature":"Stack(children: any[], direction?: \"row\" | \"column\", gap?: \"none\" | \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"2xl\", align?: \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\", justify?: \"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\", wrap?: boolean)","description":"Flex container. direction: \"row\"|\"column\" (default \"column\"). gap: \"none\"|\"xs\"|\"s\"|\"m\"|\"l\"|\"xl\"|\"2xl\" (default \"m\"). align: \"start\"|\"center\"|\"end\"|\"stretch\"|\"baseline\". justify: \"start\"|\"center\"|\"end\"|\"between\"|\"around\"|\"evenly\".","tier":"core","pack":"core","examples":[]},{"name":"Tabs","group":"Layout","signature":"Tabs(items: TabItem[])","description":"Tabbed container","tier":"core","pack":"core","examples":[]},{"name":"TabItem","group":"Layout","signature":"TabItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[])","description":"value is unique id, trigger is tab label, content is array of components","tier":"core","pack":"core","examples":[]},{"name":"Accordion","group":"Layout","signature":"Accordion(items: AccordionItem[])","description":"Collapsible sections","tier":"core","pack":"core","examples":[]},{"name":"AccordionItem","group":"Layout","signature":"AccordionItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[])","description":"value is unique id, trigger is section title","tier":"core","pack":"core","examples":[]},{"name":"Steps","group":"Layout","signature":"Steps(items: StepsItem[])","description":"Step-by-step guide","tier":"core","pack":"core","examples":[]},{"name":"StepsItem","group":"Layout","signature":"StepsItem(title: string, details: string)","description":"title and details text for one step","tier":"core","pack":"core","examples":[]},{"name":"Separator","group":"Layout","signature":"Separator(orientation?: \"horizontal\" | \"vertical\", decorative?: boolean)","description":"Visual divider between content sections","tier":"core","pack":"core","examples":[]},{"name":"TagBlock","group":"Data","signature":"TagBlock(tags: string[], size?: \"sm\" | \"md\" | \"lg\")","description":"tags is an array of strings; optional size sm | md | lg","tier":"extended","pack":"core","examples":[]},{"name":"Tag","group":"Data","signature":"Tag(text: string, icon?: Icon, size?: \"sm\" | \"md\" | \"lg\", variant?: \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\")","description":"Styled tag/badge with optional Icon and variant","tier":"core","pack":"core","examples":[]},{"name":"Icon","group":"Data","signature":"Icon(name: string, category?: string)","description":"A lucide icon by kebab-case name (e.g. 'circle-check'). Optional category picks a topical fallback when the name doesn't resolve.","tier":"core","pack":"core","examples":[]},{"name":"EntityList","group":"Data","signature":"EntityList(rows?: {left: string, right: string, rightVariant?: \"text\" | \"number\"}[], size?: \"small\" | \"default\", header?: {left: string, right: string, rightVariant?: \"text\" | \"number\"}, footer?: {left: string, right: string, rightVariant?: \"text\" | \"number\"})","description":"Two-column key/value rows (left label, right value). size 'default' supports optional header and footer rows; rightVariant 'number' uses tabular numbers.","tier":"core","pack":"core","examples":[]},{"name":"Text","group":"Data","signature":"Text(variant?: \"text\" | \"number\", value: string, subtext?: string, subtextVariant?: \"text\" | \"number\" | \"metric\", size?: \"xs\" | \"sm\" | \"md\" | \"lg\")","description":"Plain text line with optional subtext. variant 'number' uses tabular number styling; subtextVariant 'metric' colors a leading +/- subtext green/red.","tier":"core","pack":"core","examples":[]},{"name":"BoldText","group":"Data","signature":"BoldText(variant?: \"text\" | \"number\", value: string, subtext?: string, subtextVariant?: \"text\" | \"number\" | \"metric\", size?: \"xs\" | \"sm\" | \"md\" | \"lg\")","description":"Emphasized (bold) text line with optional subtext. variant 'number' uses tabular number styling; subtextVariant 'metric' colors a leading +/- subtext green/red.","tier":"extended","pack":"core","examples":[]},{"name":"IconText","group":"Data","signature":"IconText(icon: Icon, iconVariant?: \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\" | \"inverted\" | \"filled\" | \"soft\", iconSize?: \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"sm\" | \"md\" | \"lg\", title: string, subtitle?: string, bold?: boolean, layout?: \"horizontal\" | \"vertical\")","description":"An icon badge with a title and optional subtitle, laid out horizontally or vertically. iconVariant sets the badge color.","tier":"extended","pack":"core","examples":[]},{"name":"MetricIndicatorInline","group":"Data","signature":"MetricIndicatorInline(value: string, subtext?: string, trend?: {direction: \"up\" | \"down\", value: number})","description":"Headline metric value with an optional +/- percentage trend and subtext, all on one line.","tier":"core","pack":"core","examples":[]},{"name":"OverviewCardItem","group":"Data","signature":"OverviewCardItem(id?: string, top: IconText | Text, bottom?: MetricIndicatorInline)","description":"One overview card: a heading slot at the top (IconText, ImageText or Text) and an optional MetricIndicatorInline at the bottom.","tier":"core","pack":"core","examples":[]},{"name":"OverviewCardBlock","group":"Data","signature":"OverviewCardBlock(items: OverviewCardItem[], layout?: \"grid\" | \"carousel\", responsive?: boolean, action?: ActionExpression, gap?: number | string)","description":"A grid or horizontal carousel of compact overview cards, each with a heading (icon/image/text) on top and an inline metric below; optionally clickable with a shared action.","tier":"core","pack":"core","examples":[]},{"name":"Architecture","group":"Content","signature":"Architecture(nodes: Node[], edges: Edge[], groups?: Group[], direction?: \"auto\" | \"right\" | \"down\" | \"lanes\")","description":"System diagram laid out automatically: write no coordinates. Every Node, Edge and Group is its own statement and click target. direction auto (default) runs right when that fits the page legibly, else down; lanes makes each top-level Group a column.","tier":"core","pack":"core","examples":["platformMap = Architecture([browser, edge, api, sessions, cache], [toEdge, toApi, readSessions, readCache], [vpc])\nbrowser = Node(\"Browser\", \"external\")\nedge = Node(\"Edge worker\", \"cloud\", \"Cloudflare\")\napi = Node(\"API\", \"backend\", \"Node :8080\")\nsessions = Node(\"Sessions\", \"database\", \"Postgres\", \"new\")\ncache = Node(\"Redis\", \"database\", \"read-through\")\ntoEdge = Edge(browser, edge, \"HTTPS\", \"emphasis\")\ntoApi = Edge(edge, api)\nreadSessions = Edge(api, sessions, \"SQL\")\nreadCache = Edge(api, cache, \"cache\", \"dashed\")\nvpc = Group(\"Platform VPC\", [api, sessions, cache])","ownership = Architecture([web, ordersApi, ledger, ordersDb, billingJob], [placeOrder, writeOrder, readOrders, postLedger], [clients, ordersRepo, billingRepo], \"lanes\")\nweb = Node(\"Web app\", \"frontend\")\nordersApi = Node(\"Orders API\", \"backend\", \"Node :8080\")\nordersDb = Node(\"orders table\", \"database\", \"Postgres\", null, \"success\")\nbillingJob = Node(\"Nightly billing\", \"backend\", \"cron 02:00\")\nledger = Node(\"Ledger\", \"database\", \"append-only\", null, \"info\")\nplaceOrder = Edge(web, ordersApi, \"POST /orders\")\nwriteOrder = Edge(ordersApi, ordersDb, \"insert\")\nreadOrders = Edge(billingJob, ordersDb, \"read unbilled\", null, \"danger\")\npostLedger = Edge(billingJob, ledger, \"post\")\nclients = Group(\"CLIENTS\", [web], \"plain\")\nordersRepo = Group(\"orders-service\", [ordersApi, ordersDb])\nbillingRepo = Group(\"billing\", [billingJob, ledger])"]},{"name":"Node","group":"Content","signature":"Node(label: string, kind?: \"frontend\" | \"backend\" | \"database\" | \"messagebus\" | \"cloud\" | \"security\" | \"external\", detail?: string, tag?: string, tone?: \"info\" | \"success\" | \"warning\" | \"danger\" | \"strong\", row?: number)","description":"A box in an Architecture. kind sets icon and color (default backend); detail is monospace, up to 2 lines (\\n breaks); tag is a small chip such as \"new\"; tone tints it (danger = risk, strong = where it ends); row pins it to a row (1, 2, ...) in lanes.","tier":"core","pack":"core","examples":[]},{"name":"Edge","group":"Content","signature":"Edge(from: Node, to: Node, label?: string, style?: \"solid\" | \"dashed\" | \"emphasis\" | \"security\", tone?: \"info\" | \"success\" | \"warning\" | \"danger\")","description":"An arrow between two Node statements (pass their names, not strings). style: dashed for async, emphasis for the main path, security for auth or trust flows. tone colors it, e.g. danger for a risky write, success for a read.","tier":"core","pack":"core","examples":[]},{"name":"Group","group":"Content","signature":"Group(label: string, members: Node[], kind?: \"region\" | \"security\" | \"plain\")","description":"A boundary around Node statements (a VPC, a cluster, a trust zone); members still sit where their edges put them. A group whose members are a subset of another's is drawn inside it; kind security draws it red, plain draws only its name.","tier":"core","pack":"core","examples":[]},{"name":"Diff","group":"Content","signature":"Diff(patch: string, title?: string)","description":"Unified diff (git diff output, one or more files) with line numbers. patch is a raw multi-line string. Clicked lines reach the agent as file:+line.","tier":"core","pack":"core","examples":["retryPatch = Diff(\"diff --git a/src/retry.ts b/src/retry.ts\n--- a/src/retry.ts\n+++ b/src/retry.ts\n@@ -10,2 +10,3 @@\n- for (let i = 0; i < 3; i++) {\n+ for (let i = 0; i < MAX_ATTEMPTS; i++) {\n+ if (i > 0) await sleep(backoff(i));\")"]},{"name":"FileTree","group":"Data","signature":"FileTree(entries: string[])","description":"File tree, one path per entry, optionally prefixed with a git status code (A new, M modified, D deleted, R old -> new) and suffixed with \" # note\". Folders are inferred. Clicked files and folders reach the agent by path.","tier":"core","pack":"core","examples":["changedFiles = FileTree([\"M src/session/store.ts # read-through to Postgres\", \"A src/session/pg-store.ts\", \"A migrations/0042_sessions.sql # table + expiry index\", \"R src/session/redis.ts -> src/session/redis-cache.ts\", \"D src/session/legacy.ts\", \"M src/config.ts # SESSIONS_PG flag\"])"]},{"name":"Mermaid","group":"Content","signature":"Mermaid(source: string, caption?: string)","description":"Diagram from Mermaid source (flowchart, sequence, state, class, ER, gantt). source is a raw multi-line string. Keep node labels to a few words; clicked nodes reach the agent by label.","tier":"core","pack":"@austere-axi/pack-mermaid","examples":["requestPath = Mermaid(\"flowchart LR\n client[Client] --> api[API]\n api --> pg[(Postgres)]\", \"Request path\")"]},{"name":"Progress","group":"Data","signature":"Progress(value: number, label?: string, max?: number)","description":"Progress bar. value counts toward max (default 100); label names what is progressing. Shows a percentage.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["migration = Progress(64, \"Rows migrated\")"]},{"name":"Avatar","group":"Data","signature":"Avatar(name: string, src?: string, size?: \"md\" | \"lg\")","description":"Round avatar for a person. Shows the initials of name; src is an optional https image URL.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["owner = Avatar(\"Ada Lovelace\")"]},{"name":"AvatarGroup","group":"Data","signature":"AvatarGroup(avatars: Avatar[], max?: number)","description":"Overlapping Avatars, for reviewers or owners. max (default 4) shows the rest as +N.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["team = AvatarGroup([ada, grace, linus], 2)\nada = Avatar(\"Ada Lovelace\")\ngrace = Avatar(\"Grace Hopper\")\nlinus = Avatar(\"Linus Torvalds\")"]},{"name":"Tooltip","group":"Content","signature":"Tooltip(text: string, tip: string)","description":"Inline text with a short hint on hover or focus (a term and its definition). Keep tip to one line.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["term = Tooltip(\"idempotent\", \"Safe to run twice with the same result.\")"]},{"name":"HoverCard","group":"Content","signature":"HoverCard(text: string, title: string, body?: string)","description":"Inline text that shows a small card (title and a line or two of body) on hover or focus, e.g. a person or a service.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["svc = HoverCard(\"billing-api\", \"billing-api\", \"Owns invoices and refunds. On call: payments team.\")"]},{"name":"Breadcrumb","group":"Content","signature":"Breadcrumb(items: BreadcrumbItem[])","description":"Path trail such as Repo / Package / File, from BreadcrumbItems. Each item is a click target.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["trail = Breadcrumb([repo, pkg, file])\nrepo = BreadcrumbItem(\"austere-axi\")\npkg = BreadcrumbItem(\"packs\")\nfile = BreadcrumbItem(\"shadcn\")"]},{"name":"BreadcrumbItem","group":"Content","signature":"BreadcrumbItem(label: string, href?: string)","description":"One step of a Breadcrumb. href is optional; the last item is the current page.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":[]},{"name":"Kbd","group":"Content","signature":"Kbd(keys: string)","description":"Keyboard shortcut. keys joins keys with \"+\", e.g. \"Ctrl+K\"; each key is drawn as a key cap.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["shortcut = Kbd(\"Ctrl+Shift+P\")"]},{"name":"Skeleton","group":"Content","signature":"Skeleton(lines?: number, shape?: \"lines\" | \"avatar\")","description":"Grey placeholder block for content not yet written or loaded (default 3 lines). shape avatar adds a round head.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["pending = Skeleton(3, \"avatar\")"]},{"name":"Calendar","group":"Data","signature":"Calendar(month: string, highlights?: string[])","description":"Read-only month grid (not a date picker). month is \"YYYY-MM\"; highlights lists \"YYYY-MM-DD\" or \"YYYY-MM-DD label\" entries to mark, e.g. a freeze or a release. Marked days are click targets.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["window = Calendar(\"2026-11\", [\"2026-11-04 Code freeze\", \"2026-11-12 Release\", \"2026-11-13\"])"]},{"name":"Timeline","group":"Content","signature":"Timeline(items: TimelineItem[])","description":"Vertical history or schedule of TimelineItems, oldest first. Each item is a click target.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["history = Timeline([found, fixed, rollout])\nfound = TimelineItem(\"Bug found\", \"Oct 28\", \"Retries double-charge on timeout.\")\nfixed = TimelineItem(\"Fix merged\", \"Oct 29\", \"Idempotency key added.\", \"current\")\nrollout = TimelineItem(\"Rollout\", \"Nov 3\", \"\", \"upcoming\")"]},{"name":"TimelineItem","group":"Content","signature":"TimelineItem(title: string, date?: string, detail?: string, state?: \"done\" | \"current\" | \"upcoming\")","description":"One event in a Timeline. state: done (default), current or upcoming.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":[]}]}
@@ -1 +1 @@
1
- :root{--bg: #0b0b0d;--bg-2: #111114;--surface: #17171b;--surface-2: #1e1e23;--line: rgba(255, 255, 255, .08);--line-2: rgba(255, 255, 255, .14);--text: #ececef;--muted: #9d9da6;--faint: #6c6c76;--accent: #8fb0ff;--accent-ink: #0d1220;--green: #74d3a0;--amber: #f2c46d;--red: #f08a8a;--panel-w: 380px;--bar-h: 48px;--dock-h: 52px;color-scheme:dark}*{box-sizing:border-box}html,body{margin:0;height:100%;background:var(--bg);color:var(--text);font:14px/1.45 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Inter,sans-serif;-webkit-font-smoothing:antialiased;overflow:hidden}button{font:inherit;color:inherit}code{font-family:ui-monospace,SF Mono,Menlo,Consolas,monospace;font-size:.92em}.app{display:flex;flex-direction:column;height:100dvh}.bar{flex:none;height:var(--bar-h);display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:12px;padding:0 12px 0 16px;border-bottom:1px solid var(--line);background:var(--bg-2)}.bar-title{display:flex;align-items:baseline;gap:10px;min-width:0}.title{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file{color:var(--faint);font:12px ui-monospace,SF Mono,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bar-mid{display:flex;align-items:center;gap:8px}.bar-right{display:flex;justify-content:flex-end;align-items:center;gap:8px;min-width:0}.seg{display:inline-flex;padding:2px;border-radius:8px;background:var(--surface);border:1px solid var(--line)}.seg button{border:0;background:transparent;padding:5px 12px;border-radius:6px;color:var(--muted);font-size:13px;font-weight:500;cursor:pointer}.seg button.on{background:var(--surface-2);color:var(--text);box-shadow:inset 0 0 0 1px var(--line-2)}.seg button[data-mode=annotate].on{color:var(--accent)}.changes{border:1px solid rgba(242,196,109,.35);background:#f2c46d14;color:var(--amber);border-radius:999px;padding:4px 10px;font-size:12px;font-weight:600;cursor:pointer}.changes[aria-pressed=true]{border-color:var(--amber);background:var(--amber);color:#1a1408}.presence{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;color:var(--muted);white-space:nowrap}.presence:before{content:"";width:7px;height:7px;border-radius:50%;background:var(--faint)}.presence.listening{color:var(--text)}.presence.listening:before{background:var(--green);box-shadow:0 0 0 3px #74d3a02e}.presence.working:before{background:var(--amber);animation:blink 1.2s infinite}.presence.ended:before{background:var(--red)}@keyframes blink{50%{opacity:.35}}.icon-btn{border:1px solid transparent;background:transparent;color:var(--muted);width:32px;height:32px;border-radius:8px;cursor:pointer;font-size:18px;line-height:1}.icon-btn:hover{background:var(--surface);color:var(--text)}.menu{position:relative}.menu-pop{position:absolute;right:0;top:38px;z-index:20;min-width:220px;padding:6px;border-radius:10px;background:var(--surface);border:1px solid var(--line-2);box-shadow:0 16px 40px #00000080;display:grid}.menu-pop[hidden]{display:none}.menu-pop button,.menu-pop a{text-align:left;border:0;background:transparent;padding:8px 10px;border-radius:6px;color:var(--text);text-decoration:none;font-size:13px;cursor:pointer}.menu-pop button:hover,.menu-pop a:hover{background:var(--surface-2)}.menu-pop .danger{color:var(--red)}.banner{flex:none;display:flex;align-items:center;justify-content:center;gap:12px;padding:8px 16px;background:#2a2414;color:#f3dfae;border-bottom:1px solid rgba(242,196,109,.25);font-size:13px}.banner[hidden]{display:none}.notice{flex:none;padding:6px 16px;background:#1c1a14;color:#d9c79a;border-bottom:1px solid rgba(242,196,109,.18);font-size:12.5px;text-align:center;overflow-wrap:anywhere}.notice[hidden]{display:none}@media(max-width:860px){.notice{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left}}.main{flex:1;min-height:0;display:grid;grid-template-columns:minmax(0,1fr) var(--panel-w)}.stage{min-width:0;min-height:0;position:relative;background:#0a0a0a}.frame{display:block;width:100%;height:100%;border:0;background:#0a0a0a}.scrim{display:none}.panel{min-height:0;display:flex;flex-direction:column;border-left:1px solid var(--line);background:var(--bg-2)}.panel-head{flex:none;display:flex;align-items:center;justify-content:space-between;padding:12px 16px 8px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--faint)}.sheet-close{display:none}.log{flex:1;min-height:0;overflow-y:auto;padding:4px 12px 16px;display:flex;flex-direction:column;gap:10px;overscroll-behavior:contain}.empty{margin:8px 4px;padding:14px;border:1px dashed var(--line-2);border-radius:12px;color:var(--muted);font-size:13px}.empty p{margin:0 0 6px}.empty strong{color:var(--text);font-weight:600}.msg{border-radius:12px;padding:10px 12px;border:1px solid var(--line)}.msg .who{font-size:11.5px;color:var(--faint);margin-bottom:6px}.msg.user{background:var(--surface);margin-left:18px}.msg.agent{background:#8fb0ff0f;border-color:#8fb0ff2e;margin-right:18px}.md p{margin:0 0 6px}.md p:last-child{margin-bottom:0}.md ul,.md ol{margin:4px 0 6px;padding-left:20px}.md pre{margin:6px 0;padding:8px 10px;border-radius:8px;background:#0d0d10;overflow-x:auto}.md code{background:#ffffff0f;padding:1px 4px;border-radius:4px}.md pre code{background:none;padding:0}.md a{color:var(--accent)}.item+.item{margin-top:10px;padding-top:10px;border-top:1px solid var(--line)}.anchor{display:inline-flex;align-items:center;gap:6px;max-width:100%;border:0;background:transparent;padding:0;cursor:pointer;text-align:left}.anchor-icon{color:var(--accent);font-size:12px}.anchor-text{font:500 12.5px ui-monospace,SF Mono,Menlo,monospace;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kind-action .anchor-text{font-family:inherit}.anchor-sub{font-size:11px;color:var(--faint)}.anchor:hover .anchor-text{color:var(--accent)}.item blockquote{margin:6px 0 0;padding:2px 0 2px 9px;border-left:2px solid var(--line-2);color:var(--muted);font-size:12.5px;overflow-wrap:anywhere}.note{margin-top:6px;white-space:pre-wrap;overflow-wrap:anywhere}textarea.edit{width:100%;margin-top:6px;padding:7px 9px;border-radius:8px;border:1px solid rgba(143,176,255,.5);background:#0f0f12;color:var(--text);font:inherit;resize:vertical}.receipt{display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px;margin-top:10px;font-size:11px;color:var(--faint)}.receipt span:before{content:"\25cb "}.receipt span.on{color:var(--green)}.receipt span.on:before{content:"\25cf "}.receipt em{font-style:normal;color:var(--faint);flex-basis:100%}.queued{border:1px dashed rgba(143,176,255,.4);border-radius:12px;padding:10px 12px;background:#8fb0ff08}.queued-head{font-size:11.5px;font-weight:600;color:var(--accent);margin-bottom:8px}.q-actions{display:flex;gap:6px;margin-top:6px}.issues{border:1px solid rgba(240,138,138,.3);background:#f060600f;border-radius:12px;padding:10px 12px}.issues-head{font-size:11.5px;font-weight:600;color:var(--red);margin-bottom:6px}.issue{display:flex;gap:8px;align-items:flex-start;justify-content:space-between;font-size:12.5px;color:#f0caca}.issue+.issue{margin-top:6px}.issue-text{overflow-wrap:anywhere}.load-earlier{align-self:center;margin:0 auto 8px}.ended-note{text-align:center;font-size:12px;color:var(--faint);padding:6px}.btn{border:1px solid var(--line-2);background:var(--surface-2);color:var(--text);border-radius:8px;padding:7px 12px;font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap}.btn:disabled{opacity:.45;cursor:default}.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.btn.ghost{background:transparent}.btn.tiny{padding:3px 8px;font-size:11.5px;font-weight:500;border-radius:6px}.composer{flex:none;padding:10px 12px 12px;border-top:1px solid var(--line);background:var(--bg-2)}.composer textarea{width:100%;min-height:44px;max-height:160px;resize:vertical;padding:9px 11px;border-radius:10px;border:1px solid var(--line-2);background:#0f0f12;color:var(--text);font:inherit;outline:none}.composer textarea:focus{border-color:#8fb0ff8c}.composer-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:8px}.composer-hint{font-size:11px;color:var(--faint)}.composer-actions{display:flex;gap:6px}.composer-error{margin-top:8px;font-size:12px;color:var(--red)}.composer-error[hidden]{display:none}.presence.hosted:before{background:var(--accent)}.name-ask,.report-box{flex:none;display:flex;flex-direction:column;gap:6px;padding:10px 12px;border-top:1px solid var(--line);background:var(--surface);font-size:12.5px;color:var(--muted)}.name-ask[hidden],.report-box[hidden]{display:none}.name-row,.report-row{display:flex;align-items:center;gap:6px}.name-ask input,.report-box textarea{flex:1;min-width:0;padding:7px 10px;border-radius:8px;border:1px solid var(--line-2);background:#0f0f12;color:var(--text);font:inherit;font-size:13px;outline:none;resize:vertical}.name-ask input:focus,.report-box textarea:focus{border-color:#8fb0ff8c}.report-status{margin-right:auto;color:var(--red)}.hosted-foot{flex:none;display:flex;align-items:center;gap:6px;padding:7px 14px 9px;border-top:1px solid var(--line);font-size:11.5px;color:var(--faint)}.hosted-foot a,.link-btn{color:var(--muted);text-decoration:none}.hosted-foot a:hover,.link-btn:hover{color:var(--text);text-decoration:underline}.link-btn{padding:0;border:0;background:none;font:inherit;cursor:pointer}.dock{display:none}@media(max-width:860px){.bar{grid-template-columns:minmax(0,1fr) auto;padding:0 6px 0 12px}.bar-mid{order:3}.bar-right{order:4}.bar-title .file,.presence{display:none}.bar{grid-template-columns:minmax(0,1fr) auto auto}.main{grid-template-columns:1fr;padding-bottom:calc(var(--dock-h) + env(safe-area-inset-bottom))}.panel{position:fixed;left:0;right:0;bottom:0;z-index:30;height:min(78dvh,640px);border-left:0;border-top:1px solid var(--line-2);border-radius:16px 16px 0 0;box-shadow:0 -20px 50px #0000008c;transform:translateY(105%);transition:transform .22s ease-out;padding-bottom:env(safe-area-inset-bottom)}html.sheet-open .panel{transform:none}.scrim{display:block;position:fixed;inset:0;z-index:25;background:#00000080;opacity:0;pointer-events:none;transition:opacity .2s}html.sheet-open .scrim{opacity:1;pointer-events:auto}.panel-head{padding-top:14px}.sheet-close{display:inline-block}.composer-hint{display:none}.dock{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:24;height:calc(var(--dock-h) + env(safe-area-inset-bottom));padding:0 16px env(safe-area-inset-bottom);align-items:center;justify-content:center;gap:10px;border:0;border-top:1px solid var(--line-2);background:var(--bg-2);color:var(--text);font-size:13.5px;font-weight:500;cursor:pointer}.dock-grip{position:absolute;top:6px;left:50%;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--line-2)}.dock-presence{width:8px;height:8px;border-radius:50%;background:var(--faint)}.dock-presence.listening{background:var(--green)}.dock-presence.working{background:var(--amber)}.dock-presence.ended{background:var(--red)}.dock.pulse{animation:dockpulse .6s}@keyframes dockpulse{40%{background:#1a2338}}}.receipt.held span:before{content:""}.receipt.held .held-label{color:var(--amber);margin-right:auto}.msg.user.held{border-style:dashed;border-color:#f2c46d73}.held-bar{display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;padding:8px 12px;border:1px dashed rgba(242,196,109,.45);border-radius:12px;font-size:12px;color:var(--amber)}.held-bar span{margin-right:auto}
1
+ :root{--bg: #0b0b0d;--bg-2: #111114;--surface: #17171b;--surface-2: #1e1e23;--line: rgba(255, 255, 255, .08);--line-2: rgba(255, 255, 255, .14);--text: #ececef;--muted: #9d9da6;--faint: #6c6c76;--accent: #8fb0ff;--accent-ink: #0d1220;--green: #74d3a0;--amber: #f2c46d;--red: #f08a8a;--panel-w: 380px;--bar-h: 48px;--dock-h: 52px;color-scheme:dark}*{box-sizing:border-box}html,body{margin:0;height:100%;background:var(--bg);color:var(--text);font:14px/1.45 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Inter,sans-serif;-webkit-font-smoothing:antialiased;overflow:hidden}button{font:inherit;color:inherit}code{font-family:ui-monospace,SF Mono,Menlo,Consolas,monospace;font-size:.92em}.app{display:flex;flex-direction:column;height:100dvh}.bar{flex:none;height:var(--bar-h);display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:12px;padding:0 12px 0 16px;border-bottom:1px solid var(--line);background:var(--bg-2)}.bar-title{display:flex;align-items:baseline;gap:10px;min-width:0}.title{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file{color:var(--faint);font:12px ui-monospace,SF Mono,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bar-mid{display:flex;align-items:center;gap:8px}.bar-right{display:flex;justify-content:flex-end;align-items:center;gap:8px;min-width:0}.seg{display:inline-flex;padding:2px;border-radius:8px;background:var(--surface);border:1px solid var(--line)}.seg button{border:0;background:transparent;padding:5px 12px;border-radius:6px;color:var(--muted);font-size:13px;font-weight:500;cursor:pointer}.seg button.on{background:var(--surface-2);color:var(--text);box-shadow:inset 0 0 0 1px var(--line-2)}.seg button[data-mode=annotate].on{color:var(--accent)}.changes{border:1px solid rgba(242,196,109,.35);background:#f2c46d14;color:var(--amber);border-radius:999px;padding:4px 10px;font-size:12px;font-weight:600;cursor:pointer}.changes[aria-pressed=true]{border-color:var(--amber);background:var(--amber);color:#1a1408}.presence{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;color:var(--muted);white-space:nowrap}.presence:before{content:"";width:7px;height:7px;border-radius:50%;background:var(--faint)}.presence.listening{color:var(--text)}.presence.listening:before{background:var(--green);box-shadow:0 0 0 3px #74d3a02e}.presence.working:before{background:var(--amber);animation:blink 1.2s infinite}.presence.ended:before{background:var(--red)}@keyframes blink{50%{opacity:.35}}.icon-btn{border:1px solid transparent;background:transparent;color:var(--muted);width:32px;height:32px;border-radius:8px;cursor:pointer;font-size:18px;line-height:1}.icon-btn:hover{background:var(--surface);color:var(--text)}.menu{position:relative}.menu-pop{position:absolute;right:0;top:38px;z-index:20;min-width:220px;padding:6px;border-radius:10px;background:var(--surface);border:1px solid var(--line-2);box-shadow:0 16px 40px #00000080;display:grid}.menu-pop[hidden]{display:none}.menu-pop button,.menu-pop a{text-align:left;border:0;background:transparent;padding:8px 10px;border-radius:6px;color:var(--text);text-decoration:none;font-size:13px;cursor:pointer}.menu-pop button:hover,.menu-pop a:hover{background:var(--surface-2)}.menu-pop .danger{color:var(--red)}.banner{flex:none;display:flex;align-items:center;justify-content:center;gap:12px;padding:8px 16px;background:#2a2414;color:#f3dfae;border-bottom:1px solid rgba(242,196,109,.25);font-size:13px}.banner[hidden]{display:none}.notice{flex:none;padding:6px 16px;background:#1c1a14;color:#d9c79a;border-bottom:1px solid rgba(242,196,109,.18);font-size:12.5px;text-align:center;overflow-wrap:anywhere}.notice[hidden]{display:none}@media(max-width:860px){.notice{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left}}.main{flex:1;min-height:0;display:grid;grid-template-columns:minmax(0,1fr) var(--panel-w)}.stage{min-width:0;min-height:0;position:relative;background:#0a0a0a}.frame{display:block;width:100%;height:100%;border:0;background:#0a0a0a}.scrim{display:none}html.exploring .main{grid-template-columns:minmax(0,1fr);padding-bottom:0}html.exploring .panel,html.exploring .scrim,html.exploring .dock{display:none}.panel{min-height:0;display:flex;flex-direction:column;border-left:1px solid var(--line);background:var(--bg-2)}.panel-head{flex:none;display:flex;align-items:center;justify-content:space-between;padding:12px 16px 8px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--faint)}.sheet-close{display:none}.log{flex:1;min-height:0;overflow-y:auto;padding:4px 12px 16px;display:flex;flex-direction:column;gap:10px;overscroll-behavior:contain}.empty{margin:8px 4px;padding:14px;border:1px dashed var(--line-2);border-radius:12px;color:var(--muted);font-size:13px}.empty p{margin:0 0 6px}.empty strong{color:var(--text);font-weight:600}.msg{border-radius:12px;padding:10px 12px;border:1px solid var(--line)}.msg .who{font-size:11.5px;color:var(--faint);margin-bottom:6px}.msg.user{background:var(--surface);margin-left:18px}.msg.agent{background:#8fb0ff0f;border-color:#8fb0ff2e;margin-right:18px}.md p{margin:0 0 6px}.md p:last-child{margin-bottom:0}.md ul,.md ol{margin:4px 0 6px;padding-left:20px}.md pre{margin:6px 0;padding:8px 10px;border-radius:8px;background:#0d0d10;overflow-x:auto}.md code{background:#ffffff0f;padding:1px 4px;border-radius:4px}.md pre code{background:none;padding:0}.md a{color:var(--accent)}.item+.item{margin-top:10px;padding-top:10px;border-top:1px solid var(--line)}.anchor{display:inline-flex;align-items:center;gap:6px;max-width:100%;border:0;background:transparent;padding:0;cursor:pointer;text-align:left}.anchor-icon{color:var(--accent);font-size:12px}.anchor-text{font:500 12.5px ui-monospace,SF Mono,Menlo,monospace;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kind-action .anchor-text{font-family:inherit}.anchor-sub{font-size:11px;color:var(--faint)}.anchor:hover .anchor-text{color:var(--accent)}.item blockquote{margin:6px 0 0;padding:2px 0 2px 9px;border-left:2px solid var(--line-2);color:var(--muted);font-size:12.5px;overflow-wrap:anywhere}.note{margin-top:6px;white-space:pre-wrap;overflow-wrap:anywhere}textarea.edit{width:100%;margin-top:6px;padding:7px 9px;border-radius:8px;border:1px solid rgba(143,176,255,.5);background:#0f0f12;color:var(--text);font:inherit;resize:vertical}.receipt{display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px;margin-top:10px;font-size:11px;color:var(--faint)}.receipt span:before{content:"\25cb "}.receipt span.on{color:var(--green)}.receipt span.on:before{content:"\25cf "}.receipt em{font-style:normal;color:var(--faint);flex-basis:100%}.queued{border:1px dashed rgba(143,176,255,.4);border-radius:12px;padding:10px 12px;background:#8fb0ff08}.queued-head{font-size:11.5px;font-weight:600;color:var(--accent);margin-bottom:8px}.q-actions{display:flex;gap:6px;margin-top:6px}.issues{border:1px solid rgba(240,138,138,.3);background:#f060600f;border-radius:12px;padding:10px 12px}.issues-head{font-size:11.5px;font-weight:600;color:var(--red);margin-bottom:6px}.issue{display:flex;gap:8px;align-items:flex-start;justify-content:space-between;font-size:12.5px;color:#f0caca}.issue+.issue{margin-top:6px}.issue-text{overflow-wrap:anywhere}.load-earlier{align-self:center;margin:0 auto 8px}.ended-note{text-align:center;font-size:12px;color:var(--faint);padding:6px}.btn{border:1px solid var(--line-2);background:var(--surface-2);color:var(--text);border-radius:8px;padding:7px 12px;font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap}.btn:disabled{opacity:.45;cursor:default}.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.btn.ghost{background:transparent}.btn.tiny{padding:3px 8px;font-size:11.5px;font-weight:500;border-radius:6px}.composer{flex:none;padding:10px 12px 12px;border-top:1px solid var(--line);background:var(--bg-2)}.composer textarea{width:100%;min-height:44px;max-height:160px;resize:vertical;padding:9px 11px;border-radius:10px;border:1px solid var(--line-2);background:#0f0f12;color:var(--text);font:inherit;outline:none}.composer textarea:focus{border-color:#8fb0ff8c}.composer-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:8px}.composer-hint{font-size:11px;color:var(--faint)}.composer-actions{display:flex;gap:6px}.composer-error{margin-top:8px;font-size:12px;color:var(--red)}.composer-error[hidden]{display:none}.presence.hosted:before{background:var(--accent)}.name-ask,.report-box{flex:none;display:flex;flex-direction:column;gap:6px;padding:10px 12px;border-top:1px solid var(--line);background:var(--surface);font-size:12.5px;color:var(--muted)}.name-ask[hidden],.report-box[hidden]{display:none}.name-row,.report-row{display:flex;align-items:center;gap:6px}.name-ask input,.report-box textarea{flex:1;min-width:0;padding:7px 10px;border-radius:8px;border:1px solid var(--line-2);background:#0f0f12;color:var(--text);font:inherit;font-size:13px;outline:none;resize:vertical}.name-ask input:focus,.report-box textarea:focus{border-color:#8fb0ff8c}.report-status{margin-right:auto;color:var(--red)}.hosted-foot{flex:none;display:flex;align-items:center;gap:6px;padding:7px 14px 9px;border-top:1px solid var(--line);font-size:11.5px;color:var(--faint)}.hosted-foot a,.link-btn{color:var(--muted);text-decoration:none}.hosted-foot a:hover,.link-btn:hover{color:var(--text);text-decoration:underline}.link-btn{padding:0;border:0;background:none;font:inherit;cursor:pointer}.dock{display:none}@media(max-width:860px){.bar{grid-template-columns:minmax(0,1fr) auto;padding:0 6px 0 12px}.bar-mid{order:3}.bar-right{order:4}.bar-title .file,.presence{display:none}.bar{grid-template-columns:minmax(0,1fr) auto auto}.main{grid-template-columns:1fr;padding-bottom:calc(var(--dock-h) + env(safe-area-inset-bottom))}.panel{position:fixed;left:0;right:0;bottom:0;z-index:30;height:min(78dvh,640px);border-left:0;border-top:1px solid var(--line-2);border-radius:16px 16px 0 0;box-shadow:0 -20px 50px #0000008c;transform:translateY(105%);transition:transform .22s ease-out;padding-bottom:env(safe-area-inset-bottom)}html.sheet-open .panel{transform:none}.scrim{display:block;position:fixed;inset:0;z-index:25;background:#00000080;opacity:0;pointer-events:none;transition:opacity .2s}html.sheet-open .scrim{opacity:1;pointer-events:auto}.panel-head{padding-top:14px}.sheet-close{display:inline-block}.composer-hint{display:none}.dock{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:24;height:calc(var(--dock-h) + env(safe-area-inset-bottom));padding:0 16px env(safe-area-inset-bottom);align-items:center;justify-content:center;gap:10px;border:0;border-top:1px solid var(--line-2);background:var(--bg-2);color:var(--text);font-size:13.5px;font-weight:500;cursor:pointer}.dock-grip{position:absolute;top:6px;left:50%;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--line-2)}.dock-presence{width:8px;height:8px;border-radius:50%;background:var(--faint)}.dock-presence.listening{background:var(--green)}.dock-presence.working{background:var(--amber)}.dock-presence.ended{background:var(--red)}.dock.pulse{animation:dockpulse .6s}@keyframes dockpulse{40%{background:#1a2338}}}.receipt.held span:before{content:""}.receipt.held .held-label{color:var(--amber);margin-right:auto}.msg.user.held{border-style:dashed;border-color:#f2c46d73}.held-bar{display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;padding:8px 12px;border:1px dashed rgba(242,196,109,.45);border-radius:12px;font-size:12px;color:var(--amber)}.held-bar span{margin-right:auto}
@@ -1,4 +1,4 @@
1
- (()=>{var b=window.__AUSTERE__??JSON.parse(document.getElementById("austere-boot")?.textContent||"{}"),w=b.id,g=b.hosted||null,f=b.hosted?.base??`/s/${w}`,Z=b.viewer||{role:"owner",name:""},m=!g&&Z.role==="owner",J=40,q={get(e,t){try{let s=localStorage.getItem(e);return s==null?t:JSON.parse(s)}catch{return t}},set(e,t){try{localStorage.setItem(e,JSON.stringify(t))}catch{}}},I={get(e){try{return JSON.parse(sessionStorage.getItem(e)||"null")}catch{return null}},set(e,t){try{sessionStorage.setItem(e,JSON.stringify(t))}catch{}}},v=g?String(q.get("austere:name","")||"").slice(0,J):Z.name,n={mode:q.get("austere:mode","annotate"),queue:q.get(`austere:queue:${w}`,[]),transcript:[],presence:"idle",status:"open",endedBy:null,closed:!1,unread:0,more:!1,program:null,frameReady:!1,scrollY:0,parserErrors:[],runtimeErrors:[],changes:[],highlight:!1,sheetOpen:I.get(`austere:sheet:${w}`)===!0,sending:!1,connected:!1,editing:null,links:null},l=(e,t=document)=>t.querySelector(e);function o(e,t={},...s){let r=document.createElement(e);for(let[a,i]of Object.entries(t))i==null||i===!1||(a==="class"?r.className=String(i):a.startsWith("on")?typeof i=="function"&&r.addEventListener(a.slice(2),i):a==="text"?r.textContent=String(i):a==="html"?r.innerHTML=String(i):r.setAttribute(a,i===!0?"":String(i)));for(let a of s.flat())a!=null&&a!==!1&&r.append(a instanceof Node?a:document.createTextNode(String(a)));return r}document.body.innerHTML=`
1
+ (()=>{var y=window.__AUSTERE__??JSON.parse(document.getElementById("austere-boot")?.textContent||"{}"),w=y.id,h=y.hosted||null,b=y.hosted?.base??`/s/${w}`,te=y.viewer||{role:"owner",name:""},m=!h&&te.role==="owner",_=40,A={get(e,n){try{let s=localStorage.getItem(e);return s==null?n:JSON.parse(s)}catch{return n}},set(e,n){try{localStorage.setItem(e,JSON.stringify(n))}catch{}}},q={get(e){try{return JSON.parse(sessionStorage.getItem(e)||"null")}catch{return null}},set(e,n){try{sessionStorage.setItem(e,JSON.stringify(n))}catch{}}},v=h?String(A.get("austere:name","")||"").slice(0,_):te.name,t={mode:A.get("austere:mode","annotate"),queue:A.get(`austere:queue:${w}`,[]),transcript:[],presence:"idle",status:"open",endedBy:null,closed:!1,unread:0,more:!1,program:null,revision:0,errorsRevision:0,renderReport:null,thirdParty:!1,frameReady:!1,scrollY:0,parserErrors:[],runtimeErrors:[],changes:[],highlight:!1,sheetOpen:q.get(`austere:sheet:${w}`)===!0,sending:!1,connected:!1,editing:null,links:null},l=(e,n=document)=>n.querySelector(e);function o(e,n={},...s){let r=document.createElement(e);for(let[i,a]of Object.entries(n))a==null||a===!1||(i==="class"?r.className=String(a):i.startsWith("on")?typeof a=="function"&&r.addEventListener(i.slice(2),a):i==="text"?r.textContent=String(a):i==="html"?r.innerHTML=String(a):r.setAttribute(i,a===!0?"":String(a)));for(let i of s.flat())i!=null&&i!==!1&&r.append(i instanceof Node?i:document.createTextNode(String(i)));return r}document.body.innerHTML=`
2
2
  <div class="app">
3
3
  <header class="bar">
4
4
  <div class="bar-title"><span class="title"></span><span class="file"></span></div>
@@ -17,7 +17,7 @@
17
17
  <button type="button" data-cmd="copy-path">Copy file path</button>
18
18
  <button type="button" data-cmd="copy-link">Copy review link</button>
19
19
  <button type="button" data-cmd="reload">Reload artifact</button>
20
- <a data-cmd="export" href="${f}/export">Download standalone HTML</a>
20
+ <a data-cmd="export" href="${b}/export">Download standalone HTML</a>
21
21
  <button type="button" data-cmd="end" class="danger">End review</button>
22
22
  <button type="button" data-cmd="close" class="danger">Close review</button>
23
23
  <button type="button" data-cmd="reopen">Reopen review</button>
@@ -52,7 +52,7 @@
52
52
  </aside>
53
53
  </div>
54
54
  <button type="button" class="dock"></button>
55
- </div>`;if(!m)for(let e of['[data-cmd="copy-path"]','[data-cmd="copy-link"]','[data-cmd="export"]','[data-cmd="end"]','[data-cmd="close"]','[data-cmd="reopen"]',".send-end"])l(e)?.remove();function de(){if(m)return;l(".file").textContent=v?`reviewing as ${v}`:"";let e=g||Z.auto?"Enter sends":"the owner sends your notes on \xB7 Enter sends";l(".composer-hint").textContent=v?`Posting as ${v} \xB7 ${e}`:e}var x=g?o("form",{class:"name-ask",hidden:!0},o("label",{for:"austere-name",text:"Your name, shown with your notes"}),o("div",{class:"name-row"},o("input",{id:"austere-name",maxlength:String(J),autocomplete:"name",placeholder:"Name"}),o("button",{type:"submit",class:"btn primary",text:"Continue"}))):null;x&&(l(".composer").before(x),x.addEventListener("submit",e=>{e.preventDefault();let t=l("input",x),s=t.value.trim().slice(0,J);if(!s)return t.focus();v=s,q.set("austere:name",s),x.hidden=!0,de(),O(!1)}));var h=g?.report?o("form",{class:"report-box",hidden:!0},o("label",{for:"austere-report",text:"What is wrong with this page?"}),o("textarea",{id:"austere-report",rows:"3",maxlength:"1000"}),o("div",{class:"report-row"},o("span",{class:"report-status"}),o("button",{type:"button",class:"btn tiny ghost",text:"Cancel",onclick:()=>{h&&(h.hidden=!0)}}),o("button",{type:"submit",class:"btn tiny primary",text:"Send report"}))):null;if(g){let e=o("footer",{class:"hosted-foot"},o("a",{href:"https://github.com/MandalorianBatman/austere-axi",target:"_blank",rel:"noopener noreferrer",text:"Made with austere-axi"}));h&&(e.append(o("span",{"aria-hidden":"true",text:"\xB7"}),o("button",{type:"button",class:"link-btn report-open",text:"Report",onclick:()=>{h.hidden=!1,l(".report-status",h).textContent="",l("textarea",h).focus()}})),l(".panel").append(h),h.addEventListener("submit",async t=>{t.preventDefault();let s=l("textarea",h).value.trim(),r=l(".report-status",h);if(!s)return l("textarea",h).focus();r.textContent="Sending\u2026";try{let a=await fetch(g.report,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:s})});if(!a.ok)throw new Error(`HTTP ${a.status}`);l("textarea",h).value="",h.hidden=!0,l(".report-open",e).replaceWith(o("span",{class:"report-done",text:"Reported. Thank you."}))}catch(a){r.textContent=`Not sent: ${a.message}`}})),l(".panel").append(e)}var ee=l(".frame"),u=l(".log"),A=l(".composer textarea"),P=l(".banner");l(".title").textContent=b.title;m&&(l(".file").textContent=b.name);de();l(".file").title=b.file||"";document.title=`${b.title} \xB7 austere`;function j(e){return String(e).replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}function W(e){return j(e).replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>").replace(/(^|[^*])\*([^*\n]+)\*/g,"$1<em>$2</em>").replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g,'<a href="$2" target="_blank" rel="noopener">$1</a>')}function ge(e){let t=String(e).replace(/\r\n/g,`
55
+ </div>`;if(!m)for(let e of['[data-cmd="copy-path"]','[data-cmd="copy-link"]','[data-cmd="export"]','[data-cmd="end"]','[data-cmd="close"]','[data-cmd="reopen"]',".send-end"])l(e)?.remove();function pe(){if(m)return;l(".file").textContent=v?`reviewing as ${v}`:"";let e=h||te.auto?"Enter sends":"the owner sends your notes on \xB7 Enter sends";l(".composer-hint").textContent=v?`Posting as ${v} \xB7 ${e}`:e}var x=h?o("form",{class:"name-ask",hidden:!0},o("label",{for:"austere-name",text:"Your name, shown with your notes"}),o("div",{class:"name-row"},o("input",{id:"austere-name",maxlength:String(_),autocomplete:"name",placeholder:"Name"}),o("button",{type:"submit",class:"btn primary",text:"Continue"}))):null;x&&(l(".composer").before(x),x.addEventListener("submit",e=>{e.preventDefault();let n=l("input",x),s=n.value.trim().slice(0,_);if(!s)return n.focus();v=s,A.set("austere:name",s),x.hidden=!0,pe(),N(!1)}));var f=h?.report?o("form",{class:"report-box",hidden:!0},o("label",{for:"austere-report",text:"What is wrong with this page?"}),o("textarea",{id:"austere-report",rows:"3",maxlength:"1000"}),o("div",{class:"report-row"},o("span",{class:"report-status"}),o("button",{type:"button",class:"btn tiny ghost",text:"Cancel",onclick:()=>{f&&(f.hidden=!0)}}),o("button",{type:"submit",class:"btn tiny primary",text:"Send report"}))):null;if(h){let e=o("footer",{class:"hosted-foot"},o("a",{href:"https://github.com/MandalorianBatman/austere-axi",target:"_blank",rel:"noopener noreferrer",text:"Made with austere-axi"}));f&&(e.append(o("span",{"aria-hidden":"true",text:"\xB7"}),o("button",{type:"button",class:"link-btn report-open",text:"Report",onclick:()=>{f.hidden=!1,l(".report-status",f).textContent="",l("textarea",f).focus()}})),l(".panel").append(f),f.addEventListener("submit",async n=>{n.preventDefault();let s=l("textarea",f).value.trim(),r=l(".report-status",f);if(!s)return l("textarea",f).focus();r.textContent="Sending\u2026";try{let i=await fetch(h.report,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:s})});if(!i.ok)throw new Error(`HTTP ${i.status}`);l("textarea",f).value="",f.hidden=!0,l(".report-open",e).replaceWith(o("span",{class:"report-done",text:"Reported. Thank you."}))}catch(i){r.textContent=`Not sent: ${i.message}`}})),l(".panel").append(e)}var ne=l(".frame"),c=l(".log"),R=l(".composer textarea"),O=l(".banner");l(".title").textContent=y.title;m&&(l(".file").textContent=y.name);pe();l(".file").title=y.file||"";document.title=`${y.title} \xB7 austere`;function B(e){return String(e).replace(/[&<>"']/g,n=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[n])}function F(e){return B(e).replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>").replace(/(^|[^*])\*([^*\n]+)\*/g,"$1<em>$2</em>").replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g,'<a href="$2" target="_blank" rel="noopener">$1</a>')}function fe(e){let n=String(e).replace(/\r\n/g,`
56
56
  `).split(`
57
- `),s=[],r=0;for(;r<t.length;){let a=t[r];if(/^```/.test(a)){let d=[];for(r++;r<t.length&&!/^```/.test(t[r]);)d.push(t[r++]);r++,s.push(`<pre><code>${j(d.join(`
58
- `))}</code></pre>`);continue}if(/^\s*([-*]|\d+\.)\s+/.test(a)){let d=/^\s*\d+\./.test(a),c=[];for(;r<t.length&&/^\s*([-*]|\d+\.)\s+/.test(t[r]);)c.push(t[r++].replace(/^\s*([-*]|\d+\.)\s+/,""));s.push(`<${d?"ol":"ul"}>${c.map(y=>`<li>${W(y)}</li>`).join("")}</${d?"ol":"ul"}>`);continue}if(/^#{1,6}\s+/.test(a)){s.push(`<p><strong>${W(a.replace(/^#+\s+/,""))}</strong></p>`),r++;continue}if(!a.trim()){r++;continue}let i=[];for(;r<t.length&&t[r].trim()&&!/^```|^\s*([-*]|\d+\.)\s+|^#{1,6}\s/.test(t[r]);)i.push(t[r++]);s.push(`<p>${i.map(W).join("<br>")}</p>`)}return s.join("")}function C(e){ee.contentWindow?.postMessage({austere:1,...e},"*")}function te(){n.frameReady=!1,ee.src=`${b.artifactUrl??`${f}/a/`}?r=${Date.now()}`}window.addEventListener("message",e=>{if(e.source!==ee.contentWindow)return;let t=e.data;if(!(!t||t.austere!==1))switch(t.type){case"hello":C({type:"init",mode:n.closed?"explore":n.mode,state:I.get(`austere:form:${w}`),scrollY:n.scrollY});break;case"ready":n.frameReady=!0,t.title&&(l(".title").textContent=t.title,document.title=`${t.title} \xB7 austere`),n.highlight&&z(!1);break;case"annotate":V(t.item);break;case"action":V(t.item),we();break;case"errors":n.parserErrors=(t.errors||[]).filter(r=>r.severity!=="warning"),p();break;case"runtime-error":let s=t.error;s&&!n.runtimeErrors.some(r=>r.statementId===s.statementId&&r.message===s.message)&&(n.runtimeErrors.push(s),p());break;case"state":I.set(`austere:form:${w}`,t.state);break;case"scroll":n.scrollY=t.y||0;break;case"changes":n.changes=Array.isArray(t.ids)?t.ids.filter(r=>typeof r=="string").slice(0,50):[],re(),n.highlight&&z(!1);break;case"toggle-mode":M(n.mode==="annotate"?"explore":"annotate");break;case"open-url":he(t.url);break}});function he(e){let t;try{t=new URL(String(e))}catch{return}!/^(https?|mailto):$/.test(t.protocol)||t.origin===location.origin||window.open(t.href,"_blank","noopener")}function _(){q.set(`austere:queue:${w}`,n.queue)}function V(e){if(n.closed||!e||typeof e!="object"||typeof e.kind!="string")return;let t=e;for(let r of["target","component","note","text","selection","excerpt","message","statement"])r in t&&(t[r]=ue(t[r]));let s=e;s.qid=`q${Date.now().toString(36)}${Math.random().toString(36).slice(2,6)}`,n.queue.push(s),_(),p(!0),S()}function ce(e){n.queue=n.queue.filter(t=>t.qid!==e),_(),p(),S()}function fe(e){return e.kind==="annotation"?{icon:"\u270E",text:e.target,sub:e.component}:e.kind==="action"?{icon:"\u2197",text:e.message,sub:"button"}:e.kind==="render-error"?{icon:"!",text:e.statement||"page",sub:"render issue"}:{icon:"",text:"",sub:""}}function ne(e,t=""){let s=[];for(let[r,a]of Object.entries(e||{}))a&&typeof a=="object"&&!Array.isArray(a)?s.push(...ne(a,"")):s.push(`${t}${r}: ${Array.isArray(a)?a.join(", "):a}`);return s}var ue=e=>typeof e=="string"?e:e==null?"":String(e);function me(e){if(e.selection)return e.selection;if(e.detail?.part)return e.excerpt?`${e.detail.part} ${e.excerpt}`:e.detail.part;if(e.detail?.column){let t=[e.detail.row&&`row ${e.detail.row}`,`column ${e.detail.column}`].filter(Boolean).join(", ");return e.excerpt?`${e.excerpt} (${t})`:t}return e.kind==="action"&&e.form?ne(e.form).join(" \xB7 "):e.kind==="render-error"?e.message:e.excerpt||""}function ae(e,t={}){try{return be(e,t)}catch{return o("div",{class:"item"},o("div",{class:"note",text:"This note could not be displayed."}))}}function be(e,{queued:t,held:s}={}){let r=fe(e),a=o("div",{class:`item kind-${e.kind}`});if(r.text){let c=o("button",{type:"button",class:"anchor",title:e.kind==="annotation"?"Show in the page":"",onclick:()=>{(e.kind==="annotation"||e.kind==="render-error")&&(C({type:"reveal",id:e.target||e.statement}),oe()&&T(!1))}},o("span",{class:"anchor-icon",text:r.icon}),o("span",{class:"anchor-text",text:r.text}),r.sub?o("span",{class:"anchor-sub",text:r.sub}):null);a.append(c)}if(s&&e.kind==="annotation"){let c=(Array.isArray(e.path)?e.path:[]).slice(0,-1).filter(y=>typeof y=="string"&&y!=="root");c.length&&a.append(o("div",{class:"anchor-sub",text:`within ${c.join(" > ")}`})),e.detail&&typeof e.detail=="object"&&a.append(o("div",{class:"anchor-sub",text:ne(e.detail).join(" \xB7 ")}))}let i=ue(me(e));i&&a.append(o("blockquote",{text:s||i.length<=260?i:i.slice(0,259)+"\u2026"}));let d=e.kind==="message"?e.text:e.note;if(t&&n.editing===e.qid){let c=o("textarea",{class:"edit",rows:"2"});c.value=d||"";let y=()=>{let k=c.value.trim();if(e.kind==="message"){if(!k)return ce(e.qid);e.text=k}else e.note=k;n.editing=null,_(),p()};c.addEventListener("keydown",k=>{k.key==="Enter"&&!k.shiftKey?(k.preventDefault(),y()):k.key==="Escape"&&(n.editing=null,p())}),c.addEventListener("blur",y),a.append(c),setTimeout(()=>c.focus(),0)}else d&&a.append(o("div",{class:"note",text:d}));return a}function X(e){return e?new Date(e).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"}):""}function ye(e){return m?e.author||"You":e.author===v?"You":e.author||"Owner"}async function D(e,t){try{await fetch(`${f}/${e}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t?{ids:t}:{all:!0})})}catch{Y("Could not reach the server. Try again.")}}function ve(e){if(g)return e.author===v?o("div",{class:"receipt"},o("em",{text:"Sent to the page owner"})):null;if(e.held){let r=o("div",{class:"receipt held"},o("span",{class:"held-label",text:m?"Held \xB7 not sent to the agent":"Waiting for the owner to send it on"}));return m&&r.append(o("button",{type:"button",class:"btn tiny primary",text:"Send",onclick:()=>D("release",[e.id])}),o("button",{type:"button",class:"btn tiny ghost",text:"Discard",onclick:()=>D("discard",[e.id])})),r}let t=[["Seen",e.seenAt],["Working",e.workAt],["Done",e.doneAt]],s=o("div",{class:"receipt"});for(let[r,a]of t)s.append(o("span",{class:a?"on":"",title:a?`${r} ${X(a)}`:`Not ${r.toLowerCase()} yet`,text:r}));return e.seenAt||s.append(o("em",{text:n.presence==="listening"?"":n.unread?`waiting for the agent (${n.unread} unread)`:"waiting for the agent"})),s}var ie=0;function p(e=!1){let t=u.scrollHeight-u.scrollTop-u.clientHeight<40;u.textContent="";let s=[...n.parserErrors,...n.runtimeErrors];if(s.length){let i=o("div",{class:"issues"},o("div",{class:"issues-head",text:`Render issues (${s.length})`}));for(let d of s.slice(0,8))i.append(o("div",{class:"issue"},o("div",{class:"issue-text"},d.statementId?o("code",{text:d.statementId}):null," ",d.message),n.closed?null:o("button",{type:"button",class:"btn tiny",text:"Queue fix",onclick:()=>V({kind:"render-error",statement:d.statementId,target:d.statementId,message:d.message,note:"Fix this render error."})})));u.append(i)}n.more&&u.append(o("button",{type:"button",class:"btn tiny ghost load-earlier",text:"Load earlier messages",onclick:ke})),!n.transcript.length&&!n.queue.length&&u.append(o("div",{class:"empty"},o("p",{html:"<strong>Point at anything to comment on it.</strong>"}),o("p",{text:`In Annotate mode, click a section, table cell, diagram node or diff line, or select text. Notes queue here; Send delivers them to the ${g?"page owner":"agent"}.`})));for(let i of n.transcript)if(i.role==="agent")u.append(o("div",{class:"msg agent"},o("div",{class:"who",text:`Agent \xB7 ${X(i.at)}`}),o("div",{class:"md",html:ge(i.text)})));else{let d=o("div",{class:`msg user${i.held?" held":""}`},o("div",{class:"who",text:`${ye(i)} \xB7 ${X(i.at)}${i.end?" \xB7 ended review":""}`}));for(let y of i.items||[])d.append(ae(y,{held:!!i.held}));let c=ve(i);c&&d.append(c),u.append(d)}let r=m?n.transcript.filter(i=>i.held).length:0;if(r&&u.append(o("div",{class:"held-bar"},o("span",{text:`${r} held from teammates`}),o("button",{type:"button",class:"btn tiny primary",text:"Send all",onclick:()=>D("release",n.transcript.filter(i=>i.held).map(i=>i.id))}),o("button",{type:"button",class:"btn tiny ghost",text:"Discard all",onclick:()=>D("discard")}))),n.queue.length){let i=o("div",{class:"queued"},o("div",{class:"queued-head",text:`Queued \xB7 ${n.queue.length}`}));for(let d of n.queue){let c=ae(d,{queued:!0});c.classList.add("q"),c.append(o("div",{class:"q-actions"},o("button",{type:"button",class:"btn tiny ghost",text:"Edit",onclick:()=>{n.editing=d.qid,p()}}),o("button",{type:"button",class:"btn tiny ghost",text:"Remove",onclick:()=>ce(d.qid)}))),i.append(c)}u.append(i)}if(n.closed)u.append(o("div",{class:"ended-note",text:m?"You closed this review.":"The owner closed this review."}));else if(n.status==="ended"){let i=n.endedBy==="agent"?"The agent ended this review.":m?"You ended this review.":"The owner ended this review.";u.append(o("div",{class:"ended-note",text:`${i} Notes you send now reach the agent the next time it polls.`}))}let a=n.transcript.length+n.queue.length;(e||t||a!==ie)&&(u.scrollTop=u.scrollHeight),ie=a,se()}var N=!1;function se(){let e=n.closed,t=n.queue.length;A.disabled=e||N||n.sending,l(".send").disabled=e||N||n.sending,l(".send-end")&&(l(".send-end").disabled=e||n.sending),l(".send").textContent=n.sending?"Sending\u2026":t?`Send ${t}`:"Send",A.placeholder=e?"Review closed":t?"Add a message (optional)\u2026":"Message the agent\u2026"}function Y(e){let t=l(".composer-error");t.textContent=e||"",t.hidden=!e}async function O(e=!1){if(n.sending||n.closed)return;let t=A.value.trim(),s=n.queue.map(({qid:r,...a})=>a);if(t&&s.push({kind:"message",text:t}),!s.length&&!e){A.focus();return}if(!(e&&!m)&&!(e&&!s.length&&!confirm("End this review? The agent will stop listening; notes you send later still reach it."))){if(g&&!v&&x){x.hidden=!1,l("input",x).focus();return}n.sending=!0,Y(""),se();try{let r=await fetch(`${f}/send`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(g?{name:v,items:s}:{items:s,end:e})}),a=await r.json().catch(()=>({}));if(!r.ok)throw new Error(a.error||`HTTP ${r.status}`);n.queue=[],_(),A.value="";let i=a.entry;i&&!n.transcript.some(d=>d.id===i.id)&&n.transcript.push(i),g&&R()}catch(r){Y(`Not sent: ${r.message}. Your notes are still queued.`)}finally{n.sending=!1,p(!0),S()}}}l(".composer").addEventListener("submit",e=>{e.preventDefault(),O(!1)});l(".send-end")?.addEventListener("click",()=>O(!0));A.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&!e.isComposing&&(e.preventDefault(),O(!1))});function M(e){n.mode=e==="explore"?"explore":"annotate",q.set("austere:mode",n.mode);for(let t of document.querySelectorAll(".seg button"))t.classList.toggle("on",t.dataset.mode===n.mode);C({type:"mode",mode:n.closed?"explore":n.mode})}for(let e of document.querySelectorAll(".seg button"))e.addEventListener("click",()=>M(e.dataset.mode));document.addEventListener("keydown",e=>{(e.metaKey||e.ctrlKey)&&(e.key==="i"||e.key==="I")&&(e.preventDefault(),M(n.mode==="annotate"?"explore":"annotate"))});function E(){let e=l(".presence"),t,s;n.closed?[t,s]=["Review closed","ended"]:N?[t,s]=["Page removed","ended"]:n.connected?g?[t,s]=["Shared page","hosted"]:n.presence==="listening"?[t,s]=["Agent listening","listening"]:n.presence==="working"?[t,s]=["Agent working","working"]:n.status==="ended"?[t,s]=["Agent stopped","idle"]:[t,s]=["Agent not listening","idle"]:[t,s]=["Reconnecting\u2026","off"],s==="idle"&&n.unread&&(t+=` \xB7 ${n.unread} waiting`),e.className=`presence ${s}`,e.textContent=t,e.title=s==="idle"?"Your notes are kept and reach the agent the next time it polls.":s==="working"?"The agent has your last batch.":s==="hosted"?"Notes go to the page owner. Replies show up within 30 seconds.":"",document.documentElement.classList.toggle("is-ended",n.closed),m&&(l('[data-cmd="end"]').hidden=n.status==="ended",l('[data-cmd="close"]').hidden=n.closed,l('[data-cmd="reopen"]').hidden=!n.closed&&n.status!=="ended"),S()}function re(){let e=l(".changes");if(e.setAttribute("aria-pressed",String(n.highlight)),!n.changes.length&&!n.highlight){e.hidden=!0;return}e.hidden=!1,e.textContent=`${n.changes.length} changed`,e.title=n.changes.length?`${n.highlight?"Hide":"Highlight"} what the last turn changed: ${n.changes.join(", ")}`:"The last turn changed nothing on the page. Click to stop highlighting."}function z(e){C({type:"highlight",ids:n.highlight?n.changes:[],scroll:e})}l(".changes").addEventListener("click",()=>{n.highlight=!n.highlight,re(),z(n.highlight)});var Q=l(".menu-pop");l(".menu-btn").addEventListener("click",e=>{e.stopPropagation(),Q.hidden=!Q.hidden});document.addEventListener("click",()=>Q.hidden=!0);Q.addEventListener("click",async e=>{let t=e.target instanceof Element?e.target.closest("[data-cmd]")?.dataset.cmd:void 0;if(t){if(t==="copy-path"&&navigator.clipboard?.writeText(b.file),t==="copy-link"&&navigator.clipboard?.writeText(n.links?.remote||location.href),t==="reload"&&te(),t==="end"){if(n.queue.length)return O(!0);if(!confirm("End this review? The agent will stop listening."))return;await fetch(`${f}/end`,{method:"POST"}).catch(()=>{})}if(t==="close"){if(!confirm("Close this review? No one can add notes until you reopen it."))return;await fetch(`${f}/close`,{method:"POST"}).catch(()=>{})}t==="reopen"&&await fetch(`${f}/reopen`,{method:"POST"}).catch(()=>{})}});var oe=()=>window.matchMedia("(max-width: 860px)").matches;function T(e){n.sheetOpen=e,I.set(`austere:sheet:${w}`,e),document.documentElement.classList.toggle("sheet-open",e),e&&setTimeout(()=>u.scrollTop=u.scrollHeight,50)}function S(){let e=l(".dock"),t=n.queue.length,s=["Conversation"];t&&s.push(`${t} queued`);let r=n.transcript[n.transcript.length-1];r?.role==="agent"&&r.at>(I.get(`austere:seen:${w}`)||0)&&s.push("new reply"),e.innerHTML=`<span class="dock-grip"></span><span>${j(s.join(" \xB7 "))}</span><span class="dock-presence ${j(l(".presence").className.replace("presence","").trim())}"></span>`}function we(){let e=l(".dock");e.classList.remove("pulse"),e.offsetWidth,e.classList.add("pulse")}l(".dock").addEventListener("click",()=>{I.set(`austere:seen:${w}`,Date.now()),T(!0),S()});l(".sheet-close").addEventListener("click",()=>T(!1));l(".scrim").addEventListener("click",()=>T(!1));document.addEventListener("keydown",e=>{e.key==="Escape"&&n.sheetOpen&&oe()&&T(!1)});var $=null;l(".panel-head").addEventListener("touchstart",e=>$=e.touches[0].clientY,{passive:!0});l(".panel-head").addEventListener("touchend",e=>{$!=null&&e.changedTouches[0].clientY-$>60&&T(!1),$=null},{passive:!0});l(".dock").addEventListener("touchstart",e=>$=e.touches[0].clientY,{passive:!0});l(".dock").addEventListener("touchend",e=>{$!=null&&$-e.changedTouches[0].clientY>40&&T(!0),$=null},{passive:!0});function G(e){let t=l(".notice");if(!e||!e.length){t.hidden=!0;return}t.textContent=`Third-party pack code runs in this page: check queued items before you send (${e.join(", ")}).`,t.title=t.textContent,t.hidden=!1}function L(e,t=[]){if(P.textContent="",!e){P.hidden=!0;return}P.append(o("span",{text:e}),...t),P.hidden=!1}function pe(e){for(let t of e){let s=n.transcript.findIndex(r=>r.id===t.id);t.discarded?s>=0&&n.transcript.splice(s,1):s>=0?n.transcript[s]=t:n.transcript.push(t)}n.transcript.sort((t,s)=>t.at-s.at)}async function ke(){let e=n.transcript[0];if(e)try{let t=await fetch(`${f}/transcript?before=${encodeURIComponent(e.id)}`);if(!t.ok)throw new Error(`HTTP ${t.status}`);let s=await t.json(),r=new Set(n.transcript.map(i=>i.id));n.transcript=[...(s.entries||[]).filter(i=>!i.discarded&&!r.has(i.id)),...n.transcript],n.more=!!s.more;let a=u.scrollHeight-u.scrollTop;p(),u.scrollTop=u.scrollHeight-a}catch(t){Y(`Could not load earlier messages: ${t.message}`)}}var le=null,B=0;function xe(){le=new EventSource(`${f}/events`);let e=le;e.addEventListener("open",()=>{n.connected=!0,B=0,L(""),E()}),e.addEventListener("error",()=>{n.connected=!1,B||(B=Date.now()),E(),setTimeout(()=>{!n.connected&&Date.now()-B>3500&&L("Lost the austere-axi server. Run any austere-axi command to restart it; this page reconnects on its own.",[o("button",{type:"button",class:"btn tiny",text:"Reload",onclick:()=>location.reload()})])},4e3)}),e.addEventListener("snapshot",t=>{let s=JSON.parse(t.data);n.transcript=(s.transcript||[]).filter(r=>!r.discarded),n.presence=s.presence,n.status=s.session.status,n.endedBy=s.session.endedBy,n.closed=!!s.session.closed,n.unread=s.session.unread||0,n.more=!!s.more,n.links=s.links,s.version&&s.version!==b.version&&L("austere-axi was updated. This page is running the previous version.",[o("button",{type:"button",class:"btn tiny",text:"Reload",onclick:()=>location.reload()})]),G(s.thirdPartyPacks),n.program===null?n.program=s.program:s.program!==n.program&&(n.program=s.program,n.frameReady&&C({type:"source",program:s.program})),E(),p(),M(n.mode)}),e.addEventListener("transcript",t=>{pe(JSON.parse(t.data).entries||[]),p(),S()}),e.addEventListener("presence",t=>{let s=JSON.parse(t.data);n.presence=s.presence,n.status=s.status,n.endedBy=s.endedBy,n.closed=!!s.closed,n.unread=s.unread||0,M(n.mode),E(),p()}),e.addEventListener("changed",t=>{let s=JSON.parse(t.data);n.parserErrors=[],n.runtimeErrors=[],G(s.thirdPartyPacks),s.shellChanged||!n.frameReady?(n.program=s.program,n.changes=[],re(),te()):(n.program=s.program,C({type:"source",program:s.program})),p()}),e.addEventListener("ended",t=>{let s=JSON.parse(t.data);n.status="ended",n.endedBy=s.by,s.closed&&(n.closed=!0,C({type:"mode",mode:"explore"})),E(),p()}),e.addEventListener("server",t=>{let s=JSON.parse(t.data);if(s.state==="revoked"){e.close(),n.connected=!1,L("Your invite was revoked. Ask the owner for a new link.");return}s.state==="stopped"&&L("The austere-axi server stopped. Run any austere-axi command to start it again; this page reconnects on its own.")})}var Ee=3e4,F=null,U=null,H=null,K=!1;function $e(e){let t=(e.notes||[]).map(r=>({id:`n${r.seq}`,role:"user",author:String(r.author||"").slice(0,J),items:Array.isArray(r.items)?r.items.filter(a=>!!a&&typeof a=="object"):[],at:r.at})),s=(e.replies||[]).map(r=>({id:`r${r.seq}`,role:"agent",text:String(r.text||""),at:r.at}));return[...t,...s]}async function R(){if(!N)return H?(K=!0,H):(H=(async()=>{try{let e=await fetch(`${f}/conversation${F==null?"":`?after=${encodeURIComponent(F)}`}`,{cache:"no-store"});if(e.status===404||e.status===410){N=!0,n.connected=!1,L("This page is no longer available."),E(),se();return}if(!e.ok)throw new Error(`HTTP ${e.status}`);let t=await e.json();n.connected=!0,U==null?U=t.publishedAt:t.publishedAt!==U&&L("New version, reload",[o("button",{type:"button",class:"btn tiny",text:"Reload",onclick:()=>location.reload()})]),t.cursor!=null&&(F=t.cursor),pe($e(t)),G(t.thirdPartyPacks)}catch{n.connected=!1}finally{H=null,E(),p(),S()}K&&(K=!1,await R())})(),H)}function Te(){R(),setInterval(R,Ee),window.addEventListener("focus",()=>R())}g||window.addEventListener("pagehide",e=>{e.persisted||navigator.sendBeacon?.(`${f}/bye`)});M(n.mode);oe()&&n.sheetOpen&&T(!0);E();p();S();te();g?Te():xe();})();
57
+ `),s=[],r=0;for(;r<n.length;){let i=n[r];if(/^```/.test(i)){let u=[];for(r++;r<n.length&&!/^```/.test(n[r]);)u.push(n[r++]);r++,s.push(`<pre><code>${B(u.join(`
58
+ `))}</code></pre>`);continue}if(/^\s*([-*]|\d+\.)\s+/.test(i)){let u=/^\s*\d+\./.test(i),d=[];for(;r<n.length&&/^\s*([-*]|\d+\.)\s+/.test(n[r]);)d.push(n[r++].replace(/^\s*([-*]|\d+\.)\s+/,""));s.push(`<${u?"ol":"ul"}>${d.map(g=>`<li>${F(g)}</li>`).join("")}</${u?"ol":"ul"}>`);continue}if(/^#{1,6}\s+/.test(i)){s.push(`<p><strong>${F(i.replace(/^#+\s+/,""))}</strong></p>`),r++;continue}if(!i.trim()){r++;continue}let a=[];for(;r<n.length&&n[r].trim()&&!/^```|^\s*([-*]|\d+\.)\s+|^#{1,6}\s/.test(n[r]);)a.push(n[r++]);s.push(`<p>${a.map(F).join("<br>")}</p>`)}return s.join("")}function C(e){ne.contentWindow?.postMessage({austere:1,...e},"*")}function se(){t.frameReady=!1,ne.src=`${y.artifactUrl??`${b}/a/`}?r=${Date.now()}`}window.addEventListener("message",e=>{if(e.source!==ne.contentWindow)return;let n=e.data;if(!(!n||n.austere!==1))switch(n.type){case"hello":C({type:"init",mode:t.closed?"explore":t.mode,state:q.get(`austere:form:${w}`),scrollY:t.scrollY});break;case"ready":t.frameReady=!0,n.title&&(l(".title").textContent=n.title,document.title=`${n.title} \xB7 austere`),t.highlight&&Z(!1);break;case"annotate":X(n.item);break;case"action":X(n.item),xe();break;case"errors":t.parserErrors=(n.errors||[]).filter(r=>r.severity!=="warning"),t.errorsRevision=t.revision,p(),z();break;case"runtime-error":let s=n.error;s&&!t.runtimeErrors.some(r=>r.statementId===s.statementId&&r.message===s.message)&&(t.runtimeErrors.push(s),t.errorsRevision=t.revision,p(),z());break;case"state":q.set(`austere:form:${w}`,n.state);break;case"scroll":t.scrollY=n.y||0;break;case"changes":t.changes=Array.isArray(n.ids)?n.ids.filter(r=>typeof r=="string").slice(0,50):[],ie(),t.highlight&&Z(!1);break;case"toggle-mode":I(t.mode==="annotate"?"explore":"annotate");break;case"open-url":me(n.url);break}});function me(e){let n;try{n=new URL(String(e))}catch{return}!/^(https?|mailto):$/.test(n.protocol)||n.origin===location.origin||window.open(n.href,"_blank","noopener")}function W(){A.set(`austere:queue:${w}`,t.queue)}function X(e){if(t.closed||!e||typeof e!="object"||typeof e.kind!="string")return;let n=e;for(let r of["target","component","note","text","selection","excerpt","message","statement"])r in n&&(n[r]=D(n[r]));let s=e;s.qid=`q${Date.now().toString(36)}${Math.random().toString(36).slice(2,6)}`,t.queue.push(s),W(),p(!0),S()}var le;function z(){!m||t.closed||t.thirdParty||(clearTimeout(le),le=setTimeout(async()=>{let e=[...t.parserErrors,...t.runtimeErrors].map(s=>({statement:D(s.statementId),message:D(s.message)}));if(!e.length)return;let n=await fetch(`${b}/render-errors`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({revision:t.errorsRevision,errors:e})}).then(async s=>s.ok?await s.json():null,()=>null);t.renderReport=n&&!n.stale?"sent":n?null:"failed",p()},500))}function ge(e){t.queue=t.queue.filter(n=>n.qid!==e),W(),p(),S()}function be(e){return e.kind==="annotation"?{icon:"\u270E",text:e.target,sub:e.component}:e.kind==="action"?{icon:"\u2197",text:e.message,sub:"button"}:e.kind==="render-error"?{icon:"!",text:e.statement||"page",sub:"render issue"}:{icon:"",text:"",sub:""}}function re(e,n=""){let s=[];for(let[r,i]of Object.entries(e||{}))i&&typeof i=="object"&&!Array.isArray(i)?s.push(...re(i,"")):s.push(`${n}${r}: ${Array.isArray(i)?i.join(", "):i}`);return s}var D=e=>typeof e=="string"?e:e==null?"":String(e);function ye(e){if(e.selection)return e.selection;if(e.detail?.part)return e.excerpt?`${e.detail.part} ${e.excerpt}`:e.detail.part;if(e.detail?.column){let n=[e.detail.row&&`row ${e.detail.row}`,`column ${e.detail.column}`].filter(Boolean).join(", ");return e.excerpt?`${e.excerpt} (${n})`:n}return e.kind==="action"&&e.form?re(e.form).join(" \xB7 "):e.kind==="render-error"?e.message:e.excerpt||""}function de(e,n={}){try{return ve(e,n)}catch{return o("div",{class:"item"},o("div",{class:"note",text:"This note could not be displayed."}))}}function ve(e,{queued:n,held:s}={}){let r=be(e),i=o("div",{class:`item kind-${e.kind}`});if(r.text){let d=o("button",{type:"button",class:"anchor",title:e.kind==="annotation"?"Show in the page":"",onclick:()=>{(e.kind==="annotation"||e.kind==="render-error")&&(C({type:"reveal",id:e.target||e.statement}),ae()&&T(!1))}},o("span",{class:"anchor-icon",text:r.icon}),o("span",{class:"anchor-text",text:r.text}),r.sub?o("span",{class:"anchor-sub",text:r.sub}):null);i.append(d)}if(s&&e.kind==="annotation"){let d=(Array.isArray(e.path)?e.path:[]).slice(0,-1).filter(g=>typeof g=="string"&&g!=="root");d.length&&i.append(o("div",{class:"anchor-sub",text:`within ${d.join(" > ")}`})),e.detail&&typeof e.detail=="object"&&i.append(o("div",{class:"anchor-sub",text:re(e.detail).join(" \xB7 ")}))}let a=D(ye(e));a&&i.append(o("blockquote",{text:s||a.length<=260?a:a.slice(0,259)+"\u2026"}));let u=e.kind==="message"?e.text:e.note;if(n&&t.editing===e.qid){let d=o("textarea",{class:"edit",rows:"2"});d.value=u||"";let g=()=>{let k=d.value.trim();if(e.kind==="message"){if(!k)return ge(e.qid);e.text=k}else e.note=k;t.editing=null,W(),p()};d.addEventListener("keydown",k=>{k.key==="Enter"&&!k.shiftKey?(k.preventDefault(),g()):k.key==="Escape"&&(t.editing=null,p())}),d.addEventListener("blur",g),i.append(d),setTimeout(()=>d.focus(),0)}else u&&i.append(o("div",{class:"note",text:u}));return i}function G(e){return e?new Date(e).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"}):""}function we(e){return m?e.author||"You":e.author===v?"You":e.author||"Owner"}async function Y(e,n){try{await fetch(`${b}/${e}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n?{ids:n}:{all:!0})})}catch{Q("Could not reach the server. Try again.")}}function ke(e){if(h)return e.author===v?o("div",{class:"receipt"},o("em",{text:"Sent to the page owner"})):null;if(e.held){let r=o("div",{class:"receipt held"},o("span",{class:"held-label",text:m?"Held \xB7 not sent to the agent":"Waiting for the owner to send it on"}));return m&&r.append(o("button",{type:"button",class:"btn tiny primary",text:"Send",onclick:()=>Y("release",[e.id])}),o("button",{type:"button",class:"btn tiny ghost",text:"Discard",onclick:()=>Y("discard",[e.id])})),r}let n=[["Seen",e.seenAt],["Working",e.workAt],["Done",e.doneAt]],s=o("div",{class:"receipt"});for(let[r,i]of n)s.append(o("span",{class:i?"on":"",title:i?`${r} ${G(i)}`:`Not ${r.toLowerCase()} yet`,text:r}));return e.seenAt||s.append(o("em",{text:t.presence==="listening"?"":t.unread?`waiting for the agent (${t.unread} unread)`:"waiting for the agent"})),s}var ce=0;function p(e=!1){let n=c.scrollHeight-c.scrollTop-c.clientHeight<40;c.textContent="";let s=[...t.parserErrors,...t.runtimeErrors];if(s.length){let a=m&&t.renderReport==="sent",u=`Render issues (${s.length})${a?" \xB7 sent to the agent":""}`,d=o("div",{class:"issues"},o("div",{class:"issues-head",text:u}));for(let g of s.slice(0,8))d.append(o("div",{class:"issue"},o("div",{class:"issue-text"},g.statementId?o("code",{text:g.statementId}):null," ",g.message),t.closed||a?null:o("button",{type:"button",class:"btn tiny",text:"Queue fix",onclick:()=>X({kind:"render-error",statement:g.statementId,target:g.statementId,message:g.message,note:"Fix this render error."})})));c.append(d)}t.more&&c.append(o("button",{type:"button",class:"btn tiny ghost load-earlier",text:"Load earlier messages",onclick:Ee})),!t.transcript.length&&!t.queue.length&&c.append(o("div",{class:"empty"},o("p",{html:"<strong>Point at anything to comment on it.</strong>"}),o("p",{text:`In Annotate mode, click a section, table cell, diagram node or diff line, or select text. Notes queue here; Send delivers them to the ${h?"page owner":"agent"}.`})));for(let a of t.transcript)if(a.role==="agent")c.append(o("div",{class:"msg agent"},o("div",{class:"who",text:`Agent \xB7 ${G(a.at)}`}),o("div",{class:"md",html:fe(a.text)})));else{let u=o("div",{class:`msg user${a.held?" held":""}`},o("div",{class:"who",text:`${we(a)} \xB7 ${G(a.at)}${a.end?" \xB7 ended review":""}`}));for(let g of a.items||[])u.append(de(g,{held:!!a.held}));let d=ke(a);d&&u.append(d),c.append(u)}let r=m?t.transcript.filter(a=>a.held).length:0;if(r&&c.append(o("div",{class:"held-bar"},o("span",{text:`${r} held from teammates`}),o("button",{type:"button",class:"btn tiny primary",text:"Send all",onclick:()=>Y("release",t.transcript.filter(a=>a.held).map(a=>a.id))}),o("button",{type:"button",class:"btn tiny ghost",text:"Discard all",onclick:()=>Y("discard")}))),t.queue.length){let a=o("div",{class:"queued"},o("div",{class:"queued-head",text:`Queued \xB7 ${t.queue.length}`}));for(let u of t.queue){let d=de(u,{queued:!0});d.classList.add("q"),d.append(o("div",{class:"q-actions"},o("button",{type:"button",class:"btn tiny ghost",text:"Edit",onclick:()=>{t.editing=u.qid,p()}}),o("button",{type:"button",class:"btn tiny ghost",text:"Remove",onclick:()=>ge(u.qid)}))),a.append(d)}c.append(a)}if(t.closed)c.append(o("div",{class:"ended-note",text:m?"You closed this review.":"The owner closed this review."}));else if(t.status==="ended"){let a=t.endedBy==="agent"?"The agent ended this review.":m?"You ended this review.":"The owner ended this review.";c.append(o("div",{class:"ended-note",text:`${a} Notes you send now reach the agent the next time it polls.`}))}let i=t.transcript.length+t.queue.length;(e||n||i!==ce)&&(c.scrollTop=c.scrollHeight),ce=i,oe()}var H=!1;function oe(){let e=t.closed,n=t.queue.length;R.disabled=e||H||t.sending,l(".send").disabled=e||H||t.sending,l(".send-end")&&(l(".send-end").disabled=e||t.sending),l(".send").textContent=t.sending?"Sending\u2026":n?`Send ${n}`:"Send",R.placeholder=e?"Review closed":n?"Add a message (optional)\u2026":"Message the agent\u2026"}function Q(e){let n=l(".composer-error");n.textContent=e||"",n.hidden=!e}async function N(e=!1){if(t.sending||t.closed)return;let n=R.value.trim(),s=t.queue.map(({qid:r,...i})=>i);if(n&&s.push({kind:"message",text:n}),!s.length&&!e){R.focus();return}if(!(e&&!m)&&!(e&&!s.length&&!confirm("End this review? The agent will stop listening; notes you send later still reach it."))){if(h&&!v&&x){x.hidden=!1,l("input",x).focus();return}t.sending=!0,Q(""),oe();try{let r=await fetch(`${b}/send`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(h?{name:v,items:s}:{items:s,end:e})}),i=await r.json().catch(()=>({}));if(!r.ok)throw new Error(i.error||`HTTP ${r.status}`);t.queue=[],W(),R.value="";let a=i.entry;a&&!t.transcript.some(u=>u.id===a.id)&&t.transcript.push(a),h&&P()}catch(r){Q(`Not sent: ${r.message}. Your notes are still queued.`)}finally{t.sending=!1,p(!0),S()}}}l(".composer").addEventListener("submit",e=>{e.preventDefault(),N(!1)});l(".send-end")?.addEventListener("click",()=>N(!0));R.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&!e.isComposing&&(e.preventDefault(),N(!1))});function I(e){t.mode=e==="explore"?"explore":"annotate",A.set("austere:mode",t.mode);let n=document.documentElement,s=n.classList.contains("exploring");n.classList.toggle("exploring",t.mode==="explore"),s&&t.mode==="annotate"&&(c.scrollTop=c.scrollHeight);for(let r of document.querySelectorAll(".seg button"))r.classList.toggle("on",r.dataset.mode===t.mode);C({type:"mode",mode:t.closed?"explore":t.mode})}for(let e of document.querySelectorAll(".seg button"))e.addEventListener("click",()=>I(e.dataset.mode));document.addEventListener("keydown",e=>{(e.metaKey||e.ctrlKey)&&(e.key==="i"||e.key==="I")&&(e.preventDefault(),I(t.mode==="annotate"?"explore":"annotate"))});function E(){let e=l(".presence"),n,s;t.closed?[n,s]=["Review closed","ended"]:H?[n,s]=["Page removed","ended"]:t.connected?h?[n,s]=["Shared page","hosted"]:t.presence==="listening"?[n,s]=["Agent listening","listening"]:t.presence==="working"?[n,s]=["Agent working","working"]:t.status==="ended"?[n,s]=["Agent stopped","idle"]:[n,s]=["Agent not listening","idle"]:[n,s]=["Reconnecting\u2026","off"],s==="idle"&&t.unread&&(n+=` \xB7 ${t.unread} waiting`),e.className=`presence ${s}`,e.textContent=n,e.title=s==="idle"?"Your notes are kept and reach the agent the next time it polls.":s==="working"?"The agent has your last batch.":s==="hosted"?"Notes go to the page owner. Replies show up within 30 seconds.":"",document.documentElement.classList.toggle("is-ended",t.closed),m&&(l('[data-cmd="end"]').hidden=t.status==="ended",l('[data-cmd="close"]').hidden=t.closed,l('[data-cmd="reopen"]').hidden=!t.closed&&t.status!=="ended"),S()}function ie(){let e=l(".changes");if(e.setAttribute("aria-pressed",String(t.highlight)),!t.changes.length&&!t.highlight){e.hidden=!0;return}e.hidden=!1,e.textContent=`${t.changes.length} changed`,e.title=t.changes.length?`${t.highlight?"Hide":"Highlight"} what the last turn changed: ${t.changes.join(", ")}`:"The last turn changed nothing on the page. Click to stop highlighting."}function Z(e){C({type:"highlight",ids:t.highlight?t.changes:[],scroll:e})}l(".changes").addEventListener("click",()=>{t.highlight=!t.highlight,ie(),Z(t.highlight)});var J=l(".menu-pop");l(".menu-btn").addEventListener("click",e=>{e.stopPropagation(),J.hidden=!J.hidden});document.addEventListener("click",()=>J.hidden=!0);J.addEventListener("click",async e=>{let n=e.target instanceof Element?e.target.closest("[data-cmd]")?.dataset.cmd:void 0;if(n){if(n==="copy-path"&&navigator.clipboard?.writeText(y.file),n==="copy-link"&&navigator.clipboard?.writeText(t.links?.remote||location.href),n==="reload"&&se(),n==="end"){if(t.queue.length)return N(!0);if(!confirm("End this review? The agent will stop listening."))return;await fetch(`${b}/end`,{method:"POST"}).catch(()=>{})}if(n==="close"){if(!confirm("Close this review? No one can add notes until you reopen it."))return;await fetch(`${b}/close`,{method:"POST"}).catch(()=>{})}n==="reopen"&&await fetch(`${b}/reopen`,{method:"POST"}).catch(()=>{})}});var ae=()=>window.matchMedia("(max-width: 860px)").matches;function T(e){t.sheetOpen=e,q.set(`austere:sheet:${w}`,e),document.documentElement.classList.toggle("sheet-open",e),e&&setTimeout(()=>c.scrollTop=c.scrollHeight,50)}function S(){let e=l(".dock"),n=t.queue.length,s=["Conversation"];n&&s.push(`${n} queued`);let r=t.transcript[t.transcript.length-1];r?.role==="agent"&&r.at>(q.get(`austere:seen:${w}`)||0)&&s.push("new reply"),e.innerHTML=`<span class="dock-grip"></span><span>${B(s.join(" \xB7 "))}</span><span class="dock-presence ${B(l(".presence").className.replace("presence","").trim())}"></span>`}function xe(){let e=l(".dock");e.classList.remove("pulse"),e.offsetWidth,e.classList.add("pulse")}l(".dock").addEventListener("click",()=>{q.set(`austere:seen:${w}`,Date.now()),T(!0),S()});l(".sheet-close").addEventListener("click",()=>T(!1));l(".scrim").addEventListener("click",()=>T(!1));document.addEventListener("keydown",e=>{e.key==="Escape"&&t.sheetOpen&&ae()&&T(!1)});var $=null;l(".panel-head").addEventListener("touchstart",e=>$=e.touches[0].clientY,{passive:!0});l(".panel-head").addEventListener("touchend",e=>{$!=null&&e.changedTouches[0].clientY-$>60&&T(!1),$=null},{passive:!0});l(".dock").addEventListener("touchstart",e=>$=e.touches[0].clientY,{passive:!0});l(".dock").addEventListener("touchend",e=>{$!=null&&$-e.changedTouches[0].clientY>40&&T(!0),$=null},{passive:!0});function ee(e){let n=l(".notice");if(!e||!e.length){n.hidden=!0;return}n.textContent=`Third-party pack code runs in this page: check queued items before you send (${e.join(", ")}).`,n.title=n.textContent,n.hidden=!1}function L(e,n=[]){if(O.textContent="",!e){O.hidden=!0;return}O.append(o("span",{text:e}),...n),O.hidden=!1}function he(e){for(let n of e){let s=t.transcript.findIndex(r=>r.id===n.id);n.discarded?s>=0&&t.transcript.splice(s,1):s>=0?t.transcript[s]=n:t.transcript.push(n)}t.transcript.sort((n,s)=>n.at-s.at)}async function Ee(){let e=t.transcript[0];if(e)try{let n=await fetch(`${b}/transcript?before=${encodeURIComponent(e.id)}`);if(!n.ok)throw new Error(`HTTP ${n.status}`);let s=await n.json(),r=new Set(t.transcript.map(a=>a.id));t.transcript=[...(s.entries||[]).filter(a=>!a.discarded&&!r.has(a.id)),...t.transcript],t.more=!!s.more;let i=c.scrollHeight-c.scrollTop;p(),c.scrollTop=c.scrollHeight-i}catch(n){Q(`Could not load earlier messages: ${n.message}`)}}var ue=null,j=0;function $e(){ue=new EventSource(`${b}/events`);let e=ue;e.addEventListener("open",()=>{t.connected=!0,j=0,L(""),E()}),e.addEventListener("error",()=>{t.connected=!1,j||(j=Date.now()),E(),setTimeout(()=>{!t.connected&&Date.now()-j>3500&&L("Lost the austere-axi server. Run any austere-axi command to restart it; this page reconnects on its own.",[o("button",{type:"button",class:"btn tiny",text:"Reload",onclick:()=>location.reload()})])},4e3)}),e.addEventListener("snapshot",n=>{let s=JSON.parse(n.data);t.transcript=(s.transcript||[]).filter(r=>!r.discarded),t.presence=s.presence,t.status=s.session.status,t.endedBy=s.session.endedBy,t.closed=!!s.session.closed,t.unread=s.session.unread||0,t.more=!!s.more,t.links=s.links,s.version&&s.version!==y.version&&L("austere-axi was updated. This page is running the previous version.",[o("button",{type:"button",class:"btn tiny",text:"Reload",onclick:()=>location.reload()})]),ee(s.thirdPartyPacks),t.thirdParty=!!s.thirdPartyPacks?.length,t.program===null||s.revision===t.revision||s.program===t.program?(s.revision!==t.revision&&(t.errorsRevision=s.revision),t.revision=s.revision,z()):(t.revision=s.revision,t.parserErrors=[],t.runtimeErrors=[],t.renderReport=null),t.program===null?t.program=s.program:s.program!==t.program&&(t.program=s.program,t.frameReady&&C({type:"source",program:s.program})),E(),p(),I(t.mode)}),e.addEventListener("transcript",n=>{he(JSON.parse(n.data).entries||[]),p(),S()}),e.addEventListener("presence",n=>{let s=JSON.parse(n.data);t.presence=s.presence,t.status=s.status,t.endedBy=s.endedBy,t.closed=!!s.closed,t.unread=s.unread||0,I(t.mode),E(),p()}),e.addEventListener("changed",n=>{let s=JSON.parse(n.data);t.revision=s.revision,t.renderReport=null,t.parserErrors=[],t.runtimeErrors=[],ee(s.thirdPartyPacks),t.thirdParty=!!s.thirdPartyPacks?.length,s.shellChanged||!t.frameReady?(t.program=s.program,t.changes=[],ie(),se()):(t.program=s.program,C({type:"source",program:s.program})),p()}),e.addEventListener("ended",n=>{let s=JSON.parse(n.data);t.status="ended",t.endedBy=s.by,s.closed&&(t.closed=!0,C({type:"mode",mode:"explore"})),E(),p()}),e.addEventListener("server",n=>{let s=JSON.parse(n.data);if(s.state==="revoked"){e.close(),t.connected=!1,L("Your invite was revoked. Ask the owner for a new link.");return}s.state==="stopped"&&L("The austere-axi server stopped. Run any austere-axi command to start it again; this page reconnects on its own.")})}var Te=3e4,U=null,K=null,M=null,V=!1;function Se(e){let n=(e.notes||[]).map(r=>({id:`n${r.seq}`,role:"user",author:String(r.author||"").slice(0,_),items:Array.isArray(r.items)?r.items.filter(i=>!!i&&typeof i=="object"):[],at:r.at})),s=(e.replies||[]).map(r=>({id:`r${r.seq}`,role:"agent",text:String(r.text||""),at:r.at}));return[...n,...s]}async function P(){if(!H)return M?(V=!0,M):(M=(async()=>{try{let e=await fetch(`${b}/conversation${U==null?"":`?after=${encodeURIComponent(U)}`}`,{cache:"no-store"});if(e.status===404||e.status===410){H=!0,t.connected=!1,L("This page is no longer available."),E(),oe();return}if(!e.ok)throw new Error(`HTTP ${e.status}`);let n=await e.json();t.connected=!0,K==null?K=n.publishedAt:n.publishedAt!==K&&L("New version, reload",[o("button",{type:"button",class:"btn tiny",text:"Reload",onclick:()=>location.reload()})]),n.cursor!=null&&(U=n.cursor),he(Se(n)),ee(n.thirdPartyPacks)}catch{t.connected=!1}finally{M=null,E(),p(),S()}V&&(V=!1,await P())})(),M)}function Le(){P(),setInterval(P,Te),window.addEventListener("focus",()=>P())}h||window.addEventListener("pagehide",e=>{e.persisted||navigator.sendBeacon?.(`${b}/bye`)});I(t.mode);ae()&&t.sheetOpen&&T(!0);E();p();S();se();h?Le():$e();})();