typebulb 0.31.1 → 0.31.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.
|
@@ -383,7 +383,8 @@ body {
|
|
|
383
383
|
.composer-queue { padding: .15rem 0 0; }
|
|
384
384
|
.queue-row { font-size: .78rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
385
385
|
.queue-tag { color: var(--accent); font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; margin-right: .5rem; }
|
|
386
|
-
/* The @-file-mention popup, anchored above the composer.
|
|
386
|
+
/* The @-file-mention popup, anchored above the composer. Tall enough for the pickers' full row
|
|
387
|
+
caps (10 files / 12 commands); the vh guard + arrow-key scroll-into-view cover short windows. */
|
|
387
388
|
.at-popup {
|
|
388
389
|
position: absolute;
|
|
389
390
|
bottom: calc(100% + .25rem);
|
|
@@ -391,7 +392,7 @@ body {
|
|
|
391
392
|
right: 0;
|
|
392
393
|
max-width: 600px;
|
|
393
394
|
margin-inline: auto;
|
|
394
|
-
max-height:
|
|
395
|
+
max-height: min(24rem, 50vh);
|
|
395
396
|
overflow-y: auto;
|
|
396
397
|
background: var(--panel);
|
|
397
398
|
border: 1px solid var(--border);
|
package/dist/agents/pi/client.js
CHANGED
|
@@ -1533,12 +1533,12 @@ ${a?"New conversation \u2014 not saved yet":`Session: ${s.sessionId}`}`;return Z
|
|
|
1533
1533
|
|
|
1534
1534
|
`)}stickToBottomNextRender(){this.#r=!0}#a(s,a){s.segments=[...s.segments??[s.text],a],s.text=s.segments.join(`
|
|
1535
1535
|
|
|
1536
|
-
`)}applyUser(s){let a=this.messages[this.messages.length-1];if(a&&a.role==="user"){this.#a(a,s.text),a.copy?a.copy.setText(a.text):a.text&&(a.copy=this.#u(a.text));return}this.#h({id:++this.#e,role:"user",text:s.text,thinking:"",tools:[]})}applyAssistant(s){let a=this.#h({id:++this.#e,role:"assistant",text:s.text,thinking:s.thinking,tools:s.tools.map(p=>({...p,isError:!1}))}),d=this.messages[this.messages.length-2];if(a.turnCopy=d?.role==="assistant"&&d.turnCopy||this.#u(""),a.turnCopy.setText(this.#g()),this.#l(a,s.text),this.#d(),s.live)for(let p of s.tools)Lvn.has(p.name)&&this.openTools.add(p.id)}#l(s,a){let d=zxe(a);d.some(p=>p.kind==="bulb")&&(s.body=d.map(p=>{if(p.kind==="md")return p.text;let b=new Zre(p.source,`embed-${this.#n++}`);return this.bulbEmbeds.push(b),b}))}#d(){let s=this.bulbEmbeds.map(p=>p.name),a=vSt(s),d=ySt(s);this.#t=new Set(this.bulbEmbeds.filter((p,b)=>a[b])),this.bulbEmbeds.forEach((p,b)=>{p.setChainPosition(d[b]),p.setMounted(!a[b]||this.expandedEmbeds.has(p.key))})}#p(s){Wxe(this.expandedEmbeds,s),this.#d(),this.update()}applyToolResult(s){let a=this.#b(s.id);a&&(a.result=s.content,a.isError=s.isError,a.digest=s.digest)}applyFork(s){let a=this.#w(s.events);this.#h({id:++this.#e,role:"fork",text:"",thinking:"",tools:[],fork:{count:s.count,sub:a}})}#w(s){let a=[];for(let d of s)if(d.type==="user"){let p=a[a.length-1];p&&p.role==="user"?this.#a(p,d.text):a.push({id:++this.#e,role:"user",text:d.text,thinking:"",tools:[]})}else if(d.type==="assistant")a.push({id:++this.#e,role:"assistant",text:d.text,thinking:d.thinking,tools:d.tools.map(p=>({...p,isError:!1}))});else if(d.type==="tool_result"){let p=a.flatMap(b=>b.tools).find(b=>b.id===d.id);p&&(p.result=d.content,p.isError=d.isError,p.digest=d.digest)}return a}#b(s){return this.messages.flatMap(a=>a.tools).find(a=>a.id===s)}scrollSoon(){requestAnimationFrame(()=>{this.scrollEl&&this.#r&&(this.scrollEl.scrollTop=this.scrollEl.scrollHeight)})}onScroll(){let s=this.scrollEl;s&&(this.#r=TF(s,50))}view(){return Zt({class:"messages",onScroll:()=>this.onScroll(),onMounted:s=>{this.scrollEl=s}},this.parent.ready?this.renderMessages():Zt({class:"note"},"Connecting\u2026"))}renderMessages(){let s=[],a=-1;for(let w of this.messages)w.role==="user"||s.length===0?(w.role==="user"&&a++,s.push({idx:Math.max(0,a),msgs:[w]})):s[s.length-1].msgs.push(w);let d=s.flatMap((w,I)=>this.renderTurn(w.msgs,w.idx,I===s.length-1));d.unshift(this.#m());let p=this.parent.echo,b=this.parent.draft,v=p?a+1:Math.max(0,a);return p&&d.push(this.#f(p,v)),b?d.push(this.#A(b,v)):(this.#i.clear(),this.#o=!1),d}#m(){return Zt({class:"masthead",key:"masthead"},Wee({class:"masthead-logo light",src:Svn,alt:"typebulb"}),Wee({class:"masthead-logo dark",src:Tvn,alt:"typebulb"}),Zt({class:"masthead-tagline"},"a live mirror of your agent\u2019s session, with embedded bulbs, latex, svg, mermaid and more"))}#f(s,a){return Zt({class:["bubble","user",_F(a)],key:`echo-${s.length}`},this.#
|
|
1537
|
-
`).length;return Zt({class:"md",key:`draft-bulb-${a}`},Zt({class:"embed bulb-streaming"},Zt({class:"bulb-streaming-head"},Nr("\u{1F4A1} "),Nr({class:"shimmer-text shimmer-slow"},`${d?`${d} \u2014 `:""}writing\u2026`),Nr({class:"bulb-streaming-count"},`${p} lines`)),Zt({class:["bulb-code","md"],key:`bulb-code-${s.length}`,...this.#v(a,b=>{b.innerHTML=rD.render(IH(s))})})))}renderTurn(s,a,d){if(this.parent.prose){let T=s.filter(D=>D.role==="user"||D.role==="fork"||D.text||D.body),B=[...T].reverse().find(D=>D.role==="assistant"&&!!D.text);return T.map(D=>{if(D.role==="fork")return this.forkStub(D,a);let P=D.role==="user"?D.copy:D===B?D.turnCopy:void 0;return this.bubble(D,a,P??null)})}let p=s.filter(T=>T.role==="assistant");if(d||p.length<2)return s.map(T=>T.role==="fork"?this.forkStub(T,a):this.bubble(T,a));let b=this.expandedTurns.has(a),v=p.slice(0,-1),w=p[p.length-1],I=s.filter(T=>T.role==="user").map(T=>this.bubble(T,a));for(let T of s)T.role==="fork"&&I.push(this.forkStub(T,a));if(I.push(this.turnSummary(v,a)),b)for(let T of v)I.push(this.bubble(T,a));return I.push(this.bubble(w,a)),I}#
|
|
1536
|
+
`)}applyUser(s){let a=this.messages[this.messages.length-1];if(a&&a.role==="user"){this.#a(a,s.text),a.copy?a.copy.setText(a.text):a.text&&(a.copy=this.#u(a.text));return}this.#h({id:++this.#e,role:"user",text:s.text,thinking:"",tools:[]})}applyAssistant(s){let a=this.#h({id:++this.#e,role:"assistant",text:s.text,thinking:s.thinking,tools:s.tools.map(p=>({...p,isError:!1}))}),d=this.messages[this.messages.length-2];if(a.turnCopy=d?.role==="assistant"&&d.turnCopy||this.#u(""),a.turnCopy.setText(this.#g()),this.#l(a,s.text),this.#d(),s.live)for(let p of s.tools)Lvn.has(p.name)&&this.openTools.add(p.id)}#l(s,a){let d=zxe(a);d.some(p=>p.kind==="bulb")&&(s.body=d.map(p=>{if(p.kind==="md")return p.text;let b=new Zre(p.source,`embed-${this.#n++}`);return this.bulbEmbeds.push(b),b}))}#d(){let s=this.bulbEmbeds.map(p=>p.name),a=vSt(s),d=ySt(s);this.#t=new Set(this.bulbEmbeds.filter((p,b)=>a[b])),this.bulbEmbeds.forEach((p,b)=>{p.setChainPosition(d[b]),p.setMounted(!a[b]||this.expandedEmbeds.has(p.key))})}#p(s){Wxe(this.expandedEmbeds,s),this.#d(),this.update()}applyToolResult(s){let a=this.#b(s.id);a&&(a.result=s.content,a.isError=s.isError,a.digest=s.digest)}applyFork(s){let a=this.#w(s.events);this.#h({id:++this.#e,role:"fork",text:"",thinking:"",tools:[],fork:{count:s.count,sub:a}})}#w(s){let a=[];for(let d of s)if(d.type==="user"){let p=a[a.length-1];p&&p.role==="user"?this.#a(p,d.text):a.push({id:++this.#e,role:"user",text:d.text,thinking:"",tools:[]})}else if(d.type==="assistant")a.push({id:++this.#e,role:"assistant",text:d.text,thinking:d.thinking,tools:d.tools.map(p=>({...p,isError:!1}))});else if(d.type==="tool_result"){let p=a.flatMap(b=>b.tools).find(b=>b.id===d.id);p&&(p.result=d.content,p.isError=d.isError,p.digest=d.digest)}return a}#b(s){return this.messages.flatMap(a=>a.tools).find(a=>a.id===s)}scrollSoon(){requestAnimationFrame(()=>{this.scrollEl&&this.#r&&(this.scrollEl.scrollTop=this.scrollEl.scrollHeight)})}onScroll(){let s=this.scrollEl;s&&(this.#r=TF(s,50))}view(){return Zt({class:"messages",onScroll:()=>this.onScroll(),onMounted:s=>{this.scrollEl=s}},this.parent.ready?this.renderMessages():Zt({class:"note"},"Connecting\u2026"))}renderMessages(){let s=[],a=-1;for(let w of this.messages)w.role==="user"||s.length===0?(w.role==="user"&&a++,s.push({idx:Math.max(0,a),msgs:[w]})):s[s.length-1].msgs.push(w);let d=s.flatMap((w,I)=>this.renderTurn(w.msgs,w.idx,I===s.length-1));d.unshift(this.#m());let p=this.parent.echo,b=this.parent.draft,v=p?a+1:Math.max(0,a);return p&&d.push(this.#f(p,v)),b?d.push(this.#A(b,v)):(this.#i.clear(),this.#o=!1),d}#m(){return Zt({class:"masthead",key:"masthead"},Wee({class:"masthead-logo light",src:Svn,alt:"typebulb"}),Wee({class:"masthead-logo dark",src:Tvn,alt:"typebulb"}),Zt({class:"masthead-tagline"},"a live mirror of your agent\u2019s session, with embedded bulbs, latex, svg, mermaid and more"))}#f(s,a){return Zt({class:["bubble","user",_F(a)],key:`echo-${s.length}`},this.#y(`md-echo-${s.length}`,Uxe({id:-1,role:"user",text:s,thinking:"",tools:[]})))}#A(s,a){let d=`draft-${s.text.length}-${s.thinking.length}`;return s.text.length<this.#s&&this.#i.clear(),this.#s=s.text.length,Zt({class:["bubble","assistant","draft",_F(a)],key:d},!this.parent.prose&&s.thinking?Fye({class:"thinking",open:this.#o,onToggle:p=>{this.#o=p.currentTarget.open}},$ye("thinking"),this.#x(s.thinking)):null,s.text.trim()?this.#k(d,s.text):Zt({class:"draft-wait shimmer-text"},"\u2026"))}#k(s,a){let d=zxe(a);return d.some(p=>p.kind==="bulb")?d.map((p,b)=>p.kind==="md"?p.text.trim()?this.#y(`${s}-md-${b}`,SH(p.text)):null:this.#E(p.source,b)):this.#y(s,SH(a))}#v(s,a){return{onScroll:d=>{let p=d.currentTarget;this.#i.set(s,{top:p.scrollTop,stuck:TF(p,50)})},onMounted:d=>{a?.(d);let p=this.#i.get(s);d.scrollTop=p&&!p.stuck?p.top:d.scrollHeight}}}#x(s){return i6({key:`think-${s.length}`,...this.#v(-1)},s)}#E(s,a){let d=CH(s),p=s.split(`
|
|
1537
|
+
`).length;return Zt({class:"md",key:`draft-bulb-${a}`},Zt({class:"embed bulb-streaming"},Zt({class:"bulb-streaming-head"},Nr("\u{1F4A1} "),Nr({class:"shimmer-text shimmer-slow"},`${d?`${d} \u2014 `:""}writing\u2026`),Nr({class:"bulb-streaming-count"},`${p} lines`)),Zt({class:["bulb-code","md"],key:`bulb-code-${s.length}`,...this.#v(a,b=>{b.innerHTML=rD.render(IH(s))})})))}renderTurn(s,a,d){if(this.parent.prose){let T=s.filter(D=>D.role==="user"||D.role==="fork"||D.text||D.body),B=[...T].reverse().find(D=>D.role==="assistant"&&!!D.text);return T.map(D=>{if(D.role==="fork")return this.forkStub(D,a);let P=D.role==="user"?D.copy:D===B?D.turnCopy:void 0;return this.bubble(D,a,P??null)})}let p=s.filter(T=>T.role==="assistant");if(d||p.length<2)return s.map(T=>T.role==="fork"?this.forkStub(T,a):this.bubble(T,a));let b=this.expandedTurns.has(a),v=p.slice(0,-1),w=p[p.length-1],I=s.filter(T=>T.role==="user").map(T=>this.bubble(T,a));for(let T of s)T.role==="fork"&&I.push(this.forkStub(T,a));if(I.push(this.turnSummary(v,a)),b)for(let T of v)I.push(this.bubble(T,a));return I.push(this.bubble(w,a)),I}#C(s,a,d,p,b,v){let w=d.has(p);return Zt({class:["bubble","assistant",_F(a)],key:s},Zt({class:"turn-summary",onClick:()=>{Wxe(d,p),this.update()}},Kxe(w),Nr({class:"turn-summary-text"},b)),v??null)}forkStub(s,a){let d=s.fork,p=this.expandedForks.has(s.id)?Zt({class:"fork-body"},d.sub.map(v=>this.bubble(v,a,void 0,!1))):void 0,b=[Dvn(),` ${d.count} message${d.count===1?"":"s"} on an abandoned branch`];return this.#C(`fork-${s.id}`,a,this.expandedForks,s.id,b,p)}turnSummary(s,a){return this.#C(`summary-${a}`,a,this.expandedTurns,a,Pvn(s))}bubble(s,a,d=s.copy,p=!0){let b=this.parent.prose,v=s.role==="assistant"&&!s.text&&!s.thinking&&s.tools.length>0;return Zt({class:["bubble",s.role,v?"tools-only":"",p?_F(a):""],key:s.id},!b&&s.thinking?Fye({class:"thinking"},$ye("thinking"),i6(s.thinking)):null,this.#T(s),s.role==="user"?this.#I(s):null,b?null:s.tools.map(w=>this.tool(w)),d?d.view():null)}#I(s){let d=[...s.text.matchAll(Ivn)].map(p=>p[1]).map(p=>this.#S(p)).filter(p=>!!p);return d.length?Zt({class:"paste-thumbs"},d.map(p=>Wee({class:"paste-thumb",src:p,alt:"pasted image"}))):null}#S(s){let a=this.#c.get(s);if(!(a==="pending"||a==="failed")){if(a)return a;this.#c.set(s,"pending"),(async()=>{try{let d=await tb.server.composerPasteRead(s);this.#c.set(s,d?.ok?`data:${d.mime};base64,${d.base64}`:"failed"),d?.ok&&(this.update(),this.scrollSoon())}catch{this.#c.set(s,"failed")}})()}}#T(s){return s.body?s.body.flatMap((a,d)=>typeof a=="string"?a.trim()?[this.#y(`md-${s.id}-${d}`,SH(a))]:[]:this.#_(a)):s.text?this.#y(`md-${s.id}`,s.role==="user"?Uxe(s):SH(s.text)):null}#_(s){if(!this.#t.has(s))return[s.view()];let a=this.expandedEmbeds.has(s.key),d=Zt({class:"md",key:`fold-${s.key}`},Zt({class:"bulb-fold",onClick:()=>this.#p(s.key)},Kxe(a),Nr({class:"bulb-fold-text"},`${s.name??"bulb"} \u2014 Version ${s.chainPosition}`),Nr({class:"bulb-fold-app"},"\u{1F4A1}")));return a?[d,s.view()]:[d]}#y(s,a){return Zt({class:"md",key:s,onMounted:a})}tool(s){let a=this.openTools.has(s.id),d=Nvn(s.name,s.input),p=ISt(s),b=d?d6t(d,this.parent.cwd):Mvn(_vn(s.input));return Zt({class:["tool",s.isError?"err":""]},Zt({class:"tool-head"},Zt({class:"tool-label"},Nr({class:"tool-name",...s.name.startsWith("mcp__")?{title:s.name}:{}},Yxe(s.name)),b?d?gC({class:"tool-sum link",title:d,onClick:v=>{v.preventDefault(),tb.server.openFile(d)}},b):Nr({class:"tool-sum"},b):null),s.result===void 0?Nr({class:"tool-run"},"\u2026"):null),s.result!==void 0?Zt({class:["tool-digest",a?"open":""],onClick:()=>{Wxe(this.openTools,s.id),this.update()}},Kxe(a),(p?Ovn(p):s.digest)||"(no output)"):null,a?Zt({class:"tool-card"},this.toolBody(s),s.result!==void 0&&(s.isError||!(p||CSt(s).length&&s.result.trim()===s.digest))?i6({class:"tool-out"},s.result.slice(0,4e3)):null):null)}toolBody(s){let a=ISt(s);if(a)return Zt({class:"todo-list"},a.map(b=>Zt({class:["todo",b.status??""]},Nr({class:"todo-icon"},b.status==="completed"?"\u2714":b.status==="in_progress"?"\u25FC":"\u25FB"),Nr({class:"todo-text"},b.content??""))));let d=CSt(s);if(d.length)return Zt({class:"tool-in"},d.map(ESt));let p=Rvn(s);if(!p){let b=Object.entries(s.input??{});return b.length?Zt({class:"tool-in"},b.map(([v,w])=>Zt({class:"tool-field"},Nr({class:"tool-key"},`${v}:`),typeof w=="string"&&SSt(w)?ESt(w):i6({class:"tool-val"},typeof w=="string"?w:JSON.stringify(w,null,2))))):null}return Zt({class:"diff"},...p.flatMap((b,v)=>[p.length>1?Zt({class:"diff-step"},`edit ${v+1}/${p.length}`):null,b.old?i6({class:"diff-old"},String(b.old)):null,b.new?i6({class:"diff-new"},String(b.new)):null]))}};var tie=class extends wu{ready=!1;cwd="";sessionId=null;sessionPicker=new zre;tokenPill=new Kre;bulbsPill=new Wre;prosePill=new Yre;diffPill=new Xre;messageList=new eie;tokens={in:0,out:0,cached:0,cacheCreate:0};cost=0;stats=null;working=!1;latestModel=null;driverModel=null;busy=[];prose=!1;ownPid=0;pills;composer;draft=null;echo=null;#e=0;#n=!1;#t=!1;#r;#i=null;#s;#o;constructor(s){super(),this.pills=s.pills,this.composer=s.composer,this.#r=s.title,this.#s=s.overlays??[],this.#o=s.onPollTick}onAttached(){this.#t||(this.#t=!0,this.init())}closePopups(s){this.sessionPicker!==s&&this.sessionPicker.close(),this.bulbsPill!==s&&this.bulbsPill.close(),this.diffPill!==s&&this.diffPill.close();for(let a of this.pills)a!==s&&a.close?.()}async init(){let s=await tb.server.info();this.cwd=s.cwd,this.ownPid=s.pid??0,this.#i=s.elsewhere??null,this.composer&&(this.composer.enabled=!!s.composer),this.ready=!0,this.updateTitle(),this.update(),this.pump()}updateTitle(){let s=qye(this.cwd),a=this.sessionPicker.currentPreview(),d=fM(a,40);document.title=d&&s?`${d} \u2014 ${s}`:d||(s?`${this.#r} \u2014 ${s}`:this.#r)}refreshSessions(){this.sessionPicker.loadSessions().then(()=>this.update()).catch(()=>{})}pump(){if(this.#n)return;this.#n=!0;let s=async()=>{try{let{events:a,cursor:d,working:p,latestModel:b,composer:v,busy:w}=await tb.server.poll(this.#e);this.#e=d;for(let j of a)this.apply(j);let I=p!==this.working;this.working=p;let T=Array.isArray(w)?w:[],B=T.join(`
|
|
1538
1538
|
`)!==this.busy.join(`
|
|
1539
1539
|
`);this.busy=T;let D=b!==this.latestModel;this.latestModel=b??null;let P=this.composer&&v?this.composer.syncFromPoll(v):!1;(a.length||I||D||P||B)&&this.update(),(a.length||P)&&this.messageList.scrollSoon(),this.#o?.()}catch(a){console.error("[mirror] poll failed",a)}setTimeout(s,600)};s()}apply(s){switch(s.type){case"cleared":this.messageList.clear(),this.tokens={in:0,out:0,cached:0,cacheCreate:0},this.cost=0;break;case"session":this.sessionId=s.sessionId,this.updateTitle();break;case"user":this.messageList.applyUser(s);break;case"assistant":this.messageList.applyAssistant(s);break;case"tool_result":this.messageList.applyToolResult(s);break;case"fork":this.messageList.applyFork(s);break;case"usage":this.tokens={in:s.in,out:s.out,cached:s.cached,cacheCreate:s.cacheCreate},this.cost+=s.cost??0;break}}view(){return Zt({class:"app"},Zt({class:"chat"},this.diffPill.viewing?this.diffPill.docView():this.messageList.view(),this.#c(),...this.#s.map(s=>s()),this.statusbar()),this.composer?.enabled?this.composer.view():null)}#c(){let s=this.#i;return!this.ready||this.sessionId!==null||!s?null:Zt({class:"cwd-hint"},Zt({class:"cwd-hint-head"},`No sessions for ${this.cwd}`),Zt(`${s.count} session${s.count===1?"":"s"} exist for `,Nr({class:"cwd-hint-dir"},s.dir)," \u2014 this mirror was likely launched from a subfolder. Relaunch it from the project root."))}statusbar(){return Zt({class:"statusbar"},Zt({class:["statusbar-actions",this.diffPill.viewing?"doc-open":""]},this.diffPill.view(),this.prosePill.view(),...this.pills.map(s=>s.view()),this.tokenPill.view(),this.sessionPicker.view(),this.bulbsPill.view()))}};function Xxe(o,s,a){let d=o.map(p=>({item:p,rank:s(p)})).filter(p=>p.rank>=0);return d.sort((p,b)=>p.rank-b.rank),(a?d.slice(0,a):d).map(p=>p.item)}var nie=class extends wu{enabled=!1;streaming=!1;driverError;sendError;input="";status=null;localNotice=null;dlg=null;dlgInput="";dlgFilter="";dlgIndex=0;queue=null;recipes;harnessCommands=[];slashQuery=null;slashIndex=0;composerHeight=90;files=[];atQuery=null;atIndex=0;atStart=0;#e=!1;#n=0;#t="";#r=null;constructor(s=[]){super(),this.recipes=s}get parent(){return this.ctx.parent}onAttached(){this.#i()}async#i(){try{let s=await tb.server.composerFiles();Array.isArray(s)&&(this.files=s,this.update())}catch{}}syncFromPoll(s){let a=s.streaming!==this.streaming||s.error!==this.driverError;this.streaming=s.streaming,this.driverError=s.error;let d=this.parent,p=s.draft??null;(p?.text!==d.draft?.text||p?.thinking!==d.draft?.thinking)&&(d.draft=p,a=!0);let b=s.echo??null;b!==d.echo&&(d.echo=b,a=!0);let v=s.stats??null;(v?.cost!==d.stats?.cost||v?.contextTokens!==d.stats?.contextTokens||v?.contextPercent!==d.stats?.contextPercent)&&(d.stats=v,a=!0);let w=s.model??null;w!==d.driverModel&&(d.driverModel=w,a=!0),this.localNotice&&Date.now()>=this.localNotice.until&&(this.localNotice=null,a=!0),((s.status?.text??null)!==(this.status?.text??null)||s.status?.kind!==this.status?.kind)&&(this.status=s.status??null,a=!0);let I=s.queue??null;this.#s(I)!==this.#s(this.queue)&&(this.queue=I,a=!0);let T=s.dialog??null;return this.dlg&&!this.dlg.local&&(!T||T.id!==this.dlg.req.id)&&(this.dlg=null,a=!0),T&&!this.dlg&&T.id!==this.#r&&(this.#g(T),a=!0),a}#s(s){return s?JSON.stringify(s):""}#o(){return this.parent.working&&!this.streaming}async send(s=!1){let a=this.input.trim();if(!(!a||this.#e||this.#o())){if(a.startsWith("!"))return this.runBash(a.slice(1).trim());this.atQuery=null,await this.#c("send failed \u2014 mirror unreachable",async()=>{let d=await tb.server.composerSend(a,{followUp:s});d?.ok?(this.input="",this.streaming=!0):this.sendError=d?.error??"send failed"})}}async#c(s,a){this.#e=!0,this.sendError=void 0,this.update();try{await a()}catch{this.sendError=s}finally{this.#e=!1,this.update(),this.#f()}}async stopTurn(){let s=this.queue,a=s?[...s.steering,...s.followUp]:[];try{await tb.server.composerStop()}catch{}a.length&&(this.input=a.join(`
|
|
1540
1540
|
`)+(this.input?`
|
|
1541
|
-
`+this.input:""),this.queue=null,this.update(),this.#f())}async runBash(s){s&&await this.#c("bash failed \u2014 mirror unreachable",async()=>{let a=await tb.server.composerRpc({type:"bash",command:s},{spawn:!0});a?.ok?(this.input="",this.#p("ran \u2014 output joins context on the next message")):this.sendError=a?.error??"bash failed"})}onPaste(s){let a=[...s.clipboardData?.items??[]].find(p=>p.type.startsWith("image/"));if(!a)return;s.preventDefault();let d=a.getAsFile();d&&this.#u(d,d.type,s.target.selectionStart)}async captureClipboard(){let s=this.#m(),a=s?s.selectionStart:this.input.length;try{for(let d of await navigator.clipboard.read()){let p=d.types.find(b=>b.startsWith("image/"));if(p){this.#u(await d.getType(p),p,a);return}}}catch{}try{let d=(await navigator.clipboard.readText()).trim();if(!d){this.#p("clipboard is empty"),this.update();return}this.#h({kind:"text",data:d},a)}catch{this.sendError="clipboard unavailable",this.update()}}#u(s,a,d){let p=new FileReader;p.onload=()=>{let b=String(p.result??"");this.#h({kind:"image",data:b.slice(b.indexOf(",")+1),mime:a},d)},p.readAsDataURL(s)}async#h(s,a){let d=p=>{this.sendError=p,this.localNotice=null,this.update()};this.#p(s.kind==="image"?"saving image\u2026":"saving text\u2026"),this.update();try{let p=await tb.server.composerPaste(s);if(!p?.ok)return d(p?.error??"paste failed");let b=`@${p.path} `,v=Math.min(a,this.input.length);this.input=this.input.slice(0,v)+b+this.input.slice(v),this.localNotice=null,this.update(),this.#f(v+b.length)}catch{d("paste failed \u2014 mirror unreachable")}}#g(s){this.dlgInput=s.prefill??"",this.#l(s),this.dlg={req:s,local:!1,resolve:a=>{this.#r=s.id,tb.server.composerUiRespond(s.id,a)}}}#a(s){return new Promise(a=>{this.dlgInput=s.prefill??"",this.#l(s),this.dlg={req:{id:`local-${++this.#n}`,...s},local:!0,resolve:a},this.update()})}#l(s){this.dlgFilter="",this.#t="",this.dlgIndex=s.selected?Math.max(0,(s.options??[]).indexOf(s.selected)):0}#d=s=>s.focus();answerDialog(s){let a=this.dlg;a&&(this.dlg=null,a.resolve(s),this.update(),this.#f())}#p(s){this.localNotice={text:s,until:Date.now()+6e3}}#w(){return{rpc:s=>tb.server.composerRpc(s,{spawn:!0}),select:async(s,a,d)=>{let p=await this.#a({method:"select",title:s,options:a,selected:d});return p.cancelled?null:p.value??null},input:async(s,a,d)=>{let p=await this.#a({method:"input",title:s,placeholder:a,prefill:d});return p.cancelled?null:p.value??""},confirm:async(s,a)=>!!(await this.#a({method:"confirm",title:s,message:a})).confirmed,setInput:s=>{this.input=s,this.update(),this.#f()},refreshSessions:()=>this.parent.refreshSessions()}}async runRecipe(s){this.slashQuery=null,this.sendError=void 0,this.update();try{let a=await s.run(this.#w());typeof a=="string"&&a&&this.#p(a)}catch(a){this.sendError=a instanceof Error?a.message:String(a)}this.dlg=null,this.update()}async#b(){try{let s=await tb.server.composerRpc({type:"get_commands"},{spawn:!1}),a=s?.ok?s.data?.commands:null;Array.isArray(a)&&(this.harnessCommands=a.map(d=>({name:String(d.name),description:d.description??""})),this.update())}catch{}}filteredCommands(){if(this.slashQuery===null)return[];let s=this.slashQuery.toLowerCase(),a=[...this.recipes.filter(d=>!d.hidden).map(d=>({name:d.name,description:d.description,recipe:d})),...this.harnessCommands.filter(d=>!this.recipes.some(p=>p.name===d.name)).map(d=>({name:d.name,description:d.description,recipe:void 0}))];return s?Xxe(a,d=>{let p=d.name.toLowerCase().indexOf(s);return p===0?0:p>0?100+p:-1},12):a.slice(0,12)}applySlashSelection(){let s=this.filteredCommands();if(this.slashQuery===null||s.length===0)return!1;let a=s[this.slashIndex];if(!a)return!1;this.slashQuery=null;let d=this.input.replace(/^\/\S*\s?/,"");if(a.recipe)return this.input=d,this.runRecipe(a.recipe),!0;let p="/"+a.name+" ";return this.input=p+d,this.update(),this.#f(p.length),!0}async newConversation(){this.#e||await this.#c("could not start a new conversation \u2014 mirror unreachable",async()=>{let s=await tb.server.composerNew();if(!s?.ok){this.sendError=s?.error??"could not start a new conversation";return}this.#i()})}filteredFiles(){if(this.atQuery===null)return[];let s=this.atQuery.toLowerCase();return s?Xxe(this.files,a=>{let d=a.toLowerCase().indexOf(s);if(d===-1)return-1;let p=a.slice(a.lastIndexOf("/")+1).toLowerCase().indexOf(s);return p===-1?1e3+d:p},10):this.files.slice(0,10)}onComposerInput(s){let a=s.selectionStart,d=s.value,p=null,b=0;for(let D=a-1;D>=0;D--){let P=d[D];if(P==="@"){let j=D>0?d[D-1]:"";(!j||/[\s([{]/.test(j))&&(p=d.slice(D+1,a),b=D);break}if(/\s/.test(P))break}let v=null;if(d.startsWith("/")&&a>=1){let D=d.slice(1,a);/\s/.test(D)||(v=D)}v!==null&&(p=null);let w=!(p===this.atQuery&&(p===null||b===this.atStart)),I=v!==this.slashQuery;if(!w&&!I)return;let T=p!==null&&this.atQuery===null,B=v!==null&&this.slashQuery===null;this.atQuery=p,p!==null&&(this.atStart=b,this.atIndex=0),this.slashQuery=v,v!==null&&I&&(this.slashIndex=0),T&&this.#i(),B&&this.#b(),this.update()}applyAtSelection(){let s=this.#m(),a=this.filteredFiles();if(!s||this.atQuery===null||a.length===0)return!1;let d=a[this.atIndex];if(!d)return!1;let p=this.input.slice(0,this.atStart),b=this.input.slice(s.selectionStart),v="@"+d+" ";return this.input=p+v+b,this.atQuery=null,this.update(),this.#f(p.length+v.length),!0}#m(){return document.querySelector(".composer-input")}#f(s){setTimeout(()=>{let a=this.#m();a&&(a.focus(),a.selectionStart=a.selectionEnd=s??a.value.length)},0)}beginSplitterDrag(s){s.preventDefault();let a=s.clientY,d=this.composerHeight,p=document.body.style.cursor,b=document.body.style.userSelect;document.body.style.cursor="row-resize",document.body.style.userSelect="none";let v=I=>{let T=d-(I.clientY-a),B=Math.max(120,window.innerHeight*.6);this.composerHeight=Math.max(40,Math.min(B,T)),this.update()},w=()=>{document.removeEventListener("mousemove",v),document.removeEventListener("mouseup",w),document.body.style.cursor=p,document.body.style.userSelect=b};document.addEventListener("mousemove",v),document.addEventListener("mouseup",w)}#A(s){if(this.sendError&&s.key!=="Enter"&&(this.sendError=void 0,this.update()),s.key===";"&&(s.ctrlKey||s.metaKey)){s.preventDefault(),this.captureClipboard();return}this.slashQuery!==null&&this.#x(s,this.filteredCommands().length,this.slashIndex,a=>this.slashIndex=a,()=>this.slashQuery=null,()=>this.applySlashSelection())||this.atQuery!==null&&this.#x(s,this.filteredFiles().length,this.atIndex,a=>this.atIndex=a,()=>this.atQuery=null,()=>this.applyAtSelection())||s.key==="Enter"&&!s.shiftKey&&(s.preventDefault(),this.send(s.altKey))}#x(s,a,d,p,b,v){return s.key==="ArrowDown"||s.key==="ArrowUp"?(s.preventDefault(),p(Math.max(0,Math.min(d+(s.key==="ArrowDown"?1:-1),a-1))),this.update(),!0):s.key==="Escape"?(s.preventDefault(),b(),this.update(),!0):(s.key==="Enter"||s.key==="Tab")&&a>0?(s.preventDefault(),v(),!0):!1}view(){let s=this.#o(),a=s||this.#e?{disabled:!0}:{},d=this.sendError??this.driverError,p=this.localNotice&&Date.now()<this.localNotice.until?{text:this.localNotice.text,kind:"info"}:this.status,b=s?"the agent is working in a terminal \u2014 watch only":this.streaming?"Steer \u2014 Enter queues for after this step, Alt+Enter for after the turn":"Message the agent\u2026 (Enter to send, Shift+Enter for newline, @ files, / commands, ! bash)";return Zt({class:"composer"},this.dlg?this.#C()?this.dialogPopup():this.dialogView():null,this.atQuery!==null?this.atPopup():null,this.slashQuery!==null?this.slashPopup():null,Zt({class:"splitter",key:"splitter",onMouseDown:v=>this.beginSplitterDrag(v)},""),d?Zt({class:"composer-err"},d):null,!d&&p?Zt({class:["composer-status",p.kind],key:"composer-status"},p.text):null,this.queueStrip(),Zt({class:"input-wrap",key:"composer-row"},Hye({target:this,prop:()=>this.input,attrs:{class:"composer-input",placeholder:b,ariaLabel:"Message the agent",style:{height:`${this.composerHeight}px`},onKeyDown:v=>this.#A(v),onKeyUp:v=>this.onComposerInput(v.target),onClick:v=>this.onComposerInput(v.target),onPaste:v=>this.onPaste(v),...a}}),Zt({class:"composer-actions"},wc({class:"composer-clip",type:"button",title:"Capture clipboard to a file (Ctrl+;)",ariaLabel:"Capture clipboard",...a,onClick:()=>{this.captureClipboard()}},W0("attach")),wc({class:"composer-new",type:"button",title:"New conversation",ariaLabel:"New conversation",...this.#e?{disabled:!0}:{},onClick:()=>{this.newConversation()}},W0("addCircle")),this.streaming?wc({class:"composer-act stop",type:"button",title:"Stop the current turn",ariaLabel:"Stop",onClick:()=>{this.stopTurn()}},W0("stopCircle")):wc({class:"composer-act send",type:"button",title:"Send (Enter)",ariaLabel:"Send",...a,onClick:()=>{this.send()}},W0("playCircle")))))}queueStrip(){let s=this.queue,a=s?[...s.steering.map(d=>({tag:"steer",t:d})),...s.followUp.map(d=>({tag:"follow-up",t:d}))]:[];return a.length?Zt({class:"composer-queue",key:"composer-queue"},a.map(d=>Zt({class:"queue-row"},Nr({class:"queue-tag"},d.tag),d.t))):null}atPopup(){return this.#v("at-popup",this.filteredFiles(),"No matches",this.atIndex,s=>{this.atIndex=s,this.applyAtSelection()},s=>s)}slashPopup(){return this.#v("slash-popup",this.filteredCommands(),"No commands",this.slashIndex,s=>{this.slashIndex=s,this.applySlashSelection()},s=>[Nr({class:"slash-name"},"/"+s.name),s.description?Nr({class:"slash-desc"},s.description):null])}#v(s,a,d,p,b,v){return Zt({class:"at-popup",key:s},this.#E(a,d,p,b,v))}#E(s,a,d,p,b){return s.length===0?Zt({class:"at-empty"},a):s.map((v,w)=>Zt({class:["at-row",w===d?"active":""],onMouseDown:I=>{I.preventDefault(),p(w)}},b(v)))}#C(){return!!this.dlg&&this.dlg.local&&(this.dlg.req.method==="select"||this.dlg.req.method==="input")}dialogPopup(){let{req:s}=this.dlg;return Zt({key:"composer-dlg"},Zt({class:"dlg-catcher",onMouseDown:a=>{a.preventDefault(),this.answerDialog({cancelled:!0})}},""),Zt({class:"at-popup dlg-popup",key:"dlg-popup"},s.method==="select"?this.selectBody():[s.title?Zt({class:"dlg-label"},s.title):null,lM({target:this,prop:()=>this.dlgInput,attrs:{class:"dlg-input",placeholder:s.placeholder??"",onMounted:this.#d,onKeyDown:a=>this.#y(a)}})]))}dialogView(){let{req:s}=this.dlg,a=s.method==="confirm"?"Confirm":s.method==="select"?"Choose":"Input";return Zt({class:"trust-back",key:"composer-dlg"},Zt({class:"trust-modal composer-dlg"},Zt({class:"trust-modal-h"},s.title??a),s.message?Zt({class:"trust-modal-b"},s.message):null,s.method==="select"?this.selectBody():null,s.method==="input"?lM({target:this,prop:()=>this.dlgInput,attrs:{class:"dlg-input",placeholder:s.placeholder??"",onMounted:this.#d,onKeyDown:d=>this.#y(d)}}):null,s.method==="editor"?Hye({target:this,prop:()=>this.dlgInput,attrs:{class:"dlg-editor",onMounted:this.#d,onKeyDown:d=>{d.key==="Escape"&&(d.preventDefault(),this.answerDialog({cancelled:!0}))}}}):null,Zt({class:"trust-modal-acts"},wc({class:"trust-no",type:"button",onClick:()=>this.answerDialog(s.method==="confirm"?{confirmed:!1}:{cancelled:!0})},s.method==="confirm"?"No":"Cancel"),s.method==="confirm"?wc({class:"trust-yes",type:"button",onClick:()=>this.answerDialog({confirmed:!0})},"Yes"):s.method!=="select"?wc({class:"trust-yes",type:"button",onClick:()=>this.answerDialog({value:this.dlgInput})},"OK"):null)))}#y(s){s.key==="Enter"&&(s.preventDefault(),this.answerDialog({value:this.dlgInput})),s.key==="Escape"&&(s.preventDefault(),this.answerDialog({cancelled:!0}))}filteredDlgOptions(){let s=this.dlg?.req.options??[],a=this.dlgFilter.trim().toLowerCase();return a?Xxe(s,d=>d.toLowerCase().indexOf(a)):s}selectBody(){let s=this.filteredDlgOptions(),a=Math.min(this.dlgIndex,Math.max(0,s.length-1));return Zt({key:"dlg-select"},Zt({class:"dlg-opts",onMounted:d=>d.querySelector(".at-row.active")?.scrollIntoView({block:"nearest"})},this.#E(s,"No matches",a,d=>this.answerDialog({value:s[d]}),d=>d)),lM({target:this,prop:()=>this.dlgFilter,attrs:{class:"dlg-input dlg-filter",placeholder:"Type to filter\u2026",onMounted:this.#d,onKeyDown:d=>this.#I(d),onKeyUp:()=>{this.dlgFilter!==this.#t&&(this.#t=this.dlgFilter,this.dlgIndex=0,this.update())}}}))}#I(s){let a=this.filteredDlgOptions(),d=Math.min(this.dlgIndex,Math.max(0,a.length-1));if(s.key==="ArrowDown"||s.key==="ArrowUp"){s.preventDefault(),this.dlgIndex=Math.max(0,Math.min(d+(s.key==="ArrowDown"?1:-1),a.length-1)),this.update(),setTimeout(()=>document.querySelector(".dlg-opts .at-row.active")?.scrollIntoView({block:"nearest"}),0);return}if(s.key==="Enter"){s.preventDefault();let p=a[d];p!==void 0&&this.answerDialog({value:p});return}s.key==="Escape"&&(s.preventDefault(),this.answerDialog({cancelled:!0}))}};var rie=class extends wu{get parent(){return this.ctx.parent}view(){let s=this.parent.driverModel??this.parent.latestModel;if(!s)return Nr({class:"model-wrap"});let a=s.includes("/")?s.slice(s.lastIndexOf("/")+1):s;return Nr({class:"model-wrap"},Nr({class:"token",title:`Model: ${s}`},a))}};var Vxe=o=>`~${ete(o??0)}`;async function U4(o,s,a){let d=await o.rpc(s);if(!d.ok)throw new Error(d.error??a);return d.data}async function TSt(o){return(await o.rpc({type:"get_state"})).data}var oD=(o,s)=>`${o} ${s}${o===1?"":"s"}`;async function MSt(){let o=await tb.server.composerNew();if(!o?.ok)throw new Error(o?.error??"could not start a new conversation");return"new conversation \u2014 /model now lists your local models"}async function _St(o){let s=o.replace(/\\/g,"/").split("/").pop().replace(/\.jsonl$/,"");return!!(await tb.server.attach(s))?.ok}async function jvn(o,s,a){if(!await o.confirm(s?"Add local Ollama models?":`Sync ${oD(a,"new Ollama model")}?`,"Rewrites the ollama provider in pi's models.json to exactly what's installed. Prefer a hand-curated list? Cancel \u2014 the file is yours."))return;let p=await tb.server.ollamaSync();if(!p?.ok)throw new Error(p?.error??"Ollama sync failed");return await o.confirm(`${oD(p.count??0,"Ollama model")} configured`,"Pi loads models.json when it boots \u2014 start a new conversation now to use them?")?MSt():`${oD(p.count??0,"Ollama model")} configured \u2014 new conversations (+) and terminal pi will list them`}var DSt=[{name:"model",description:"Switch the model",async run(o){let[s,a]=await Promise.all([U4(o,{type:"get_available_models"},"could not list models"),tb.server.ollamaOffer().catch(()=>null)]),d=s?.models??[],p=d.map(K=>`${K.provider}/${K.id}`),{installed:b,configured:v}=a??{installed:[],configured:null},w=b.filter(K=>!v?.includes(K)),I=w.length?v===null?`+ add ${oD(w.length,"local Ollama model")}\u2026`:`\u27F3 sync ${oD(w.length,"new local Ollama model")}\u2026`:null,T=new Set(d.filter(K=>K.provider==="ollama").map(K=>K.id)),B=(v??[]).filter(K=>!T.has(K)).length,D=B?`\u27F3 ${oD(B,"Ollama model")} pending \u2014 new conversation loads them`:null;if(I&&p.push(I),D&&p.push(D),!p.length)return"no models configured";let P=(await TSt(o))?.model,j=await o.select("Switch model",p,P?`${P.provider}/${P.id}`:void 0);if(j===null)return;if(I!==null&&j===I)return jvn(o,v===null,w.length);if(D!==null&&j===D)return MSt();let U=d[p.indexOf(j)];return await U4(o,{type:"set_model",provider:U.provider,modelId:U.id},"set_model failed"),`model: ${j}`}},{name:"thinking",description:"Set the thinking level",async run(o){let s=await o.select("Thinking level",["off","minimal","low","medium","high","xhigh"],(await TSt(o))?.thinkingLevel);if(s!==null)return await U4(o,{type:"set_thinking_level",level:s},"set_thinking_level failed"),`thinking: ${s}`}},{name:"compact",description:"Compact the conversation context",async run(o){let s=await o.input("Compact context","optional instructions \u2014 Enter to compact");if(s===null)return;let a=await U4(o,{type:"compact",...s.trim()?{customInstructions:s.trim()}:{}},"compact failed");return a?.tokensBefore?`compacted: ${Vxe(a.tokensBefore)} \u2192 ${Vxe(a.estimatedTokensAfter)} tokens`:"compacted"}},{name:"fork",description:"Fork from an earlier message",async run(o){let a=(await U4(o,{type:"get_fork_messages"},"could not list fork points"))?.messages??[];if(!a.length)return"nothing to fork from";let d=a.map((w,I)=>`${I+1}. ${fM(w.text,80)}`),p=await o.select("Fork from\u2026",d);if(p===null)return;let b=a[d.indexOf(p)],v=await U4(o,{type:"fork",entryId:b.entryId},"fork failed");if(v?.sessionFile&&!await _St(v.sessionFile)){let w=await tb.server.composerNew();if(!w?.ok)throw new Error(w?.error??"could not open a new conversation")}return v?.text&&o.setInput(v.text),"forked \u2014 edit the restored prompt and send"}},{name:"clone",description:"Duplicate this branch into a new session",async run(o){if(!await o.confirm("Clone session?","Duplicates the current branch into a new session file."))return;let s=await U4(o,{type:"clone"},"clone failed");if(s?.sessionFile&&!await _St(s.sessionFile))throw new Error("clone finished but its session file never appeared");return"cloned \u2014 this is the duplicate"}},{name:"name",description:"Name this session",async run(o){let s=await o.input("Session name");if(!(s===null||!s.trim()))return await U4(o,{type:"set_session_name",name:s.trim()},"set_session_name failed"),o.refreshSessions(),`named: ${s.trim()}`}},{name:"export",description:"Export the session to HTML",async run(o){return`exported: ${(await U4(o,{type:"export_html"},"export failed"))?.path??"done"}`}},{name:"stats",description:"Token usage, cost, and context headroom",hidden:!0,async run(o){let s=await U4(o,{type:"get_session_stats"},"get_session_stats failed"),a=[s?.tokens?.total!==void 0?`${Vxe(s.tokens.total)} tokens`:null,s?.cost!==void 0?`$${s.cost.toFixed(2)}`:null,s?.contextUsage?.percent!=null?`context ${s.contextUsage.percent}%`:null].filter(Boolean);return a.length?a.join(" \xB7 "):"no stats yet"}}];document.title="Pi Mirror";new c6t({root:new tie({title:"Pi Mirror",pills:[new rie],composer:new nie(DSt)}),id:"app"});
|
|
1541
|
+
`+this.input:""),this.queue=null,this.update(),this.#f())}async runBash(s){s&&await this.#c("bash failed \u2014 mirror unreachable",async()=>{let a=await tb.server.composerRpc({type:"bash",command:s},{spawn:!0});a?.ok?(this.input="",this.#p("ran \u2014 output joins context on the next message")):this.sendError=a?.error??"bash failed"})}onPaste(s){let a=[...s.clipboardData?.items??[]].find(p=>p.type.startsWith("image/"));if(!a)return;s.preventDefault();let d=a.getAsFile();d&&this.#u(d,d.type,s.target.selectionStart)}async captureClipboard(){let s=this.#m(),a=s?s.selectionStart:this.input.length;try{for(let d of await navigator.clipboard.read()){let p=d.types.find(b=>b.startsWith("image/"));if(p){this.#u(await d.getType(p),p,a);return}}}catch{}try{let d=(await navigator.clipboard.readText()).trim();if(!d){this.#p("clipboard is empty"),this.update();return}this.#h({kind:"text",data:d},a)}catch{this.sendError="clipboard unavailable",this.update()}}#u(s,a,d){let p=new FileReader;p.onload=()=>{let b=String(p.result??"");this.#h({kind:"image",data:b.slice(b.indexOf(",")+1),mime:a},d)},p.readAsDataURL(s)}async#h(s,a){let d=p=>{this.sendError=p,this.localNotice=null,this.update()};this.#p(s.kind==="image"?"saving image\u2026":"saving text\u2026"),this.update();try{let p=await tb.server.composerPaste(s);if(!p?.ok)return d(p?.error??"paste failed");let b=`@${p.path} `,v=Math.min(a,this.input.length);this.input=this.input.slice(0,v)+b+this.input.slice(v),this.localNotice=null,this.update(),this.#f(v+b.length)}catch{d("paste failed \u2014 mirror unreachable")}}#g(s){this.dlgInput=s.prefill??"",this.#l(s),this.dlg={req:s,local:!1,resolve:a=>{this.#r=s.id,tb.server.composerUiRespond(s.id,a)}}}#a(s){return new Promise(a=>{this.dlgInput=s.prefill??"",this.#l(s),this.dlg={req:{id:`local-${++this.#n}`,...s},local:!0,resolve:a},this.update()})}#l(s){this.dlgFilter="",this.#t="",this.dlgIndex=s.selected?Math.max(0,(s.options??[]).indexOf(s.selected)):0}#d=s=>s.focus();answerDialog(s){let a=this.dlg;a&&(this.dlg=null,a.resolve(s),this.update(),this.#f())}#p(s){this.localNotice={text:s,until:Date.now()+6e3}}#w(){return{rpc:s=>tb.server.composerRpc(s,{spawn:!0}),select:async(s,a,d)=>{let p=await this.#a({method:"select",title:s,options:a,selected:d});return p.cancelled?null:p.value??null},input:async(s,a,d)=>{let p=await this.#a({method:"input",title:s,placeholder:a,prefill:d});return p.cancelled?null:p.value??""},confirm:async(s,a)=>!!(await this.#a({method:"confirm",title:s,message:a})).confirmed,setInput:s=>{this.input=s,this.update(),this.#f()},refreshSessions:()=>this.parent.refreshSessions()}}async runRecipe(s){this.slashQuery=null,this.sendError=void 0,this.update();try{let a=await s.run(this.#w());typeof a=="string"&&a&&this.#p(a)}catch(a){this.sendError=a instanceof Error?a.message:String(a)}this.dlg=null,this.update()}async#b(){try{let s=await tb.server.composerRpc({type:"get_commands"},{spawn:!1}),a=s?.ok?s.data?.commands:null;Array.isArray(a)&&(this.harnessCommands=a.map(d=>({name:String(d.name),description:d.description??""})),this.update())}catch{}}filteredCommands(){if(this.slashQuery===null)return[];let s=this.slashQuery.toLowerCase(),a=[...this.recipes.filter(d=>!d.hidden).map(d=>({name:d.name,description:d.description,recipe:d})),...this.harnessCommands.filter(d=>!this.recipes.some(p=>p.name===d.name)).map(d=>({name:d.name,description:d.description,recipe:void 0}))];return s?Xxe(a,d=>{let p=d.name.toLowerCase().indexOf(s);return p===0?0:p>0?100+p:-1},12):a.slice(0,12)}applySlashSelection(){let s=this.filteredCommands();if(this.slashQuery===null||s.length===0)return!1;let a=s[this.slashIndex];if(!a)return!1;this.slashQuery=null;let d=this.input.replace(/^\/\S*\s?/,"");if(a.recipe)return this.input=d,this.runRecipe(a.recipe),!0;let p="/"+a.name+" ";return this.input=p+d,this.update(),this.#f(p.length),!0}async newConversation(){this.#e||await this.#c("could not start a new conversation \u2014 mirror unreachable",async()=>{let s=await tb.server.composerNew();if(!s?.ok){this.sendError=s?.error??"could not start a new conversation";return}this.#i()})}filteredFiles(){if(this.atQuery===null)return[];let s=this.atQuery.toLowerCase();return s?Xxe(this.files,a=>{let d=a.toLowerCase().indexOf(s);if(d===-1)return-1;let p=a.slice(a.lastIndexOf("/")+1).toLowerCase().indexOf(s);return p===-1?1e3+d:p},10):this.files.slice(0,10)}onComposerInput(s){let a=s.selectionStart,d=s.value,p=null,b=0;for(let D=a-1;D>=0;D--){let P=d[D];if(P==="@"){let j=D>0?d[D-1]:"";(!j||/[\s([{]/.test(j))&&(p=d.slice(D+1,a),b=D);break}if(/\s/.test(P))break}let v=null;if(d.startsWith("/")&&a>=1){let D=d.slice(1,a);/\s/.test(D)||(v=D)}v!==null&&(p=null);let w=!(p===this.atQuery&&(p===null||b===this.atStart)),I=v!==this.slashQuery;if(!w&&!I)return;let T=p!==null&&this.atQuery===null,B=v!==null&&this.slashQuery===null;this.atQuery=p,p!==null&&(this.atStart=b,this.atIndex=0),this.slashQuery=v,v!==null&&I&&(this.slashIndex=0),T&&this.#i(),B&&this.#b(),this.update()}applyAtSelection(){let s=this.#m(),a=this.filteredFiles();if(!s||this.atQuery===null||a.length===0)return!1;let d=a[this.atIndex];if(!d)return!1;let p=this.input.slice(0,this.atStart),b=this.input.slice(s.selectionStart),v="@"+d+" ";return this.input=p+v+b,this.atQuery=null,this.update(),this.#f(p.length+v.length),!0}#m(){return document.querySelector(".composer-input")}#f(s){setTimeout(()=>{let a=this.#m();a&&(a.focus(),a.selectionStart=a.selectionEnd=s??a.value.length)},0)}beginSplitterDrag(s){s.preventDefault();let a=s.clientY,d=this.composerHeight,p=document.body.style.cursor,b=document.body.style.userSelect;document.body.style.cursor="row-resize",document.body.style.userSelect="none";let v=I=>{let T=d-(I.clientY-a),B=Math.max(120,window.innerHeight*.6);this.composerHeight=Math.max(40,Math.min(B,T)),this.update()},w=()=>{document.removeEventListener("mousemove",v),document.removeEventListener("mouseup",w),document.body.style.cursor=p,document.body.style.userSelect=b};document.addEventListener("mousemove",v),document.addEventListener("mouseup",w)}#A(s){if(this.sendError&&s.key!=="Enter"&&(this.sendError=void 0,this.update()),s.key===";"&&(s.ctrlKey||s.metaKey)){s.preventDefault(),this.captureClipboard();return}this.slashQuery!==null&&this.#k(s,this.filteredCommands().length,this.slashIndex,a=>this.slashIndex=a,()=>this.slashQuery=null,()=>this.applySlashSelection())||this.atQuery!==null&&this.#k(s,this.filteredFiles().length,this.atIndex,a=>this.atIndex=a,()=>this.atQuery=null,()=>this.applyAtSelection())||s.key==="Enter"&&!s.shiftKey&&(s.preventDefault(),this.send(s.altKey))}#k(s,a,d,p,b,v){return s.key==="ArrowDown"||s.key==="ArrowUp"?(s.preventDefault(),p(Math.max(0,Math.min(d+(s.key==="ArrowDown"?1:-1),a-1))),this.update(),this.#v(".at-popup"),!0):s.key==="Escape"?(s.preventDefault(),b(),this.update(),!0):(s.key==="Enter"||s.key==="Tab")&&a>0?(s.preventDefault(),v(),!0):!1}#v(s){setTimeout(()=>document.querySelector(`${s} .at-row.active`)?.scrollIntoView({block:"nearest"}),0)}view(){let s=this.#o(),a=s||this.#e?{disabled:!0}:{},d=this.sendError??this.driverError,p=this.localNotice&&Date.now()<this.localNotice.until?{text:this.localNotice.text,kind:"info"}:this.status,b=s?"the agent is working in a terminal \u2014 watch only":this.streaming?"Steer \u2014 Enter queues for after this step, Alt+Enter for after the turn":"Message the agent\u2026 (Enter to send, Shift+Enter for newline, @ files, / commands, ! bash)";return Zt({class:"composer"},this.dlg?this.#C()?this.dialogPopup():this.dialogView():null,this.atQuery!==null?this.atPopup():null,this.slashQuery!==null?this.slashPopup():null,Zt({class:"splitter",key:"splitter",onMouseDown:v=>this.beginSplitterDrag(v)},""),d?Zt({class:"composer-err"},d):null,!d&&p?Zt({class:["composer-status",p.kind],key:"composer-status"},p.text):null,this.queueStrip(),Zt({class:"input-wrap",key:"composer-row"},Hye({target:this,prop:()=>this.input,attrs:{class:"composer-input",placeholder:b,ariaLabel:"Message the agent",style:{height:`${this.composerHeight}px`},onKeyDown:v=>this.#A(v),onKeyUp:v=>{v.key!=="Escape"&&this.onComposerInput(v.target)},onClick:v=>this.onComposerInput(v.target),onPaste:v=>this.onPaste(v),...a}}),Zt({class:"composer-actions"},wc({class:"composer-clip",type:"button",title:"Capture clipboard to a file (Ctrl+;)",ariaLabel:"Capture clipboard",...a,onClick:()=>{this.captureClipboard()}},W0("attach")),wc({class:"composer-new",type:"button",title:"New conversation",ariaLabel:"New conversation",...this.#e?{disabled:!0}:{},onClick:()=>{this.newConversation()}},W0("addCircle")),this.streaming?wc({class:"composer-act stop",type:"button",title:"Stop the current turn",ariaLabel:"Stop",onClick:()=>{this.stopTurn()}},W0("stopCircle")):wc({class:"composer-act send",type:"button",title:"Send (Enter)",ariaLabel:"Send",...a,onClick:()=>{this.send()}},W0("playCircle")))))}queueStrip(){let s=this.queue,a=s?[...s.steering.map(d=>({tag:"steer",t:d})),...s.followUp.map(d=>({tag:"follow-up",t:d}))]:[];return a.length?Zt({class:"composer-queue",key:"composer-queue"},a.map(d=>Zt({class:"queue-row"},Nr({class:"queue-tag"},d.tag),d.t))):null}atPopup(){return this.#x("at-popup",this.filteredFiles(),"No matches",this.atIndex,s=>{this.atIndex=s,this.applyAtSelection()},s=>s)}slashPopup(){return this.#x("slash-popup",this.filteredCommands(),"No commands",this.slashIndex,s=>{this.slashIndex=s,this.applySlashSelection()},s=>[Nr({class:"slash-name"},"/"+s.name),s.description?Nr({class:"slash-desc"},s.description):null])}#x(s,a,d,p,b,v){return Zt({class:"at-popup",key:s},this.#E(a,d,p,b,v))}#E(s,a,d,p,b){return s.length===0?Zt({class:"at-empty"},a):s.map((v,w)=>Zt({class:["at-row",w===d?"active":""],onMouseDown:I=>{I.preventDefault(),p(w)}},b(v)))}#C(){return!!this.dlg&&this.dlg.local&&(this.dlg.req.method==="select"||this.dlg.req.method==="input")}dialogPopup(){let{req:s}=this.dlg;return Zt({key:"composer-dlg"},Zt({class:"dlg-catcher",onMouseDown:a=>{a.preventDefault(),this.answerDialog({cancelled:!0})}},""),Zt({class:"at-popup dlg-popup",key:"dlg-popup"},s.method==="select"?this.selectBody():[s.title?Zt({class:"dlg-label"},s.title):null,lM({target:this,prop:()=>this.dlgInput,attrs:{class:"dlg-input",placeholder:s.placeholder??"",onMounted:this.#d,onKeyDown:a=>this.#I(a)}})]))}dialogView(){let{req:s}=this.dlg,a=s.method==="confirm"?"Confirm":s.method==="select"?"Choose":"Input";return Zt({class:"trust-back",key:"composer-dlg"},Zt({class:"trust-modal composer-dlg"},Zt({class:"trust-modal-h"},s.title??a),s.message?Zt({class:"trust-modal-b"},s.message):null,s.method==="select"?this.selectBody():null,s.method==="input"?lM({target:this,prop:()=>this.dlgInput,attrs:{class:"dlg-input",placeholder:s.placeholder??"",onMounted:this.#d,onKeyDown:d=>this.#I(d)}}):null,s.method==="editor"?Hye({target:this,prop:()=>this.dlgInput,attrs:{class:"dlg-editor",onMounted:this.#d,onKeyDown:d=>{d.key==="Escape"&&(d.preventDefault(),this.answerDialog({cancelled:!0}))}}}):null,Zt({class:"trust-modal-acts"},wc({class:"trust-no",type:"button",onClick:()=>this.answerDialog(s.method==="confirm"?{confirmed:!1}:{cancelled:!0})},s.method==="confirm"?"No":"Cancel"),s.method==="confirm"?wc({class:"trust-yes",type:"button",onClick:()=>this.answerDialog({confirmed:!0})},"Yes"):s.method!=="select"?wc({class:"trust-yes",type:"button",onClick:()=>this.answerDialog({value:this.dlgInput})},"OK"):null)))}#I(s){s.key==="Enter"&&(s.preventDefault(),this.answerDialog({value:this.dlgInput})),s.key==="Escape"&&(s.preventDefault(),this.answerDialog({cancelled:!0}))}filteredDlgOptions(){let s=this.dlg?.req.options??[],a=this.dlgFilter.trim().toLowerCase();return a?Xxe(s,d=>d.toLowerCase().indexOf(a)):s}selectBody(){let s=this.filteredDlgOptions(),a=Math.min(this.dlgIndex,Math.max(0,s.length-1));return Zt({key:"dlg-select"},Zt({class:"dlg-opts",onMounted:d=>d.querySelector(".at-row.active")?.scrollIntoView({block:"nearest"})},this.#E(s,"No matches",a,d=>this.answerDialog({value:s[d]}),d=>d)),lM({target:this,prop:()=>this.dlgFilter,attrs:{class:"dlg-input dlg-filter",placeholder:"Type to filter\u2026",onMounted:this.#d,onKeyDown:d=>this.#S(d),onKeyUp:()=>{this.dlgFilter!==this.#t&&(this.#t=this.dlgFilter,this.dlgIndex=0,this.update())}}}))}#S(s){let a=this.filteredDlgOptions(),d=Math.min(this.dlgIndex,Math.max(0,a.length-1));if(s.key==="ArrowDown"||s.key==="ArrowUp"){s.preventDefault(),this.dlgIndex=Math.max(0,Math.min(d+(s.key==="ArrowDown"?1:-1),a.length-1)),this.update(),this.#v(".dlg-opts");return}if(s.key==="Enter"){s.preventDefault();let p=a[d];p!==void 0&&this.answerDialog({value:p});return}s.key==="Escape"&&(s.preventDefault(),this.answerDialog({cancelled:!0}))}};var rie=class extends wu{get parent(){return this.ctx.parent}view(){let s=this.parent.driverModel??this.parent.latestModel;if(!s)return Nr({class:"model-wrap"});let a=s.includes("/")?s.slice(s.lastIndexOf("/")+1):s;return Nr({class:"model-wrap"},Nr({class:"token",title:`Model: ${s}`},a))}};var Vxe=o=>`~${ete(o??0)}`;async function U4(o,s,a){let d=await o.rpc(s);if(!d.ok)throw new Error(d.error??a);return d.data}async function TSt(o){return(await o.rpc({type:"get_state"})).data}var oD=(o,s)=>`${o} ${s}${o===1?"":"s"}`;async function MSt(){let o=await tb.server.composerNew();if(!o?.ok)throw new Error(o?.error??"could not start a new conversation");return"new conversation \u2014 /model now lists your local models"}async function _St(o){let s=o.replace(/\\/g,"/").split("/").pop().replace(/\.jsonl$/,"");return!!(await tb.server.attach(s))?.ok}async function jvn(o,s,a){if(!await o.confirm(s?"Add local Ollama models?":`Sync ${oD(a,"new Ollama model")}?`,"Rewrites the ollama provider in pi's models.json to exactly what's installed. Prefer a hand-curated list? Cancel \u2014 the file is yours."))return;let p=await tb.server.ollamaSync();if(!p?.ok)throw new Error(p?.error??"Ollama sync failed");return await o.confirm(`${oD(p.count??0,"Ollama model")} configured`,"Pi loads models.json when it boots \u2014 start a new conversation now to use them?")?MSt():`${oD(p.count??0,"Ollama model")} configured \u2014 new conversations (+) and terminal pi will list them`}var DSt=[{name:"model",description:"Switch the model",async run(o){let[s,a]=await Promise.all([U4(o,{type:"get_available_models"},"could not list models"),tb.server.ollamaOffer().catch(()=>null)]),d=s?.models??[],p=d.map(K=>`${K.provider}/${K.id}`),{installed:b,configured:v}=a??{installed:[],configured:null},w=b.filter(K=>!v?.includes(K)),I=w.length?v===null?`+ add ${oD(w.length,"local Ollama model")}\u2026`:`\u27F3 sync ${oD(w.length,"new local Ollama model")}\u2026`:null,T=new Set(d.filter(K=>K.provider==="ollama").map(K=>K.id)),B=(v??[]).filter(K=>!T.has(K)).length,D=B?`\u27F3 ${oD(B,"Ollama model")} pending \u2014 new conversation loads them`:null;if(I&&p.push(I),D&&p.push(D),!p.length)return"no models configured";let P=(await TSt(o))?.model,j=await o.select("Switch model",p,P?`${P.provider}/${P.id}`:void 0);if(j===null)return;if(I!==null&&j===I)return jvn(o,v===null,w.length);if(D!==null&&j===D)return MSt();let U=d[p.indexOf(j)];return await U4(o,{type:"set_model",provider:U.provider,modelId:U.id},"set_model failed"),`model: ${j}`}},{name:"thinking",description:"Set the thinking level",async run(o){let s=await o.select("Thinking level",["off","minimal","low","medium","high","xhigh"],(await TSt(o))?.thinkingLevel);if(s!==null)return await U4(o,{type:"set_thinking_level",level:s},"set_thinking_level failed"),`thinking: ${s}`}},{name:"compact",description:"Compact the conversation context",async run(o){let s=await o.input("Compact context","optional instructions \u2014 Enter to compact");if(s===null)return;let a=await U4(o,{type:"compact",...s.trim()?{customInstructions:s.trim()}:{}},"compact failed");return a?.tokensBefore?`compacted: ${Vxe(a.tokensBefore)} \u2192 ${Vxe(a.estimatedTokensAfter)} tokens`:"compacted"}},{name:"fork",description:"Fork from an earlier message",async run(o){let a=(await U4(o,{type:"get_fork_messages"},"could not list fork points"))?.messages??[];if(!a.length)return"nothing to fork from";let d=a.map((w,I)=>`${I+1}. ${fM(w.text,80)}`),p=await o.select("Fork from\u2026",d);if(p===null)return;let b=a[d.indexOf(p)],v=await U4(o,{type:"fork",entryId:b.entryId},"fork failed");if(v?.sessionFile&&!await _St(v.sessionFile)){let w=await tb.server.composerNew();if(!w?.ok)throw new Error(w?.error??"could not open a new conversation")}return v?.text&&o.setInput(v.text),"forked \u2014 edit the restored prompt and send"}},{name:"clone",description:"Duplicate this branch into a new session",async run(o){if(!await o.confirm("Clone session?","Duplicates the current branch into a new session file."))return;let s=await U4(o,{type:"clone"},"clone failed");if(s?.sessionFile&&!await _St(s.sessionFile))throw new Error("clone finished but its session file never appeared");return"cloned \u2014 this is the duplicate"}},{name:"name",description:"Name this session",async run(o){let s=await o.input("Session name");if(!(s===null||!s.trim()))return await U4(o,{type:"set_session_name",name:s.trim()},"set_session_name failed"),o.refreshSessions(),`named: ${s.trim()}`}},{name:"export",description:"Export the session to HTML",async run(o){return`exported: ${(await U4(o,{type:"export_html"},"export failed"))?.path??"done"}`}},{name:"stats",description:"Token usage, cost, and context headroom",hidden:!0,async run(o){let s=await U4(o,{type:"get_session_stats"},"get_session_stats failed"),a=[s?.tokens?.total!==void 0?`${Vxe(s.tokens.total)} tokens`:null,s?.cost!==void 0?`$${s.cost.toFixed(2)}`:null,s?.contextUsage?.percent!=null?`context ${s.contextUsage.percent}%`:null].filter(Boolean);return a.length?a.join(" \xB7 "):"no stats yet"}}];document.title="Pi Mirror";new c6t({root:new tie({title:"Pi Mirror",pills:[new rie],composer:new nie(DSt)}),id:"app"});
|
|
1542
1542
|
/*! Bundled license information:
|
|
1543
1543
|
|
|
1544
1544
|
dompurify/dist/purify.es.mjs:
|
|
@@ -2280,7 +2280,7 @@ var Patcher = class {
|
|
|
2280
2280
|
|
|
2281
2281
|
// cli/agents/pi/server/piPatcherExtension.ts
|
|
2282
2282
|
var DESCRIPTION = true ? "Apply unified diffs to files \u2014 tolerant of form, strict about intent. Repairs sloppy AI-generated diffs (mangled headers, whitespace drift, missing prefixes; line numbers can be wrong or absent \u2014 hunks anchor by fuzzy-matched context lines) and applies all hunks atomically when the intent is unambiguous; fails with a precise, typed error when it isn't. Use for multi-hunk edits in one step, or when exact string-replacement editing fails on whitespace or invisible characters." : "Apply unified diffs to files.";
|
|
2283
|
-
var BUILD_TAG = true ? "matchu-patchu-pi 0.3.1, built 2026-07-
|
|
2283
|
+
var BUILD_TAG = true ? "matchu-patchu-pi 0.3.1, built 2026-07-19 06:41:31" : "matchu-patchu-pi dev";
|
|
2284
2284
|
var errorBlocks = (errors) => {
|
|
2285
2285
|
const parts = [`Patch failed with ${errors.length} error(s):`];
|
|
2286
2286
|
for (const e of errors) parts.push("", e.toString());
|
|
@@ -383,7 +383,8 @@ body {
|
|
|
383
383
|
.composer-queue { padding: .15rem 0 0; }
|
|
384
384
|
.queue-row { font-size: .78rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
385
385
|
.queue-tag { color: var(--accent); font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; margin-right: .5rem; }
|
|
386
|
-
/* The @-file-mention popup, anchored above the composer.
|
|
386
|
+
/* The @-file-mention popup, anchored above the composer. Tall enough for the pickers' full row
|
|
387
|
+
caps (10 files / 12 commands); the vh guard + arrow-key scroll-into-view cover short windows. */
|
|
387
388
|
.at-popup {
|
|
388
389
|
position: absolute;
|
|
389
390
|
bottom: calc(100% + .25rem);
|
|
@@ -391,7 +392,7 @@ body {
|
|
|
391
392
|
right: 0;
|
|
392
393
|
max-width: 600px;
|
|
393
394
|
margin-inline: auto;
|
|
394
|
-
max-height:
|
|
395
|
+
max-height: min(24rem, 50vh);
|
|
395
396
|
overflow-y: auto;
|
|
396
397
|
background: var(--panel);
|
|
397
398
|
border: 1px solid var(--border);
|