typebulb 0.59.0 → 0.59.2
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 +2 -2
- package/SKILL.md +4 -4
- package/dist/agents/claude/client.js +13 -1
- package/dist/agents/codex/client.js +13 -1
- package/dist/agents/pi/client.js +46 -34
- package/dist/ai/aiProvider.d.ts +1 -1
- package/dist/ai/aiProvider.d.ts.map +1 -1
- package/dist/ai/aiProviders.d.ts +0 -1
- package/dist/ai/aiProviders.d.ts.map +1 -1
- package/dist/ai/aiProviders.js +1 -1
- package/dist/ai/aiProviders.js.map +1 -1
- package/dist/ai/providers/openRouter.d.ts +7 -1
- package/dist/ai/providers/openRouter.d.ts.map +1 -1
- package/dist/ai/providers/openRouter.js +9 -9
- package/dist/ai/providers/openRouter.js.map +1 -1
- package/dist/dts/tbTypings.d.ts.map +1 -1
- package/dist/dts/tbTypings.js +2 -0
- package/dist/dts/tbTypings.js.map +1 -1
- package/dist/index.js +251 -237
- package/dist/render.js +12 -0
- package/dist/servers.js +35 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ This document is dedicated to the typebulb CLI. At its core, it compiles and ser
|
|
|
21
21
|
- **Env files** — `.env` / `.env.local` load from cwd, `.env.local` overriding `.env` (an exported shell var wins over both). `--mode <name>` adds `.env.<name>` to switch environments (local/staging/prod); a startup line reports which keys loaded from where.
|
|
22
22
|
- **Server mode** — `--server` runs only the `**server.ts**` section in Node, skipping the web server. Bulbs with only `**server.ts**` (no `**code.tsx**`) use this mode automatically.
|
|
23
23
|
- **Type-check without running** — `typebulb check <file>` runs `tsc --noEmit` against the bulb: non-zero exit with diagnostics on errors, a one-line all-clear on stderr on success.
|
|
24
|
-
- **Filesystem access** — `tb.fs.read()` (UTF-8 text), `tb.fs.readBytes()` (raw `Uint8Array`), `tb.fs.write()` (text or bytes),
|
|
24
|
+
- **Filesystem access** — `tb.fs.read()` (UTF-8 text), `tb.fs.readBytes()` (raw `Uint8Array`), `tb.fs.write()` (text or bytes), `tb.fs.list()` (a folder's immediate children as `{ name, dir, mtime }`), and `tb.fs.remove()` (a file, or a folder and its contents); relative paths land in the bulb's own folder, and nothing reaches outside it (`../` throws). Requires `--trust`.
|
|
25
25
|
- **Hot reload** — Recompiles on save and refreshes the browser (on by default; disable with `--no-watch`)
|
|
26
26
|
- **Package resolution** — Client dependencies are automatically resolved by generating import maps (same resolver as typebulb.com). Server dependencies are automatically installed via npm.
|
|
27
27
|
- **Replace dependency** — `--replace <name>=<path>` replaces a declared dependency with a local *built* package folder (browser-ready ESM, no external bare imports) instead of a CDN, for testing an unpublished build. Supplies both runtime bytes and types; applies to `run` and `check`. Under `--watch` the folder is watched and the browser reloads on rebuild (`--no-watch` freezes it). Dev-only; nothing is written to the bulb.
|
|
@@ -202,7 +202,7 @@ everywhere.
|
|
|
202
202
|
| `tb.aiAccess()` | What backs `tb.ai` — `'own' \| 'courtesy' \| 'none'` | |
|
|
203
203
|
| `tb.log(...)` | Print to the CLI's stdout (read back with `typebulb logs`); falls back to the browser console when no CLI serves the page | |
|
|
204
204
|
| `tb.onMessage(cb)` | Receive a value pushed in from the terminal by `typebulb send`; a non-`undefined` return becomes the reply `send --wait` prints — inert when inline (no sender) | |
|
|
205
|
-
| `tb.fs.read/readBytes/write/list` | Read, write, and
|
|
205
|
+
| `tb.fs.read/readBytes/write/list/remove` | Read, write, list, and remove local files | yes |
|
|
206
206
|
| `tb.dir` | The bulb's folder (absolute path), where relative `tb.fs` paths land | |
|
|
207
207
|
| `tb.server.<name>(...)` | Call a function exported from the `server.ts` block | yes |
|
|
208
208
|
| `tb.ai({ messages, … })` | General-purpose AI call (chat, agents) | yes |
|
package/SKILL.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: typebulb
|
|
3
3
|
description: "Author and run Typebulb bulbs — single-file markdown apps (TypeScript/TSX) that run locally via `npx typebulb` (full power: filesystem, database, `server.ts`, `tb.ai`) or render live inline in your coding agent's session through Typebulb's agent mirror (sandboxed, client-only). A bulb can be a visual widget (chart, simulation, diagram, calculator, UI), a full-stack tool with a Node backend, or an AI app that calls models at runtime. Covers the bulb format, the `tb.*` API, trust, and the local run/inline workflow. Use when the user wants a bulb, a quick local tool (visual, backend-backed, or AI-powered), or something visual rendered inline in the conversation."
|
|
4
|
-
version: 0.59.
|
|
4
|
+
version: 0.59.2
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
> Generated from typebulb v0.59.
|
|
7
|
+
> Generated from typebulb v0.59.2. `npx typebulb agent` prints the running version alongside the path to its packaged SKILL.md: if that version is newer than this one, replace this file with that one.
|
|
8
8
|
|
|
9
9
|
# typebulb
|
|
10
10
|
|
|
@@ -29,7 +29,7 @@ This document is dedicated to the typebulb CLI. At its core, it compiles and ser
|
|
|
29
29
|
- **Env files** — `.env` / `.env.local` load from cwd, `.env.local` overriding `.env` (an exported shell var wins over both). `--mode <name>` adds `.env.<name>` to switch environments (local/staging/prod); a startup line reports which keys loaded from where.
|
|
30
30
|
- **Server mode** — `--server` runs only the `**server.ts**` section in Node, skipping the web server. Bulbs with only `**server.ts**` (no `**code.tsx**`) use this mode automatically.
|
|
31
31
|
- **Type-check without running** — `typebulb check <file>` runs `tsc --noEmit` against the bulb: non-zero exit with diagnostics on errors, a one-line all-clear on stderr on success.
|
|
32
|
-
- **Filesystem access** — `tb.fs.read()` (UTF-8 text), `tb.fs.readBytes()` (raw `Uint8Array`), `tb.fs.write()` (text or bytes),
|
|
32
|
+
- **Filesystem access** — `tb.fs.read()` (UTF-8 text), `tb.fs.readBytes()` (raw `Uint8Array`), `tb.fs.write()` (text or bytes), `tb.fs.list()` (a folder's immediate children as `{ name, dir, mtime }`), and `tb.fs.remove()` (a file, or a folder and its contents); relative paths land in the bulb's own folder, and nothing reaches outside it (`../` throws). Requires `--trust`.
|
|
33
33
|
- **Hot reload** — Recompiles on save and refreshes the browser (on by default; disable with `--no-watch`)
|
|
34
34
|
- **Package resolution** — Client dependencies are automatically resolved by generating import maps (same resolver as typebulb.com). Server dependencies are automatically installed via npm.
|
|
35
35
|
- **Replace dependency** — `--replace <name>=<path>` replaces a declared dependency with a local *built* package folder (browser-ready ESM, no external bare imports) instead of a CDN, for testing an unpublished build. Supplies both runtime bytes and types; applies to `run` and `check`. Under `--watch` the folder is watched and the browser reloads on rebuild (`--no-watch` freezes it). Dev-only; nothing is written to the bulb.
|
|
@@ -210,7 +210,7 @@ everywhere.
|
|
|
210
210
|
| `tb.aiAccess()` | What backs `tb.ai` — `'own' \| 'courtesy' \| 'none'` | |
|
|
211
211
|
| `tb.log(...)` | Print to the CLI's stdout (read back with `typebulb logs`); falls back to the browser console when no CLI serves the page | |
|
|
212
212
|
| `tb.onMessage(cb)` | Receive a value pushed in from the terminal by `typebulb send`; a non-`undefined` return becomes the reply `send --wait` prints — inert when inline (no sender) | |
|
|
213
|
-
| `tb.fs.read/readBytes/write/list` | Read, write, and
|
|
213
|
+
| `tb.fs.read/readBytes/write/list/remove` | Read, write, list, and remove local files | yes |
|
|
214
214
|
| `tb.dir` | The bulb's folder (absolute path), where relative `tb.fs` paths land | |
|
|
215
215
|
| `tb.server.<name>(...)` | Call a function exported from the `server.ts` block | yes |
|
|
216
216
|
| `tb.ai({ messages, … })` | General-purpose AI call (chat, agents) | yes |
|
|
@@ -1218,6 +1218,18 @@ ${V9t}
|
|
|
1218
1218
|
});
|
|
1219
1219
|
await failIfNotOk(resp, 'list', path);
|
|
1220
1220
|
return resp.json();
|
|
1221
|
+
},
|
|
1222
|
+
// Remove a file, or a folder and its contents. Throws on a missing path; the bulb's folder
|
|
1223
|
+
// itself is refused.
|
|
1224
|
+
remove: async (path) => {
|
|
1225
|
+
if (isFramed) throw inlineErr('tb.fs');
|
|
1226
|
+
const resp = await fetch('/__fs/remove', {
|
|
1227
|
+
method: 'POST',
|
|
1228
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1229
|
+
body: JSON.stringify({ path })
|
|
1230
|
+
});
|
|
1231
|
+
await failIfNotOk(resp, 'remove', path);
|
|
1232
|
+
return true;
|
|
1221
1233
|
}
|
|
1222
1234
|
};
|
|
1223
1235
|
|
|
@@ -2176,7 +2188,7 @@ ${MCt(b)}`};let v=SCt(d.code,{jsxImportSource:p.ts?.jsxImportSource});if(v.error
|
|
|
2176
2188
|
`)}),d.push({kind:"bulb",source:b.source}),p=b.end);return p<s.length&&d.push({kind:"md",text:s.slice(p).join(`
|
|
2177
2189
|
`)}),d}var Xre=class extends y4{sessions=[];keepOpenSelector=".sid-wrap";filterId="session-filter";listSelector="#session-list";filterNoun="title";#e=new Map;#n=!1;#t=null;#r=new Map;search(s){return tb.server.searchSessions(s)}onActivate(s){let a=this.rows()[s];a&&this.pick(a.sessionId)}filtered(){let s=this.filter.trim().toLowerCase();return s?this.sessions.filter(a=>a.preview.toLowerCase().includes(s)):this.sessions}rows(){return this.searchActive?this.results:this.filtered()}currentIndex(){let s=this.rows(),a=s.findIndex(d=>d.sessionId===this.parent.sessionId);return a<0?s.length-1:a}async loadSessions(){this.sessions=(await tb.server.listSessions()).reverse(),this.#i(),this.parent.updateTitle()}#i(){let s=this.parent.sessionId;for(let a of this.sessions)(!this.#n||a.sessionId===s)&&this.#e.set(a.sessionId,a.mtime);this.#n=!0}#s(s){if(s.sessionId===this.parent.sessionId)return!1;let a=this.#e.get(s.sessionId);return a===void 0||s.mtime>a}onAttached(){this.loadSessions().then(()=>this.update()).catch(()=>{})}currentRow(){return this.sessions.find(s=>s.sessionId===this.parent.sessionId)}currentPreview(){return this.currentRow()?.preview?.trim()??""}async show(){this.beginOpen(),this.refreshList(this.currentIndex()),this.focusFilter();try{await this.loadSessions(),this.refreshList(this.currentIndex())}catch(s){console.error("[mirror] listSessions failed",s)}this.armClose()}async pick(s){let a=this.rows().find(d=>d.sessionId===s);a&&this.#e.set(s,a.mtime),this.close(),this.parent.messageList.stickToBottomNextRender(),await tb.server.attach(s)}view(){let s=this.parent;if(s.sessionId===null)return _t({class:"sid-wrap"});let a=s.sessionId==="",d=a?"new conversation":this.currentPreview()||"current",p=tte(d,25),b=`${s.cwd}
|
|
2178
2190
|
${a||this.currentRow()?.pending?"New conversation \u2014 not saved yet":`Session: ${s.sessionId}`}`;return _t({class:"sid-wrap"},Yo({class:"pill","data-tip":b,onClick:v=>{v.stopPropagation(),this.open?this.close():this.show()}},p),this.open?this.picker():null)}picker(){let s=this.rows();return _t({class:"picker"},_t({class:"picker-body"},s.length===0?this.emptyState("No sessions yet \u2014 start one in your terminal."):_t({id:"session-list",class:"picker-list",onScroll:()=>this.onListScroll()},s.map((a,d)=>this.pickerRow(a,d))),this.peekCard()),this.filterBox(this.sessions.length,"session"))}pickerRow(s,a){let d=s.sessionId===this.parent.sessionId,p=this.parent.busy.includes(s.sessionId);return _t({class:["picker-row",a===this.highlighted?"active":"",d?"current":""],onMouseEnter:()=>{this.highlighted!==a&&(this.highlighted=a,this.update())},onClick:()=>this.pick(s.sessionId)},_t({class:"picker-row-main"},gr({class:"picker-dot"}),gr({class:["picker-preview",p?"shimmer-text shimmer-slow":""]},s.preview||"(no preview)"),Vee(s.hitCount),gr({class:["picker-time",this.#s(s)?"unseen":""],onMouseEnter:()=>this.#o(s),onMouseLeave:()=>this.#c(s)},ete(s.mtime))),Zee(s.snippet,this.filter.trim()))}#o(s){this.#t=`${s.sessionId}:${s.mtime}`,this.#u(s.sessionId,this.#t),this.update()}#c(s){this.#t===`${s.sessionId}:${s.mtime}`&&(this.#t=null,this.update())}async#u(s,a){if(this.#r.has(a))return;let d="";try{d=String((await tb.server.sessionPeek(s))?.text??"")}catch{}this.#r.set(a,d),this.#t===a&&this.update()}peekCard(){if(!this.#t)return null;let s=this.#r.get(this.#t);return s===void 0?_t({class:"picker-peek muted"},"\u2026"):s?_t({class:"picker-peek md",key:`peek:${this.#t}`,onMounted:a=>{a.innerHTML=eD.render(s)}}):_t({class:"picker-peek muted"},"(no assistant text yet)")}onClosed(){super.onClosed(),this.#t=null}};var Vre=class extends Ia{get parent(){return this.ctx.parent}view(){let s=this.parent.tokens,a=this.parent.stats,d=a?a.contextTokens??0:s.in+s.out+s.cached+s.cacheCreate,p=a?.cost??this.parent.cost,b=this.parent.working,v=p>0?`$${p>=1?p.toFixed(2):p.toFixed(3)}`:"",w=[];if(d>0&&w.push(Wye(d)),a&&a.contextPercent!=null&&w.push(`${Math.round(a.contextPercent)}%`),v&&w.push(v),!w.length&&!b)return gr({class:"token-wrap"});let I=w.length?w.length===1&&d>0?`${w[0]} tokens`:w.join(" \xB7 "):"working\u2026",T=w.length?[d>0?`${Wye(d)} tokens in the context window`:"",a&&a.contextPercent!=null?`${Math.round(a.contextPercent)}% of the model\u2019s window`:"",v?`${v} spent this session`:""].filter(Boolean).join(`
|
|
2179
|
-
`):"Waiting for the harness to flush this turn";return gr({class:"token-wrap"},gr({class:["token",C6t(b)],"data-tip":T},I))}};var GSt={"play_arrow.fill":'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M320-200v-560l440 280-440 280Z"/></svg>',"stop.fill":'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M240-240v-480h480v480H240Z"/></svg>',play_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m380-300 280-180-280-180v360ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',stop_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M320-320h320v-320H320v320ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',add_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M440-280h80v-160h160v-80H520v-160h-80v160H280v80h160v160Zm40 200q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',attach_file:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M720-330q0 104-73 177T470-80q-104 0-177-73t-73-177v-370q0-75 52.5-127.5T400-880q75 0 127.5 52.5T580-700v350q0 46-32 78t-78 32q-46 0-78-32t-32-78v-370h80v370q0 13 8.5 21.5T470-320q13 0 21.5-8.5T500-350v-350q-1-42-29.5-71T400-800q-42 0-71 29t-29 71v370q-1 71 49 120.5T470-160q70 0 119-49.5T640-330v-390h80v390Z"/></svg>'};var Xvn={play:"play_arrow.fill",stop:"stop.fill",playCircle:"play_circle",stopCircle:"stop_circle",addCircle:"add_circle",attach:"attach_file"},Vvn={caret:'<svg viewBox="0 0 16 16"><path d="M4 1.5 L13 8 L4 14.5 Z" fill="currentColor"/></svg>',push:'<svg viewBox="0 0 16 16" fill="none"><path d="M8 11 V3 M4.75 6.25 L8 3 L11.25 6.25" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 14 H13" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',pull:'<svg viewBox="0 0 16 16" fill="none"><path d="M8 3 V11 M4.75 7.75 L8 11 L11.25 7.75" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 14 H13" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',check:'<svg viewBox="0 0 16 16" fill="none"><path d="M3 8.5 L6.5 12 L13 4.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',close:'<svg viewBox="0 0 16 16" fill="none"><path d="M4 4 L12 12 M12 4 L4 12" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',fork:'<svg viewBox="0 0 16 16" fill="none"><path d="M4 2 V8 H12 V2 M8 8 V14" stroke="currentColor" stroke-width="1.8" stroke-linecap="square"/></svg>',diff:'<svg viewBox="4 2 8 12" fill="none"><path d="M5.25 6 H10.75 M8 3.25 V8.75" stroke="var(--diff-add)" stroke-width="1.8" stroke-linecap="round"/><path d="M5.25 12.5 H10.75" stroke="var(--err)" stroke-width="1.8" stroke-linecap="round"/></svg>'},Zvn=o=>{let s=Vvn[o]??GSt[Xvn[o]??""]??"";return/<svg[^>]*\bfill=/.test(s)?s:s.replace("<svg ",'<svg fill="currentColor" ')},_3=(o,s="")=>gr({class:["icon",...Array.isArray(s)?s:[s]],key:`icon-${o}`,onMounted:a=>{a.innerHTML=Zvn(o)}},"");function QSt(o,...s){return o.slice().sort((a,d)=>{for(let{key:p,desc:b}of s){let v=p(a),w=p(d);if(v!==w)return v==null?b?-1:1:w==null||v<w?b?1:-1:b?-1:1}return 0})}var e5n=()=>_3("play","btn-icon"),t5n=()=>_3("stop","btn-icon"),n5n=o=>`typebulbs/u/samples/${o}.bulb.md`,rD=o=>Gh(o.path).match(/typebulbs\/u\/([^/]+)\//)?.[1],r5n=(o,s)=>`https://typebulb.com/u/${s}/${aM(o.path)}/full`,JSt=o=>o==="HTTP 404"?"not found \u2014 check the URL (private bulbs need their owner's token)":o==="HTTP 401"||o==="HTTP 403"?"private \u2014 needs its owner's TYPEBULB_TOKEN in .env":o||"pull failed",Zre=class extends y4{files=[];servers=[];samples=[];mine=[];myUser;keepOpenSelector=".servers-wrap";filterId="bulb-filter";listSelector=".bulb-list";filterNoun="name";launching=new Set;dismissedDenials=new Set;pendingLaunch;openLog;#e;spotlightPid;openFolds=new Set;pendingOverwrite;pullErrors=new Map;syncBusy=new Set;syncDone=new Set;search(s){return tb.server.searchBulbs(s)}onActivate(s){let a=this.rows()[s];a&&(a.group?this.toggleFold(a.group.user):a.running?SF(a.running.url):a.remote?this.downloadAndLaunch(a):this.launch(a.path))}onAttached(){this.refresh(),setInterval(()=>this.refreshServers(),800),setInterval(()=>this.refreshFiles(),3e3),window.addEventListener("tb-breakout",s=>this.spotlight(s.detail?.pid))}spotlight(s){this.spotlightPid=s,this.show()}refresh(){return Promise.all([this.refreshServers(),this.refreshFiles(),this.refreshSamples(),this.refreshMine()])}async refreshServers(){try{let s=await tb.server.listBreakouts(),a=s.length!==this.servers.length||s.some((d,p)=>d.pid!==this.servers[p]?.pid||d.denied!==this.servers[p]?.denied||d.predicted!==this.servers[p]?.predicted||d.trust!==this.servers[p]?.trust);this.servers=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] server list refresh failed",s)}}async refreshFiles(){try{let s=await tb.server.listBulbFiles(),a=s.length!==this.files.length||s.some((d,p)=>d.path!==this.files[p]?.path||d.mtime!==this.files[p]?.mtime||d.lastRunAt!==this.files[p]?.lastRunAt);this.files=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] bulb list refresh failed",s)}}async refreshSamples(){try{let s=await tb.server.listSamples(),a=s.length!==this.samples.length||s.some((d,p)=>d.slug!==this.samples[p]?.slug);this.samples=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] samples refresh failed",s)}}async refreshMine(){try{let{user:s,bulbs:a}=await tb.server.listMyBulbs(),d=s!==this.myUser||a.length!==this.mine.length||a.some((p,b)=>p.slug!==this.mine[b]?.slug);this.myUser=s,this.mine=a,d&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] my-bulbs refresh failed",s)}}merged(){let s=new Map,a=this.files.map(p=>Gh(p.path)),d=[...this.samples.map(p=>[n5n(p.slug),p]),...this.myUser?this.mine.map(p=>[`typebulbs/u/${this.myUser}/${p.slug}.bulb.md`,p]):[]];for(let[p,b]of d)a.some(v=>v.endsWith("/"+p))||s.set(p,{path:p,name:b.name,recent:0,remote:b});this.files.forEach((p,b)=>{s.set(a[b],{path:p.path,name:p.name,recent:Math.max(p.mtime,p.lastRunAt??0),trusted:p.trusted})});for(let p of this.servers){if(p.pid===this.parent.ownPid)continue;let b=Gh(p.file),v=aM(p.file)||p.file,w=s.get(b)??{path:p.file,name:v,recent:0};w.running=p,w.recent=Math.max(w.recent,p.startedAt),s.set(b,w)}return[...s.values()]}rows(){let s=this.merged();if(this.searchActive){let a=new Map(this.results.map(d=>[Gh(d.path),d]));s=s.flatMap(d=>{let p=a.get(Gh(d.path));return p?[{...d,hitCount:p.hitCount,snippet:p.snippet}]:[]})}else{let a=this.filter.trim().toLowerCase(),d=_6t(this.filter);if(d){let p=`typebulbs/u/${d.user}/${d.slug}.bulb.md`,b=s.find(v=>("/"+Gh(v.path)).endsWith("/"+p));return b?[b]:[{path:p,name:d.slug,recent:0,remote:{slug:d.slug,name:d.slug,description:`u/${d.user}/${d.slug} on typebulb.com \u2014 play pulls it into the project`}}]}a?s=s.filter(p=>p.name.toLowerCase().includes(a)||p.path.toLowerCase().includes(a)):[...new Set(s.filter(b=>b.remote).map(b=>rD(b)))].forEach((b,v)=>{let w=T=>!!T.remote&&rD(T)===b,I=s.filter(w).length;this.openFolds.has(b)?s=s.map(T=>w(T)?{...T,recent:-.5-v}:T):s=s.filter(T=>!w(T)),s.push({path:`::${b}`,name:this.foldLabel(b),recent:-1-v,group:{user:b,count:I}})})}return QSt(s,{key:a=>!!a.running},{key:a=>a.recent})}show(){this.beginOpen(),this.refresh(),this.refreshList(),this.armClose(),this.focusFilter()}onClosed(){super.onClosed(),this.closeLog(),this.openFolds.clear(),this.pendingOverwrite=void 0,this.pullErrors.clear(),this.spotlightPid=void 0}toggleFold(s){this.openFolds.delete(s)||this.openFolds.add(s),this.update()}foldLabel(s){return s==="samples"?"Samples from typebulb.com":`${s}'s bulbs`}foldTitle(s){return s==="samples"?"Runnable sample bulbs from typebulb.com \u2014 click to browse":`${s===this.myUser?"Your":`${s}'s`} bulbs on typebulb.com \u2014 click to browse; play pulls one into the project`}async launch(s){if(!this.files.find(d=>Gh(d.path)===Gh(s))?.trusted){let d;try{d=(await tb.server.predictTrustOf(s)).cap}catch(p){console.error("[mirror] predictTrustOf failed",p)}if(d){this.pendingLaunch={path:s,name:this.displayName(s),cap:d},this.update();return}}await this.doLaunch(s)}async downloadAndLaunch(s){let a=Gh(s.path);this.launching.add(a),this.pullErrors.delete(a),this.update();try{let d=rD(s),p=d==="samples"?await tb.server.downloadSample(s.remote.slug):await tb.server.pullRemoteBulb(d,s.remote.slug);if(!p.ok||!p.file){console.error("[mirror] remote pull failed",p.error),this.pullErrors.set(a,JSt(p.error));return}await this.refreshFiles();let b=this.files.find(v=>Gh(v.path).endsWith("/"+Gh(p.file)))?.path??p.file;await this.launch(b)}catch(d){console.error("[mirror] remote pull failed",d),this.pullErrors.set(a,JSt(d?.message))}finally{this.launching.delete(a),this.update()}}async syncRow(s,a,d=!1){let p=`${Gh(s.path)}:${a}`;this.syncBusy.add(p),this.update();try{let b=a==="pull"?await tb.server.pullRemoteBulb(rD(s),aM(s.path),d):await tb.server.pushLocalBulb(s.path,d);b.ok?this.tickDone(p):b.conflict?this.pendingOverwrite={...a==="pull"?{heading:`\u201C${s.name}\u201D has local changes`,body:"Your local file differs from the typebulb.com copy. Overwrite the local file with the site's version?",yesLabel:"Overwrite local"}:{heading:`The site copy of \u201C${s.name}\u201D changed`,body:"It changed since your last sync. Overwrite the typebulb.com copy with your local file?",yesLabel:"Overwrite site copy"},onYes:()=>this.syncRow(s,a,!0)}:console.error(`[mirror] ${a} failed`,b.error)}catch(b){console.error(`[mirror] ${a} failed`,b)}finally{this.syncBusy.delete(p)}await(a==="pull"?this.refreshFiles():this.refreshMine()),this.update()}tickDone(s){this.syncDone.add(s),setTimeout(()=>{this.syncDone.delete(s),this.update()},1200)}async doLaunch(s,a,d=!1){let p=Gh(s);this.launching.add(p),this.update();let b;try{b=await tb.server.launchBulb(s,a)}catch(v){console.error("[mirror] launchBulb failed",v)}finally{this.launching.delete(p)}d&&b?.pid&&this.dismissedDenials.add(b.pid),b?.url&&SF(b.url),await this.refresh(),this.pinToBottom()}displayName(s){return this.files.find(d=>Gh(d.path)===Gh(s))?.name||aM(s)||s}launchTrusted(){let s=this.pendingLaunch;this.pendingLaunch=void 0,s&&this.doLaunch(s.path,!0)}launchRestricted(){let s=this.pendingLaunch;this.pendingLaunch=void 0,s&&this.doLaunch(s.path,void 0,!0)}cancelLaunch(){this.pendingLaunch=void 0,this.update()}async stop(s){this.openLog?.pid===s&&this.closeLog(),this.dismissedDenials.delete(s);try{await tb.server.stopBreakout(s)}catch(a){console.error("[mirror] stopBreakout failed",a)}await this.refresh()}pendingDenial(){return this.servers.find(s=>s.denied&&s.pid!==this.parent.ownPid&&!this.dismissedDenials.has(s.pid))}async elevate(s){this.dismissedDenials.add(s.pid),this.openLog?.pid===s.pid&&this.closeLog();try{await tb.server.launchBulb(s.file,!0)}catch(a){console.error("[mirror] elevate failed",a)}await this.refresh()}dismissDenial(s){this.dismissedDenials.add(s),this.update()}async toggleTrust(s){let a=!(s.running?s.running.trust:s.trusted);try{await tb.server.setBulbTrust(s.path,a),s.running&&(this.openLog?.pid===s.running.pid&&this.closeLog(),await tb.server.launchBulb(s.path,a))}catch(d){console.error("[mirror] toggleTrust failed",d)}await this.refresh()}showLog(s,a){if(this.openLog?.pid===s.pid){this.closeLog();return}this.closeLog(),this.openLog={pid:s.pid,name:a,text:"",offset:0},this.update(),setTimeout(()=>document.getElementById("bulb-console")?.focus()),this.#n(),this.#e=setInterval(()=>this.#n(),700)}closeLog(){this.#e&&(clearInterval(this.#e),this.#e=void 0),this.openLog&&(this.openLog=void 0,this.update())}async#n(){let s=this.openLog;if(s)try{let{text:a,offset:d}=await tb.server.readBulbLog(s.pid,s.offset);if(this.openLog!==s)return;a?(s.text=(s.text+a).slice(-5e4),s.offset=d,this.update()):d!==s.offset&&(s.offset=d)}catch(a){console.error("[mirror] readBulbLog failed",a)}}view(){let s=this.servers.filter(p=>p.pid!==this.parent.ownPid).length;if(!(this.files.length>0||this.samples.length>0)&&s===0)return _t({class:"servers-wrap"});let d=this.pendingDenial();return _t({class:"servers-wrap"},Yo({class:["pill",s>0?"":"glyph",this.open?"on":""],"data-tip":"Launch a project bulb \xB7 stop a running one",ariaLabel:s>0?`Bulbs (${s} running)`:"Bulbs",onClick:p=>{p.stopPropagation(),this.open?this.close():this.show()}},s>0?`${s}\u{1F4A1}`:gr({class:"glyph-img"},"\u{1F4A1}")),this.open?this.popup():null,this.pendingLaunch?this.launchModal(this.pendingLaunch):this.pendingOverwrite?this.overwriteModal(this.pendingOverwrite):d?this.denialModal(d):null)}overwriteModal(s){let a=()=>{this.pendingOverwrite=void 0,this.update()};return this.trustModal({heading:s.heading,body:s.body,warn:"There is no undo \u2014 the overwritten side's changes are gone.",noLabel:"Cancel",yesLabel:s.yesLabel,onNo:a,onYes:()=>{this.pendingOverwrite=void 0,s.onYes()},onDismiss:a})}trustModal(s){let a=(d,p)=>{d.stopPropagation(),p(),this.open&&this.focusFilter()};return _t({class:"trust-back",tabIndex:0,onMounted:d=>d.focus(),onKeyDown:d=>{d.key==="Escape"&&a(d,s.onDismiss)},onClick:d=>a(d,s.onDismiss)},_t({class:"trust-modal",onClick:d=>d.stopPropagation()},_t({class:"trust-modal-h"},s.heading),_t({class:"trust-modal-b"},s.body),_t({class:"trust-modal-warn"},s.warn),_t({class:"trust-modal-acts"},Yo({class:"trust-no",onClick:d=>a(d,s.onNo)},s.noLabel),Yo({class:"trust-yes",onClick:d=>a(d,s.onYes)},s.yesLabel))))}launchModal(s){return this.trustModal({heading:`\u201C${s.name}\u201D will likely need access`,body:`It looks like it uses ${s.cap}, which is blocked unless you launch it Trusted. Launch Trusted to allow filesystem / AI / server.ts?`,warn:"Only trust bulbs you wrote or have read \u2014 be careful with anything off the internet.",noLabel:"Launch restricted",yesLabel:"Launch trusted",onNo:()=>this.launchRestricted(),onYes:()=>this.launchTrusted(),onDismiss:()=>this.cancelLaunch()})}denialModal(s){let a=aM(s.file)||s.file,d=!s.predicted;return this.trustModal({heading:d?`\u201C${a}\u201D reached for access it didn't declare`:`\u201C${a}\u201D wants more access`,body:d?`It tried to use ${s.denied} at runtime, though nothing in its code obviously needed it. Relaunch it Trusted to allow filesystem / AI / server.ts?`:`It tried to use ${s.denied}, blocked while it's restricted. Relaunch it Trusted to allow filesystem / AI / server.ts?`,warn:d?"Code that reaches for capabilities it never visibly uses can be hiding something \u2014 only continue if you wrote this bulb or have read it.":"Only trust bulbs you wrote or have read \u2014 be careful with anything off the internet.",noLabel:"Keep restricted",yesLabel:"Trust & relaunch",onNo:()=>this.dismissDenial(s.pid),onYes:()=>this.elevate(s),onDismiss:()=>this.dismissDenial(s.pid)})}popup(){if(this.openLog)return this.consoleView(this.openLog);let s=this.rows(),a=this.spotlightPid,d=a!==void 0&&s.some(p=>p.running?.pid===a);return _t({class:"servers-pop"},s.length===0?this.emptyState("No bulbs in this project yet."):_t({class:"bulb-list",onScroll:()=>this.onListScroll()},s.map((p,b)=>this.row(p,b,d&&p.running?.pid!==a))),this.filterBox(this.merged().filter(p=>!p.remote).length,"bulb"))}consoleView(s){return _t({id:"bulb-console",class:"servers-pop log-mode",tabIndex:0,onKeyDown:a=>{a.key==="Escape"&&(a.stopPropagation(),this.closeLog())}},_t({class:"bulb-log-head"},gr({class:"bulb-log-name"},`console \xB7 ${s.name}`),Yo({class:"bulb-log-back",title:"Back to the bulb list",onClick:a=>{a.stopPropagation(),this.closeLog()}},"\u2190 bulbs")),i6({class:"bulb-log-body"},gr({class:"bulb-log-text"},s.text||"(no output yet)")))}hoverRow(s,a){let d=a!==void 0&&a.pid===this.spotlightPid;d&&(this.spotlightPid=void 0),(this.highlighted!==s||d)&&(this.highlighted=s,this.update())}row(s,a,d=!1){if(s.group)return this.groupRow(s,a,d);let p=s.running,b=p&&this.openLog?.pid===p.pid,v=this.pullErrors.get(Gh(s.path)),w=p?!!p.trust:!!s.trusted,I=!!s.remote&&!this.searchActive&&!this.filter.trim();return _t({class:["server-row",I?"fold-row":"",a===this.highlighted?"active":"",d?"dimmed":""],onMouseEnter:()=>this.hoverRow(a,p)},p?Yo({class:"server-stop",title:"Stop this server",ariaLabel:"Stop",onClick:T=>{T.stopPropagation(),this.stop(p.pid)}},t5n()):Yo({class:["bulb-launch",this.launching.has(Gh(s.path))?"launching shimmer":""],title:s.remote?"Download from typebulb.com & launch":w?"Launch (trusted \u2014 remembered)":"Launch (restricted)",ariaLabel:"Launch",onClick:T=>{T.stopPropagation(),s.remote?this.downloadAndLaunch(s):this.launch(s.path)}},e5n()),_t({class:"bulb-name-cell"},s.remote?gr({class:"server-name stopped remote",title:s.remote.description},s.name):gr({class:["server-name",p?"":"stopped"],title:`Open ${s.path}`,onClick:T=>{T.stopPropagation(),tb.server.openFile(s.path)}},s.name),this.userLink(s),Vee(s.hitCount),v?gr({class:"pull-error"},v):null),this.syncCell(s),p||w?this.trustToggle(s,w):gr({class:"cell-empty"}),p?gC({class:["server-logs",b?"on":""],title:"Show this server\u2019s console",onClick:T=>{T.stopPropagation(),this.showLog(p,s.name)}},"logs"):gr({class:"cell-empty"}),p?gC({class:"server-port",href:zye(p.url),target:"_blank",rel:"noopener noreferrer",title:`Open ${p.url}`},`:${p.port}`):s.remote?gr({class:"cell-empty"}):gr({class:"bulb-time"},ete(s.recent)),Zee(s.snippet,this.filter.trim()))}userLink(s){let a=rD(s);return a?gC({class:"sample-link",href:r5n(s,a),target:"_blank",rel:"noopener noreferrer",title:"Open this bulb's page on typebulb.com"},a):null}syncCell(s){let a=rD(s);if(!a||s.remote)return gr({class:"cell-empty"});let d=(p,b)=>{let v=`${Gh(s.path)}:${p}`,w=this.syncBusy.has(v),I=this.syncDone.has(v);return Yo({class:["sync-btn",w?"busy shimmer":"",I?"done":""],title:b,ariaLabel:p,onClick:T=>{T.stopPropagation(),w||this.syncRow(s,p)}},_3(I?"check":p,"btn-icon-sm"))};return gr({class:"sync-cell"},d("pull",`Pull \u2014 update the local file from typebulb.com/u/${a}`),a===this.myUser?d("push","Push \u2014 upload this file to typebulb.com"):null)}groupRow(s,a,d){let{user:p,count:b}=s.group,v=this.openFolds.has(p);return _t({class:["server-row","sample-group",v?"open":"",a===this.highlighted?"active":"",d?"dimmed":""],title:v?"Collapse":this.foldTitle(p),onMouseEnter:()=>this.hoverRow(a),onClick:w=>{w.stopPropagation(),this.toggleFold(p)}},gr({class:"fold-caret"},_3("caret",["caret-tri",v?"open":""])),gr({class:"sample-group-label"},`${s.name} (${b})`))}trustToggle(s,a){return Yo({class:["trust-toggle",a?"on":""],title:`${a?"Trusted":"Restricted"} \u2014 click to switch to ${a?"restricted":"trusted"}`+(s.running?" (stops + relaunches it)":""),onClick:d=>{d.stopPropagation(),this.toggleTrust(s)}},a?"trusted":"restricted")}};var eie=class{#e;#n;#t;#r=!1;#i;#s;constructor(s){this.#e=s}#o(){this.#e()?.classList.add("oruler-host")}#c(s){this.#n=s;let a=this.#e();a&&(this.#o(),this.#s?.disconnect(),this.#s=new ResizeObserver(()=>this.sync()),this.#s.observe(a),this.sync())}release(){this.#s?.disconnect(),this.#s=void 0,this.#e()?.classList.remove("oruler-host"),this.#n=void 0,this.#t=void 0}sync(){this.#r||(this.#r=!0,requestAnimationFrame(()=>{this.#r=!1;let s=this.#e(),a=this.#t;if(!s||!a||!s.scrollHeight)return;this.#o();let d=s.scrollHeight-s.clientHeight;a.style.display=d>0?"":"none",a.style.top=`${s.scrollTop/s.scrollHeight*100}%`,a.style.height=`${s.clientHeight/s.scrollHeight*100}%`,this.#n?.setAttribute("aria-valuenow",String(d>0?Math.round(s.scrollTop/d*100):0))}))}view(s,a={}){return _t({class:["oruler",a.class??""],title:a.title,role:"scrollbar",ariaOrientation:"vertical",ariaLabel:a.label??"Scroll position",ariaControls:a.controls,ariaValueMin:0,ariaValueMax:100,ariaValueNow:0,onMounted:d=>this.#c(d),onPointerDown:d=>this.#u(d),onPointerMove:d=>this.#f(d),onPointerUp:d=>this.#l(d),onPointerCancel:d=>this.#l(d),onWheel:d=>this.#h(d)},_t({class:"oruler-win",onMounted:d=>{this.#t=d,this.sync()}}),s.map(d=>_t({class:["oruler-mark",d.class??""],style:{top:`${d.top*100}%`,height:`${d.height*100}%`}})))}#u(s){let a=this.#e(),d=s.currentTarget;if(!a)return;s.preventDefault();let p=this.#t?.getBoundingClientRect(),b=!!p&&s.clientY>=p.top&&s.clientY<=p.bottom;this.#i=b?s.clientY-p.top:(p?.height??0)/2,d.setPointerCapture(s.pointerId),this.#a(s,d,a)}#f(s){let a=this.#e();this.#i===void 0||!a||this.#a(s,s.currentTarget,a)}#l(s){let a=s.currentTarget;this.#i=void 0,a.hasPointerCapture(s.pointerId)&&a.releasePointerCapture(s.pointerId)}#h(s){let a=this.#e();if(!a)return;let d=s.deltaMode===1?16:s.deltaMode===2?a.clientHeight:1;a.scrollTop+=s.deltaY*d,this.sync()}#a(s,a,d){let p=s.clientY-a.getBoundingClientRect().top-(this.#i??0);d.scrollTop=p/a.clientHeight*d.scrollHeight,this.sync()}};var i5n=4e5,rEe="diff-doc-body";function s5n(o,s){let a=o.split(`
|
|
2191
|
+
`):"Waiting for the harness to flush this turn";return gr({class:"token-wrap"},gr({class:["token",C6t(b)],"data-tip":T},I))}};var GSt={"play_arrow.fill":'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M320-200v-560l440 280-440 280Z"/></svg>',"stop.fill":'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M240-240v-480h480v480H240Z"/></svg>',play_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m380-300 280-180-280-180v360ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',stop_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M320-320h320v-320H320v320ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',add_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M440-280h80v-160h160v-80H520v-160h-80v160H280v80h160v160Zm40 200q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',attach_file:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M720-330q0 104-73 177T470-80q-104 0-177-73t-73-177v-370q0-75 52.5-127.5T400-880q75 0 127.5 52.5T580-700v350q0 46-32 78t-78 32q-46 0-78-32t-32-78v-370h80v370q0 13 8.5 21.5T470-320q13 0 21.5-8.5T500-350v-350q-1-42-29.5-71T400-800q-42 0-71 29t-29 71v370q-1 71 49 120.5T470-160q70 0 119-49.5T640-330v-390h80v390Z"/></svg>'};var Xvn={play:"play_arrow.fill",stop:"stop.fill",playCircle:"play_circle",stopCircle:"stop_circle",addCircle:"add_circle",attach:"attach_file"},Vvn={caret:'<svg viewBox="0 0 16 16"><path d="M4 1.5 L13 8 L4 14.5 Z" fill="currentColor"/></svg>',push:'<svg viewBox="0 0 16 16" fill="none"><path d="M8 11 V3 M4.75 6.25 L8 3 L11.25 6.25" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 14 H13" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',pull:'<svg viewBox="0 0 16 16" fill="none"><path d="M8 3 V11 M4.75 7.75 L8 11 L11.25 7.75" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 14 H13" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',check:'<svg viewBox="0 0 16 16" fill="none"><path d="M3 8.5 L6.5 12 L13 4.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',close:'<svg viewBox="0 0 16 16" fill="none"><path d="M4 4 L12 12 M12 4 L4 12" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',fork:'<svg viewBox="0 0 16 16" fill="none"><path d="M4 2 V8 H12 V2 M8 8 V14" stroke="currentColor" stroke-width="1.8" stroke-linecap="square"/></svg>',diff:'<svg viewBox="4 2 8 12" fill="none"><path d="M5.25 6 H10.75 M8 3.25 V8.75" stroke="var(--diff-add)" stroke-width="1.8" stroke-linecap="round"/><path d="M5.25 12.5 H10.75" stroke="var(--err)" stroke-width="1.8" stroke-linecap="round"/></svg>'},Zvn=o=>{let s=Vvn[o]??GSt[Xvn[o]??""]??"";return/<svg[^>]*\bfill=/.test(s)?s:s.replace("<svg ",'<svg fill="currentColor" ')},_3=(o,s="")=>gr({class:["icon",...Array.isArray(s)?s:[s]],key:`icon-${o}`,onMounted:a=>{a.innerHTML=Zvn(o)}},"");function QSt(o,...s){return o.slice().sort((a,d)=>{for(let{key:p,desc:b}of s){let v=p(a),w=p(d);if(v!==w)return v==null?b?-1:1:w==null||v<w?b?1:-1:b?-1:1}return 0})}var e5n=()=>_3("play","btn-icon"),t5n=()=>_3("stop","btn-icon"),n5n=o=>`typebulbs/u/samples/${o}.bulb.md`,rD=o=>Gh(o.path).match(/typebulbs\/u\/([^/]+)\//)?.[1],r5n=(o,s)=>`https://typebulb.com/u/${s}/${aM(o.path)}/full`,JSt=o=>o==="HTTP 404"?"not found \u2014 check the URL (private bulbs need their owner's token)":o==="HTTP 401"||o==="HTTP 403"?"private \u2014 needs its owner's TYPEBULB_TOKEN in .env":o||"pull failed",Zre=class extends y4{files=[];servers=[];samples=[];mine=[];myUser;keepOpenSelector=".servers-wrap";filterId="bulb-filter";listSelector=".bulb-list";filterNoun="name";launching=new Set;dismissedDenials=new Set;pendingLaunch;openLog;#e;spotlightPid;openFolds=new Set;pendingOverwrite;pullErrors=new Map;syncBusy=new Set;syncDone=new Set;search(s){return tb.server.searchBulbs(s)}onActivate(s){let a=this.rows()[s];a&&(a.group?this.toggleFold(a.group.user):a.running?SF(a.running.url):a.remote?this.downloadAndLaunch(a):this.launch(a.path))}onAttached(){this.refresh(),setInterval(()=>this.refreshServers(),800),setInterval(()=>this.refreshFiles(),3e3),window.addEventListener("tb-breakout",s=>this.spotlight(s.detail?.pid))}spotlight(s){this.spotlightPid=s,this.show()}refresh(){return Promise.all([this.refreshServers(),this.refreshFiles(),this.refreshSamples(),this.refreshMine()])}async refreshServers(){try{let s=await tb.server.listBreakouts(),a=s.length!==this.servers.length||s.some((d,p)=>d.pid!==this.servers[p]?.pid||d.denied!==this.servers[p]?.denied||d.predicted!==this.servers[p]?.predicted||d.trust!==this.servers[p]?.trust);this.servers=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] server list refresh failed",s)}}async refreshFiles(){try{let s=await tb.server.listBulbFiles(),a=s.length!==this.files.length||s.some((d,p)=>d.path!==this.files[p]?.path||d.mtime!==this.files[p]?.mtime||d.lastRunAt!==this.files[p]?.lastRunAt);this.files=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] bulb list refresh failed",s)}}async refreshSamples(){try{let s=await tb.server.listSamples(),a=s.length!==this.samples.length||s.some((d,p)=>d.slug!==this.samples[p]?.slug);this.samples=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] samples refresh failed",s)}}async refreshMine(){try{let{user:s,bulbs:a}=await tb.server.listMyBulbs(),d=s!==this.myUser||a.length!==this.mine.length||a.some((p,b)=>p.slug!==this.mine[b]?.slug);this.myUser=s,this.mine=a,d&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] my-bulbs refresh failed",s)}}merged(){let s=new Map,a=this.files.map(p=>Gh(p.path)),d=[...this.samples.map(p=>[n5n(p.slug),p]),...this.myUser?this.mine.map(p=>[`typebulbs/u/${this.myUser}/${p.slug}.bulb.md`,p]):[]];for(let[p,b]of d)a.some(v=>v.endsWith("/"+p))||s.set(p,{path:p,name:b.name,recent:0,remote:b});this.files.forEach((p,b)=>{s.set(a[b],{path:p.path,name:p.name,recent:Math.max(p.mtime,p.lastRunAt??0),trusted:p.trusted})});for(let p of this.servers){if(p.pid===this.parent.ownPid)continue;let b=Gh(p.file),v=aM(p.file)||p.file,w=s.get(b)??{path:p.file,name:v,recent:0};w.running=p,w.recent=Math.max(w.recent,p.startedAt),s.set(b,w)}return[...s.values()]}rows(){let s=this.merged();if(this.searchActive){let a=new Map(this.results.map(d=>[Gh(d.path),d]));s=s.flatMap(d=>{let p=a.get(Gh(d.path));return p?[{...d,hitCount:p.hitCount,snippet:p.snippet}]:[]})}else{let a=this.filter.trim().toLowerCase(),d=_6t(this.filter);if(d){let p=`typebulbs/u/${d.user}/${d.slug}.bulb.md`,b=s.find(v=>("/"+Gh(v.path)).endsWith("/"+p));return b?[b]:[{path:p,name:d.slug,recent:0,remote:{slug:d.slug,name:d.slug,description:`u/${d.user}/${d.slug} on typebulb.com \u2014 play pulls it into the project`}}]}a?s=s.filter(p=>p.name.toLowerCase().includes(a)||p.path.toLowerCase().includes(a)):[...new Set(s.filter(b=>b.remote).map(b=>rD(b)))].forEach((b,v)=>{let w=T=>!!T.remote&&rD(T)===b,I=s.filter(w).length;this.openFolds.has(b)?s=s.map(T=>w(T)?{...T,recent:-.5-v}:T):s=s.filter(T=>!w(T)),s.push({path:`::${b}`,name:this.foldLabel(b),recent:-1-v,group:{user:b,count:I}})})}return QSt(s,{key:a=>!!a.running},{key:a=>a.recent})}show(){this.beginOpen(),this.refresh(),this.refreshList(),this.armClose(),this.focusFilter()}onClosed(){super.onClosed(),this.closeLog(),this.openFolds.clear(),this.pendingOverwrite=void 0,this.pullErrors.clear(),this.spotlightPid=void 0}toggleFold(s){this.openFolds.delete(s)||this.openFolds.add(s),this.update()}foldLabel(s){return s==="samples"?"Samples from typebulb.com":`${s}'s bulbs`}foldTitle(s){return s==="samples"?"Runnable sample bulbs from typebulb.com \u2014 click to browse":`${s===this.myUser?"Your":`${s}'s`} bulbs on typebulb.com \u2014 click to browse; play pulls one into the project`}async launch(s){if(!this.files.find(d=>Gh(d.path)===Gh(s))?.trusted){let d;try{d=(await tb.server.predictTrustOf(s)).cap}catch(p){console.error("[mirror] predictTrustOf failed",p)}if(d){this.pendingLaunch={path:s,name:this.displayName(s),cap:d},this.update();return}}await this.doLaunch(s)}async downloadAndLaunch(s){let a=Gh(s.path);this.launching.add(a),this.pullErrors.delete(a),this.update();try{let d=rD(s),p=d==="samples"?await tb.server.downloadSample(s.remote.slug):await tb.server.pullRemoteBulb(d,s.remote.slug);if(!p.ok||!p.file){console.error("[mirror] remote pull failed",p.error),this.pullErrors.set(a,JSt(p.error));return}await this.refreshFiles();let b=this.files.find(v=>Gh(v.path).endsWith("/"+Gh(p.file)))?.path??p.file;await this.launch(b)}catch(d){console.error("[mirror] remote pull failed",d),this.pullErrors.set(a,JSt(d?.message))}finally{this.launching.delete(a),this.update()}}async syncRow(s,a,d=!1){let p=`${Gh(s.path)}:${a}`;this.syncBusy.add(p),this.update();try{let b=a==="pull"?await tb.server.pullRemoteBulb(rD(s),aM(s.path),d):await tb.server.pushLocalBulb(s.path,d);b.ok?this.tickDone(p):b.conflict?this.pendingOverwrite={...a==="pull"?{heading:`\u201C${s.name}\u201D has local changes`,body:"Your local file differs from the typebulb.com copy. Overwrite the local file with the site's version?",yesLabel:"Overwrite local"}:{heading:`The site copy of \u201C${s.name}\u201D changed`,body:"It changed since your last sync. Overwrite the typebulb.com copy with your local file?",yesLabel:"Overwrite site copy"},onYes:()=>this.syncRow(s,a,!0)}:console.error(`[mirror] ${a} failed`,b.error)}catch(b){console.error(`[mirror] ${a} failed`,b)}finally{this.syncBusy.delete(p),this.update()}await(a==="pull"?this.refreshFiles():this.refreshMine()),this.update()}tickDone(s){this.syncDone.add(s),setTimeout(()=>{this.syncDone.delete(s),this.update()},1200)}async doLaunch(s,a,d=!1){let p=Gh(s);this.launching.add(p),this.update();let b;try{b=await tb.server.launchBulb(s,a)}catch(v){console.error("[mirror] launchBulb failed",v)}finally{this.launching.delete(p)}d&&b?.pid&&this.dismissedDenials.add(b.pid),b?.url&&SF(b.url),await this.refresh(),this.pinToBottom()}displayName(s){return this.files.find(d=>Gh(d.path)===Gh(s))?.name||aM(s)||s}launchTrusted(){let s=this.pendingLaunch;this.pendingLaunch=void 0,s&&this.doLaunch(s.path,!0)}launchRestricted(){let s=this.pendingLaunch;this.pendingLaunch=void 0,s&&this.doLaunch(s.path,void 0,!0)}cancelLaunch(){this.pendingLaunch=void 0,this.update()}async stop(s){this.openLog?.pid===s&&this.closeLog(),this.dismissedDenials.delete(s);try{await tb.server.stopBreakout(s)}catch(a){console.error("[mirror] stopBreakout failed",a)}await this.refresh()}pendingDenial(){return this.servers.find(s=>s.denied&&s.pid!==this.parent.ownPid&&!this.dismissedDenials.has(s.pid))}async elevate(s){this.dismissedDenials.add(s.pid),this.openLog?.pid===s.pid&&this.closeLog();try{await tb.server.launchBulb(s.file,!0)}catch(a){console.error("[mirror] elevate failed",a)}await this.refresh()}dismissDenial(s){this.dismissedDenials.add(s),this.update()}async toggleTrust(s){let a=!(s.running?s.running.trust:s.trusted);try{await tb.server.setBulbTrust(s.path,a),s.running&&(this.openLog?.pid===s.running.pid&&this.closeLog(),await tb.server.launchBulb(s.path,a))}catch(d){console.error("[mirror] toggleTrust failed",d)}await this.refresh()}showLog(s,a){if(this.openLog?.pid===s.pid){this.closeLog();return}this.closeLog(),this.openLog={pid:s.pid,name:a,text:"",offset:0},this.update(),setTimeout(()=>document.getElementById("bulb-console")?.focus()),this.#n(),this.#e=setInterval(()=>this.#n(),700)}closeLog(){this.#e&&(clearInterval(this.#e),this.#e=void 0),this.openLog&&(this.openLog=void 0,this.update())}async#n(){let s=this.openLog;if(s)try{let{text:a,offset:d}=await tb.server.readBulbLog(s.pid,s.offset);if(this.openLog!==s)return;a?(s.text=(s.text+a).slice(-5e4),s.offset=d,this.update()):d!==s.offset&&(s.offset=d)}catch(a){console.error("[mirror] readBulbLog failed",a)}}view(){let s=this.servers.filter(p=>p.pid!==this.parent.ownPid).length;if(!(this.files.length>0||this.samples.length>0)&&s===0)return _t({class:"servers-wrap"});let d=this.pendingDenial();return _t({class:"servers-wrap"},Yo({class:["pill",s>0?"":"glyph",this.open?"on":""],"data-tip":"Launch a project bulb \xB7 stop a running one",ariaLabel:s>0?`Bulbs (${s} running)`:"Bulbs",onClick:p=>{p.stopPropagation(),this.open?this.close():this.show()}},s>0?`${s}\u{1F4A1}`:gr({class:"glyph-img"},"\u{1F4A1}")),this.open?this.popup():null,this.pendingLaunch?this.launchModal(this.pendingLaunch):this.pendingOverwrite?this.overwriteModal(this.pendingOverwrite):d?this.denialModal(d):null)}overwriteModal(s){let a=()=>{this.pendingOverwrite=void 0,this.update()};return this.trustModal({heading:s.heading,body:s.body,warn:"There is no undo \u2014 the overwritten side's changes are gone.",noLabel:"Cancel",yesLabel:s.yesLabel,onNo:a,onYes:()=>{this.pendingOverwrite=void 0,s.onYes()},onDismiss:a})}trustModal(s){let a=(d,p)=>{d.stopPropagation(),p(),this.open&&this.focusFilter()};return _t({class:"trust-back",tabIndex:0,onMounted:d=>d.focus(),onKeyDown:d=>{d.key==="Escape"&&a(d,s.onDismiss)},onClick:d=>a(d,s.onDismiss)},_t({class:"trust-modal",onClick:d=>d.stopPropagation()},_t({class:"trust-modal-h"},s.heading),_t({class:"trust-modal-b"},s.body),_t({class:"trust-modal-warn"},s.warn),_t({class:"trust-modal-acts"},Yo({class:"trust-no",onClick:d=>a(d,s.onNo)},s.noLabel),Yo({class:"trust-yes",onClick:d=>a(d,s.onYes)},s.yesLabel))))}launchModal(s){return this.trustModal({heading:`\u201C${s.name}\u201D will likely need access`,body:`It looks like it uses ${s.cap}, which is blocked unless you launch it Trusted. Launch Trusted to allow filesystem / AI / server.ts?`,warn:"Only trust bulbs you wrote or have read \u2014 be careful with anything off the internet.",noLabel:"Launch restricted",yesLabel:"Launch trusted",onNo:()=>this.launchRestricted(),onYes:()=>this.launchTrusted(),onDismiss:()=>this.cancelLaunch()})}denialModal(s){let a=aM(s.file)||s.file,d=!s.predicted;return this.trustModal({heading:d?`\u201C${a}\u201D reached for access it didn't declare`:`\u201C${a}\u201D wants more access`,body:d?`It tried to use ${s.denied} at runtime, though nothing in its code obviously needed it. Relaunch it Trusted to allow filesystem / AI / server.ts?`:`It tried to use ${s.denied}, blocked while it's restricted. Relaunch it Trusted to allow filesystem / AI / server.ts?`,warn:d?"Code that reaches for capabilities it never visibly uses can be hiding something \u2014 only continue if you wrote this bulb or have read it.":"Only trust bulbs you wrote or have read \u2014 be careful with anything off the internet.",noLabel:"Keep restricted",yesLabel:"Trust & relaunch",onNo:()=>this.dismissDenial(s.pid),onYes:()=>this.elevate(s),onDismiss:()=>this.dismissDenial(s.pid)})}popup(){if(this.openLog)return this.consoleView(this.openLog);let s=this.rows(),a=this.spotlightPid,d=a!==void 0&&s.some(p=>p.running?.pid===a);return _t({class:"servers-pop"},s.length===0?this.emptyState("No bulbs in this project yet."):_t({class:"bulb-list",onScroll:()=>this.onListScroll()},s.map((p,b)=>this.row(p,b,d&&p.running?.pid!==a))),this.filterBox(this.merged().filter(p=>!p.remote).length,"bulb"))}consoleView(s){return _t({id:"bulb-console",class:"servers-pop log-mode",tabIndex:0,onKeyDown:a=>{a.key==="Escape"&&(a.stopPropagation(),this.closeLog())}},_t({class:"bulb-log-head"},gr({class:"bulb-log-name"},`console \xB7 ${s.name}`),Yo({class:"bulb-log-back",title:"Back to the bulb list",onClick:a=>{a.stopPropagation(),this.closeLog()}},"\u2190 bulbs")),i6({class:"bulb-log-body"},gr({class:"bulb-log-text"},s.text||"(no output yet)")))}hoverRow(s,a){let d=a!==void 0&&a.pid===this.spotlightPid;d&&(this.spotlightPid=void 0),(this.highlighted!==s||d)&&(this.highlighted=s,this.update())}row(s,a,d=!1){if(s.group)return this.groupRow(s,a,d);let p=s.running,b=p&&this.openLog?.pid===p.pid,v=this.pullErrors.get(Gh(s.path)),w=p?!!p.trust:!!s.trusted,I=!!s.remote&&!this.searchActive&&!this.filter.trim();return _t({class:["server-row",I?"fold-row":"",a===this.highlighted?"active":"",d?"dimmed":""],onMouseEnter:()=>this.hoverRow(a,p)},p?Yo({class:"server-stop",title:"Stop this server",ariaLabel:"Stop",onClick:T=>{T.stopPropagation(),this.stop(p.pid)}},t5n()):Yo({class:["bulb-launch",this.launching.has(Gh(s.path))?"launching shimmer":""],title:s.remote?"Download from typebulb.com & launch":w?"Launch (trusted \u2014 remembered)":"Launch (restricted)",ariaLabel:"Launch",onClick:T=>{T.stopPropagation(),s.remote?this.downloadAndLaunch(s):this.launch(s.path)}},e5n()),_t({class:"bulb-name-cell"},s.remote?gr({class:"server-name stopped remote",title:s.remote.description},s.name):gr({class:["server-name",p?"":"stopped"],title:`Open ${s.path}`,onClick:T=>{T.stopPropagation(),tb.server.openFile(s.path)}},s.name),this.userLink(s),Vee(s.hitCount),v?gr({class:"pull-error"},v):null),this.syncCell(s),p||w?this.trustToggle(s,w):gr({class:"cell-empty"}),p?gC({class:["server-logs",b?"on":""],title:"Show this server\u2019s console",onClick:T=>{T.stopPropagation(),this.showLog(p,s.name)}},"logs"):gr({class:"cell-empty"}),p?gC({class:"server-port",href:zye(p.url),target:"_blank",rel:"noopener noreferrer",title:`Open ${p.url}`},`:${p.port}`):s.remote?gr({class:"cell-empty"}):gr({class:"bulb-time"},ete(s.recent)),Zee(s.snippet,this.filter.trim()))}userLink(s){let a=rD(s);return a?gC({class:"sample-link",href:r5n(s,a),target:"_blank",rel:"noopener noreferrer",title:"Open this bulb's page on typebulb.com"},a):null}syncCell(s){let a=rD(s);if(!a||s.remote)return gr({class:"cell-empty"});let d=(p,b)=>{let v=`${Gh(s.path)}:${p}`,w=this.syncBusy.has(v),I=this.syncDone.has(v);return Yo({class:["sync-btn",w?"busy shimmer":"",I?"done":""],title:b,ariaLabel:p,onClick:T=>{T.stopPropagation(),w||this.syncRow(s,p)}},_3(I?"check":p,"btn-icon-sm"))};return gr({class:"sync-cell"},d("pull",`Pull \u2014 update the local file from typebulb.com/u/${a}`),a===this.myUser?d("push","Push \u2014 upload this file to typebulb.com"):null)}groupRow(s,a,d){let{user:p,count:b}=s.group,v=this.openFolds.has(p);return _t({class:["server-row","sample-group",v?"open":"",a===this.highlighted?"active":"",d?"dimmed":""],title:v?"Collapse":this.foldTitle(p),onMouseEnter:()=>this.hoverRow(a),onClick:w=>{w.stopPropagation(),this.toggleFold(p)}},gr({class:"fold-caret"},_3("caret",["caret-tri",v?"open":""])),gr({class:"sample-group-label"},`${s.name} (${b})`))}trustToggle(s,a){return Yo({class:["trust-toggle",a?"on":""],title:`${a?"Trusted":"Restricted"} \u2014 click to switch to ${a?"restricted":"trusted"}`+(s.running?" (stops + relaunches it)":""),onClick:d=>{d.stopPropagation(),this.toggleTrust(s)}},a?"trusted":"restricted")}};var eie=class{#e;#n;#t;#r=!1;#i;#s;constructor(s){this.#e=s}#o(){this.#e()?.classList.add("oruler-host")}#c(s){this.#n=s;let a=this.#e();a&&(this.#o(),this.#s?.disconnect(),this.#s=new ResizeObserver(()=>this.sync()),this.#s.observe(a),this.sync())}release(){this.#s?.disconnect(),this.#s=void 0,this.#e()?.classList.remove("oruler-host"),this.#n=void 0,this.#t=void 0}sync(){this.#r||(this.#r=!0,requestAnimationFrame(()=>{this.#r=!1;let s=this.#e(),a=this.#t;if(!s||!a||!s.scrollHeight)return;this.#o();let d=s.scrollHeight-s.clientHeight;a.style.display=d>0?"":"none",a.style.top=`${s.scrollTop/s.scrollHeight*100}%`,a.style.height=`${s.clientHeight/s.scrollHeight*100}%`,this.#n?.setAttribute("aria-valuenow",String(d>0?Math.round(s.scrollTop/d*100):0))}))}view(s,a={}){return _t({class:["oruler",a.class??""],title:a.title,role:"scrollbar",ariaOrientation:"vertical",ariaLabel:a.label??"Scroll position",ariaControls:a.controls,ariaValueMin:0,ariaValueMax:100,ariaValueNow:0,onMounted:d=>this.#c(d),onPointerDown:d=>this.#u(d),onPointerMove:d=>this.#f(d),onPointerUp:d=>this.#l(d),onPointerCancel:d=>this.#l(d),onWheel:d=>this.#h(d)},_t({class:"oruler-win",onMounted:d=>{this.#t=d,this.sync()}}),s.map(d=>_t({class:["oruler-mark",d.class??""],style:{top:`${d.top*100}%`,height:`${d.height*100}%`}})))}#u(s){let a=this.#e(),d=s.currentTarget;if(!a)return;s.preventDefault();let p=this.#t?.getBoundingClientRect(),b=!!p&&s.clientY>=p.top&&s.clientY<=p.bottom;this.#i=b?s.clientY-p.top:(p?.height??0)/2,d.setPointerCapture(s.pointerId),this.#a(s,d,a)}#f(s){let a=this.#e();this.#i===void 0||!a||this.#a(s,s.currentTarget,a)}#l(s){let a=s.currentTarget;this.#i=void 0,a.hasPointerCapture(s.pointerId)&&a.releasePointerCapture(s.pointerId)}#h(s){let a=this.#e();if(!a)return;let d=s.deltaMode===1?16:s.deltaMode===2?a.clientHeight:1;a.scrollTop+=s.deltaY*d,this.sync()}#a(s,a,d){let p=s.clientY-a.getBoundingClientRect().top-(this.#i??0);d.scrollTop=p/a.clientHeight*d.scrollHeight,this.sync()}};var i5n=4e5,rEe="diff-doc-body";function s5n(o,s){let a=o.split(`
|
|
2180
2192
|
`).map(v=>v.replace(/\r$/,""));if(a.length&&a[a.length-1]===""&&a.pop(),s)return a.map(v=>({kind:"add",text:v}));let d=[],p=!1,b=!1;for(let v of a){if(v.startsWith("@@")){b&&d.push({kind:"gap",text:"\u22EF"}),b=!0,p=!0;continue}if(!p){v.startsWith("Binary file")&&d.push({kind:"ctx",text:v});continue}v.startsWith("\\")||(v.startsWith("+")?d.push({kind:"add",text:v.slice(1)}):v.startsWith("-")?d.push({kind:"del",text:v.slice(1)}):d.push({kind:"ctx",text:v.slice(1)}))}return d}function o5n(o,s,a){let d=s5n(o,s),p=o.length>i5n?"txt":a.split(".").pop()??"",b=[],v=[];for(let P of d)(P.kind==="ctx"||P.kind==="del")&&b.push(P.text),(P.kind==="ctx"||P.kind==="add")&&v.push(P.text);let w=eEe(b.join(`
|
|
2181
2193
|
`),p),I=eEe(v.join(`
|
|
2182
2194
|
`),p),T=0,B=0,D=[];for(let P of d){if(P.kind==="gap"){D.push('<div class="udiff-gap">\u22EF</div>');continue}let F=P.kind==="del"?w[T++]:(P.kind==="add"||T++,I[B++]);D.push(`<div class="udiff-line${P.kind==="add"?" udiff-add":P.kind==="del"?" udiff-del":""}">${F??""}</div>`)}return{kinds:d.map(P=>P.kind),bodyHtml:D.join("")}}var tie=class extends y4{files=[];repo=!0;root="";viewing;#e=0;#n=!1;#t=()=>document.getElementById(rEe);#r=new eie(this.#t);#i=[];#s=-1;keepOpenSelector=".gitdiff-wrap";filterId="gitdiff-filter";listSelector=".gitdiff-list";filterNoun="file";search(){return Promise.resolve([])}onActivate(s){let a=this.rows()[s];a&&this.openDiff(a)}onAttached(){this.refreshFiles(),setInterval(()=>this.refreshFiles(),3e3),setInterval(()=>{this.viewing&&this.refreshDiff()},1500)}rows(){let s=this.filter.trim().toLowerCase();return s?this.files.filter(a=>a.path.toLowerCase().includes(s)):this.files}async refreshFiles(){try{let{repo:s,root:a,files:d}=await tb.server.gitChangedFiles(),p=s!==this.repo||d.length!==this.files.length||d.some((b,v)=>{let w=this.files[v];return b.path!==w?.path||b.status!==w.status||b.add!==w.add||b.del!==w.del||b.binary!==w.binary});this.repo=s,this.root=a,this.files=d,p&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] gitChangedFiles failed",s)}}#o(s,a,d){this.viewing?.path!==s&&(this.#i=[]),this.viewing={path:s,status:a,text:d.text,truncated:!!d.truncated,...o5n(d.text,!!d.untracked,s)},this.#e++,this.update()}async openDiff(s){try{let a=await tb.server.gitDiff(s.path);if(!a.ok){console.error("[mirror] gitDiff failed:",a.error);return}this.#o(s.path,s.status,a),this.#n=!0,this.close()}catch(a){console.error("[mirror] gitDiff failed",a)}}async refreshDiff(){let s=this.viewing;if(s)try{let a=await tb.server.gitDiff(s.path);if(this.viewing!==s||!a.ok||a.text===s.text)return;this.#o(s.path,s.status,a)}catch(a){console.error("[mirror] gitDiff refresh failed",a)}}closeDoc(){this.#r.release(),this.viewing=void 0,this.update()}show(){this.beginOpen(),this.refreshFiles(),this.refreshList(),this.armClose(),this.focusFilter()}view(){let s=this.files.length;return!this.repo||s===0&&!this.viewing&&!this.open?_t({class:"gitdiff-wrap"}):_t({class:"gitdiff-wrap pop-center"},this.viewing?this.viewingPill():Yo({class:["pill","glyph","gitdiff-pill",this.open?"on":""],"data-tip":`${s} changed file${s===1?"":"s"} \u2014 view a diff`,ariaLabel:`${s} changed file${s===1?"":"s"}`,onClick:a=>{a.stopPropagation(),this.open?this.close():this.show()}},_3("diff"),gr({class:"gitdiff-count"},String(s))),this.open?this.popup():null)}viewingPill(){let s=this.viewing,a=s.kinds.filter(p=>p==="add").length,d=s.kinds.filter(p=>p==="del").length;return Yo({class:["pill","glyph","gitdiff-pill","viewing","on"],"data-tip":`${this.files.length} changed file${this.files.length===1?"":"s"} \u2014 switch file`,onClick:p=>{p.stopPropagation(),this.open?this.close():this.show()}},gr({class:["gitdiff-status",`s-${s.status}`]},s.status),gr({class:"gitdiff-doc-path",title:`Open ${s.path}`,onClick:p=>{p.stopPropagation(),tb.server.openFile(`${this.root}/${s.path}`)}},s.path),gr({class:"gitdiff-counts"},gr({class:"count-add"},`+${a}`),gr({class:"count-del"},`\u2212${d}`)),gr({class:"gitdiff-close","data-tip":"Back to the conversation (Esc)",ariaLabel:"Back to the conversation",onClick:p=>{p.stopPropagation(),this.closeDoc()}},_3("close")))}popup(){let s=this.rows();return _t({class:"servers-pop gitdiff-pop"},s.length===0?this.emptyState("No changes in the working tree."):_t({class:"gitdiff-list",onScroll:()=>this.onListScroll()},s.map((a,d)=>this.row(a,d))),this.filterRow())}filterRow(){let s=this.files.length;return Xee({target:this,prop:()=>this.filter,id:this.filterId,placeholder:s?`Filter ${s} file${s===1?"":"s"}\u2026`:"Filter files\u2026",hasValue:!!this.filter,onKeyDown:a=>this.onFilterKey(a),onClear:()=>this.clearFilter()})}row(s,a){return _t({class:["gitdiff-row",a===this.highlighted?"active":"",this.viewing?.path===s.path?"viewing":""],onMouseEnter:()=>{this.highlighted!==a&&(this.highlighted=a,this.update())},onClick:d=>{d.stopPropagation(),this.openDiff(s)}},gr({class:["gitdiff-status",`s-${s.status}`]},s.status),gr({class:"gitdiff-path",title:s.path},s.path),s.add!==void 0||s.del!==void 0?gr({class:"gitdiff-counts"},gr({class:"count-add"},`+${s.add??0}`),gr({class:"count-del"},`\u2212${s.del??0}`)):s.status==="U"?gr({class:"gitdiff-counts"},gr({class:"count-add"},"new")):gr({class:"gitdiff-counts"},gr({},s.binary?"binary":"\u2014")))}docView(){let s=this.viewing;return _t({class:"diff-doc",onKeyDown:a=>{a.key==="Escape"&&(a.stopPropagation(),this.closeDoc())}},_t({class:"diff-doc-scroll"},_t({class:"diff-doc-body",id:rEe,tabIndex:0,onMounted:a=>a.focus(),onScroll:()=>this.#r.sync()},s.kinds.length===0?_t({class:"diff-doc-empty"},"No changes."):_t({class:"diff-doc-code",key:`diff-code-${this.#e}`,onMounted:a=>{a.innerHTML=s.bodyHtml,this.#c(a),this.#u(a)}},""),s.truncated?_t({class:"diff-doc-trunc"},"Diff truncated \u2014 file too large to show in full."):null),s.kinds.length?this.#r.view(this.#i,{class:"diff-ruler",title:"Jump to a change",label:"Diff scroll position",controls:rEe}):null))}#c(s){if(!this.#n)return;this.#n=!1;let a=s.querySelector(".udiff-add, .udiff-del"),d=this.#t();a&&d&&(d.scrollTop=a.offsetTop-d.clientHeight/3)}#u(s){this.#s!==this.#e&&(this.#s=this.#e,requestAnimationFrame(()=>{this.#f(s),this.update(),this.#r.sync()}))}#f(s){let a=this.viewing,d=this.#t();if(!a||!d?.scrollHeight)return;let p=d.scrollHeight,b=[];for(let v=0;v<a.kinds.length;v++){let w=a.kinds[v];if(w!=="add"&&w!=="del")continue;let I=v;for(;I+1<a.kinds.length&&a.kinds[I+1]===w;)I++;let T=s.children[v],B=s.children[I];T&&B&&b.push({top:T.offsetTop/p,height:(B.offsetTop+B.offsetHeight-T.offsetTop)/p,class:w}),v=I}this.#i=b}};var iD=class extends Ia{done=!1;#e;constructor(s){super(),this.#e=s}setText(s){this.#e=s}flash(){navigator.clipboard?.writeText(this.#e),this.done=!0,this.update(),setTimeout(()=>{this.done=!1,this.update()},600)}view(){return Yo({class:["overlay-pill","copy",this.done?"done":""],onClick:s=>{s.stopPropagation(),this.flash()}},this.done?"copied":"copy")}};var USt=new Map,zSt=(o,s)=>s+`
|
|
@@ -1218,6 +1218,18 @@ ${q9t}
|
|
|
1218
1218
|
});
|
|
1219
1219
|
await failIfNotOk(resp, 'list', path);
|
|
1220
1220
|
return resp.json();
|
|
1221
|
+
},
|
|
1222
|
+
// Remove a file, or a folder and its contents. Throws on a missing path; the bulb's folder
|
|
1223
|
+
// itself is refused.
|
|
1224
|
+
remove: async (path) => {
|
|
1225
|
+
if (isFramed) throw inlineErr('tb.fs');
|
|
1226
|
+
const resp = await fetch('/__fs/remove', {
|
|
1227
|
+
method: 'POST',
|
|
1228
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1229
|
+
body: JSON.stringify({ path })
|
|
1230
|
+
});
|
|
1231
|
+
await failIfNotOk(resp, 'remove', path);
|
|
1232
|
+
return true;
|
|
1221
1233
|
}
|
|
1222
1234
|
};
|
|
1223
1235
|
|
|
@@ -2176,7 +2188,7 @@ ${vCt(b)}`};let v=mCt(d.code,{jsxImportSource:p.ts?.jsxImportSource});if(v.error
|
|
|
2176
2188
|
`)}),d.push({kind:"bulb",source:b.source}),p=b.end);return p<s.length&&d.push({kind:"md",text:s.slice(p).join(`
|
|
2177
2189
|
`)}),d}var Wre=class extends M7{sessions=[];keepOpenSelector=".sid-wrap";filterId="session-filter";listSelector="#session-list";filterNoun="title";#e=new Map;#n=!1;#t=null;#r=new Map;search(s){return tb.server.searchSessions(s)}onActivate(s){let a=this.rows()[s];a&&this.pick(a.sessionId)}filtered(){let s=this.filter.trim().toLowerCase();return s?this.sessions.filter(a=>a.preview.toLowerCase().includes(s)):this.sessions}rows(){return this.searchActive?this.results:this.filtered()}currentIndex(){let s=this.rows(),a=s.findIndex(d=>d.sessionId===this.parent.sessionId);return a<0?s.length-1:a}async loadSessions(){this.sessions=(await tb.server.listSessions()).reverse(),this.#i(),this.parent.updateTitle()}#i(){let s=this.parent.sessionId;for(let a of this.sessions)(!this.#n||a.sessionId===s)&&this.#e.set(a.sessionId,a.mtime);this.#n=!0}#s(s){if(s.sessionId===this.parent.sessionId)return!1;let a=this.#e.get(s.sessionId);return a===void 0||s.mtime>a}onAttached(){this.loadSessions().then(()=>this.update()).catch(()=>{})}currentRow(){return this.sessions.find(s=>s.sessionId===this.parent.sessionId)}currentPreview(){return this.currentRow()?.preview?.trim()??""}async show(){this.beginOpen(),this.refreshList(this.currentIndex()),this.focusFilter();try{await this.loadSessions(),this.refreshList(this.currentIndex())}catch(s){console.error("[mirror] listSessions failed",s)}this.armClose()}async pick(s){let a=this.rows().find(d=>d.sessionId===s);a&&this.#e.set(s,a.mtime),this.close(),this.parent.messageList.stickToBottomNextRender(),await tb.server.attach(s)}view(){let s=this.parent;if(s.sessionId===null)return un({class:"sid-wrap"});let a=s.sessionId==="",d=a?"new conversation":this.currentPreview()||"current",p=Zee(d,25),b=`${s.cwd}
|
|
2178
2190
|
${a||this.currentRow()?.pending?"New conversation \u2014 not saved yet":`Session: ${s.sessionId}`}`;return un({class:"sid-wrap"},ha({class:"pill","data-tip":b,onClick:v=>{v.stopPropagation(),this.open?this.close():this.show()}},p),this.open?this.picker():null)}picker(){let s=this.rows();return un({class:"picker"},un({class:"picker-body"},s.length===0?this.emptyState("No sessions yet \u2014 start one in your terminal."):un({id:"session-list",class:"picker-list",onScroll:()=>this.onListScroll()},s.map((a,d)=>this.pickerRow(a,d))),this.peekCard()),this.filterBox(this.sessions.length,"session"))}pickerRow(s,a){let d=s.sessionId===this.parent.sessionId,p=this.parent.busy.includes(s.sessionId);return un({class:["picker-row",a===this.highlighted?"active":"",d?"current":""],onMouseEnter:()=>{this.highlighted!==a&&(this.highlighted=a,this.update())},onClick:()=>this.pick(s.sessionId)},un({class:"picker-row-main"},Nr({class:"picker-dot"}),Nr({class:["picker-preview",p?"shimmer-text shimmer-slow":""]},s.preview||"(no preview)"),Yee(s.hitCount),Nr({class:["picker-time",this.#s(s)?"unseen":""],onMouseEnter:()=>this.#o(s),onMouseLeave:()=>this.#c(s)},Vee(s.mtime))),Xee(s.snippet,this.filter.trim()))}#o(s){this.#t=`${s.sessionId}:${s.mtime}`,this.#u(s.sessionId,this.#t),this.update()}#c(s){this.#t===`${s.sessionId}:${s.mtime}`&&(this.#t=null,this.update())}async#u(s,a){if(this.#r.has(a))return;let d="";try{d=String((await tb.server.sessionPeek(s))?.text??"")}catch{}this.#r.set(a,d),this.#t===a&&this.update()}peekCard(){if(!this.#t)return null;let s=this.#r.get(this.#t);return s===void 0?un({class:"picker-peek muted"},"\u2026"):s?un({class:"picker-peek md",key:`peek:${this.#t}`,onMounted:a=>{a.innerHTML=eD.render(s)}}):un({class:"picker-peek muted"},"(no assistant text yet)")}onClosed(){super.onClosed(),this.#t=null}};var Yre=class extends Ku{get parent(){return this.ctx.parent}view(){let s=this.parent.tokens,a=this.parent.stats,d=a?a.contextTokens??0:s.in+s.out+s.cached+s.cacheCreate,p=a?.cost??this.parent.cost,b=this.parent.working,v=p>0?`$${p>=1?p.toFixed(2):p.toFixed(3)}`:"",w=[];if(d>0&&w.push(Qye(d)),a&&a.contextPercent!=null&&w.push(`${Math.round(a.contextPercent)}%`),v&&w.push(v),!w.length&&!b)return Nr({class:"token-wrap"});let I=w.length?w.length===1&&d>0?`${w[0]} tokens`:w.join(" \xB7 "):"working\u2026",T=w.length?[d>0?`${Qye(d)} tokens in the context window`:"",a&&a.contextPercent!=null?`${Math.round(a.contextPercent)}% of the model\u2019s window`:"",v?`${v} spent this session`:""].filter(Boolean).join(`
|
|
2179
|
-
`):"Waiting for the harness to flush this turn";return Nr({class:"token-wrap"},Nr({class:["token",g6t(b)],"data-tip":T},I))}};var LSt={"play_arrow.fill":'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M320-200v-560l440 280-440 280Z"/></svg>',"stop.fill":'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M240-240v-480h480v480H240Z"/></svg>',play_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m380-300 280-180-280-180v360ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',stop_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M320-320h320v-320H320v320ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',add_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M440-280h80v-160h160v-80H520v-160h-80v160H280v80h160v160Zm40 200q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',attach_file:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M720-330q0 104-73 177T470-80q-104 0-177-73t-73-177v-370q0-75 52.5-127.5T400-880q75 0 127.5 52.5T580-700v350q0 46-32 78t-78 32q-46 0-78-32t-32-78v-370h80v370q0 13 8.5 21.5T470-320q13 0 21.5-8.5T500-350v-350q-1-42-29.5-71T400-800q-42 0-71 29t-29 71v370q-1 71 49 120.5T470-160q70 0 119-49.5T640-330v-390h80v390Z"/></svg>'};var jvn={play:"play_arrow.fill",stop:"stop.fill",playCircle:"play_circle",stopCircle:"stop_circle",addCircle:"add_circle",attach:"attach_file"},Fvn={caret:'<svg viewBox="0 0 16 16"><path d="M4 1.5 L13 8 L4 14.5 Z" fill="currentColor"/></svg>',push:'<svg viewBox="0 0 16 16" fill="none"><path d="M8 11 V3 M4.75 6.25 L8 3 L11.25 6.25" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 14 H13" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',pull:'<svg viewBox="0 0 16 16" fill="none"><path d="M8 3 V11 M4.75 7.75 L8 11 L11.25 7.75" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 14 H13" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',check:'<svg viewBox="0 0 16 16" fill="none"><path d="M3 8.5 L6.5 12 L13 4.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',close:'<svg viewBox="0 0 16 16" fill="none"><path d="M4 4 L12 12 M12 4 L4 12" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',fork:'<svg viewBox="0 0 16 16" fill="none"><path d="M4 2 V8 H12 V2 M8 8 V14" stroke="currentColor" stroke-width="1.8" stroke-linecap="square"/></svg>',diff:'<svg viewBox="4 2 8 12" fill="none"><path d="M5.25 6 H10.75 M8 3.25 V8.75" stroke="var(--diff-add)" stroke-width="1.8" stroke-linecap="round"/><path d="M5.25 12.5 H10.75" stroke="var(--err)" stroke-width="1.8" stroke-linecap="round"/></svg>'},$vn=o=>{let s=Fvn[o]??LSt[jvn[o]??""]??"";return/<svg[^>]*\bfill=/.test(s)?s:s.replace("<svg ",'<svg fill="currentColor" ')},T3=(o,s="")=>Nr({class:["icon",...Array.isArray(s)?s:[s]],key:`icon-${o}`,onMounted:a=>{a.innerHTML=$vn(o)}},"");function NSt(o,...s){return o.slice().sort((a,d)=>{for(let{key:p,desc:b}of s){let v=p(a),w=p(d);if(v!==w)return v==null?b?-1:1:w==null||v<w?b?1:-1:b?-1:1}return 0})}var Hvn=()=>T3("play","btn-icon"),qvn=()=>T3("stop","btn-icon"),Gvn=o=>`typebulbs/u/samples/${o}.bulb.md`,rD=o=>Gh(o.path).match(/typebulbs\/u\/([^/]+)\//)?.[1],Qvn=(o,s)=>`https://typebulb.com/u/${s}/${aM(o.path)}/full`,RSt=o=>o==="HTTP 404"?"not found \u2014 check the URL (private bulbs need their owner's token)":o==="HTTP 401"||o==="HTTP 403"?"private \u2014 needs its owner's TYPEBULB_TOKEN in .env":o||"pull failed",Xre=class extends M7{files=[];servers=[];samples=[];mine=[];myUser;keepOpenSelector=".servers-wrap";filterId="bulb-filter";listSelector=".bulb-list";filterNoun="name";launching=new Set;dismissedDenials=new Set;pendingLaunch;openLog;#e;spotlightPid;openFolds=new Set;pendingOverwrite;pullErrors=new Map;syncBusy=new Set;syncDone=new Set;search(s){return tb.server.searchBulbs(s)}onActivate(s){let a=this.rows()[s];a&&(a.group?this.toggleFold(a.group.user):a.running?SF(a.running.url):a.remote?this.downloadAndLaunch(a):this.launch(a.path))}onAttached(){this.refresh(),setInterval(()=>this.refreshServers(),800),setInterval(()=>this.refreshFiles(),3e3),window.addEventListener("tb-breakout",s=>this.spotlight(s.detail?.pid))}spotlight(s){this.spotlightPid=s,this.show()}refresh(){return Promise.all([this.refreshServers(),this.refreshFiles(),this.refreshSamples(),this.refreshMine()])}async refreshServers(){try{let s=await tb.server.listBreakouts(),a=s.length!==this.servers.length||s.some((d,p)=>d.pid!==this.servers[p]?.pid||d.denied!==this.servers[p]?.denied||d.predicted!==this.servers[p]?.predicted||d.trust!==this.servers[p]?.trust);this.servers=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] server list refresh failed",s)}}async refreshFiles(){try{let s=await tb.server.listBulbFiles(),a=s.length!==this.files.length||s.some((d,p)=>d.path!==this.files[p]?.path||d.mtime!==this.files[p]?.mtime||d.lastRunAt!==this.files[p]?.lastRunAt);this.files=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] bulb list refresh failed",s)}}async refreshSamples(){try{let s=await tb.server.listSamples(),a=s.length!==this.samples.length||s.some((d,p)=>d.slug!==this.samples[p]?.slug);this.samples=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] samples refresh failed",s)}}async refreshMine(){try{let{user:s,bulbs:a}=await tb.server.listMyBulbs(),d=s!==this.myUser||a.length!==this.mine.length||a.some((p,b)=>p.slug!==this.mine[b]?.slug);this.myUser=s,this.mine=a,d&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] my-bulbs refresh failed",s)}}merged(){let s=new Map,a=this.files.map(p=>Gh(p.path)),d=[...this.samples.map(p=>[Gvn(p.slug),p]),...this.myUser?this.mine.map(p=>[`typebulbs/u/${this.myUser}/${p.slug}.bulb.md`,p]):[]];for(let[p,b]of d)a.some(v=>v.endsWith("/"+p))||s.set(p,{path:p,name:b.name,recent:0,remote:b});this.files.forEach((p,b)=>{s.set(a[b],{path:p.path,name:p.name,recent:Math.max(p.mtime,p.lastRunAt??0),trusted:p.trusted})});for(let p of this.servers){if(p.pid===this.parent.ownPid)continue;let b=Gh(p.file),v=aM(p.file)||p.file,w=s.get(b)??{path:p.file,name:v,recent:0};w.running=p,w.recent=Math.max(w.recent,p.startedAt),s.set(b,w)}return[...s.values()]}rows(){let s=this.merged();if(this.searchActive){let a=new Map(this.results.map(d=>[Gh(d.path),d]));s=s.flatMap(d=>{let p=a.get(Gh(d.path));return p?[{...d,hitCount:p.hitCount,snippet:p.snippet}]:[]})}else{let a=this.filter.trim().toLowerCase(),d=A6t(this.filter);if(d){let p=`typebulbs/u/${d.user}/${d.slug}.bulb.md`,b=s.find(v=>("/"+Gh(v.path)).endsWith("/"+p));return b?[b]:[{path:p,name:d.slug,recent:0,remote:{slug:d.slug,name:d.slug,description:`u/${d.user}/${d.slug} on typebulb.com \u2014 play pulls it into the project`}}]}a?s=s.filter(p=>p.name.toLowerCase().includes(a)||p.path.toLowerCase().includes(a)):[...new Set(s.filter(b=>b.remote).map(b=>rD(b)))].forEach((b,v)=>{let w=T=>!!T.remote&&rD(T)===b,I=s.filter(w).length;this.openFolds.has(b)?s=s.map(T=>w(T)?{...T,recent:-.5-v}:T):s=s.filter(T=>!w(T)),s.push({path:`::${b}`,name:this.foldLabel(b),recent:-1-v,group:{user:b,count:I}})})}return NSt(s,{key:a=>!!a.running},{key:a=>a.recent})}show(){this.beginOpen(),this.refresh(),this.refreshList(),this.armClose(),this.focusFilter()}onClosed(){super.onClosed(),this.closeLog(),this.openFolds.clear(),this.pendingOverwrite=void 0,this.pullErrors.clear(),this.spotlightPid=void 0}toggleFold(s){this.openFolds.delete(s)||this.openFolds.add(s),this.update()}foldLabel(s){return s==="samples"?"Samples from typebulb.com":`${s}'s bulbs`}foldTitle(s){return s==="samples"?"Runnable sample bulbs from typebulb.com \u2014 click to browse":`${s===this.myUser?"Your":`${s}'s`} bulbs on typebulb.com \u2014 click to browse; play pulls one into the project`}async launch(s){if(!this.files.find(d=>Gh(d.path)===Gh(s))?.trusted){let d;try{d=(await tb.server.predictTrustOf(s)).cap}catch(p){console.error("[mirror] predictTrustOf failed",p)}if(d){this.pendingLaunch={path:s,name:this.displayName(s),cap:d},this.update();return}}await this.doLaunch(s)}async downloadAndLaunch(s){let a=Gh(s.path);this.launching.add(a),this.pullErrors.delete(a),this.update();try{let d=rD(s),p=d==="samples"?await tb.server.downloadSample(s.remote.slug):await tb.server.pullRemoteBulb(d,s.remote.slug);if(!p.ok||!p.file){console.error("[mirror] remote pull failed",p.error),this.pullErrors.set(a,RSt(p.error));return}await this.refreshFiles();let b=this.files.find(v=>Gh(v.path).endsWith("/"+Gh(p.file)))?.path??p.file;await this.launch(b)}catch(d){console.error("[mirror] remote pull failed",d),this.pullErrors.set(a,RSt(d?.message))}finally{this.launching.delete(a),this.update()}}async syncRow(s,a,d=!1){let p=`${Gh(s.path)}:${a}`;this.syncBusy.add(p),this.update();try{let b=a==="pull"?await tb.server.pullRemoteBulb(rD(s),aM(s.path),d):await tb.server.pushLocalBulb(s.path,d);b.ok?this.tickDone(p):b.conflict?this.pendingOverwrite={...a==="pull"?{heading:`\u201C${s.name}\u201D has local changes`,body:"Your local file differs from the typebulb.com copy. Overwrite the local file with the site's version?",yesLabel:"Overwrite local"}:{heading:`The site copy of \u201C${s.name}\u201D changed`,body:"It changed since your last sync. Overwrite the typebulb.com copy with your local file?",yesLabel:"Overwrite site copy"},onYes:()=>this.syncRow(s,a,!0)}:console.error(`[mirror] ${a} failed`,b.error)}catch(b){console.error(`[mirror] ${a} failed`,b)}finally{this.syncBusy.delete(p)}await(a==="pull"?this.refreshFiles():this.refreshMine()),this.update()}tickDone(s){this.syncDone.add(s),setTimeout(()=>{this.syncDone.delete(s),this.update()},1200)}async doLaunch(s,a,d=!1){let p=Gh(s);this.launching.add(p),this.update();let b;try{b=await tb.server.launchBulb(s,a)}catch(v){console.error("[mirror] launchBulb failed",v)}finally{this.launching.delete(p)}d&&b?.pid&&this.dismissedDenials.add(b.pid),b?.url&&SF(b.url),await this.refresh(),this.pinToBottom()}displayName(s){return this.files.find(d=>Gh(d.path)===Gh(s))?.name||aM(s)||s}launchTrusted(){let s=this.pendingLaunch;this.pendingLaunch=void 0,s&&this.doLaunch(s.path,!0)}launchRestricted(){let s=this.pendingLaunch;this.pendingLaunch=void 0,s&&this.doLaunch(s.path,void 0,!0)}cancelLaunch(){this.pendingLaunch=void 0,this.update()}async stop(s){this.openLog?.pid===s&&this.closeLog(),this.dismissedDenials.delete(s);try{await tb.server.stopBreakout(s)}catch(a){console.error("[mirror] stopBreakout failed",a)}await this.refresh()}pendingDenial(){return this.servers.find(s=>s.denied&&s.pid!==this.parent.ownPid&&!this.dismissedDenials.has(s.pid))}async elevate(s){this.dismissedDenials.add(s.pid),this.openLog?.pid===s.pid&&this.closeLog();try{await tb.server.launchBulb(s.file,!0)}catch(a){console.error("[mirror] elevate failed",a)}await this.refresh()}dismissDenial(s){this.dismissedDenials.add(s),this.update()}async toggleTrust(s){let a=!(s.running?s.running.trust:s.trusted);try{await tb.server.setBulbTrust(s.path,a),s.running&&(this.openLog?.pid===s.running.pid&&this.closeLog(),await tb.server.launchBulb(s.path,a))}catch(d){console.error("[mirror] toggleTrust failed",d)}await this.refresh()}showLog(s,a){if(this.openLog?.pid===s.pid){this.closeLog();return}this.closeLog(),this.openLog={pid:s.pid,name:a,text:"",offset:0},this.update(),setTimeout(()=>document.getElementById("bulb-console")?.focus()),this.#n(),this.#e=setInterval(()=>this.#n(),700)}closeLog(){this.#e&&(clearInterval(this.#e),this.#e=void 0),this.openLog&&(this.openLog=void 0,this.update())}async#n(){let s=this.openLog;if(s)try{let{text:a,offset:d}=await tb.server.readBulbLog(s.pid,s.offset);if(this.openLog!==s)return;a?(s.text=(s.text+a).slice(-5e4),s.offset=d,this.update()):d!==s.offset&&(s.offset=d)}catch(a){console.error("[mirror] readBulbLog failed",a)}}view(){let s=this.servers.filter(p=>p.pid!==this.parent.ownPid).length;if(!(this.files.length>0||this.samples.length>0)&&s===0)return un({class:"servers-wrap"});let d=this.pendingDenial();return un({class:"servers-wrap"},ha({class:["pill",s>0?"":"glyph",this.open?"on":""],"data-tip":"Launch a project bulb \xB7 stop a running one",ariaLabel:s>0?`Bulbs (${s} running)`:"Bulbs",onClick:p=>{p.stopPropagation(),this.open?this.close():this.show()}},s>0?`${s}\u{1F4A1}`:Nr({class:"glyph-img"},"\u{1F4A1}")),this.open?this.popup():null,this.pendingLaunch?this.launchModal(this.pendingLaunch):this.pendingOverwrite?this.overwriteModal(this.pendingOverwrite):d?this.denialModal(d):null)}overwriteModal(s){let a=()=>{this.pendingOverwrite=void 0,this.update()};return this.trustModal({heading:s.heading,body:s.body,warn:"There is no undo \u2014 the overwritten side's changes are gone.",noLabel:"Cancel",yesLabel:s.yesLabel,onNo:a,onYes:()=>{this.pendingOverwrite=void 0,s.onYes()},onDismiss:a})}trustModal(s){let a=(d,p)=>{d.stopPropagation(),p(),this.open&&this.focusFilter()};return un({class:"trust-back",tabIndex:0,onMounted:d=>d.focus(),onKeyDown:d=>{d.key==="Escape"&&a(d,s.onDismiss)},onClick:d=>a(d,s.onDismiss)},un({class:"trust-modal",onClick:d=>d.stopPropagation()},un({class:"trust-modal-h"},s.heading),un({class:"trust-modal-b"},s.body),un({class:"trust-modal-warn"},s.warn),un({class:"trust-modal-acts"},ha({class:"trust-no",onClick:d=>a(d,s.onNo)},s.noLabel),ha({class:"trust-yes",onClick:d=>a(d,s.onYes)},s.yesLabel))))}launchModal(s){return this.trustModal({heading:`\u201C${s.name}\u201D will likely need access`,body:`It looks like it uses ${s.cap}, which is blocked unless you launch it Trusted. Launch Trusted to allow filesystem / AI / server.ts?`,warn:"Only trust bulbs you wrote or have read \u2014 be careful with anything off the internet.",noLabel:"Launch restricted",yesLabel:"Launch trusted",onNo:()=>this.launchRestricted(),onYes:()=>this.launchTrusted(),onDismiss:()=>this.cancelLaunch()})}denialModal(s){let a=aM(s.file)||s.file,d=!s.predicted;return this.trustModal({heading:d?`\u201C${a}\u201D reached for access it didn't declare`:`\u201C${a}\u201D wants more access`,body:d?`It tried to use ${s.denied} at runtime, though nothing in its code obviously needed it. Relaunch it Trusted to allow filesystem / AI / server.ts?`:`It tried to use ${s.denied}, blocked while it's restricted. Relaunch it Trusted to allow filesystem / AI / server.ts?`,warn:d?"Code that reaches for capabilities it never visibly uses can be hiding something \u2014 only continue if you wrote this bulb or have read it.":"Only trust bulbs you wrote or have read \u2014 be careful with anything off the internet.",noLabel:"Keep restricted",yesLabel:"Trust & relaunch",onNo:()=>this.dismissDenial(s.pid),onYes:()=>this.elevate(s),onDismiss:()=>this.dismissDenial(s.pid)})}popup(){if(this.openLog)return this.consoleView(this.openLog);let s=this.rows(),a=this.spotlightPid,d=a!==void 0&&s.some(p=>p.running?.pid===a);return un({class:"servers-pop"},s.length===0?this.emptyState("No bulbs in this project yet."):un({class:"bulb-list",onScroll:()=>this.onListScroll()},s.map((p,b)=>this.row(p,b,d&&p.running?.pid!==a))),this.filterBox(this.merged().filter(p=>!p.remote).length,"bulb"))}consoleView(s){return un({id:"bulb-console",class:"servers-pop log-mode",tabIndex:0,onKeyDown:a=>{a.key==="Escape"&&(a.stopPropagation(),this.closeLog())}},un({class:"bulb-log-head"},Nr({class:"bulb-log-name"},`console \xB7 ${s.name}`),ha({class:"bulb-log-back",title:"Back to the bulb list",onClick:a=>{a.stopPropagation(),this.closeLog()}},"\u2190 bulbs")),r6({class:"bulb-log-body"},Nr({class:"bulb-log-text"},s.text||"(no output yet)")))}hoverRow(s,a){let d=a!==void 0&&a.pid===this.spotlightPid;d&&(this.spotlightPid=void 0),(this.highlighted!==s||d)&&(this.highlighted=s,this.update())}row(s,a,d=!1){if(s.group)return this.groupRow(s,a,d);let p=s.running,b=p&&this.openLog?.pid===p.pid,v=this.pullErrors.get(Gh(s.path)),w=p?!!p.trust:!!s.trusted,I=!!s.remote&&!this.searchActive&&!this.filter.trim();return un({class:["server-row",I?"fold-row":"",a===this.highlighted?"active":"",d?"dimmed":""],onMouseEnter:()=>this.hoverRow(a,p)},p?ha({class:"server-stop",title:"Stop this server",ariaLabel:"Stop",onClick:T=>{T.stopPropagation(),this.stop(p.pid)}},qvn()):ha({class:["bulb-launch",this.launching.has(Gh(s.path))?"launching shimmer":""],title:s.remote?"Download from typebulb.com & launch":w?"Launch (trusted \u2014 remembered)":"Launch (restricted)",ariaLabel:"Launch",onClick:T=>{T.stopPropagation(),s.remote?this.downloadAndLaunch(s):this.launch(s.path)}},Hvn()),un({class:"bulb-name-cell"},s.remote?Nr({class:"server-name stopped remote",title:s.remote.description},s.name):Nr({class:["server-name",p?"":"stopped"],title:`Open ${s.path}`,onClick:T=>{T.stopPropagation(),tb.server.openFile(s.path)}},s.name),this.userLink(s),Yee(s.hitCount),v?Nr({class:"pull-error"},v):null),this.syncCell(s),p||w?this.trustToggle(s,w):Nr({class:"cell-empty"}),p?gC({class:["server-logs",b?"on":""],title:"Show this server\u2019s console",onClick:T=>{T.stopPropagation(),this.showLog(p,s.name)}},"logs"):Nr({class:"cell-empty"}),p?gC({class:"server-port",href:qye(p.url),target:"_blank",rel:"noopener noreferrer",title:`Open ${p.url}`},`:${p.port}`):s.remote?Nr({class:"cell-empty"}):Nr({class:"bulb-time"},Vee(s.recent)),Xee(s.snippet,this.filter.trim()))}userLink(s){let a=rD(s);return a?gC({class:"sample-link",href:Qvn(s,a),target:"_blank",rel:"noopener noreferrer",title:"Open this bulb's page on typebulb.com"},a):null}syncCell(s){let a=rD(s);if(!a||s.remote)return Nr({class:"cell-empty"});let d=(p,b)=>{let v=`${Gh(s.path)}:${p}`,w=this.syncBusy.has(v),I=this.syncDone.has(v);return ha({class:["sync-btn",w?"busy shimmer":"",I?"done":""],title:b,ariaLabel:p,onClick:T=>{T.stopPropagation(),w||this.syncRow(s,p)}},T3(I?"check":p,"btn-icon-sm"))};return Nr({class:"sync-cell"},d("pull",`Pull \u2014 update the local file from typebulb.com/u/${a}`),a===this.myUser?d("push","Push \u2014 upload this file to typebulb.com"):null)}groupRow(s,a,d){let{user:p,count:b}=s.group,v=this.openFolds.has(p);return un({class:["server-row","sample-group",v?"open":"",a===this.highlighted?"active":"",d?"dimmed":""],title:v?"Collapse":this.foldTitle(p),onMouseEnter:()=>this.hoverRow(a),onClick:w=>{w.stopPropagation(),this.toggleFold(p)}},Nr({class:"fold-caret"},T3("caret",["caret-tri",v?"open":""])),Nr({class:"sample-group-label"},`${s.name} (${b})`))}trustToggle(s,a){return ha({class:["trust-toggle",a?"on":""],title:`${a?"Trusted":"Restricted"} \u2014 click to switch to ${a?"restricted":"trusted"}`+(s.running?" (stops + relaunches it)":""),onClick:d=>{d.stopPropagation(),this.toggleTrust(s)}},a?"trusted":"restricted")}};var Vre=class{#e;#n;#t;#r=!1;#i;#s;constructor(s){this.#e=s}#o(){this.#e()?.classList.add("oruler-host")}#c(s){this.#n=s;let a=this.#e();a&&(this.#o(),this.#s?.disconnect(),this.#s=new ResizeObserver(()=>this.sync()),this.#s.observe(a),this.sync())}release(){this.#s?.disconnect(),this.#s=void 0,this.#e()?.classList.remove("oruler-host"),this.#n=void 0,this.#t=void 0}sync(){this.#r||(this.#r=!0,requestAnimationFrame(()=>{this.#r=!1;let s=this.#e(),a=this.#t;if(!s||!a||!s.scrollHeight)return;this.#o();let d=s.scrollHeight-s.clientHeight;a.style.display=d>0?"":"none",a.style.top=`${s.scrollTop/s.scrollHeight*100}%`,a.style.height=`${s.clientHeight/s.scrollHeight*100}%`,this.#n?.setAttribute("aria-valuenow",String(d>0?Math.round(s.scrollTop/d*100):0))}))}view(s,a={}){return un({class:["oruler",a.class??""],title:a.title,role:"scrollbar",ariaOrientation:"vertical",ariaLabel:a.label??"Scroll position",ariaControls:a.controls,ariaValueMin:0,ariaValueMax:100,ariaValueNow:0,onMounted:d=>this.#c(d),onPointerDown:d=>this.#u(d),onPointerMove:d=>this.#f(d),onPointerUp:d=>this.#l(d),onPointerCancel:d=>this.#l(d),onWheel:d=>this.#h(d)},un({class:"oruler-win",onMounted:d=>{this.#t=d,this.sync()}}),s.map(d=>un({class:["oruler-mark",d.class??""],style:{top:`${d.top*100}%`,height:`${d.height*100}%`}})))}#u(s){let a=this.#e(),d=s.currentTarget;if(!a)return;s.preventDefault();let p=this.#t?.getBoundingClientRect(),b=!!p&&s.clientY>=p.top&&s.clientY<=p.bottom;this.#i=b?s.clientY-p.top:(p?.height??0)/2,d.setPointerCapture(s.pointerId),this.#a(s,d,a)}#f(s){let a=this.#e();this.#i===void 0||!a||this.#a(s,s.currentTarget,a)}#l(s){let a=s.currentTarget;this.#i=void 0,a.hasPointerCapture(s.pointerId)&&a.releasePointerCapture(s.pointerId)}#h(s){let a=this.#e();if(!a)return;let d=s.deltaMode===1?16:s.deltaMode===2?a.clientHeight:1;a.scrollTop+=s.deltaY*d,this.sync()}#a(s,a,d){let p=s.clientY-a.getBoundingClientRect().top-(this.#i??0);d.scrollTop=p/a.clientHeight*d.scrollHeight,this.sync()}};var Jvn=4e5,Vxe="diff-doc-body";function Uvn(o,s){let a=o.split(`
|
|
2191
|
+
`):"Waiting for the harness to flush this turn";return Nr({class:"token-wrap"},Nr({class:["token",g6t(b)],"data-tip":T},I))}};var LSt={"play_arrow.fill":'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M320-200v-560l440 280-440 280Z"/></svg>',"stop.fill":'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M240-240v-480h480v480H240Z"/></svg>',play_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m380-300 280-180-280-180v360ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',stop_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M320-320h320v-320H320v320ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',add_circle:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M440-280h80v-160h160v-80H520v-160h-80v160H280v80h160v160Zm40 200q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>',attach_file:'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M720-330q0 104-73 177T470-80q-104 0-177-73t-73-177v-370q0-75 52.5-127.5T400-880q75 0 127.5 52.5T580-700v350q0 46-32 78t-78 32q-46 0-78-32t-32-78v-370h80v370q0 13 8.5 21.5T470-320q13 0 21.5-8.5T500-350v-350q-1-42-29.5-71T400-800q-42 0-71 29t-29 71v370q-1 71 49 120.5T470-160q70 0 119-49.5T640-330v-390h80v390Z"/></svg>'};var jvn={play:"play_arrow.fill",stop:"stop.fill",playCircle:"play_circle",stopCircle:"stop_circle",addCircle:"add_circle",attach:"attach_file"},Fvn={caret:'<svg viewBox="0 0 16 16"><path d="M4 1.5 L13 8 L4 14.5 Z" fill="currentColor"/></svg>',push:'<svg viewBox="0 0 16 16" fill="none"><path d="M8 11 V3 M4.75 6.25 L8 3 L11.25 6.25" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 14 H13" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',pull:'<svg viewBox="0 0 16 16" fill="none"><path d="M8 3 V11 M4.75 7.75 L8 11 L11.25 7.75" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 14 H13" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',check:'<svg viewBox="0 0 16 16" fill="none"><path d="M3 8.5 L6.5 12 L13 4.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',close:'<svg viewBox="0 0 16 16" fill="none"><path d="M4 4 L12 12 M12 4 L4 12" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',fork:'<svg viewBox="0 0 16 16" fill="none"><path d="M4 2 V8 H12 V2 M8 8 V14" stroke="currentColor" stroke-width="1.8" stroke-linecap="square"/></svg>',diff:'<svg viewBox="4 2 8 12" fill="none"><path d="M5.25 6 H10.75 M8 3.25 V8.75" stroke="var(--diff-add)" stroke-width="1.8" stroke-linecap="round"/><path d="M5.25 12.5 H10.75" stroke="var(--err)" stroke-width="1.8" stroke-linecap="round"/></svg>'},$vn=o=>{let s=Fvn[o]??LSt[jvn[o]??""]??"";return/<svg[^>]*\bfill=/.test(s)?s:s.replace("<svg ",'<svg fill="currentColor" ')},T3=(o,s="")=>Nr({class:["icon",...Array.isArray(s)?s:[s]],key:`icon-${o}`,onMounted:a=>{a.innerHTML=$vn(o)}},"");function NSt(o,...s){return o.slice().sort((a,d)=>{for(let{key:p,desc:b}of s){let v=p(a),w=p(d);if(v!==w)return v==null?b?-1:1:w==null||v<w?b?1:-1:b?-1:1}return 0})}var Hvn=()=>T3("play","btn-icon"),qvn=()=>T3("stop","btn-icon"),Gvn=o=>`typebulbs/u/samples/${o}.bulb.md`,rD=o=>Gh(o.path).match(/typebulbs\/u\/([^/]+)\//)?.[1],Qvn=(o,s)=>`https://typebulb.com/u/${s}/${aM(o.path)}/full`,RSt=o=>o==="HTTP 404"?"not found \u2014 check the URL (private bulbs need their owner's token)":o==="HTTP 401"||o==="HTTP 403"?"private \u2014 needs its owner's TYPEBULB_TOKEN in .env":o||"pull failed",Xre=class extends M7{files=[];servers=[];samples=[];mine=[];myUser;keepOpenSelector=".servers-wrap";filterId="bulb-filter";listSelector=".bulb-list";filterNoun="name";launching=new Set;dismissedDenials=new Set;pendingLaunch;openLog;#e;spotlightPid;openFolds=new Set;pendingOverwrite;pullErrors=new Map;syncBusy=new Set;syncDone=new Set;search(s){return tb.server.searchBulbs(s)}onActivate(s){let a=this.rows()[s];a&&(a.group?this.toggleFold(a.group.user):a.running?SF(a.running.url):a.remote?this.downloadAndLaunch(a):this.launch(a.path))}onAttached(){this.refresh(),setInterval(()=>this.refreshServers(),800),setInterval(()=>this.refreshFiles(),3e3),window.addEventListener("tb-breakout",s=>this.spotlight(s.detail?.pid))}spotlight(s){this.spotlightPid=s,this.show()}refresh(){return Promise.all([this.refreshServers(),this.refreshFiles(),this.refreshSamples(),this.refreshMine()])}async refreshServers(){try{let s=await tb.server.listBreakouts(),a=s.length!==this.servers.length||s.some((d,p)=>d.pid!==this.servers[p]?.pid||d.denied!==this.servers[p]?.denied||d.predicted!==this.servers[p]?.predicted||d.trust!==this.servers[p]?.trust);this.servers=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] server list refresh failed",s)}}async refreshFiles(){try{let s=await tb.server.listBulbFiles(),a=s.length!==this.files.length||s.some((d,p)=>d.path!==this.files[p]?.path||d.mtime!==this.files[p]?.mtime||d.lastRunAt!==this.files[p]?.lastRunAt);this.files=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] bulb list refresh failed",s)}}async refreshSamples(){try{let s=await tb.server.listSamples(),a=s.length!==this.samples.length||s.some((d,p)=>d.slug!==this.samples[p]?.slug);this.samples=s,a&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] samples refresh failed",s)}}async refreshMine(){try{let{user:s,bulbs:a}=await tb.server.listMyBulbs(),d=s!==this.myUser||a.length!==this.mine.length||a.some((p,b)=>p.slug!==this.mine[b]?.slug);this.myUser=s,this.mine=a,d&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] my-bulbs refresh failed",s)}}merged(){let s=new Map,a=this.files.map(p=>Gh(p.path)),d=[...this.samples.map(p=>[Gvn(p.slug),p]),...this.myUser?this.mine.map(p=>[`typebulbs/u/${this.myUser}/${p.slug}.bulb.md`,p]):[]];for(let[p,b]of d)a.some(v=>v.endsWith("/"+p))||s.set(p,{path:p,name:b.name,recent:0,remote:b});this.files.forEach((p,b)=>{s.set(a[b],{path:p.path,name:p.name,recent:Math.max(p.mtime,p.lastRunAt??0),trusted:p.trusted})});for(let p of this.servers){if(p.pid===this.parent.ownPid)continue;let b=Gh(p.file),v=aM(p.file)||p.file,w=s.get(b)??{path:p.file,name:v,recent:0};w.running=p,w.recent=Math.max(w.recent,p.startedAt),s.set(b,w)}return[...s.values()]}rows(){let s=this.merged();if(this.searchActive){let a=new Map(this.results.map(d=>[Gh(d.path),d]));s=s.flatMap(d=>{let p=a.get(Gh(d.path));return p?[{...d,hitCount:p.hitCount,snippet:p.snippet}]:[]})}else{let a=this.filter.trim().toLowerCase(),d=A6t(this.filter);if(d){let p=`typebulbs/u/${d.user}/${d.slug}.bulb.md`,b=s.find(v=>("/"+Gh(v.path)).endsWith("/"+p));return b?[b]:[{path:p,name:d.slug,recent:0,remote:{slug:d.slug,name:d.slug,description:`u/${d.user}/${d.slug} on typebulb.com \u2014 play pulls it into the project`}}]}a?s=s.filter(p=>p.name.toLowerCase().includes(a)||p.path.toLowerCase().includes(a)):[...new Set(s.filter(b=>b.remote).map(b=>rD(b)))].forEach((b,v)=>{let w=T=>!!T.remote&&rD(T)===b,I=s.filter(w).length;this.openFolds.has(b)?s=s.map(T=>w(T)?{...T,recent:-.5-v}:T):s=s.filter(T=>!w(T)),s.push({path:`::${b}`,name:this.foldLabel(b),recent:-1-v,group:{user:b,count:I}})})}return NSt(s,{key:a=>!!a.running},{key:a=>a.recent})}show(){this.beginOpen(),this.refresh(),this.refreshList(),this.armClose(),this.focusFilter()}onClosed(){super.onClosed(),this.closeLog(),this.openFolds.clear(),this.pendingOverwrite=void 0,this.pullErrors.clear(),this.spotlightPid=void 0}toggleFold(s){this.openFolds.delete(s)||this.openFolds.add(s),this.update()}foldLabel(s){return s==="samples"?"Samples from typebulb.com":`${s}'s bulbs`}foldTitle(s){return s==="samples"?"Runnable sample bulbs from typebulb.com \u2014 click to browse":`${s===this.myUser?"Your":`${s}'s`} bulbs on typebulb.com \u2014 click to browse; play pulls one into the project`}async launch(s){if(!this.files.find(d=>Gh(d.path)===Gh(s))?.trusted){let d;try{d=(await tb.server.predictTrustOf(s)).cap}catch(p){console.error("[mirror] predictTrustOf failed",p)}if(d){this.pendingLaunch={path:s,name:this.displayName(s),cap:d},this.update();return}}await this.doLaunch(s)}async downloadAndLaunch(s){let a=Gh(s.path);this.launching.add(a),this.pullErrors.delete(a),this.update();try{let d=rD(s),p=d==="samples"?await tb.server.downloadSample(s.remote.slug):await tb.server.pullRemoteBulb(d,s.remote.slug);if(!p.ok||!p.file){console.error("[mirror] remote pull failed",p.error),this.pullErrors.set(a,RSt(p.error));return}await this.refreshFiles();let b=this.files.find(v=>Gh(v.path).endsWith("/"+Gh(p.file)))?.path??p.file;await this.launch(b)}catch(d){console.error("[mirror] remote pull failed",d),this.pullErrors.set(a,RSt(d?.message))}finally{this.launching.delete(a),this.update()}}async syncRow(s,a,d=!1){let p=`${Gh(s.path)}:${a}`;this.syncBusy.add(p),this.update();try{let b=a==="pull"?await tb.server.pullRemoteBulb(rD(s),aM(s.path),d):await tb.server.pushLocalBulb(s.path,d);b.ok?this.tickDone(p):b.conflict?this.pendingOverwrite={...a==="pull"?{heading:`\u201C${s.name}\u201D has local changes`,body:"Your local file differs from the typebulb.com copy. Overwrite the local file with the site's version?",yesLabel:"Overwrite local"}:{heading:`The site copy of \u201C${s.name}\u201D changed`,body:"It changed since your last sync. Overwrite the typebulb.com copy with your local file?",yesLabel:"Overwrite site copy"},onYes:()=>this.syncRow(s,a,!0)}:console.error(`[mirror] ${a} failed`,b.error)}catch(b){console.error(`[mirror] ${a} failed`,b)}finally{this.syncBusy.delete(p),this.update()}await(a==="pull"?this.refreshFiles():this.refreshMine()),this.update()}tickDone(s){this.syncDone.add(s),setTimeout(()=>{this.syncDone.delete(s),this.update()},1200)}async doLaunch(s,a,d=!1){let p=Gh(s);this.launching.add(p),this.update();let b;try{b=await tb.server.launchBulb(s,a)}catch(v){console.error("[mirror] launchBulb failed",v)}finally{this.launching.delete(p)}d&&b?.pid&&this.dismissedDenials.add(b.pid),b?.url&&SF(b.url),await this.refresh(),this.pinToBottom()}displayName(s){return this.files.find(d=>Gh(d.path)===Gh(s))?.name||aM(s)||s}launchTrusted(){let s=this.pendingLaunch;this.pendingLaunch=void 0,s&&this.doLaunch(s.path,!0)}launchRestricted(){let s=this.pendingLaunch;this.pendingLaunch=void 0,s&&this.doLaunch(s.path,void 0,!0)}cancelLaunch(){this.pendingLaunch=void 0,this.update()}async stop(s){this.openLog?.pid===s&&this.closeLog(),this.dismissedDenials.delete(s);try{await tb.server.stopBreakout(s)}catch(a){console.error("[mirror] stopBreakout failed",a)}await this.refresh()}pendingDenial(){return this.servers.find(s=>s.denied&&s.pid!==this.parent.ownPid&&!this.dismissedDenials.has(s.pid))}async elevate(s){this.dismissedDenials.add(s.pid),this.openLog?.pid===s.pid&&this.closeLog();try{await tb.server.launchBulb(s.file,!0)}catch(a){console.error("[mirror] elevate failed",a)}await this.refresh()}dismissDenial(s){this.dismissedDenials.add(s),this.update()}async toggleTrust(s){let a=!(s.running?s.running.trust:s.trusted);try{await tb.server.setBulbTrust(s.path,a),s.running&&(this.openLog?.pid===s.running.pid&&this.closeLog(),await tb.server.launchBulb(s.path,a))}catch(d){console.error("[mirror] toggleTrust failed",d)}await this.refresh()}showLog(s,a){if(this.openLog?.pid===s.pid){this.closeLog();return}this.closeLog(),this.openLog={pid:s.pid,name:a,text:"",offset:0},this.update(),setTimeout(()=>document.getElementById("bulb-console")?.focus()),this.#n(),this.#e=setInterval(()=>this.#n(),700)}closeLog(){this.#e&&(clearInterval(this.#e),this.#e=void 0),this.openLog&&(this.openLog=void 0,this.update())}async#n(){let s=this.openLog;if(s)try{let{text:a,offset:d}=await tb.server.readBulbLog(s.pid,s.offset);if(this.openLog!==s)return;a?(s.text=(s.text+a).slice(-5e4),s.offset=d,this.update()):d!==s.offset&&(s.offset=d)}catch(a){console.error("[mirror] readBulbLog failed",a)}}view(){let s=this.servers.filter(p=>p.pid!==this.parent.ownPid).length;if(!(this.files.length>0||this.samples.length>0)&&s===0)return un({class:"servers-wrap"});let d=this.pendingDenial();return un({class:"servers-wrap"},ha({class:["pill",s>0?"":"glyph",this.open?"on":""],"data-tip":"Launch a project bulb \xB7 stop a running one",ariaLabel:s>0?`Bulbs (${s} running)`:"Bulbs",onClick:p=>{p.stopPropagation(),this.open?this.close():this.show()}},s>0?`${s}\u{1F4A1}`:Nr({class:"glyph-img"},"\u{1F4A1}")),this.open?this.popup():null,this.pendingLaunch?this.launchModal(this.pendingLaunch):this.pendingOverwrite?this.overwriteModal(this.pendingOverwrite):d?this.denialModal(d):null)}overwriteModal(s){let a=()=>{this.pendingOverwrite=void 0,this.update()};return this.trustModal({heading:s.heading,body:s.body,warn:"There is no undo \u2014 the overwritten side's changes are gone.",noLabel:"Cancel",yesLabel:s.yesLabel,onNo:a,onYes:()=>{this.pendingOverwrite=void 0,s.onYes()},onDismiss:a})}trustModal(s){let a=(d,p)=>{d.stopPropagation(),p(),this.open&&this.focusFilter()};return un({class:"trust-back",tabIndex:0,onMounted:d=>d.focus(),onKeyDown:d=>{d.key==="Escape"&&a(d,s.onDismiss)},onClick:d=>a(d,s.onDismiss)},un({class:"trust-modal",onClick:d=>d.stopPropagation()},un({class:"trust-modal-h"},s.heading),un({class:"trust-modal-b"},s.body),un({class:"trust-modal-warn"},s.warn),un({class:"trust-modal-acts"},ha({class:"trust-no",onClick:d=>a(d,s.onNo)},s.noLabel),ha({class:"trust-yes",onClick:d=>a(d,s.onYes)},s.yesLabel))))}launchModal(s){return this.trustModal({heading:`\u201C${s.name}\u201D will likely need access`,body:`It looks like it uses ${s.cap}, which is blocked unless you launch it Trusted. Launch Trusted to allow filesystem / AI / server.ts?`,warn:"Only trust bulbs you wrote or have read \u2014 be careful with anything off the internet.",noLabel:"Launch restricted",yesLabel:"Launch trusted",onNo:()=>this.launchRestricted(),onYes:()=>this.launchTrusted(),onDismiss:()=>this.cancelLaunch()})}denialModal(s){let a=aM(s.file)||s.file,d=!s.predicted;return this.trustModal({heading:d?`\u201C${a}\u201D reached for access it didn't declare`:`\u201C${a}\u201D wants more access`,body:d?`It tried to use ${s.denied} at runtime, though nothing in its code obviously needed it. Relaunch it Trusted to allow filesystem / AI / server.ts?`:`It tried to use ${s.denied}, blocked while it's restricted. Relaunch it Trusted to allow filesystem / AI / server.ts?`,warn:d?"Code that reaches for capabilities it never visibly uses can be hiding something \u2014 only continue if you wrote this bulb or have read it.":"Only trust bulbs you wrote or have read \u2014 be careful with anything off the internet.",noLabel:"Keep restricted",yesLabel:"Trust & relaunch",onNo:()=>this.dismissDenial(s.pid),onYes:()=>this.elevate(s),onDismiss:()=>this.dismissDenial(s.pid)})}popup(){if(this.openLog)return this.consoleView(this.openLog);let s=this.rows(),a=this.spotlightPid,d=a!==void 0&&s.some(p=>p.running?.pid===a);return un({class:"servers-pop"},s.length===0?this.emptyState("No bulbs in this project yet."):un({class:"bulb-list",onScroll:()=>this.onListScroll()},s.map((p,b)=>this.row(p,b,d&&p.running?.pid!==a))),this.filterBox(this.merged().filter(p=>!p.remote).length,"bulb"))}consoleView(s){return un({id:"bulb-console",class:"servers-pop log-mode",tabIndex:0,onKeyDown:a=>{a.key==="Escape"&&(a.stopPropagation(),this.closeLog())}},un({class:"bulb-log-head"},Nr({class:"bulb-log-name"},`console \xB7 ${s.name}`),ha({class:"bulb-log-back",title:"Back to the bulb list",onClick:a=>{a.stopPropagation(),this.closeLog()}},"\u2190 bulbs")),r6({class:"bulb-log-body"},Nr({class:"bulb-log-text"},s.text||"(no output yet)")))}hoverRow(s,a){let d=a!==void 0&&a.pid===this.spotlightPid;d&&(this.spotlightPid=void 0),(this.highlighted!==s||d)&&(this.highlighted=s,this.update())}row(s,a,d=!1){if(s.group)return this.groupRow(s,a,d);let p=s.running,b=p&&this.openLog?.pid===p.pid,v=this.pullErrors.get(Gh(s.path)),w=p?!!p.trust:!!s.trusted,I=!!s.remote&&!this.searchActive&&!this.filter.trim();return un({class:["server-row",I?"fold-row":"",a===this.highlighted?"active":"",d?"dimmed":""],onMouseEnter:()=>this.hoverRow(a,p)},p?ha({class:"server-stop",title:"Stop this server",ariaLabel:"Stop",onClick:T=>{T.stopPropagation(),this.stop(p.pid)}},qvn()):ha({class:["bulb-launch",this.launching.has(Gh(s.path))?"launching shimmer":""],title:s.remote?"Download from typebulb.com & launch":w?"Launch (trusted \u2014 remembered)":"Launch (restricted)",ariaLabel:"Launch",onClick:T=>{T.stopPropagation(),s.remote?this.downloadAndLaunch(s):this.launch(s.path)}},Hvn()),un({class:"bulb-name-cell"},s.remote?Nr({class:"server-name stopped remote",title:s.remote.description},s.name):Nr({class:["server-name",p?"":"stopped"],title:`Open ${s.path}`,onClick:T=>{T.stopPropagation(),tb.server.openFile(s.path)}},s.name),this.userLink(s),Yee(s.hitCount),v?Nr({class:"pull-error"},v):null),this.syncCell(s),p||w?this.trustToggle(s,w):Nr({class:"cell-empty"}),p?gC({class:["server-logs",b?"on":""],title:"Show this server\u2019s console",onClick:T=>{T.stopPropagation(),this.showLog(p,s.name)}},"logs"):Nr({class:"cell-empty"}),p?gC({class:"server-port",href:qye(p.url),target:"_blank",rel:"noopener noreferrer",title:`Open ${p.url}`},`:${p.port}`):s.remote?Nr({class:"cell-empty"}):Nr({class:"bulb-time"},Vee(s.recent)),Xee(s.snippet,this.filter.trim()))}userLink(s){let a=rD(s);return a?gC({class:"sample-link",href:Qvn(s,a),target:"_blank",rel:"noopener noreferrer",title:"Open this bulb's page on typebulb.com"},a):null}syncCell(s){let a=rD(s);if(!a||s.remote)return Nr({class:"cell-empty"});let d=(p,b)=>{let v=`${Gh(s.path)}:${p}`,w=this.syncBusy.has(v),I=this.syncDone.has(v);return ha({class:["sync-btn",w?"busy shimmer":"",I?"done":""],title:b,ariaLabel:p,onClick:T=>{T.stopPropagation(),w||this.syncRow(s,p)}},T3(I?"check":p,"btn-icon-sm"))};return Nr({class:"sync-cell"},d("pull",`Pull \u2014 update the local file from typebulb.com/u/${a}`),a===this.myUser?d("push","Push \u2014 upload this file to typebulb.com"):null)}groupRow(s,a,d){let{user:p,count:b}=s.group,v=this.openFolds.has(p);return un({class:["server-row","sample-group",v?"open":"",a===this.highlighted?"active":"",d?"dimmed":""],title:v?"Collapse":this.foldTitle(p),onMouseEnter:()=>this.hoverRow(a),onClick:w=>{w.stopPropagation(),this.toggleFold(p)}},Nr({class:"fold-caret"},T3("caret",["caret-tri",v?"open":""])),Nr({class:"sample-group-label"},`${s.name} (${b})`))}trustToggle(s,a){return ha({class:["trust-toggle",a?"on":""],title:`${a?"Trusted":"Restricted"} \u2014 click to switch to ${a?"restricted":"trusted"}`+(s.running?" (stops + relaunches it)":""),onClick:d=>{d.stopPropagation(),this.toggleTrust(s)}},a?"trusted":"restricted")}};var Vre=class{#e;#n;#t;#r=!1;#i;#s;constructor(s){this.#e=s}#o(){this.#e()?.classList.add("oruler-host")}#c(s){this.#n=s;let a=this.#e();a&&(this.#o(),this.#s?.disconnect(),this.#s=new ResizeObserver(()=>this.sync()),this.#s.observe(a),this.sync())}release(){this.#s?.disconnect(),this.#s=void 0,this.#e()?.classList.remove("oruler-host"),this.#n=void 0,this.#t=void 0}sync(){this.#r||(this.#r=!0,requestAnimationFrame(()=>{this.#r=!1;let s=this.#e(),a=this.#t;if(!s||!a||!s.scrollHeight)return;this.#o();let d=s.scrollHeight-s.clientHeight;a.style.display=d>0?"":"none",a.style.top=`${s.scrollTop/s.scrollHeight*100}%`,a.style.height=`${s.clientHeight/s.scrollHeight*100}%`,this.#n?.setAttribute("aria-valuenow",String(d>0?Math.round(s.scrollTop/d*100):0))}))}view(s,a={}){return un({class:["oruler",a.class??""],title:a.title,role:"scrollbar",ariaOrientation:"vertical",ariaLabel:a.label??"Scroll position",ariaControls:a.controls,ariaValueMin:0,ariaValueMax:100,ariaValueNow:0,onMounted:d=>this.#c(d),onPointerDown:d=>this.#u(d),onPointerMove:d=>this.#f(d),onPointerUp:d=>this.#l(d),onPointerCancel:d=>this.#l(d),onWheel:d=>this.#h(d)},un({class:"oruler-win",onMounted:d=>{this.#t=d,this.sync()}}),s.map(d=>un({class:["oruler-mark",d.class??""],style:{top:`${d.top*100}%`,height:`${d.height*100}%`}})))}#u(s){let a=this.#e(),d=s.currentTarget;if(!a)return;s.preventDefault();let p=this.#t?.getBoundingClientRect(),b=!!p&&s.clientY>=p.top&&s.clientY<=p.bottom;this.#i=b?s.clientY-p.top:(p?.height??0)/2,d.setPointerCapture(s.pointerId),this.#a(s,d,a)}#f(s){let a=this.#e();this.#i===void 0||!a||this.#a(s,s.currentTarget,a)}#l(s){let a=s.currentTarget;this.#i=void 0,a.hasPointerCapture(s.pointerId)&&a.releasePointerCapture(s.pointerId)}#h(s){let a=this.#e();if(!a)return;let d=s.deltaMode===1?16:s.deltaMode===2?a.clientHeight:1;a.scrollTop+=s.deltaY*d,this.sync()}#a(s,a,d){let p=s.clientY-a.getBoundingClientRect().top-(this.#i??0);d.scrollTop=p/a.clientHeight*d.scrollHeight,this.sync()}};var Jvn=4e5,Vxe="diff-doc-body";function Uvn(o,s){let a=o.split(`
|
|
2180
2192
|
`).map(v=>v.replace(/\r$/,""));if(a.length&&a[a.length-1]===""&&a.pop(),s)return a.map(v=>({kind:"add",text:v}));let d=[],p=!1,b=!1;for(let v of a){if(v.startsWith("@@")){b&&d.push({kind:"gap",text:"\u22EF"}),b=!0,p=!0;continue}if(!p){v.startsWith("Binary file")&&d.push({kind:"ctx",text:v});continue}v.startsWith("\\")||(v.startsWith("+")?d.push({kind:"add",text:v.slice(1)}):v.startsWith("-")?d.push({kind:"del",text:v.slice(1)}):d.push({kind:"ctx",text:v.slice(1)}))}return d}function zvn(o,s,a){let d=Uvn(o,s),p=o.length>Jvn?"txt":a.split(".").pop()??"",b=[],v=[];for(let P of d)(P.kind==="ctx"||P.kind==="del")&&b.push(P.text),(P.kind==="ctx"||P.kind==="add")&&v.push(P.text);let w=Wxe(b.join(`
|
|
2181
2193
|
`),p),I=Wxe(v.join(`
|
|
2182
2194
|
`),p),T=0,D=0,B=[];for(let P of d){if(P.kind==="gap"){B.push('<div class="udiff-gap">\u22EF</div>');continue}let F=P.kind==="del"?w[T++]:(P.kind==="add"||T++,I[D++]);B.push(`<div class="udiff-line${P.kind==="add"?" udiff-add":P.kind==="del"?" udiff-del":""}">${F??""}</div>`)}return{kinds:d.map(P=>P.kind),bodyHtml:B.join("")}}var Zre=class extends M7{files=[];repo=!0;root="";viewing;#e=0;#n=!1;#t=()=>document.getElementById(Vxe);#r=new Vre(this.#t);#i=[];#s=-1;keepOpenSelector=".gitdiff-wrap";filterId="gitdiff-filter";listSelector=".gitdiff-list";filterNoun="file";search(){return Promise.resolve([])}onActivate(s){let a=this.rows()[s];a&&this.openDiff(a)}onAttached(){this.refreshFiles(),setInterval(()=>this.refreshFiles(),3e3),setInterval(()=>{this.viewing&&this.refreshDiff()},1500)}rows(){let s=this.filter.trim().toLowerCase();return s?this.files.filter(a=>a.path.toLowerCase().includes(s)):this.files}async refreshFiles(){try{let{repo:s,root:a,files:d}=await tb.server.gitChangedFiles(),p=s!==this.repo||d.length!==this.files.length||d.some((b,v)=>{let w=this.files[v];return b.path!==w?.path||b.status!==w.status||b.add!==w.add||b.del!==w.del||b.binary!==w.binary});this.repo=s,this.root=a,this.files=d,p&&(this.update(),this.keepBottom())}catch(s){console.error("[mirror] gitChangedFiles failed",s)}}#o(s,a,d){this.viewing?.path!==s&&(this.#i=[]),this.viewing={path:s,status:a,text:d.text,truncated:!!d.truncated,...zvn(d.text,!!d.untracked,s)},this.#e++,this.update()}async openDiff(s){try{let a=await tb.server.gitDiff(s.path);if(!a.ok){console.error("[mirror] gitDiff failed:",a.error);return}this.#o(s.path,s.status,a),this.#n=!0,this.close()}catch(a){console.error("[mirror] gitDiff failed",a)}}async refreshDiff(){let s=this.viewing;if(s)try{let a=await tb.server.gitDiff(s.path);if(this.viewing!==s||!a.ok||a.text===s.text)return;this.#o(s.path,s.status,a)}catch(a){console.error("[mirror] gitDiff refresh failed",a)}}closeDoc(){this.#r.release(),this.viewing=void 0,this.update()}show(){this.beginOpen(),this.refreshFiles(),this.refreshList(),this.armClose(),this.focusFilter()}view(){let s=this.files.length;return!this.repo||s===0&&!this.viewing&&!this.open?un({class:"gitdiff-wrap"}):un({class:"gitdiff-wrap pop-center"},this.viewing?this.viewingPill():ha({class:["pill","glyph","gitdiff-pill",this.open?"on":""],"data-tip":`${s} changed file${s===1?"":"s"} \u2014 view a diff`,ariaLabel:`${s} changed file${s===1?"":"s"}`,onClick:a=>{a.stopPropagation(),this.open?this.close():this.show()}},T3("diff"),Nr({class:"gitdiff-count"},String(s))),this.open?this.popup():null)}viewingPill(){let s=this.viewing,a=s.kinds.filter(p=>p==="add").length,d=s.kinds.filter(p=>p==="del").length;return ha({class:["pill","glyph","gitdiff-pill","viewing","on"],"data-tip":`${this.files.length} changed file${this.files.length===1?"":"s"} \u2014 switch file`,onClick:p=>{p.stopPropagation(),this.open?this.close():this.show()}},Nr({class:["gitdiff-status",`s-${s.status}`]},s.status),Nr({class:"gitdiff-doc-path",title:`Open ${s.path}`,onClick:p=>{p.stopPropagation(),tb.server.openFile(`${this.root}/${s.path}`)}},s.path),Nr({class:"gitdiff-counts"},Nr({class:"count-add"},`+${a}`),Nr({class:"count-del"},`\u2212${d}`)),Nr({class:"gitdiff-close","data-tip":"Back to the conversation (Esc)",ariaLabel:"Back to the conversation",onClick:p=>{p.stopPropagation(),this.closeDoc()}},T3("close")))}popup(){let s=this.rows();return un({class:"servers-pop gitdiff-pop"},s.length===0?this.emptyState("No changes in the working tree."):un({class:"gitdiff-list",onScroll:()=>this.onListScroll()},s.map((a,d)=>this.row(a,d))),this.filterRow())}filterRow(){let s=this.files.length;return Wee({target:this,prop:()=>this.filter,id:this.filterId,placeholder:s?`Filter ${s} file${s===1?"":"s"}\u2026`:"Filter files\u2026",hasValue:!!this.filter,onKeyDown:a=>this.onFilterKey(a),onClear:()=>this.clearFilter()})}row(s,a){return un({class:["gitdiff-row",a===this.highlighted?"active":"",this.viewing?.path===s.path?"viewing":""],onMouseEnter:()=>{this.highlighted!==a&&(this.highlighted=a,this.update())},onClick:d=>{d.stopPropagation(),this.openDiff(s)}},Nr({class:["gitdiff-status",`s-${s.status}`]},s.status),Nr({class:"gitdiff-path",title:s.path},s.path),s.add!==void 0||s.del!==void 0?Nr({class:"gitdiff-counts"},Nr({class:"count-add"},`+${s.add??0}`),Nr({class:"count-del"},`\u2212${s.del??0}`)):s.status==="U"?Nr({class:"gitdiff-counts"},Nr({class:"count-add"},"new")):Nr({class:"gitdiff-counts"},Nr({},s.binary?"binary":"\u2014")))}docView(){let s=this.viewing;return un({class:"diff-doc",onKeyDown:a=>{a.key==="Escape"&&(a.stopPropagation(),this.closeDoc())}},un({class:"diff-doc-scroll"},un({class:"diff-doc-body",id:Vxe,tabIndex:0,onMounted:a=>a.focus(),onScroll:()=>this.#r.sync()},s.kinds.length===0?un({class:"diff-doc-empty"},"No changes."):un({class:"diff-doc-code",key:`diff-code-${this.#e}`,onMounted:a=>{a.innerHTML=s.bodyHtml,this.#c(a),this.#u(a)}},""),s.truncated?un({class:"diff-doc-trunc"},"Diff truncated \u2014 file too large to show in full."):null),s.kinds.length?this.#r.view(this.#i,{class:"diff-ruler",title:"Jump to a change",label:"Diff scroll position",controls:Vxe}):null))}#c(s){if(!this.#n)return;this.#n=!1;let a=s.querySelector(".udiff-add, .udiff-del"),d=this.#t();a&&d&&(d.scrollTop=a.offsetTop-d.clientHeight/3)}#u(s){this.#s!==this.#e&&(this.#s=this.#e,requestAnimationFrame(()=>{this.#f(s),this.update(),this.#r.sync()}))}#f(s){let a=this.viewing,d=this.#t();if(!a||!d?.scrollHeight)return;let p=d.scrollHeight,b=[];for(let v=0;v<a.kinds.length;v++){let w=a.kinds[v];if(w!=="add"&&w!=="del")continue;let I=v;for(;I+1<a.kinds.length&&a.kinds[I+1]===w;)I++;let T=s.children[v],D=s.children[I];T&&D&&b.push({top:T.offsetTop/p,height:(D.offsetTop+D.offsetHeight-T.offsetTop)/p,class:w}),v=I}this.#i=b}};var iD=class extends Ku{done=!1;#e;constructor(s){super(),this.#e=s}setText(s){this.#e=s}flash(){navigator.clipboard?.writeText(this.#e),this.done=!0,this.update(),setTimeout(()=>{this.done=!1,this.update()},600)}view(){return ha({class:["overlay-pill","copy",this.done?"done":""],onClick:s=>{s.stopPropagation(),this.flash()}},this.done?"copied":"copy")}};var OSt=new Map,PSt=(o,s)=>s+`
|