austere-axi 0.3.0 → 0.4.0
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 +41 -9
- package/dist/austere.js +2 -2
- package/dist/catalog.json +1 -1
- package/dist/chrome/chrome.css +1 -1
- package/dist/chrome/chrome.js +4 -4
- package/dist/cli.mjs +765 -296
- package/dist/packs/mermaid/catalog.json +2 -2
- package/dist/packs/mermaid/pack.js +1 -1
- package/dist/packs/shadcn/catalog.json +429 -0
- package/dist/packs/shadcn/pack.js +69 -0
- package/dist/prompt.md +5 -4
- package/dist/spec.json +1 -1
- package/package.json +19 -3
- package/skills/austere-setup/SKILL.md +3 -4
- package/types/catalog.d.ts +82 -0
- package/types/pack-kit.d.ts +46 -0
- package/types/pack.d.ts +104 -0
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ Fewer output tokens means faster pages and lower cost.
|
|
|
45
45
|
|
|
46
46
|
## Install
|
|
47
47
|
|
|
48
|
-
You need Node.js
|
|
48
|
+
You need Node.js 22.18 or later.
|
|
49
49
|
|
|
50
50
|
**Let your agent do it.** Install the setup skill, then ask your agent to "set up austere-axi":
|
|
51
51
|
|
|
@@ -92,11 +92,11 @@ agent edits statements ◄── austere-axi poll plan.html ◄─┘ (long-pol
|
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
- **A page is a plain `.html` file**: one runtime `<script>` tag and one `<script type="text/openui">` block. Opened directly, it loads the runtime from the CDN. Under review, the server serves a local copy.
|
|
95
|
-
- **Components**: 58 stock OpenUI components, plus `Architecture` (`Node`, `Edge`, `Group`), `Diff` and `FileTree`. `Mermaid`
|
|
95
|
+
- **Components**: 58 stock OpenUI components, plus `Architecture` (`Node`, `Edge`, `Group`), `Diff` and `FileTree`. `Mermaid` and nine shadcn-style components are bundled [packs](#components-and-packs).
|
|
96
96
|
- **Architecture diagrams** lay out automatically ([dagre](https://github.com/dagrejs/dagre)). The agent never writes coordinates. Each node and edge is a click target. `lanes` puts each group in a column; `tone` colors a box or line to show risk. Use Mermaid for sequences, state machines, ER and gantt.
|
|
97
97
|
- **Annotations** go to the smallest named statement under the click, with detail where it exists: table row and column, `file:+line` in a diff, a diagram node, or selected text. The reviewer can move a note to a parent section.
|
|
98
98
|
- **Buttons and forms** send feedback. `@ToAssistant("…")` sends a message with the form values. A button with no action sends its label.
|
|
99
|
-
- **Phone review**: when Tailscale runs, the server also listens on the tailnet IP and prints a phone link. The review UI has a mobile layout.
|
|
99
|
+
- **Phone review**: when Tailscale runs, the server also listens on the tailnet IP and prints a phone link. The link carries your owner key; keep it to yourself. The review UI has a mobile layout.
|
|
100
100
|
|
|
101
101
|
## CLI
|
|
102
102
|
|
|
@@ -112,7 +112,9 @@ austere-axi patch <file> merge statements from stdin into the program
|
|
|
112
112
|
austere-axi spec [group|pack...] component reference (~5.8k tokens; groups narrow it)
|
|
113
113
|
austere-axi components ... list | search | show | add | remove | new | build
|
|
114
114
|
austere-axi export <file> one self-contained HTML file, works offline
|
|
115
|
-
austere-axi hub on|off|status|list|open persistent server with an index of every page
|
|
115
|
+
austere-axi hub on|off|status|list|notes|open persistent server with an index of every page
|
|
116
|
+
austere-axi share <file> --as "<name>" invite link for a teammate on your tailnet [--auto]
|
|
117
|
+
austere-axi share list|revoke see invites, cut one off
|
|
116
118
|
austere-axi stop stop the background server
|
|
117
119
|
```
|
|
118
120
|
|
|
@@ -152,14 +154,19 @@ A page names its packs in the head, so it renders wherever you open it:
|
|
|
152
154
|
- Under review, the server serves installed packs only while they match the hash in `~/.austere-axi/packs.json`. Opened directly, the page loads them from jsDelivr with Subresource Integrity. `export` inlines them.
|
|
153
155
|
- A pack component with no pack tag is a `missing-pack` error. `check --fix` repairs it.
|
|
154
156
|
- Mermaid ships as a bundled pack. It loads only when the page calls `Mermaid(...)` and needs no tag.
|
|
157
|
+
- The `shadcn` pack ships the same way. It adds `Progress`, `Avatar`, `AvatarGroup`, `Tooltip`, `HoverCard`, `Breadcrumb` (`BreadcrumbItem`), `Kbd`, `Skeleton`, `Calendar` and `Timeline` (`TimelineItem`). They are read-only. Find them with `austere-axi components search`.
|
|
158
|
+
```
|
|
159
|
+
rollout = Progress(64, "Rows migrated")
|
|
160
|
+
freeze = Calendar("2026-11", ["2026-11-04 Code freeze"])
|
|
161
|
+
```
|
|
155
162
|
|
|
156
163
|
**Trust.** Pack code runs inside your review pages, where notes and button presses also come from. Packs under `@austere-axi/` on npm install directly. Other npm packs and any `.tgz` need `--trust`; read them first. A local folder counts as your own. A page with third-party packs shows a warning, and `poll` tells the agent.
|
|
157
164
|
|
|
158
165
|
**Write a pack:**
|
|
159
166
|
|
|
160
167
|
```sh
|
|
161
|
-
austere-axi components new austere-pack-
|
|
162
|
-
cd austere-pack-
|
|
168
|
+
austere-axi components new austere-pack-milestones
|
|
169
|
+
cd austere-pack-milestones && npm install && npm run build
|
|
163
170
|
austere-axi components add .
|
|
164
171
|
```
|
|
165
172
|
|
|
@@ -172,10 +179,33 @@ The server starts when needed and stops after 30 idle minutes. `hub on` keeps it
|
|
|
172
179
|
```sh
|
|
173
180
|
austere-axi hub on # http://127.0.0.1:4388/: pages by project and day, with search
|
|
174
181
|
austere-axi hub list # the same, for an agent: [words] [--project dir]
|
|
182
|
+
austere-axi hub notes # every note, same for an agent: [words] [--project dir] [--unanswered]
|
|
175
183
|
austere-axi hub off # back to on-demand; the index stays
|
|
176
184
|
```
|
|
177
185
|
|
|
178
|
-
The home page shows which reviews are live and how much feedback the agent has answered. The index is SQLite (`~/.austere-axi/hub.db`), copied from the session files; the review loop does not need it.
|
|
186
|
+
`/notes` lists every note on every page, newest first, with search, status and project filters. The home page shows which reviews are live and how much feedback the agent has answered. The index is SQLite (`~/.austere-axi/hub.db`), copied from the session files; the review loop does not need it.
|
|
187
|
+
|
|
188
|
+
## Team review
|
|
189
|
+
|
|
190
|
+
Teammates on your tailnet can review a page and leave notes under their own name. The server still runs on your machine. There are no accounts and no hosted service.
|
|
191
|
+
|
|
192
|
+
```sh
|
|
193
|
+
austere-axi share plan.html --as "Ana" # prints a link; notes wait for you
|
|
194
|
+
austere-axi share plan.html --as "Bo" --auto # Bo's notes go straight to the agent
|
|
195
|
+
austere-axi share list [--links] # who has a link
|
|
196
|
+
austere-axi share revoke Ana # name or id; takes effect at once
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Send the link to the teammate. The link needs Tailscale (or `AUSTERE_AXI_HOST`); without one, `share` stops with an error.
|
|
200
|
+
|
|
201
|
+
- **Hold (default):** a teammate's note waits in your side panel. Press **Send** on one note, or **Send all**. **Discard** removes it. Held notes are not in `poll` or `feedback`, so the agent does not see them until you send them.
|
|
202
|
+
- **Auto:** the note goes to the agent without your approval.
|
|
203
|
+
- **Author:** `poll` and `feedback` show `from: <name>` on each teammate's note. Your own notes have no `from`. The hub Notes view shows the author and can filter on it.
|
|
204
|
+
- **Reviewers can:** read the page and the conversation, and add notes. They cannot end the review, export, or send held notes.
|
|
205
|
+
|
|
206
|
+
Trust model: anyone who has the link, and can reach your machine on the tailnet, can post notes under that name until you revoke it. Share links only with people you trust, and revoke when the review is done. The link holds a random 192-bit token; the browser keeps it in a cookie and the address bar drops it.
|
|
207
|
+
|
|
208
|
+
Off loopback, a request without a valid invite or the owner key is refused. The hub and its Notes view are yours only. Your own phone link carries the owner key (`~/.austere-axi/owner.key`); do not forward it. To make all old phone links stop working, delete that file and restart the server. Behind a reverse proxy on the same machine, every request looks local, so it acts as you.
|
|
179
209
|
|
|
180
210
|
## Environment
|
|
181
211
|
|
|
@@ -199,6 +229,7 @@ The server binds to loopback, plus the Tailscale IP when Tailscale is up. It nev
|
|
|
199
229
|
- `/api/*` answers only loopback callers and refuses browser requests.
|
|
200
230
|
- `Host` headers must be on an allowlist (DNS-rebinding defense).
|
|
201
231
|
- Browser routes that change state refuse a foreign `Origin`.
|
|
232
|
+
- Invite tokens are random, compared in constant time, and kept out of the log. A reviewer link can only add notes to its own page. See [Team review](#team-review).
|
|
202
233
|
- The page runs in a sandboxed iframe with no same-origin access. Packs run there too.
|
|
203
234
|
- The server serves files only from the page's own directory, with no dotfiles.
|
|
204
235
|
- Third-party packs need `--trust`. Installed packs must match their lockfile hash; CDN loads use Subresource Integrity.
|
|
@@ -218,6 +249,7 @@ OpenUI's Node side has PostHog telemetry. The CLI sets `OPENUI_TELEMETRY_DISABLE
|
|
|
218
249
|
npm install
|
|
219
250
|
npm run build # dist/: runtime, packs, pack kit, Mermaid, spec, catalog, review UI, cli.mjs
|
|
220
251
|
npm test
|
|
252
|
+
npm run typecheck # tsc over src/cli, src/shared and the pack types
|
|
221
253
|
node dist/cli.mjs examples/session-migration.html
|
|
222
254
|
```
|
|
223
255
|
|
|
@@ -225,7 +257,7 @@ node dist/cli.mjs examples/session-migration.html
|
|
|
225
257
|
|---|---|
|
|
226
258
|
| `src/runtime/components/` | Custom components (`Architecture` family, `Diff`, `FileTree`), one file each |
|
|
227
259
|
| `src/runtime/stock.js` | Which stock components ship, which stay out of `spec`, their groups |
|
|
228
|
-
| `packs/` | Packs bundled with austere-axi (`mermaid`) |
|
|
260
|
+
| `packs/` | Packs bundled with austere-axi (`mermaid`, `shadcn`) |
|
|
229
261
|
| `src/runtime/pack-api.js`, `src/pack-kit/` | `austere-axi/pack` and the `components build` pipeline |
|
|
230
262
|
| `src/shared/catalog.js` | Component catalog and prompt tiers |
|
|
231
263
|
| `src/runtime/` | Library, annotation layer, review bridge, styles |
|
|
@@ -237,7 +269,7 @@ Maintainer guides (not published): [`AGENTS.md`](AGENTS.md), [`.agents/skills/ad
|
|
|
237
269
|
|
|
238
270
|
Visual checks: `npm run gallery`, `npm run shot`, and `npm run test:browser` (real review loop in headless Chrome; needs Chrome or `CHROME_PATH`).
|
|
239
271
|
|
|
240
|
-
**CI** builds and tests every push to `main` and every pull request (Node
|
|
272
|
+
**CI** builds and tests every push to `main` and every pull request (Node 22, 24, 26 on Linux; Node 24 on Windows; browser suite). **Release:** bump `version` in `package.json`, push, and publish a GitHub Release. `publish.yml` tests again and publishes to npm with trusted publishing.
|
|
241
273
|
|
|
242
274
|
## License
|
|
243
275
|
|
package/dist/austere.js
CHANGED
|
@@ -1598,11 +1598,11 @@ dataLane = Group("DATA STORES WITH A HEADING LONGER THAN THE COLUMN IS WIDE", [c
|
|
|
1598
1598
|
<button type="button" data-act="queue" class="primary">Queue</button>
|
|
1599
1599
|
</span>
|
|
1600
1600
|
</div>
|
|
1601
|
-
</div>`,document.body.appendChild(a);let r=a.querySelector(".austere-hover"),n=a.querySelector(".austere-hover-label"),o=a.querySelector(".austere-selbtn"),i=a.querySelector(".austere-pop"),l=a.querySelector(".austere-pop-path"),s=a.querySelector(".austere-pop-quote"),u=a.querySelector("textarea"),d=null,p=null,f=0,m=null,x=H=>!!H?.closest?.(`[${wX}]`),v=H=>H?.closest?.("[data-oui-id]")||null;function w(H,W,K=4){H.style.left=`${W.left-K}px`,H.style.top=`${W.top-K}px`,H.style.width=`${W.width+K*2}px`,H.style.height=`${W.height+K*2}px`}function b(H){if(!H){r.hidden=!0;return}w(r,IX(H)),n.textContent=`${H.getAttribute("data-oui-id")} \xB7 ${H.getAttribute("data-oui-c")}`,r.hidden=!1}function k(H){t!=="annotate"||!i.hidden||(m=H.target,!f&&(f=requestAnimationFrame(()=>{f=0,!x(m)&&b(v(m))})))}function I(){let H=window.getSelection();return!!H&&!H.isCollapsed&&H.toString().trim().length>0}function L(H){if(t!=="annotate"||x(H.target))return;if(!i.hidden){H.preventDefault(),H.stopPropagation();return}if(I()||!H.altKey&&H.target.closest(e$a))return;let W=v(H.target);W&&(H.preventDefault(),H.stopPropagation(),E({target:W,el:H.target,point:{x:H.clientX,y:H.clientY}}))}function M(){if(t!=="annotate"||!i.hidden)return;let H=window.getSelection();if(!H||H.isCollapsed||!H.rangeCount){o.hidden=!0,p=null;return}let W=H.toString().trim(),K=H.getRangeAt(0),J=K.commonAncestorContainer;J.nodeType!==1&&(J=J.parentElement);let F=v(J);if(!W||!F||x(J)){o.hidden=!0,p=null;return}let ne=K.getBoundingClientRect();p={target:F,el:J,selection:xv(W,600),point:{x:ne.left,y:ne.bottom}},o.hidden=!1;let ue=o.offsetWidth||140;o.style.left=`${Math.max(8,Math.min(window.innerWidth-ue-8,ne.left+ne.width/2-ue/2))}px`,o.style.top=`${Math.max(8,Math.min(window.innerHeight-40,ne.bottom+8))}px`}let _=0;function A(){clearTimeout(_),_=setTimeout(M,120)}o.addEventListener("mousedown",H=>H.preventDefault()),o.addEventListener("click",H=>{H.preventDefault(),H.stopPropagation(),p&&E(p),o.hidden=!0});function D(){l.textContent="",d.path.forEach((H,W)=>{let K=document.createElement("button");if(K.type="button",K.className="austere-chip"+(H.id===d.targetId?" active":""),K.textContent=H.id,K.title=H.component,K.addEventListener("click",()=>{d.targetId=H.id,d.component=H.component,D(),u.focus()}),l.appendChild(K),W<d.path.length-1){let J=document.createElement("span");J.className="austere-chip-sep",J.textContent="\u203A",l.appendChild(J)}})}function E({target:H,el:W,selection:K,point:J}){let F=t$a(H),ne=a$a(H,W),ue=IEt(H,W),U;K||(U=xv((ue||W).innerText||(ue||W).textContent,200)||void 0,ue&&U===ne?.part&&(U=void 0)),d={targetId:H.getAttribute("data-oui-id"),component:H.getAttribute("data-oui-c"),path:F,selection:K||void 0,excerpt:U,detail:ne},D();let Ae=K||U||ne?.part||"";s.textContent=xv(Ae,220),s.hidden=!Ae,u.value="",r.hidden=!0,o.hidden=!0,b(H),r.classList.add("locked"),i.hidden=!1,T(J),u.focus()}function T(H){if(window.innerWidth<640){i.classList.add("sheet"),i.style.left=i.style.top="";return}i.classList.remove("sheet");let W=i.offsetWidth||360,K=i.offsetHeight||200,J=H.x+12,F=H.y+12;J+W>window.innerWidth-8&&(J=Math.max(8,H.x-W-12)),F+K>window.innerHeight-8&&(F=Math.max(8,H.y-K-12)),i.style.left=`${J}px`,i.style.top=`${F}px`}function O(){i.hidden=!0,r.classList.remove("locked"),r.hidden=!0,d=null}function $(){let H=u.value.trim();if(!d)return;if(!H&&!d.selection){u.focus(),u.classList.add("shake"),setTimeout(()=>u.classList.remove("shake"),400);return}let W={kind:"annotation",target:d.targetId,component:d.component,path:d.path.map(J=>J.id),note:H};d.selection&&(W.selection=d.selection),d.excerpt&&!d.selection&&(W.excerpt=d.excerpt),d.detail&&d.targetId===d.path[d.path.length-1].id&&(W.detail=d.detail),e({type:"annotate",item:W});let K=d.targetId;O(),window.getSelection()?.removeAllRanges(),q([K],"queued")}i.addEventListener("click",H=>{let W=H.target.closest("[data-act]")?.getAttribute("data-act");W==="cancel"&&O(),W==="queue"&&$()}),u.addEventListener("keydown",H=>{H.key==="Enter"&&!H.shiftKey&&!H.isComposing?(H.preventDefault(),$()):H.key==="Escape"&&(H.preventDefault(),u.value.trim()||O())}),document.addEventListener("mousemove",k,{passive:!0}),document.addEventListener("click",L,!0),document.addEventListener("mouseup",A),document.addEventListener("keyup",H=>{(H.shiftKey||H.key.startsWith("Arrow"))&&A()}),document.addEventListener("selectionchange",()=>{"ontouchstart"in window&&A()}),document.addEventListener("keydown",H=>{(H.metaKey||H.ctrlKey)&&(H.key==="i"||H.key==="I")&&(H.preventDefault(),e({type:"toggle-mode"}))}),window.addEventListener("scroll",()=>{i.hidden&&(r.hidden=!0,o.hidden=!0)},{passive:!0}),document.addEventListener("mouseleave",()=>{i.hidden&&(r.hidden=!0)});function P(H,W,K,J=new Set){if(J.has(H))return[];J.add(H);let F=document.querySelectorAll(`[data-oui-id="${CSS.escape(H)}"]`);return F.length?Array.from(F):!W||!K?[]:K(W,H).flatMap(ne=>P(ne,W,K,J))}function q(H,W="changed",K,J){let F=new Set;for(let ue of H)for(let U of P(ue,K,J))F.add(U);let ne=Array.from(F).filter(ue=>!Array.from(F).some(U=>U!==ue&&U.contains(ue)));for(let ue of ne){let U=IX(ue),Ae=document.createElement("div");Ae.setAttribute(wX,""),Ae.className=`austere-flash ${W}`,Ae.style.left=`${U.left+window.scrollX-4}px`,Ae.style.top=`${U.top+window.scrollY-4}px`,Ae.style.width=`${U.width+8}px`,Ae.style.height=`${U.height+8}px`,document.body.appendChild(Ae),setTimeout(()=>Ae.remove(),2400)}return ne}function V(H,W,K){let J=P(H,W,K);if(!J.length)return!1;let F=IX(J[0]),ne=F.top+window.scrollY-Math.max(24,(window.innerHeight-F.height)/3);return window.scrollTo({top:Math.max(0,ne),behavior:"smooth"}),setTimeout(()=>q([H],"changed",W,K),350),!0}return{setMode(H){t=H==="annotate"?"annotate":"explore",document.documentElement.classList.toggle("austere-annotating",t==="annotate"),t!=="annotate"&&(O(),o.hidden=!0,r.hidden=!0)},flash:q,reveal:V}}var LEt={mermaid:{name:"@austere-axi/pack-mermaid",version:"0.
|
|
1601
|
+
</div>`,document.body.appendChild(a);let r=a.querySelector(".austere-hover"),n=a.querySelector(".austere-hover-label"),o=a.querySelector(".austere-selbtn"),i=a.querySelector(".austere-pop"),l=a.querySelector(".austere-pop-path"),s=a.querySelector(".austere-pop-quote"),u=a.querySelector("textarea"),d=null,p=null,f=0,m=null,x=H=>!!H?.closest?.(`[${wX}]`),v=H=>H?.closest?.("[data-oui-id]")||null;function w(H,W,K=4){H.style.left=`${W.left-K}px`,H.style.top=`${W.top-K}px`,H.style.width=`${W.width+K*2}px`,H.style.height=`${W.height+K*2}px`}function b(H){if(!H){r.hidden=!0;return}w(r,IX(H)),n.textContent=`${H.getAttribute("data-oui-id")} \xB7 ${H.getAttribute("data-oui-c")}`,r.hidden=!1}function k(H){t!=="annotate"||!i.hidden||(m=H.target,!f&&(f=requestAnimationFrame(()=>{f=0,!x(m)&&b(v(m))})))}function I(){let H=window.getSelection();return!!H&&!H.isCollapsed&&H.toString().trim().length>0}function L(H){if(t!=="annotate"||x(H.target))return;if(!i.hidden){H.preventDefault(),H.stopPropagation();return}if(I()||!H.altKey&&H.target.closest(e$a))return;let W=v(H.target);W&&(H.preventDefault(),H.stopPropagation(),E({target:W,el:H.target,point:{x:H.clientX,y:H.clientY}}))}function M(){if(t!=="annotate"||!i.hidden)return;let H=window.getSelection();if(!H||H.isCollapsed||!H.rangeCount){o.hidden=!0,p=null;return}let W=H.toString().trim(),K=H.getRangeAt(0),J=K.commonAncestorContainer;J.nodeType!==1&&(J=J.parentElement);let F=v(J);if(!W||!F||x(J)){o.hidden=!0,p=null;return}let ne=K.getBoundingClientRect();p={target:F,el:J,selection:xv(W,600),point:{x:ne.left,y:ne.bottom}},o.hidden=!1;let ue=o.offsetWidth||140;o.style.left=`${Math.max(8,Math.min(window.innerWidth-ue-8,ne.left+ne.width/2-ue/2))}px`,o.style.top=`${Math.max(8,Math.min(window.innerHeight-40,ne.bottom+8))}px`}let _=0;function A(){clearTimeout(_),_=setTimeout(M,120)}o.addEventListener("mousedown",H=>H.preventDefault()),o.addEventListener("click",H=>{H.preventDefault(),H.stopPropagation(),p&&E(p),o.hidden=!0});function D(){l.textContent="",d.path.forEach((H,W)=>{let K=document.createElement("button");if(K.type="button",K.className="austere-chip"+(H.id===d.targetId?" active":""),K.textContent=H.id,K.title=H.component,K.addEventListener("click",()=>{d.targetId=H.id,d.component=H.component,D(),u.focus()}),l.appendChild(K),W<d.path.length-1){let J=document.createElement("span");J.className="austere-chip-sep",J.textContent="\u203A",l.appendChild(J)}})}function E({target:H,el:W,selection:K,point:J}){let F=t$a(H),ne=a$a(H,W),ue=IEt(H,W),U;K||(U=xv((ue||W).innerText||(ue||W).textContent,200)||void 0,ue&&U===ne?.part&&(U=void 0)),d={targetId:H.getAttribute("data-oui-id"),component:H.getAttribute("data-oui-c"),path:F,selection:K||void 0,excerpt:U,detail:ne},D();let Ae=K||U||ne?.part||"";s.textContent=xv(Ae,220),s.hidden=!Ae,u.value="",r.hidden=!0,o.hidden=!0,b(H),r.classList.add("locked"),i.hidden=!1,T(J),u.focus()}function T(H){if(window.innerWidth<640){i.classList.add("sheet"),i.style.left=i.style.top="";return}i.classList.remove("sheet");let W=i.offsetWidth||360,K=i.offsetHeight||200,J=H.x+12,F=H.y+12;J+W>window.innerWidth-8&&(J=Math.max(8,H.x-W-12)),F+K>window.innerHeight-8&&(F=Math.max(8,H.y-K-12)),i.style.left=`${J}px`,i.style.top=`${F}px`}function O(){i.hidden=!0,r.classList.remove("locked"),r.hidden=!0,d=null}function $(){let H=u.value.trim();if(!d)return;if(!H&&!d.selection){u.focus(),u.classList.add("shake"),setTimeout(()=>u.classList.remove("shake"),400);return}let W={kind:"annotation",target:d.targetId,component:d.component,path:d.path.map(J=>J.id),note:H};d.selection&&(W.selection=d.selection),d.excerpt&&!d.selection&&(W.excerpt=d.excerpt),d.detail&&d.targetId===d.path[d.path.length-1].id&&(W.detail=d.detail),e({type:"annotate",item:W});let K=d.targetId;O(),window.getSelection()?.removeAllRanges(),q([K],"queued")}i.addEventListener("click",H=>{let W=H.target.closest("[data-act]")?.getAttribute("data-act");W==="cancel"&&O(),W==="queue"&&$()}),u.addEventListener("keydown",H=>{H.key==="Enter"&&!H.shiftKey&&!H.isComposing?(H.preventDefault(),$()):H.key==="Escape"&&(H.preventDefault(),u.value.trim()||O())}),document.addEventListener("mousemove",k,{passive:!0}),document.addEventListener("click",L,!0),document.addEventListener("mouseup",A),document.addEventListener("keyup",H=>{(H.shiftKey||H.key.startsWith("Arrow"))&&A()}),document.addEventListener("selectionchange",()=>{"ontouchstart"in window&&A()}),document.addEventListener("keydown",H=>{(H.metaKey||H.ctrlKey)&&(H.key==="i"||H.key==="I")&&(H.preventDefault(),e({type:"toggle-mode"}))}),window.addEventListener("scroll",()=>{i.hidden&&(r.hidden=!0,o.hidden=!0)},{passive:!0}),document.addEventListener("mouseleave",()=>{i.hidden&&(r.hidden=!0)});function P(H,W,K,J=new Set){if(J.has(H))return[];J.add(H);let F=document.querySelectorAll(`[data-oui-id="${CSS.escape(H)}"]`);return F.length?Array.from(F):!W||!K?[]:K(W,H).flatMap(ne=>P(ne,W,K,J))}function q(H,W="changed",K,J){let F=new Set;for(let ue of H)for(let U of P(ue,K,J))F.add(U);let ne=Array.from(F).filter(ue=>!Array.from(F).some(U=>U!==ue&&U.contains(ue)));for(let ue of ne){let U=IX(ue),Ae=document.createElement("div");Ae.setAttribute(wX,""),Ae.className=`austere-flash ${W}`,Ae.style.left=`${U.left+window.scrollX-4}px`,Ae.style.top=`${U.top+window.scrollY-4}px`,Ae.style.width=`${U.width+8}px`,Ae.style.height=`${U.height+8}px`,document.body.appendChild(Ae),setTimeout(()=>Ae.remove(),2400)}return ne}function V(H,W,K){let J=P(H,W,K);if(!J.length)return!1;let F=IX(J[0]),ne=F.top+window.scrollY-Math.max(24,(window.innerHeight-F.height)/3);return window.scrollTo({top:Math.max(0,ne),behavior:"smooth"}),setTimeout(()=>q([H],"changed",W,K),350),!0}return{setMode(H){t=H==="annotate"?"annotate":"explore",document.documentElement.classList.toggle("austere-annotating",t==="annotate"),t!=="annotate"&&(O(),o.hidden=!0,r.hidden=!0)},flash:q,reveal:V}}var LEt={mermaid:{name:"@austere-axi/pack-mermaid",version:"0.4.0",components:["Mermaid"]},shadcn:{name:"@austere-axi/pack-shadcn",version:"0.4.0",components:["Progress","Avatar","AvatarGroup","Tooltip","HoverCard","Breadcrumb","BreadcrumbItem","Kbd","Skeleton","Calendar","Timeline","TimelineItem"]}};function r$a(e){let t=e.lastIndexOf("@");return t<=0?{name:e,version:""}:{name:e.slice(0,t),version:e.slice(t+1)}}var n$a=/^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,o$a=/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;function _Et(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}=r$a(r);!n$a.test(o)||!o$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 MEt="https://cdn.jsdelivr.net/npm";function CEt(e=document){let t=e.querySelector('meta[name="austere-packs"]');return t?_Et(t.getAttribute("content")||""):[]}function AEt(e,t){let a=[...t];for(let[r,n]of Object.entries(LEt))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 SX(){return/\/__austere\/$/.test(w2)}function i$a(e){if(e.bundled)return{src:B4(`packs/${e.bundled}/pack.js`)||`${MEt}/austere-axi@${e.version}/dist/packs/${e.bundled}/pack.js`};if(SX()){let t=e.integrity?`?integrity=${encodeURIComponent(e.integrity)}`:"";return{src:`${w2}installed-packs/${encodeURIComponent(`${e.name}@${e.version}`)}/pack.js${t}`}}return{src:`${MEt}/${e.name}@${e.version}/dist/pack.js`,integrity:e.integrity}}function l$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 NEt(e,{onError:t}={}){let a=e.filter(r=>!ys().some(n=>n.name===r.name));return await Promise.all(a.map(r=>{let{src:n,integrity:o}=i$a(r);return l$a(n,o).then(()=>{ys().some(i=>i.name===r.name)||t?.(r,new Error(`${n} loaded but did not register pack ${r.name}`))},i=>t?.(r,i))})),ys()}function TEt(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 s$a=/[A-Za-z_$]/;function c$a(e){let t=[],a=String(e??""),r=0,n=1,o=a.length;for(;r<o;){let i=a[r];if(i===`
|
|
1602
1602
|
`){n++,r++;continue}if(i===" "||i===" "||i==="\r"){r++;continue}if(i==="#"||i==="/"&&a[r+1]==="/"){for(;r<o&&a[r]!==`
|
|
1603
1603
|
`;)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===`
|
|
1604
1604
|
`&&n++,r++;continue}if(x==='"'||x==="'"){d=x,r++;continue}if(x==="#"||x==="/"&&a[r+1]==="/"){for(;r<o&&a[r]!==`
|
|
1605
1605
|
`;)r++;continue}if(x==="("||x==="["||x==="{")u++;else if(x===")"||x==="]"||x==="}")u=Math.max(0,u-1);else if(x===`
|
|
1606
|
-
`){if(u===0&&!u$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&&s$a.test(m[1][0])&&t.push({id:m[1],text:f,line:s,endLine:n})}return t}function u$a(e,t){let a=t;for(;a<e.length&&(e[a]===" "||e[a]===" ");)a++;return e[a]==="?"||e[a]===":"}function vv(e){let t=new Map;for(let a of c$a(e))t.set(a.id,a);return t}function REt(e,t){let a=vv(e),r=vv(t),n=[];for(let[o,i]of r){let l=a.get(o);(!l||zEt(l.text)!==zEt(i.text))&&n.push(o)}return n}function LX(e,t){let a=new RegExp(`(^|[^\\w$])${p$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(d$a(i))&&r.push(n)}return r}function d$a(e){return e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/gs,'""')}function zEt(e){return e.replace(/\s+/g," ").trim()}function p$a(e){return e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&")}var kv=ae(It(),1),CX="0.
|
|
1606
|
+
`){if(u===0&&!u$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&&s$a.test(m[1][0])&&t.push({id:m[1],text:f,line:s,endLine:n})}return t}function u$a(e,t){let a=t;for(;a<e.length&&(e[a]===" "||e[a]===" ");)a++;return e[a]==="?"||e[a]===":"}function vv(e){let t=new Map;for(let a of c$a(e))t.set(a.id,a);return t}function REt(e,t){let a=vv(e),r=vv(t),n=[];for(let[o,i]of r){let l=a.get(o);(!l||zEt(l.text)!==zEt(i.text))&&n.push(o)}return n}function LX(e,t){let a=new RegExp(`(^|[^\\w$])${p$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(d$a(i))&&r.push(n)}return r}function d$a(e){return e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/gs,'""')}function zEt(e){return e.replace(/\s+/g," ").trim()}function p$a(e){return e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&")}var kv=ae(It(),1),CX="0.4.0",f$a='script[type="text/openui"], script[type="text/openui-lang"]';function m$a(){if(document.getElementById("austere-styles"))return;let e=document.createElement("style");if(e.id="austere-styles",e.textContent=[Coe,Loe,_oe,Moe].join(`
|
|
1607
1607
|
`),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 PEt(e){if(!e||typeof e!="object")return;let t={};for(let[a,r]of Object.entries(e))if(!a.startsWith("$"))if(r&&typeof r=="object"&&!Array.isArray(r))if("value"in r&&Object.keys(r).length<=3)t[a]=r.value;else{let n=PEt(r);n&&Object.keys(n).length&&(t[a]=n)}else t[a]=r;return Object.keys(t).length?t:void 0}function h$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;!n||n.austere!==1||typeof n.type!="string"||t.get(n.type)?.forEach(o=>o(n))}),{framed:e,connected:!1,send:a,on(r,n){return t.has(r)||t.set(r,new Set),t.get(r).add(n),()=>t.get(r).delete(n)}}}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 g$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(`
|
|
1608
1608
|
`)}var MX=null,$Et=new Set,bv=yv,EEt="";function y$a(e,t){$Et.add(e.name);let a=!e.bundled&&SX()?" (`austere-axi check <file>` says why)":"";Rc({source:"pack",component:e.name,message:`pack ${e.name}@${e.version}: ${t.message}${a}`})}function x$a(){let e=ys(),t=e.map(a=>`${a.name}@${a.version}`).join(",");if(t===EEt)return bv;EEt=t,TEt(e);try{bv=e.length?kX(e.flatMap(a=>a.components),{onSkip:(a,r)=>Rc({source:"pack",component:a.name,message:r})}):yv}catch(a){Rc({source:"pack",message:`packs could not be added (${a.message}); rendering without them`}),bv=yv}return window.__austere.library=bv,bv}async function BEt(e){MX||(MX=CEt());let t=AEt(e,MX).filter(a=>!$Et.has(a.name));return t.some(a=>!ys().some(r=>r.name===a.name))&&await NEt(t,{onError:y$a}),x$a()}function v$a({initialProgram:e,initialLibrary:t,bridge:a,annotator:r,initialState:n}){let[o,i]=(0,ti.useState)({program:e,library:t}),l=o.program,s=(0,ti.useRef)(e),u=(0,ti.useRef)(null);(0,ti.useEffect)(()=>a.on("source",async({program:m})=>{if(typeof m!="string"||m===s.current)return;let x=REt(s.current,m);s.current=m;let v=await BEt(m);s.current===m&&(u.current=x,i({program:m,library:v}))}),[a]),(0,ti.useEffect)(()=>a.on("reveal",({id:m})=>{r?.reveal(m,vv(s.current),LX)}),[a,r]),(0,ti.useEffect)(()=>{let m=u.current;if(!m)return;u.current=null;let x=setTimeout(()=>{let v=vv(s.current),w=m.filter(b=>b!=="root");r?.flash(w,"changed",v,LX),a.send({type:"changes",ids:w})},120);return()=>clearTimeout(x)},[l,r,a]);let d=(0,ti.useCallback)(m=>{if(m.type==="open_url"){let w=m.params?.url;typeof w=="string"&&/^(https?:|mailto:)/i.test(w)&&window.open(w,"_blank","noopener");return}if(m.type!=="continue_conversation")return;let x={kind:"action",message:m.humanFriendlyMessage||"(button)"},v=PEt(m.formState);if(v&&(x.form=v),m.params?.context!==void 0&&(x.context=m.params.context),a.connected)a.send({type:"action",item:x}),_X("Queued for the agent \u2014 press Send when ready");else{let w=g$a(x);navigator.clipboard?.writeText(w).then(()=>_X("Copied \u2014 paste it to your agent"),()=>_X(w))}},[a]),p=(0,ti.useCallback)(m=>{a.send({type:"errors",errors:(m||[]).map(x=>({source:x.source,code:x.code,statementId:x.statementId,message:x.message,hint:x.hint}))})},[a]),f=(0,ti.useCallback)(m=>a.send({type:"state",state:m}),[a]);return(0,kv.jsx)(Soe,{mode:"dark",children:(0,kv.jsx)("main",{className:"austere-page",children:(0,kv.jsx)(v2,{response:l,library:o.library,isStreaming:!1,onAction:d,onError:p,onStateUpdate:f,initialState:n})})})}function DEt(){m$a();let e=document.querySelector(f$a),t=e?e.textContent:"",a=document.createElement("div");a.id="austere-root",document.body.appendChild(a);let r=h$a(),n=null,o=null,i=BEt(t),l=s=>o||(o=i.then(u=>(0,OEt.createRoot)(a).render((0,kv.jsx)(v$a,{initialProgram:t,initialLibrary:u,bridge:r,annotator:n,initialState:s}))));if(!r.framed){l(void 0);return}n=SEt({post:s=>r.send(s)}),nee(s=>r.send({type:"runtime-error",error:s})),r.on("init",async s=>{if(r.connected=!0,n.setMode(s.mode),await l(s.state||void 0),s.scrollY>0){let u=0,d=()=>{window.scrollTo(0,s.scrollY),Math.abs(window.scrollY-s.scrollY)>2&&++u<20&&setTimeout(d,50)};setTimeout(d,30)}r.send({type:"ready",title:document.title,version:CX})}),r.on("mode",s=>n.setMode(s.mode)),window.addEventListener("scroll",()=>r.send({type:"scroll",y:window.scrollY}),{passive:!0}),r.send({type:"hello",title:document.title,version:CX}),setTimeout(()=>l(void 0),600)}window.__austere={version:CX,library:yv,packs:ys};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",DEt,{once:!0}):DEt();})();
|
package/dist/catalog.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"format":1,"pack":"core","version":"0.3.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)[], 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)[])","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)[])","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\")"]}]}
|
|
1
|
+
{"format":1,"pack":"core","version":"0.4.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":[]}]}
|
package/dist/chrome/chrome.css
CHANGED
|
@@ -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}.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}.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],.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}}}
|
|
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}.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}.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],.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}
|
package/dist/chrome/chrome.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(()=>{var m=window.__AUSTERE__,p=m.id,
|
|
1
|
+
(()=>{var m=window.__AUSTERE__,p=m.id,$=m.viewer||{role:"owner",name:""},v=$.role==="owner",O={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{}}},L={get(e){try{return JSON.parse(sessionStorage.getItem(e)||"null")}catch{return null}},set(e,t){try{sessionStorage.setItem(e,JSON.stringify(t))}catch{}}},n={mode:O.get("austere:mode","annotate"),queue:O.get(`austere:queue:${p}`,[]),transcript:[],presence:"idle",status:"open",endedBy:null,program:null,frameReady:!1,scrollY:0,parserErrors:[],runtimeErrors:[],changes:[],changeIdx:0,sheetOpen:L.get(`austere:sheet:${p}`)===!0,sending:!1,connected:!1,editing:null,links:null},d=(e,t=document)=>t.querySelector(e);function r(e,t={},...s){let o=document.createElement(e);for(let[a,i]of Object.entries(t))i==null||i===!1||(a==="class"?o.className=i:a.startsWith("on")?o.addEventListener(a.slice(2),i):a==="text"?o.textContent=i:a==="html"?o.innerHTML=i:o.setAttribute(a,i===!0?"":i));for(let a of s.flat())a!=null&&a!==!1&&o.append(a.nodeType?a:document.createTextNode(String(a)));return o}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>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
</aside>
|
|
51
51
|
</div>
|
|
52
52
|
<button type="button" class="dock"></button>
|
|
53
|
-
</div>`;var
|
|
53
|
+
</div>`;if(!v){for(let e of['[data-cmd="copy-path"]','[data-cmd="copy-link"]','[data-cmd="export"]','[data-cmd="end"]',".send-end"])d(e)?.remove();d(".composer-hint").textContent=$.auto?`Posting as ${$.name} \xB7 Enter sends`:`Posting as ${$.name} \xB7 the owner sends your notes on \xB7 Enter sends`}var J=d(".frame"),l=d(".log"),E=d(".composer textarea"),T=d(".banner");d(".title").textContent=m.title;d(".file").textContent=v?m.name:`reviewing as ${$.name}`;d(".file").title=m.file;document.title=`${m.title} \xB7 austere`;function A(e){return String(e).replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function j(e){return A(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 V(e){let t=String(e).replace(/\r\n/g,`
|
|
54
54
|
`).split(`
|
|
55
|
-
`),
|
|
56
|
-
`))}</code></pre>`);continue}if(/^\s*([-*]|\d+\.)\s+/.test(s)){let c=/^\s*\d+\./.test(s),$=[];for(;a<t.length&&/^\s*([-*]|\d+\.)\s+/.test(t[a]);)$.push(t[a++].replace(/^\s*([-*]|\d+\.)\s+/,""));o.push(`<${c?"ol":"ul"}>${$.map(f=>`<li>${N(f)}</li>`).join("")}</${c?"ol":"ul"}>`);continue}if(/^#{1,6}\s+/.test(s)){o.push(`<p><strong>${N(s.replace(/^#+\s+/,""))}</strong></p>`),a++;continue}if(!s.trim()){a++;continue}let i=[];for(;a<t.length&&t[a].trim()&&!/^```|^\s*([-*]|\d+\.)\s+|^#{1,6}\s/.test(t[a]);)i.push(t[a++]);o.push(`<p>${i.map(N).join("<br>")}</p>`)}return o.join("")}function b(e){D.contentWindow?.postMessage({austere:1,...e},"*")}function B(){n.frameReady=!1,D.src=`${m.artifactUrl}?r=${Date.now()}`}window.addEventListener("message",e=>{if(e.source!==D.contentWindow)return;let t=e.data;if(!(!t||t.austere!==1))switch(t.type){case"hello":b({type:"init",mode:n.status==="ended"?"explore":n.mode,state:w.get(`austere:form:${p}`),scrollY:n.scrollY});break;case"ready":n.frameReady=!0,t.title&&(d(".title").textContent=t.title,document.title=`${t.title} \xB7 austere`);break;case"annotate":R(t.item);break;case"action":R(t.item),V();break;case"errors":n.parserErrors=(t.errors||[]).filter(o=>o.severity!=="warning"),u();break;case"runtime-error":t.error&&!n.runtimeErrors.some(o=>o.statementId===t.error.statementId&&o.message===t.error.message)&&(n.runtimeErrors.push(t.error),u());break;case"state":w.set(`austere:form:${p}`,t.state);break;case"scroll":n.scrollY=t.y||0;break;case"changes":n.changes=(t.ids||[]).slice(0,50),n.changeIdx=0,G();break;case"toggle-mode":E(n.mode==="annotate"?"explore":"annotate");break}});function O(){T.set(`austere:queue:${p}`,n.queue)}function R(e){n.status!=="ended"&&(e.qid=`q${Date.now().toString(36)}${Math.random().toString(36).slice(2,6)}`,n.queue.push(e),O(),u(!0),y())}function Q(e){n.queue=n.queue.filter(t=>t.qid!==e),O(),u(),y()}function K(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 W(e,t=""){let o=[];for(let[a,s]of Object.entries(e||{}))s&&typeof s=="object"&&!Array.isArray(s)?o.push(...W(s,"")):o.push(`${t}${a}: ${Array.isArray(s)?s.join(", "):s}`);return o}function U(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?W(e.form).join(" \xB7 "):e.kind==="render-error"?e.message:e.excerpt||""}function M(e,{queued:t}={}){let o=K(e),a=r("div",{class:`item kind-${e.kind}`});if(o.text){let c=r("button",{type:"button",class:"anchor",title:e.kind==="annotation"?"Show in the page":"",onclick:()=>{(e.kind==="annotation"||e.kind==="render-error")&&(b({type:"reveal",id:e.target||e.statement}),Y()&&v(!1))}},r("span",{class:"anchor-icon",text:o.icon}),r("span",{class:"anchor-text",text:o.text}),o.sub?r("span",{class:"anchor-sub",text:o.sub}):null);a.append(c)}let s=U(e);s&&a.append(r("blockquote",{text:s.length>260?s.slice(0,259)+"\u2026":s}));let i=e.kind==="message"?e.text:e.note;if(t&&n.editing===e.qid){let c=r("textarea",{class:"edit",rows:"2"});c.value=i||"";let $=()=>{let f=c.value.trim();if(e.kind==="message"){if(!f)return Q(e.qid);e.text=f}else e.note=f;n.editing=null,O(),u()};c.addEventListener("keydown",f=>{f.key==="Enter"&&!f.shiftKey?(f.preventDefault(),$()):f.key==="Escape"&&(n.editing=null,u())}),c.addEventListener("blur",$),a.append(c),setTimeout(()=>c.focus(),0)}else i&&a.append(r("div",{class:"note",text:i}));return a}function j(e){return e?new Date(e).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"}):""}function z(e){let t=[["Seen",e.seenAt],["Working",e.workAt],["Done",e.doneAt]],o=r("div",{class:"receipt"});for(let[a,s]of t)o.append(r("span",{class:s?"on":"",title:s?`${a} ${j(s)}`:`Not ${a.toLowerCase()} yet`,text:a}));return e.seenAt||o.append(r("em",{text:n.presence==="listening"?"":n.presence==="working"?"agent is busy; delivered on its next poll":"no agent listening; delivered when one polls"})),o}var P=0;function u(e=!1){let t=l.scrollHeight-l.scrollTop-l.clientHeight<40;l.textContent="";let o=[...n.parserErrors,...n.runtimeErrors];if(o.length){let s=r("div",{class:"issues"},r("div",{class:"issues-head",text:`Render issues (${o.length})`}));for(let i of o.slice(0,8))s.append(r("div",{class:"issue"},r("div",{class:"issue-text"},i.statementId?r("code",{text:i.statementId}):null," ",i.message),n.status==="ended"?null:r("button",{type:"button",class:"btn tiny",text:"Queue fix",onclick:()=>R({kind:"render-error",statement:i.statementId,target:i.statementId,message:i.message,note:"Fix this render error."})})));l.append(s)}!n.transcript.length&&!n.queue.length&&l.append(r("div",{class:"empty"},r("p",{html:"<strong>Point at anything to comment on it.</strong>"}),r("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 agent."})));for(let s of n.transcript)if(s.role==="agent")l.append(r("div",{class:"msg agent"},r("div",{class:"who",text:`Agent \xB7 ${j(s.at)}`}),r("div",{class:"md",html:F(s.text)})));else{let i=r("div",{class:"msg user"},r("div",{class:"who",text:`You \xB7 ${j(s.at)}${s.end?" \xB7 ended review":""}`}));for(let c of s.items||[])i.append(M(c));i.append(z(s)),l.append(i)}if(n.queue.length){let s=r("div",{class:"queued"},r("div",{class:"queued-head",text:`Queued \xB7 ${n.queue.length}`}));for(let i of n.queue){let c=M(i,{queued:!0});c.classList.add("q"),c.append(r("div",{class:"q-actions"},r("button",{type:"button",class:"btn tiny ghost",text:"Edit",onclick:()=>{n.editing=i.qid,u()}}),r("button",{type:"button",class:"btn tiny ghost",text:"Remove",onclick:()=>Q(i.qid)}))),s.append(c)}l.append(s)}n.status==="ended"&&l.append(r("div",{class:"ended-note",text:n.endedBy==="agent"?"The agent ended this review.":"You ended this review."}));let a=n.transcript.length+n.queue.length;(e||t||a!==P)&&(l.scrollTop=l.scrollHeight),P=a,_()}function _(){let e=n.status==="ended",t=n.queue.length;k.disabled=e||n.sending,d(".send").disabled=e||n.sending,d(".send-end").disabled=e||n.sending,d(".send").textContent=n.sending?"Sending\u2026":t?`Send ${t}`:"Send",k.placeholder=e?"Review ended":t?"Add a message (optional)\u2026":"Message the agent\u2026"}function J(e){let t=d(".composer-error");t.textContent=e||"",t.hidden=!e}async function I(e=!1){if(n.sending||n.status==="ended")return;let t=k.value.trim(),o=n.queue.map(({qid:a,...s})=>s);if(t&&o.push({kind:"message",text:t}),!o.length&&!e){k.focus();return}if(!(e&&!o.length&&!confirm("End this review? The agent will stop listening."))){n.sending=!0,J(""),_();try{let a=await fetch(`/s/${p}/send`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({items:o,end:e})}),s=await a.json().catch(()=>({}));if(!a.ok)throw new Error(s.error||`HTTP ${a.status}`);n.queue=[],O(),k.value="",s.entry&&!n.transcript.some(i=>i.id===s.entry.id)&&n.transcript.push(s.entry)}catch(a){J(`Not sent: ${a.message}. Your notes are still queued.`)}finally{n.sending=!1,u(!0),y()}}}d(".composer").addEventListener("submit",e=>{e.preventDefault(),I(!1)});d(".send-end").addEventListener("click",()=>I(!0));k.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&!e.isComposing&&(e.preventDefault(),I(!1))});function E(e){n.mode=e==="explore"?"explore":"annotate",T.set("austere:mode",n.mode);for(let t of document.querySelectorAll(".seg button"))t.classList.toggle("on",t.dataset.mode===n.mode);b({type:"mode",mode:n.status==="ended"?"explore":n.mode})}for(let e of document.querySelectorAll(".seg button"))e.addEventListener("click",()=>E(e.dataset.mode));document.addEventListener("keydown",e=>{(e.metaKey||e.ctrlKey)&&(e.key==="i"||e.key==="I")&&(e.preventDefault(),E(n.mode==="annotate"?"explore":"annotate"))});function x(){let e=d(".presence"),t,o;n.status==="ended"?[t,o]=["Review ended","ended"]:n.connected?n.presence==="listening"?[t,o]=["Agent listening","listening"]:n.presence==="working"?[t,o]=["Agent working","working"]:[t,o]=["Agent not listening","idle"]:[t,o]=["Reconnecting\u2026","off"],e.className=`presence ${o}`,e.textContent=t,e.title=o==="idle"?"Your notes are kept and delivered the next time the agent polls.":o==="working"?"The agent has your last batch.":"",document.documentElement.classList.toggle("is-ended",n.status==="ended"),y()}function G(){let e=d(".changes");if(!n.changes.length){e.hidden=!0;return}e.hidden=!1,e.textContent=`${n.changes.length} changed`,e.title=`Show next change: ${n.changes.join(", ")}`}d(".changes").addEventListener("click",()=>{if(!n.changes.length)return;let e=n.changes[n.changeIdx%n.changes.length];n.changeIdx++,b({type:"reveal",id:e})});var A=d(".menu-pop");d(".menu-btn").addEventListener("click",e=>{e.stopPropagation(),A.hidden=!A.hidden});document.addEventListener("click",()=>A.hidden=!0);A.addEventListener("click",async e=>{let t=e.target.closest("[data-cmd]")?.dataset.cmd;if(t&&(t==="copy-path"&&navigator.clipboard?.writeText(m.file),t==="copy-link"&&navigator.clipboard?.writeText(n.links?.remote||location.href),t==="reload"&&B(),t==="end")){if(n.queue.length)return I(!0);if(!confirm("End this review? The agent will stop listening."))return;await fetch(`/s/${p}/end`,{method:"POST"}).catch(()=>{})}});var Y=()=>window.matchMedia("(max-width: 860px)").matches;function v(e){n.sheetOpen=e,w.set(`austere:sheet:${p}`,e),document.documentElement.classList.toggle("sheet-open",e),e&&setTimeout(()=>l.scrollTop=l.scrollHeight,50)}function y(){let e=d(".dock"),t=n.queue.length,o=["Conversation"];t&&o.push(`${t} queued`);let a=n.transcript[n.transcript.length-1];a?.role==="agent"&&a.at>(w.get(`austere:seen:${p}`)||0)&&o.push("new reply"),e.innerHTML=`<span class="dock-grip"></span><span>${C(o.join(" \xB7 "))}</span><span class="dock-presence ${C(d(".presence").className.replace("presence","").trim())}"></span>`}function V(){let e=d(".dock");e.classList.remove("pulse"),e.offsetWidth,e.classList.add("pulse")}d(".dock").addEventListener("click",()=>{w.set(`austere:seen:${p}`,Date.now()),v(!0),y()});d(".sheet-close").addEventListener("click",()=>v(!1));d(".scrim").addEventListener("click",()=>v(!1));document.addEventListener("keydown",e=>{e.key==="Escape"&&n.sheetOpen&&Y()&&v(!1)});var h=null;d(".panel-head").addEventListener("touchstart",e=>h=e.touches[0].clientY,{passive:!0});d(".panel-head").addEventListener("touchend",e=>{h!=null&&e.changedTouches[0].clientY-h>60&&v(!1),h=null},{passive:!0});d(".dock").addEventListener("touchstart",e=>h=e.touches[0].clientY,{passive:!0});d(".dock").addEventListener("touchend",e=>{h!=null&&h-e.changedTouches[0].clientY>40&&v(!0),h=null},{passive:!0});function H(e){let t=d(".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 S(e,t=[]){if(L.textContent="",!e){L.hidden=!0;return}L.append(r("span",{text:e}),...t),L.hidden=!1}function X(e){for(let t of e){let o=n.transcript.findIndex(a=>a.id===t.id);o>=0?n.transcript[o]=t:n.transcript.push(t)}n.transcript.sort((t,o)=>t.at-o.at)}var g=null,q=0;function Z(){g=new EventSource(`/s/${p}/events`),g.addEventListener("open",()=>{n.connected=!0,q=0,S(""),x()}),g.addEventListener("error",()=>{n.connected=!1,q||(q=Date.now()),x(),setTimeout(()=>{!n.connected&&Date.now()-q>3500&&S("Lost the austere-axi server. Run any austere-axi command to restart it; this page reconnects on its own.",[r("button",{type:"button",class:"btn tiny",text:"Reload",onclick:()=>location.reload()})])},4e3)}),g.addEventListener("snapshot",e=>{let t=JSON.parse(e.data);n.transcript=t.transcript||[],n.presence=t.presence,n.status=t.session.status,n.endedBy=t.session.endedBy,n.links=t.links,t.version&&t.version!==m.version&&S("austere-axi was updated. This page is running the previous version.",[r("button",{type:"button",class:"btn tiny",text:"Reload",onclick:()=>location.reload()})]),H(t.thirdPartyPacks),n.program===null?n.program=t.program:t.program!==n.program&&(n.program=t.program,n.frameReady&&b({type:"source",program:t.program})),x(),u(),E(n.mode)}),g.addEventListener("transcript",e=>{X(JSON.parse(e.data).entries||[]),u(),y()}),g.addEventListener("presence",e=>{let t=JSON.parse(e.data);n.presence=t.presence,n.status=t.status,n.endedBy=t.endedBy,x(),u()}),g.addEventListener("changed",e=>{let t=JSON.parse(e.data);n.parserErrors=[],n.runtimeErrors=[],H(t.thirdPartyPacks),t.shellChanged||!n.frameReady?(n.program=t.program,B()):(n.program=t.program,b({type:"source",program:t.program})),u()}),g.addEventListener("ended",e=>{let t=JSON.parse(e.data);n.status="ended",n.endedBy=t.by,b({type:"mode",mode:"explore"}),x(),u()}),g.addEventListener("server",e=>{JSON.parse(e.data).state==="stopped"&&S("The austere-axi server stopped. Run any austere-axi command to start it again; this page reconnects on its own.")})}window.addEventListener("pagehide",e=>{e.persisted||navigator.sendBeacon?.(`/s/${p}/bye`)});E(n.mode);Y()&&n.sheetOpen&&v(!0);x();u();y();B();Z();})();
|
|
55
|
+
`),s=[],o=0;for(;o<t.length;){let a=t[o];if(/^```/.test(a)){let c=[];for(o++;o<t.length&&!/^```/.test(t[o]);)c.push(t[o++]);o++,s.push(`<pre><code>${A(c.join(`
|
|
56
|
+
`))}</code></pre>`);continue}if(/^\s*([-*]|\d+\.)\s+/.test(a)){let c=/^\s*\d+\./.test(a),f=[];for(;o<t.length&&/^\s*([-*]|\d+\.)\s+/.test(t[o]);)f.push(t[o++].replace(/^\s*([-*]|\d+\.)\s+/,""));s.push(`<${c?"ol":"ul"}>${f.map(h=>`<li>${j(h)}</li>`).join("")}</${c?"ol":"ul"}>`);continue}if(/^#{1,6}\s+/.test(a)){s.push(`<p><strong>${j(a.replace(/^#+\s+/,""))}</strong></p>`),o++;continue}if(!a.trim()){o++;continue}let i=[];for(;o<t.length&&t[o].trim()&&!/^```|^\s*([-*]|\d+\.)\s+|^#{1,6}\s/.test(t[o]);)i.push(t[o++]);s.push(`<p>${i.map(j).join("<br>")}</p>`)}return s.join("")}function w(e){J.contentWindow?.postMessage({austere:1,...e},"*")}function M(){n.frameReady=!1,J.src=`${m.artifactUrl}?r=${Date.now()}`}window.addEventListener("message",e=>{if(e.source!==J.contentWindow)return;let t=e.data;if(!(!t||t.austere!==1))switch(t.type){case"hello":w({type:"init",mode:n.status==="ended"?"explore":n.mode,state:L.get(`austere:form:${p}`),scrollY:n.scrollY});break;case"ready":n.frameReady=!0,t.title&&(d(".title").textContent=t.title,document.title=`${t.title} \xB7 austere`);break;case"annotate":P(t.item);break;case"action":P(t.item),te();break;case"errors":n.parserErrors=(t.errors||[]).filter(s=>s.severity!=="warning"),u();break;case"runtime-error":t.error&&!n.runtimeErrors.some(s=>s.statementId===t.error.statementId&&s.message===t.error.message)&&(n.runtimeErrors.push(t.error),u());break;case"state":L.set(`austere:form:${p}`,t.state);break;case"scroll":n.scrollY=t.y||0;break;case"changes":n.changes=(t.ids||[]).slice(0,50),n.changeIdx=0,ee();break;case"toggle-mode":q(n.mode==="annotate"?"explore":"annotate");break}});function R(){O.set(`austere:queue:${p}`,n.queue)}function P(e){n.status!=="ended"&&(e.qid=`q${Date.now().toString(36)}${Math.random().toString(36).slice(2,6)}`,n.queue.push(e),R(),u(!0),k())}function F(e){n.queue=n.queue.filter(t=>t.qid!==e),R(),u(),k()}function z(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 K(e,t=""){let s=[];for(let[o,a]of Object.entries(e||{}))a&&typeof a=="object"&&!Array.isArray(a)?s.push(...K(a,"")):s.push(`${t}${o}: ${Array.isArray(a)?a.join(", "):a}`);return s}function G(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?K(e.form).join(" \xB7 "):e.kind==="render-error"?e.message:e.excerpt||""}function W(e,{queued:t}={}){let s=z(e),o=r("div",{class:`item kind-${e.kind}`});if(s.text){let c=r("button",{type:"button",class:"anchor",title:e.kind==="annotation"?"Show in the page":"",onclick:()=>{(e.kind==="annotation"||e.kind==="render-error")&&(w({type:"reveal",id:e.target||e.statement}),H()&&y(!1))}},r("span",{class:"anchor-icon",text:s.icon}),r("span",{class:"anchor-text",text:s.text}),s.sub?r("span",{class:"anchor-sub",text:s.sub}):null);o.append(c)}let a=G(e);a&&o.append(r("blockquote",{text:a.length>260?a.slice(0,259)+"\u2026":a}));let i=e.kind==="message"?e.text:e.note;if(t&&n.editing===e.qid){let c=r("textarea",{class:"edit",rows:"2"});c.value=i||"";let f=()=>{let h=c.value.trim();if(e.kind==="message"){if(!h)return F(e.qid);e.text=h}else e.note=h;n.editing=null,R(),u()};c.addEventListener("keydown",h=>{h.key==="Enter"&&!h.shiftKey?(h.preventDefault(),f()):h.key==="Escape"&&(n.editing=null,u())}),c.addEventListener("blur",f),o.append(c),setTimeout(()=>c.focus(),0)}else i&&o.append(r("div",{class:"note",text:i}));return o}function Y(e){return e?new Date(e).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"}):""}function X(e){return v?e.author||"You":e.author===$.name?"You":e.author||"Owner"}async function N(e,t){try{await fetch(`/s/${p}/${e}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t?{ids:t}:{all:!0})})}catch{B("Could not reach the server. Try again.")}}function Z(e){if(e.held){let o=r("div",{class:"receipt held"},r("span",{class:"held-label",text:v?"Held \xB7 not sent to the agent":"Waiting for the owner to send it on"}));return v&&o.append(r("button",{type:"button",class:"btn tiny primary",text:"Send",onclick:()=>N("release",[e.id])}),r("button",{type:"button",class:"btn tiny ghost",text:"Discard",onclick:()=>N("discard",[e.id])})),o}let t=[["Seen",e.seenAt],["Working",e.workAt],["Done",e.doneAt]],s=r("div",{class:"receipt"});for(let[o,a]of t)s.append(r("span",{class:a?"on":"",title:a?`${o} ${Y(a)}`:`Not ${o.toLowerCase()} yet`,text:o}));return e.seenAt||s.append(r("em",{text:n.presence==="listening"?"":n.presence==="working"?"agent is busy; delivered on its next poll":"no agent listening; delivered when one polls"})),s}var Q=0;function u(e=!1){let t=l.scrollHeight-l.scrollTop-l.clientHeight<40;l.textContent="";let s=[...n.parserErrors,...n.runtimeErrors];if(s.length){let i=r("div",{class:"issues"},r("div",{class:"issues-head",text:`Render issues (${s.length})`}));for(let c of s.slice(0,8))i.append(r("div",{class:"issue"},r("div",{class:"issue-text"},c.statementId?r("code",{text:c.statementId}):null," ",c.message),n.status==="ended"?null:r("button",{type:"button",class:"btn tiny",text:"Queue fix",onclick:()=>P({kind:"render-error",statement:c.statementId,target:c.statementId,message:c.message,note:"Fix this render error."})})));l.append(i)}!n.transcript.length&&!n.queue.length&&l.append(r("div",{class:"empty"},r("p",{html:"<strong>Point at anything to comment on it.</strong>"}),r("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 agent."})));for(let i of n.transcript)if(i.role==="agent")l.append(r("div",{class:"msg agent"},r("div",{class:"who",text:`Agent \xB7 ${Y(i.at)}`}),r("div",{class:"md",html:V(i.text)})));else{let c=r("div",{class:`msg user${i.held?" held":""}`},r("div",{class:"who",text:`${X(i)} \xB7 ${Y(i.at)}${i.end?" \xB7 ended review":""}`}));for(let f of i.items||[])c.append(W(f));c.append(Z(i)),l.append(c)}let o=v?n.transcript.filter(i=>i.held).length:0;if(o&&l.append(r("div",{class:"held-bar"},r("span",{text:`${o} held from teammates`}),r("button",{type:"button",class:"btn tiny primary",text:"Send all",onclick:()=>N("release")}),r("button",{type:"button",class:"btn tiny ghost",text:"Discard all",onclick:()=>N("discard")}))),n.queue.length){let i=r("div",{class:"queued"},r("div",{class:"queued-head",text:`Queued \xB7 ${n.queue.length}`}));for(let c of n.queue){let f=W(c,{queued:!0});f.classList.add("q"),f.append(r("div",{class:"q-actions"},r("button",{type:"button",class:"btn tiny ghost",text:"Edit",onclick:()=>{n.editing=c.qid,u()}}),r("button",{type:"button",class:"btn tiny ghost",text:"Remove",onclick:()=>F(c.qid)}))),i.append(f)}l.append(i)}n.status==="ended"&&l.append(r("div",{class:"ended-note",text:n.endedBy==="agent"?"The agent ended this review.":v?"You ended this review.":"The owner ended this review."}));let a=n.transcript.length+n.queue.length;(e||t||a!==Q)&&(l.scrollTop=l.scrollHeight),Q=a,U()}function U(){let e=n.status==="ended",t=n.queue.length;E.disabled=e||n.sending,d(".send").disabled=e||n.sending,d(".send-end")&&(d(".send-end").disabled=e||n.sending),d(".send").textContent=n.sending?"Sending\u2026":t?`Send ${t}`:"Send",E.placeholder=e?"Review ended":t?"Add a message (optional)\u2026":"Message the agent\u2026"}function B(e){let t=d(".composer-error");t.textContent=e||"",t.hidden=!e}async function D(e=!1){if(n.sending||n.status==="ended")return;let t=E.value.trim(),s=n.queue.map(({qid:o,...a})=>a);if(t&&s.push({kind:"message",text:t}),!s.length&&!e){E.focus();return}if(!(e&&!v)&&!(e&&!s.length&&!confirm("End this review? The agent will stop listening."))){n.sending=!0,B(""),U();try{let o=await fetch(`/s/${p}/send`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({items:s,end:e})}),a=await o.json().catch(()=>({}));if(!o.ok)throw new Error(a.error||`HTTP ${o.status}`);n.queue=[],R(),E.value="",a.entry&&!n.transcript.some(i=>i.id===a.entry.id)&&n.transcript.push(a.entry)}catch(o){B(`Not sent: ${o.message}. Your notes are still queued.`)}finally{n.sending=!1,u(!0),k()}}}d(".composer").addEventListener("submit",e=>{e.preventDefault(),D(!1)});d(".send-end")?.addEventListener("click",()=>D(!0));E.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&!e.isComposing&&(e.preventDefault(),D(!1))});function q(e){n.mode=e==="explore"?"explore":"annotate",O.set("austere:mode",n.mode);for(let t of document.querySelectorAll(".seg button"))t.classList.toggle("on",t.dataset.mode===n.mode);w({type:"mode",mode:n.status==="ended"?"explore":n.mode})}for(let e of document.querySelectorAll(".seg button"))e.addEventListener("click",()=>q(e.dataset.mode));document.addEventListener("keydown",e=>{(e.metaKey||e.ctrlKey)&&(e.key==="i"||e.key==="I")&&(e.preventDefault(),q(n.mode==="annotate"?"explore":"annotate"))});function x(){let e=d(".presence"),t,s;n.status==="ended"?[t,s]=["Review ended","ended"]:n.connected?n.presence==="listening"?[t,s]=["Agent listening","listening"]:n.presence==="working"?[t,s]=["Agent working","working"]:[t,s]=["Agent not listening","idle"]:[t,s]=["Reconnecting\u2026","off"],e.className=`presence ${s}`,e.textContent=t,e.title=s==="idle"?"Your notes are kept and delivered the next time the agent polls.":s==="working"?"The agent has your last batch.":"",document.documentElement.classList.toggle("is-ended",n.status==="ended"),k()}function ee(){let e=d(".changes");if(!n.changes.length){e.hidden=!0;return}e.hidden=!1,e.textContent=`${n.changes.length} changed`,e.title=`Show next change: ${n.changes.join(", ")}`}d(".changes").addEventListener("click",()=>{if(!n.changes.length)return;let e=n.changes[n.changeIdx%n.changes.length];n.changeIdx++,w({type:"reveal",id:e})});var I=d(".menu-pop");d(".menu-btn").addEventListener("click",e=>{e.stopPropagation(),I.hidden=!I.hidden});document.addEventListener("click",()=>I.hidden=!0);I.addEventListener("click",async e=>{let t=e.target.closest("[data-cmd]")?.dataset.cmd;if(t&&(t==="copy-path"&&navigator.clipboard?.writeText(m.file),t==="copy-link"&&navigator.clipboard?.writeText(n.links?.remote||location.href),t==="reload"&&M(),t==="end")){if(n.queue.length)return D(!0);if(!confirm("End this review? The agent will stop listening."))return;await fetch(`/s/${p}/end`,{method:"POST"}).catch(()=>{})}});var H=()=>window.matchMedia("(max-width: 860px)").matches;function y(e){n.sheetOpen=e,L.set(`austere:sheet:${p}`,e),document.documentElement.classList.toggle("sheet-open",e),e&&setTimeout(()=>l.scrollTop=l.scrollHeight,50)}function k(){let e=d(".dock"),t=n.queue.length,s=["Conversation"];t&&s.push(`${t} queued`);let o=n.transcript[n.transcript.length-1];o?.role==="agent"&&o.at>(L.get(`austere:seen:${p}`)||0)&&s.push("new reply"),e.innerHTML=`<span class="dock-grip"></span><span>${A(s.join(" \xB7 "))}</span><span class="dock-presence ${A(d(".presence").className.replace("presence","").trim())}"></span>`}function te(){let e=d(".dock");e.classList.remove("pulse"),e.offsetWidth,e.classList.add("pulse")}d(".dock").addEventListener("click",()=>{L.set(`austere:seen:${p}`,Date.now()),y(!0),k()});d(".sheet-close").addEventListener("click",()=>y(!1));d(".scrim").addEventListener("click",()=>y(!1));document.addEventListener("keydown",e=>{e.key==="Escape"&&n.sheetOpen&&H()&&y(!1)});var b=null;d(".panel-head").addEventListener("touchstart",e=>b=e.touches[0].clientY,{passive:!0});d(".panel-head").addEventListener("touchend",e=>{b!=null&&e.changedTouches[0].clientY-b>60&&y(!1),b=null},{passive:!0});d(".dock").addEventListener("touchstart",e=>b=e.touches[0].clientY,{passive:!0});d(".dock").addEventListener("touchend",e=>{b!=null&&b-e.changedTouches[0].clientY>40&&y(!0),b=null},{passive:!0});function _(e){let t=d(".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 S(e,t=[]){if(T.textContent="",!e){T.hidden=!0;return}T.append(r("span",{text:e}),...t),T.hidden=!1}function ne(e){for(let t of e){let s=n.transcript.findIndex(o=>o.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)}var g=null,C=0;function se(){g=new EventSource(`/s/${p}/events`),g.addEventListener("open",()=>{n.connected=!0,C=0,S(""),x()}),g.addEventListener("error",()=>{n.connected=!1,C||(C=Date.now()),x(),setTimeout(()=>{!n.connected&&Date.now()-C>3500&&S("Lost the austere-axi server. Run any austere-axi command to restart it; this page reconnects on its own.",[r("button",{type:"button",class:"btn tiny",text:"Reload",onclick:()=>location.reload()})])},4e3)}),g.addEventListener("snapshot",e=>{let t=JSON.parse(e.data);n.transcript=(t.transcript||[]).filter(s=>!s.discarded),n.presence=t.presence,n.status=t.session.status,n.endedBy=t.session.endedBy,n.links=t.links,t.version&&t.version!==m.version&&S("austere-axi was updated. This page is running the previous version.",[r("button",{type:"button",class:"btn tiny",text:"Reload",onclick:()=>location.reload()})]),_(t.thirdPartyPacks),n.program===null?n.program=t.program:t.program!==n.program&&(n.program=t.program,n.frameReady&&w({type:"source",program:t.program})),x(),u(),q(n.mode)}),g.addEventListener("transcript",e=>{ne(JSON.parse(e.data).entries||[]),u(),k()}),g.addEventListener("presence",e=>{let t=JSON.parse(e.data);n.presence=t.presence,n.status=t.status,n.endedBy=t.endedBy,x(),u()}),g.addEventListener("changed",e=>{let t=JSON.parse(e.data);n.parserErrors=[],n.runtimeErrors=[],_(t.thirdPartyPacks),t.shellChanged||!n.frameReady?(n.program=t.program,M()):(n.program=t.program,w({type:"source",program:t.program})),u()}),g.addEventListener("ended",e=>{let t=JSON.parse(e.data);n.status="ended",n.endedBy=t.by,w({type:"mode",mode:"explore"}),x(),u()}),g.addEventListener("server",e=>{let t=JSON.parse(e.data);if(t.state==="revoked"){g.close(),n.connected=!1,S("Your invite was revoked. Ask the owner for a new link.");return}t.state==="stopped"&&S("The austere-axi server stopped. Run any austere-axi command to start it again; this page reconnects on its own.")})}window.addEventListener("pagehide",e=>{e.persisted||navigator.sendBeacon?.(`/s/${p}/bye`)});q(n.mode);H()&&n.sheetOpen&&y(!0);x();u();k();M();se();})();
|