codexui-android 0.1.72 → 0.1.80
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/dist/assets/ReviewPane-C5squOEL.js +2 -0
- package/dist/assets/ReviewPane-C5squOEL.js.map +1 -0
- package/dist/assets/{SkillsHub-BkLIHbuN.js → SkillsHub-Crvp1wow.js} +2 -1
- package/dist/assets/SkillsHub-Crvp1wow.js.map +1 -0
- package/dist/assets/ThreadConversation-BsN7bN3q.css +1 -0
- package/dist/assets/ThreadConversation-qnvp4E2o.js +40 -0
- package/dist/assets/ThreadConversation-qnvp4E2o.js.map +1 -0
- package/dist/assets/common-BeuopZEI.js +1 -0
- package/dist/assets/common-BeuopZEI.js.map +1 -0
- package/dist/assets/index-C4y0SuPN.js +559 -0
- package/dist/assets/index-C4y0SuPN.js.map +1 -0
- package/dist/assets/index-CsHtQi-g.css +1 -0
- package/dist/assets/index.esm-BilMXo9u.js +1 -0
- package/dist/assets/index.esm-BilMXo9u.js.map +1 -0
- package/dist/assets/index.esm-DtVW_dfU.js +1 -0
- package/dist/assets/index.esm-DtVW_dfU.js.map +1 -0
- package/dist/assets/index.esm-mbv_PYjX.js +1 -0
- package/dist/assets/index.esm-mbv_PYjX.js.map +1 -0
- package/dist/index.html +2 -2
- package/dist-cli/chunk-NWKUDLO2.js +111 -0
- package/dist-cli/chunk-NWKUDLO2.js.map +1 -0
- package/dist-cli/index.js +2170 -868
- package/dist-cli/index.js.map +1 -1
- package/dist-cli/instrument.js +8 -0
- package/dist-cli/instrument.js.map +1 -0
- package/package.json +9 -4
- package/dist/assets/ReviewPane-jxaR-1Q1.js +0 -1
- package/dist/assets/ThreadConversation-1LJi-Pk9.js +0 -36
- package/dist/assets/ThreadConversation-Ct-Pc8bX.css +0 -1
- package/dist/assets/index-1Zt4k_jO.css +0 -1
- package/dist/assets/index-Tdn545FN.js +0 -62
package/README.md
CHANGED
|
@@ -96,8 +96,8 @@ If you want to use codexUI from iPhone or iPad Safari, serving it over HTTPS is
|
|
|
96
96
|
A practical private setup is to run codexUI locally and publish it inside your tailnet with Tailscale Serve:
|
|
97
97
|
|
|
98
98
|
```powershell
|
|
99
|
-
npx codexapp --no-tunnel --port
|
|
100
|
-
tailscale serve --bg
|
|
99
|
+
npx codexapp --no-tunnel --port 5900
|
|
100
|
+
tailscale serve --bg 5900
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
Then open:
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{d as et,u as tt,r as w,w as K,b as at,A as st,e as nt,o as i,c as l,a,t as r,v as U,f as h,m as ke,q as it,F as m,g as x,B as lt,C as rt,h as ge,l as O,x as D,j as ot,s as Ce,T as ut,y as _,D as vt,E as dt,G as ct,n as pt,H as Be,J as ft,_ as wt}from"./index-C4y0SuPN.js";const ht={class:"review-pane-header"},yt={class:"review-pane-heading"},mt={class:"review-pane-title"},_t={class:"review-pane-header-actions"},bt={class:"review-pane-toolbar"},kt={class:"review-pane-toolbar-tabs"},gt={class:"review-pane-segmented review-pane-segmented-primary"},Ct=["data-active","onClick"],Bt={class:"review-pane-toolbar-controls"},Lt={class:"review-pane-control-cluster"},Ft={class:"review-pane-segmented"},It=["data-active"],xt=["data-active","disabled"],Rt={key:0,class:"review-pane-control-cluster"},St={class:"review-pane-branch-select-wrap"},$t=["value"],Tt={key:1,class:"review-pane-control-cluster"},At={class:"review-pane-segmented"},Et=["data-active"],Mt=["data-active"],Nt={class:"review-pane-toolbar-actions"},Pt=["disabled"],Dt=["disabled"],Gt={key:1,class:"review-pane-meta"},Wt={class:"review-pane-summary-pill review-pane-summary-pill-add"},zt={class:"review-pane-summary-pill review-pane-summary-pill-remove"},Vt={key:0},Ht={key:1},Kt={key:2,class:"review-pane-content"},Ut={key:0,class:"review-pane-empty"},Ot={key:1,class:"review-pane-empty"},Xt=["disabled"],jt={key:2,class:"review-pane-empty"},Yt={key:0,class:"review-pane-bulk-actions"},qt=["disabled","onClick"],Jt={key:1,class:"review-pane-empty"},Qt={class:"review-pane-empty-text"},Zt={key:0,class:"review-pane-file-list"},ea=["data-expanded","onClick"],ta=["data-expanded"],aa={class:"review-pane-tree-folder-name"},sa={class:"review-pane-tree-folder-count"},na=["data-active","title","onClick"],ia={class:"review-pane-file-meta-row"},la={class:"review-pane-file-main"},ra=["data-operation"],oa={class:"review-pane-file-path"},ua={class:"review-pane-file-delta"},va={class:"review-pane-delta-add"},da={class:"review-pane-delta-remove"},ca={class:"review-pane-diff"},pa={class:"review-pane-file-header"},fa={class:"review-pane-file-header-main"},wa={class:"review-pane-file-title"},ha={class:"review-pane-file-subtitle"},ya={key:0,class:"review-pane-row-actions"},ma=["disabled","onClick"],_a={key:0,class:"review-pane-raw-diff"},ba={key:1,class:"review-pane-hunks"},ka=["data-active","onClick"],ga={class:"review-pane-hunk-header"},Ca={class:"review-pane-hunk-title"},Ba={class:"review-pane-hunk-meta"},La={key:0,class:"review-pane-row-actions"},Fa=["disabled","onClick"],Ia={class:"review-pane-lines"},xa=["data-kind"],Ra={class:"review-pane-line-number"},Sa={class:"review-pane-line-number"},$a={class:"review-pane-line-marker"},Ta={class:"review-pane-line-code"},Aa={key:3,class:"review-pane-findings"},Ea={key:0,class:"review-pane-summary-card"},Ma={class:"review-pane-summary-text"},Na={key:1,class:"review-pane-findings-list"},Pa=["onClick"],Da={class:"review-pane-finding-title"},Ga={key:0,class:"review-pane-finding-location"},Wa={key:1,class:"review-pane-finding-body"},za={key:2,class:"review-pane-empty"},Va={class:"review-pane-empty-text"},Ha={class:"review-pane-sheet-header"},Ka={class:"review-pane-sheet-count"},Ua={class:"review-pane-sheet-list"},Oa=["data-expanded","onClick"],Xa=["data-expanded"],ja={class:"review-pane-tree-folder-name"},Ya={class:"review-pane-tree-folder-count"},qa=["data-active","title","onClick"],Ja={class:"review-pane-file-meta-row"},Qa={class:"review-pane-file-main"},Za=["data-operation"],es={class:"review-pane-file-path"},ts={class:"review-pane-file-delta"},as={class:"review-pane-delta-add"},ss={class:"review-pane-delta-remove"},Le="codex-web-local.review-pane-file-list-width.v1",ns=220,is=420,Fe=288,ls=et({__name:"ReviewPane",props:{threadId:{},cwd:{},isThreadInProgress:{type:Boolean}},emits:["close"],setup(Ie){const b=Ie,{isMobile:F}=tt(),A=w("changes"),p=w("workspace"),C=w("unstaged"),u=w(null),R=w(""),X=w(!1),S=w(""),B=w(""),j=w(!1),ae=w(!1),E=w(!1),T=w(!1),Y=w(!1),G=w(""),L=w(""),M=w(""),N=w({}),q=w(""),se=new Map;let J=null,$=null;const xe=[{value:"changes",label:"Changes"},{value:"findings",label:"Findings"}],Q=_(()=>`${p.value}:${C.value}`),W=_(()=>N.value[Q.value]??null),k=_(()=>{var t,e;return((t=u.value)==null?void 0:t.files.find(n=>n.id===S.value))??((e=u.value)==null?void 0:e.files[0])??null}),P=w({}),Re=_(()=>{var t,e;return(t=u.value)!=null&&t.isGitRepo?p.value==="workspace"?C.value==="staged"?"Staged changes":"Workspace changes":(e=u.value)!=null&&e.baseBranch?`Against ${u.value.baseBranch}`:"Base branch":"Repository review"}),oe=_(()=>{var t,e;return b.threadId.trim().length>0&&b.cwd.trim().length>0&&((t=u.value)==null?void 0:t.isGitRepo)===!0&&!b.isThreadInProgress&&!(p.value==="baseBranch"&&!((e=u.value)!=null&&e.baseBranch))}),ue=_(()=>{var t;return p.value==="workspace"&&((t=u.value)==null?void 0:t.isGitRepo)===!0&&u.value.files.length>0}),ve=_(()=>ue.value&&!E.value),Se=_(()=>C.value==="staged"?[{value:"unstage",label:"Unstage all"}]:[{value:"stage",label:"Stage all"},{value:"revert",label:"Revert all"}]),$e=_(()=>C.value==="staged"?[{value:"unstage",label:"Unstage file"}]:[{value:"stage",label:"Stage file"},{value:"revert",label:"Revert file"}]),Te=_(()=>C.value==="staged"?[{value:"unstage",label:"Unstage hunk"}]:[{value:"stage",label:"Stage hunk"},{value:"revert",label:"Revert hunk"}]),de=_(()=>L.value||G.value||M.value),Ae=_(()=>!!(L.value||G.value)),Z=w(Ne()),Ee=_(()=>{const t={};return F.value||(t["--review-file-list-width"]=`${Z.value}px`),t}),ce=_(()=>{var t;return We(((t=u.value)==null?void 0:t.files)??[],P.value)}),pe=_(()=>ce.value.nodes),Me=_(()=>ce.value.folderIdsByFileId);function ne(t){return Number.isFinite(t)?Math.min(is,Math.max(ns,Math.round(t))):Fe}function Ne(){if(typeof window>"u")return Fe;const t=window.localStorage.getItem(Le),e=t?Number(t):Number.NaN;return ne(e)}function Pe(t){typeof window>"u"||window.localStorage.setItem(Le,String(ne(t)))}function De(t){if(F.value)return;t.preventDefault(),$==null||$();const e=t.clientX,n=Z.value,v=f=>{Z.value=ne(n+(f.clientX-e))},d=()=>{window.removeEventListener("pointermove",v),window.removeEventListener("pointerup",y),$=null},y=()=>{d(),Pe(Z.value)};window.addEventListener("pointermove",v),window.addEventListener("pointerup",y),$=d}function Ge(t){return t==="add"?"+":t==="remove"?"-":t==="hunk"?"@@":" "}function fe(t){const e=t.split("/").filter(Boolean);return e[e.length-1]??t}function we(t,e){return t.localeCompare(e,void 0,{numeric:!0,sensitivity:"base"})}function We(t,e){const n={folders:new Map,files:[]},v={};for(const g of t){const s=g.path.split("/").filter(Boolean),o=s.pop()??g.path;let c=n,I="";const be=[];for(const[Ze,V]of s.entries()){I=I?`${I}/${V}`:V;let H=c.folders.get(V);H||(H={id:I,name:V,depth:Ze,folders:new Map,files:[]},c.folders.set(V,H)),c=H,be.push(H.id)}c.files.push({file:g,name:o,depth:s.length}),v[g.id]=be}const d=[],y=g=>g.files.length+Array.from(g.folders.values()).reduce((s,o)=>s+y(o),0),f=g=>{const s=Array.from(g.folders.values()).sort((c,I)=>we(c.name,I.name)),o=[...g.files].sort((c,I)=>we(c.name,I.name));for(const c of s)d.push({kind:"folder",treeKey:`folder:${c.id}`,id:c.id,name:c.name,depth:c.depth,fileCount:y(c)}),e[c.id]!==!1&&f(c);for(const c of o)d.push({kind:"file",treeKey:`file:${c.file.id}`,id:c.file.id,name:c.name,depth:c.depth,file:c.file})};return f(n),{nodes:d,folderIdsByFileId:v}}function z(t){return P.value[t]!==!1}function he(t){P.value={...P.value,[t]:!z(t)}}function ie(t){const e=Me.value[t]??[];if(e.length===0)return;const n={...P.value};let v=!1;for(const d of e)n[d]===!1&&(n[d]=!0,v=!0);v&&(P.value=n)}function ee(t){const e=F.value?8:10,n=F.value?12:14;return{paddingLeft:`${e+t*n}px`}}function le(t){return t==="add"?"Added":t==="delete"?"Deleted":t==="rename"?"Renamed":"Modified"}function ze(t,e){if(!(e instanceof HTMLElement)){se.delete(t);return}se.set(t,e)}function Ve(t){const e=t.params!==null&&typeof t.params=="object"&&!Array.isArray(t.params)?t.params:null;return typeof(e==null?void 0:e.threadId)=="string"?e.threadId:""}async function te(){var t,e,n;if(b.cwd.trim()){ae.value=!0,G.value="";try{const v=p.value==="baseBranch"?R.value.trim():"",d=await vt(b.cwd,p.value,C.value,v||null);if(d.baseBranchOptions.length>0){const f=d.baseBranch??d.baseBranchOptions[0]??"";R.value!==f&&(X.value=!0,R.value=f)}else R.value!==""&&(X.value=!0,R.value="");u.value=d,d.files.some(f=>f.id===S.value)||(S.value=((t=d.files[0])==null?void 0:t.id)??"",B.value=((n=(e=d.files[0])==null?void 0:e.hunks[0])==null?void 0:n.id)??"")}catch(v){G.value=v instanceof Error?v.message:"Failed to load review snapshot"}finally{ae.value=!1}}}async function He(){if(b.threadId.trim())try{const t=await Be(b.threadId);t.result&&(N.value={...N.value,[Q.value]:t.result})}catch{}}async function ye(){await Promise.all([te(),He()])}function me(t){var n,v;ie(t),S.value=t;const e=((n=u.value)==null?void 0:n.files.find(d=>d.id===t))??null;B.value=((v=e==null?void 0:e.hunks[0])==null?void 0:v.id)??"",F.value&&(j.value=!1)}async function re(t,e,n=""){var v,d,y;if(u.value){E.value=!0,L.value="";try{const f=await ft({cwd:b.cwd,scope:p.value,workspaceView:C.value,action:t,level:e,patch:n});u.value=f,f.files.some(s=>s.id===S.value)||(S.value=((v=f.files[0])==null?void 0:v.id)??"",B.value=((y=(d=f.files[0])==null?void 0:d.hunks[0])==null?void 0:y.id)??"")}catch(f){L.value=f instanceof Error?f.message:"Failed to apply review action"}finally{E.value=!1}}}async function Ke(t){await re(t,"all")}async function Ue(t,e){await re(t,"file",e.diff)}async function Oe(t,e){B.value=e.id,await re(t,"hunk",e.patch)}async function Xe(){if(b.cwd.trim()){Y.value=!0,L.value="";try{await dt(b.cwd),await te()}catch(t){L.value=t instanceof Error?t.message:"Failed to initialize Git"}finally{Y.value=!1}}}async function je(){var t,e;if(!(!oe.value||T.value)){L.value="",M.value=p.value==="workspace"?"Reviewing current changes":`Reviewing against ${((t=u.value)==null?void 0:t.baseBranch)??"base branch"}`,T.value=!0,q.value=Q.value;try{await ct(b.threadId,p.value,C.value,R.value||(((e=u.value)==null?void 0:e.baseBranch)??null))}catch(n){T.value=!1,M.value="",L.value=n instanceof Error?n.message:"Failed to start review"}}}function Ye(t){if(!t.absolutePath)return"";const e=t.startLine?`:${t.startLine}${t.endLine&&t.endLine!==t.startLine?`-${t.endLine}`:""}`:"";return`${t.absolutePath}${e}`}function qe(t,e){if(!e.startLine)return t.hunks[0]??null;for(const n of t.hunks){if(n.newStart!==null){const v=n.newStart+Math.max(n.newLineCount,1)-1;if(e.startLine>=n.newStart&&e.startLine<=v)return n}if(n.oldStart!==null){const v=n.oldStart+Math.max(n.oldLineCount,1)-1;if(e.startLine>=n.oldStart&&e.startLine<=v)return n}}return t.hunks[0]??null}async function _e(t){await pt();const e=se.get(t);e==null||e.scrollIntoView({block:"center",behavior:"smooth"})}async function Je(t){var v;A.value="changes";const e=((v=u.value)==null?void 0:v.files.find(d=>d.absolutePath===t.absolutePath||d.previousAbsolutePath===t.absolutePath))??null;if(!e)return;ie(e.id),S.value=e.id;const n=qe(e,t);B.value=(n==null?void 0:n.id)??"",n!=null&&n.id&&await _e(n.id)}function Qe(t){if(Ve(t)!==b.threadId)return;const e=t.params!==null&&typeof t.params=="object"&&!Array.isArray(t.params)?t.params:null,n=(e==null?void 0:e.item)!==null&&typeof(e==null?void 0:e.item)=="object"&&!Array.isArray(e.item)?e.item:null,v=typeof(n==null?void 0:n.type)=="string"?n.type:"";if(t.method==="item/started"&&v==="enteredReviewMode"){T.value=!0,M.value=typeof(n==null?void 0:n.review)=="string"?n.review:"Review in progress";return}if(t.method==="item/completed"&&v==="exitedReviewMode"){const d=q.value||Q.value;T.value=!1,M.value="",Be(b.threadId).then(y=>{y.result&&(N.value={...N.value,[d]:y.result},A.value="findings")}).catch(y=>{L.value=y instanceof Error?y.message:"Failed to load review result"}).finally(()=>{q.value=""})}}return K(()=>[b.threadId,b.cwd],()=>{S.value="",B.value="",N.value={},q.value="",L.value="",M.value="",ye()},{immediate:!0}),K(()=>[p.value,C.value],()=>{S.value="",B.value="",L.value="",G.value="",te()}),K(R,(t,e)=>{if(X.value){X.value=!1;return}p.value==="baseBranch"&&(!t||t===e||te())}),K(k,t=>{var e;t&&(ie(t.id),t.hunks.some(n=>n.id===B.value)||(B.value=((e=t.hunks[0])==null?void 0:e.id)??""))}),K(B,t=>{t&&_e(t)}),at(()=>{J=st(Qe)}),nt(()=>{J&&(J(),J=null),$==null||$()}),(t,e)=>{var n,v,d,y,f,g;return i(),l("section",{class:ge(["review-pane",{"is-mobile":U(F)}]),onClick:e[9]||(e[9]=Ce(()=>{},["stop"]))},[a("header",ht,[a("div",yt,[e[10]||(e[10]=a("p",{class:"review-pane-eyebrow"},"Review",-1)),a("p",mt,r(Re.value),1)]),a("div",_t,[U(F)&&A.value==="changes"&&((n=u.value)!=null&&n.files.length)?(i(),l("button",{key:0,type:"button",class:"review-pane-mobile-files-button",onClick:e[0]||(e[0]=s=>j.value=!0)}," Files ")):h("",!0),a("button",{type:"button",class:"review-pane-close","aria-label":"Close review pane",onClick:e[1]||(e[1]=s=>t.$emit("close"))},[ke(it,{class:"icon-svg"})])])]),a("div",bt,[a("div",kt,[a("div",gt,[(i(),l(m,null,x(xe,s=>a("button",{key:s.value,type:"button",class:"review-pane-segmented-button review-pane-tab","data-active":A.value===s.value,onClick:o=>A.value=s.value},r(s.label),9,Ct)),64))])]),a("div",Bt,[a("div",Lt,[e[11]||(e[11]=a("span",{class:"review-pane-control-label"},"Compare",-1)),a("div",Ft,[a("button",{type:"button",class:"review-pane-segmented-button review-pane-scope","data-active":p.value==="workspace",onClick:e[2]||(e[2]=s=>p.value="workspace")}," Workspace ",8,It),a("button",{type:"button",class:"review-pane-segmented-button review-pane-scope","data-active":p.value==="baseBranch",disabled:!((v=u.value)!=null&&v.baseBranch),onClick:e[3]||(e[3]=s=>p.value="baseBranch")}," Base branch ",8,xt)])]),p.value==="baseBranch"&&((d=u.value)!=null&&d.baseBranchOptions.length)?(i(),l("div",Rt,[e[12]||(e[12]=a("span",{class:"review-pane-control-label"},"Branch",-1)),a("label",St,[lt(a("select",{"onUpdate:modelValue":e[4]||(e[4]=s=>R.value=s),class:"review-pane-branch-select"},[(i(!0),l(m,null,x(u.value.baseBranchOptions,s=>(i(),l("option",{key:s,value:s},r(s),9,$t))),128))],512),[[rt,R.value]])])])):h("",!0),p.value==="workspace"?(i(),l("div",Tt,[e[13]||(e[13]=a("span",{class:"review-pane-control-label"},"Changes",-1)),a("div",At,[a("button",{type:"button",class:"review-pane-segmented-button review-pane-view","data-active":C.value==="unstaged",onClick:e[5]||(e[5]=s=>C.value="unstaged")}," Unstaged ",8,Et),a("button",{type:"button",class:"review-pane-segmented-button review-pane-view","data-active":C.value==="staged",onClick:e[6]||(e[6]=s=>C.value="staged")}," Staged ",8,Mt)])])):h("",!0)]),a("div",Nt,[a("button",{type:"button",class:"review-pane-run",disabled:!oe.value||T.value,onClick:je},r(T.value?"Reviewing…":"Run review"),9,Pt),a("button",{type:"button",class:"review-pane-refresh",disabled:ae.value,onClick:ye}," Refresh ",8,Dt)])]),de.value?(i(),l("div",{key:0,class:ge(["review-pane-banner",{"is-error":Ae.value}])},r(de.value),3)):h("",!0),u.value?(i(),l("div",Gt,[a("span",null,r(u.value.summary.fileCount)+" files",1),a("span",Wt,"+"+r(u.value.summary.addedLineCount),1),a("span",zt,"-"+r(u.value.summary.removedLineCount),1),u.value.headBranch?(i(),l("span",Vt,r(u.value.headBranch),1)):h("",!0),p.value==="baseBranch"&&u.value.baseBranch?(i(),l("span",Ht,"vs "+r(u.value.baseBranch),1)):h("",!0)])):h("",!0),A.value==="changes"?(i(),l("div",Kt,[u.value?u.value.isGitRepo?p.value==="baseBranch"&&!u.value.baseBranch?(i(),l("div",jt,[...e[17]||(e[17]=[a("p",{class:"review-pane-empty-title"},"Base branch unavailable",-1),a("p",{class:"review-pane-empty-text"},"Could not resolve `origin/HEAD`, `main`, or `master` for this repository.",-1)])])):(i(),l(m,{key:3},[ue.value?(i(),l("div",Yt,[(i(!0),l(m,null,x(Se.value,s=>(i(),l("button",{key:s.value,type:"button",class:"review-pane-bulk-button",disabled:E.value,onClick:o=>Ke(s.value)},r(s.label),9,qt))),128))])):h("",!0),u.value.files.length?(i(),l("div",{key:2,class:"review-pane-main",style:O(Ee.value)},[U(F)?h("",!0):(i(),l("aside",Zt,[(i(!0),l(m,null,x(pe.value,s=>{var o;return i(),l(m,{key:s.treeKey},[s.kind==="folder"?(i(),l("button",{key:0,type:"button",class:"review-pane-tree-folder",style:O(ee(s.depth)),"data-expanded":z(s.id),onClick:c=>he(s.id)},[a("span",{class:"review-pane-tree-caret","data-expanded":z(s.id)},null,8,ta),a("span",aa,r(s.name),1),a("span",sa,r(s.fileCount),1)],12,ea)):(i(),l("button",{key:1,type:"button",class:"review-pane-file review-pane-tree-file",style:O(ee(s.depth)),"data-active":((o=k.value)==null?void 0:o.id)===s.file.id,title:s.file.path,onClick:c=>me(s.file.id)},[a("span",ia,[a("span",la,[a("span",{class:"review-pane-file-op","data-operation":s.file.operation},r(le(s.file.operation)),9,ra),a("span",oa,[D(r(s.name)+" ",1),s.file.previousPath?(i(),l(m,{key:0},[D(" ← "+r(fe(s.file.previousPath)),1)],64)):h("",!0)])]),a("span",ua,[a("span",va,"+"+r(s.file.addedLineCount),1),e[19]||(e[19]=a("span",{class:"review-pane-delta-separator"},"/",-1)),a("span",da,"-"+r(s.file.removedLineCount),1)])])],12,na))],64)}),128))])),U(F)?h("",!0):(i(),l("div",{key:1,class:"review-pane-resizer",role:"separator","aria-orientation":"vertical","aria-label":"Resize file list",onPointerdown:De},null,32)),a("section",ca,[k.value?(i(),l(m,{key:0},[a("div",pa,[a("div",fa,[a("p",wa,[D(r(k.value.path)+" ",1),k.value.previousPath?(i(),l(m,{key:0},[D(" ← "+r(k.value.previousPath),1)],64)):h("",!0)]),a("p",ha,r(le(k.value.operation))+" · +"+r(k.value.addedLineCount)+" / -"+r(k.value.removedLineCount),1)]),ve.value?(i(),l("div",ya,[(i(!0),l(m,null,x($e.value,s=>(i(),l("button",{key:`${k.value.id}:${s.value}`,type:"button",class:"review-pane-row-button",disabled:E.value,onClick:o=>Ue(s.value,k.value)},r(s.label),9,ma))),128))])):h("",!0)]),k.value.hunks.length===0?(i(),l("div",_a,[a("pre",null,r(k.value.diff||"No unified diff available."),1)])):(i(),l("div",ba,[(i(!0),l(m,null,x(k.value.hunks,s=>(i(),l("article",{key:s.id,ref_for:!0,ref:o=>ze(s.id,o),class:"review-pane-hunk","data-active":B.value===s.id,onClick:o=>B.value=s.id},[a("div",ga,[a("div",null,[a("p",Ca,r(s.header),1),a("p",Ba,"+"+r(s.addedLineCount)+" / -"+r(s.removedLineCount),1)]),ve.value?(i(),l("div",La,[(i(!0),l(m,null,x(Te.value,o=>(i(),l("button",{key:`${s.id}:${o.value}`,type:"button",class:"review-pane-row-button",disabled:E.value,onClick:c=>Oe(o.value,s)},r(o.label),9,Fa))),128))])):h("",!0)]),a("div",Ia,[(i(!0),l(m,null,x(s.lines,o=>(i(),l("div",{key:o.key,class:"review-pane-line","data-kind":o.kind},[a("span",Ra,r(o.oldLine??""),1),a("span",Sa,r(o.newLine??""),1),a("span",$a,r(Ge(o.kind)),1),a("code",Ta,r(o.text||" "),1)],8,xa))),128))])],8,ka))),128))]))],64)):h("",!0)])],4)):(i(),l("div",Jt,[e[18]||(e[18]=a("p",{class:"review-pane-empty-title"},"No changes in this scope",-1)),a("p",Qt,r(p.value==="workspace"?"Your current workspace is clean.":"No merge diff found against the base branch."),1)]))],64)):(i(),l("div",Ot,[e[15]||(e[15]=a("p",{class:"review-pane-empty-title"},"This folder is not a Git repository",-1)),e[16]||(e[16]=a("p",{class:"review-pane-empty-text"},"Initialize Git to review local changes and run Codex review.",-1)),a("button",{type:"button",class:"review-pane-primary-cta",disabled:Y.value,onClick:Xe},r(Y.value?"Initializing…":"Initialize Git"),9,Xt)])):(i(),l("div",Ut,[...e[14]||(e[14]=[a("p",{class:"review-pane-empty-title"},"Loading review state",-1)])]))])):(i(),l("div",Aa,[(y=W.value)!=null&&y.summary?(i(),l("div",Ea,[e[20]||(e[20]=a("p",{class:"review-pane-summary-title"},"Summary",-1)),a("pre",Ma,r(W.value.summary),1)])):h("",!0),(f=W.value)!=null&&f.findings.length?(i(),l("div",Na,[(i(!0),l(m,null,x(W.value.findings,s=>(i(),l("button",{key:s.id,type:"button",class:"review-pane-finding",onClick:o=>Je(s)},[a("span",Da,r(s.title),1),s.absolutePath?(i(),l("span",Ga,r(Ye(s)),1)):h("",!0),s.body?(i(),l("span",Wa,r(s.body),1)):h("",!0)],8,Pa))),128))])):(i(),l("div",za,[e[21]||(e[21]=a("p",{class:"review-pane-empty-title"},"No structured findings yet",-1)),a("p",Va,r((g=W.value)!=null&&g.summary?"The latest review only returned summary text.":"Run review to populate this pane."),1)]))])),ke(ut,{name:"review-pane-sheet"},{default:ot(()=>{var s;return[U(F)&&j.value&&((s=u.value)!=null&&s.files.length)?(i(),l("div",{key:0,class:"review-pane-sheet-backdrop",onClick:e[8]||(e[8]=o=>j.value=!1)},[a("div",{class:"review-pane-sheet",onClick:e[7]||(e[7]=Ce(()=>{},["stop"]))},[e[24]||(e[24]=a("div",{class:"review-pane-sheet-handle","aria-hidden":"true"},null,-1)),a("div",Ha,[e[22]||(e[22]=a("p",{class:"review-pane-sheet-title"},"Changed files",-1)),a("p",Ka,r(u.value.files.length),1)]),a("div",Ua,[(i(!0),l(m,null,x(pe.value,o=>{var c;return i(),l(m,{key:`sheet:${o.treeKey}`},[o.kind==="folder"?(i(),l("button",{key:0,type:"button",class:"review-pane-tree-folder review-pane-tree-folder-sheet",style:O(ee(o.depth)),"data-expanded":z(o.id),onClick:I=>he(o.id)},[a("span",{class:"review-pane-tree-caret","data-expanded":z(o.id)},null,8,Xa),a("span",ja,r(o.name),1),a("span",Ya,r(o.fileCount),1)],12,Oa)):(i(),l("button",{key:1,type:"button",class:"review-pane-file review-pane-tree-file",style:O(ee(o.depth)),"data-active":((c=k.value)==null?void 0:c.id)===o.file.id,title:o.file.path,onClick:I=>me(o.file.id)},[a("span",Ja,[a("span",Qa,[a("span",{class:"review-pane-file-op","data-operation":o.file.operation},r(le(o.file.operation)),9,Za),a("span",es,[D(r(o.name)+" ",1),o.file.previousPath?(i(),l(m,{key:0},[D(" ← "+r(fe(o.file.previousPath)),1)],64)):h("",!0)])]),a("span",ts,[a("span",as,"+"+r(o.file.addedLineCount),1),e[23]||(e[23]=a("span",{class:"review-pane-delta-separator"},"/",-1)),a("span",ss,"-"+r(o.file.removedLineCount),1)])])],12,qa))],64)}),128))])])])):h("",!0)]}),_:1})],2)}}}),os=wt(ls,[["__scopeId","data-v-cf07d06e"]]);export{os as default};
|
|
2
|
+
//# sourceMappingURL=ReviewPane-C5squOEL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReviewPane-C5squOEL.js","sources":["../../src/components/content/ReviewPane.vue"],"sourcesContent":["<template>\n <section class=\"review-pane\" :class=\"{ 'is-mobile': isMobile }\" @click.stop>\n <header class=\"review-pane-header\">\n <div class=\"review-pane-heading\">\n <p class=\"review-pane-eyebrow\">Review</p>\n <p class=\"review-pane-title\">{{ headerTitle }}</p>\n </div>\n <div class=\"review-pane-header-actions\">\n <button\n v-if=\"isMobile && activeTab === 'changes' && snapshot?.files.length\"\n type=\"button\"\n class=\"review-pane-mobile-files-button\"\n @click=\"isFileSheetOpen = true\"\n >\n Files\n </button>\n <button type=\"button\" class=\"review-pane-close\" aria-label=\"Close review pane\" @click=\"$emit('close')\">\n <IconTablerX class=\"icon-svg\" />\n </button>\n </div>\n </header>\n\n <div class=\"review-pane-toolbar\">\n <div class=\"review-pane-toolbar-tabs\">\n <div class=\"review-pane-segmented review-pane-segmented-primary\">\n <button\n v-for=\"tab in reviewTabs\"\n :key=\"tab.value\"\n type=\"button\"\n class=\"review-pane-segmented-button review-pane-tab\"\n :data-active=\"activeTab === tab.value\"\n @click=\"activeTab = tab.value\"\n >\n {{ tab.label }}\n </button>\n </div>\n </div>\n\n <div class=\"review-pane-toolbar-controls\">\n <div class=\"review-pane-control-cluster\">\n <span class=\"review-pane-control-label\">Compare</span>\n <div class=\"review-pane-segmented\">\n <button\n type=\"button\"\n class=\"review-pane-segmented-button review-pane-scope\"\n :data-active=\"activeScope === 'workspace'\"\n @click=\"activeScope = 'workspace'\"\n >\n Workspace\n </button>\n <button\n type=\"button\"\n class=\"review-pane-segmented-button review-pane-scope\"\n :data-active=\"activeScope === 'baseBranch'\"\n :disabled=\"!snapshot?.baseBranch\"\n @click=\"activeScope = 'baseBranch'\"\n >\n Base branch\n </button>\n </div>\n </div>\n\n <div v-if=\"activeScope === 'baseBranch' && snapshot?.baseBranchOptions.length\" class=\"review-pane-control-cluster\">\n <span class=\"review-pane-control-label\">Branch</span>\n <label class=\"review-pane-branch-select-wrap\">\n <select\n v-model=\"selectedBaseBranch\"\n class=\"review-pane-branch-select\"\n >\n <option\n v-for=\"branch in snapshot.baseBranchOptions\"\n :key=\"branch\"\n :value=\"branch\"\n >\n {{ branch }}\n </option>\n </select>\n </label>\n </div>\n\n <div v-if=\"activeScope === 'workspace'\" class=\"review-pane-control-cluster\">\n <span class=\"review-pane-control-label\">Changes</span>\n <div class=\"review-pane-segmented\">\n <button\n type=\"button\"\n class=\"review-pane-segmented-button review-pane-view\"\n :data-active=\"workspaceView === 'unstaged'\"\n @click=\"workspaceView = 'unstaged'\"\n >\n Unstaged\n </button>\n <button\n type=\"button\"\n class=\"review-pane-segmented-button review-pane-view\"\n :data-active=\"workspaceView === 'staged'\"\n @click=\"workspaceView = 'staged'\"\n >\n Staged\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"review-pane-toolbar-actions\">\n <button\n type=\"button\"\n class=\"review-pane-run\"\n :disabled=\"!canRunReview || isRunningReview\"\n @click=\"runReview\"\n >\n {{ isRunningReview ? 'Reviewing…' : 'Run review' }}\n </button>\n <button type=\"button\" class=\"review-pane-refresh\" :disabled=\"isLoadingSnapshot\" @click=\"reloadAll\">\n Refresh\n </button>\n </div>\n </div>\n\n <div v-if=\"reviewBannerText\" class=\"review-pane-banner\" :class=\"{ 'is-error': reviewBannerIsError }\">\n {{ reviewBannerText }}\n </div>\n\n <div v-if=\"snapshot\" class=\"review-pane-meta\">\n <span>{{ snapshot.summary.fileCount }} files</span>\n <span class=\"review-pane-summary-pill review-pane-summary-pill-add\">+{{ snapshot.summary.addedLineCount }}</span>\n <span class=\"review-pane-summary-pill review-pane-summary-pill-remove\">-{{ snapshot.summary.removedLineCount }}</span>\n <span v-if=\"snapshot.headBranch\">{{ snapshot.headBranch }}</span>\n <span v-if=\"activeScope === 'baseBranch' && snapshot.baseBranch\">vs {{ snapshot.baseBranch }}</span>\n </div>\n\n <div v-if=\"activeTab === 'changes'\" class=\"review-pane-content\">\n <template v-if=\"!snapshot\">\n <div class=\"review-pane-empty\">\n <p class=\"review-pane-empty-title\">Loading review state</p>\n </div>\n </template>\n\n <template v-else-if=\"!snapshot.isGitRepo\">\n <div class=\"review-pane-empty\">\n <p class=\"review-pane-empty-title\">This folder is not a Git repository</p>\n <p class=\"review-pane-empty-text\">Initialize Git to review local changes and run Codex review.</p>\n <button type=\"button\" class=\"review-pane-primary-cta\" :disabled=\"isInitializingGit\" @click=\"initializeGit\">\n {{ isInitializingGit ? 'Initializing…' : 'Initialize Git' }}\n </button>\n </div>\n </template>\n\n <template v-else-if=\"activeScope === 'baseBranch' && !snapshot.baseBranch\">\n <div class=\"review-pane-empty\">\n <p class=\"review-pane-empty-title\">Base branch unavailable</p>\n <p class=\"review-pane-empty-text\">Could not resolve `origin/HEAD`, `main`, or `master` for this repository.</p>\n </div>\n </template>\n\n <template v-else>\n <div v-if=\"showBulkActions\" class=\"review-pane-bulk-actions\">\n <button\n v-for=\"action in bulkActions\"\n :key=\"action.value\"\n type=\"button\"\n class=\"review-pane-bulk-button\"\n :disabled=\"isApplyingAction\"\n @click=\"applyBulkAction(action.value)\"\n >\n {{ action.label }}\n </button>\n </div>\n\n <div v-if=\"!snapshot.files.length\" class=\"review-pane-empty\">\n <p class=\"review-pane-empty-title\">No changes in this scope</p>\n <p class=\"review-pane-empty-text\">\n {{ activeScope === 'workspace' ? 'Your current workspace is clean.' : 'No merge diff found against the base branch.' }}\n </p>\n </div>\n\n <div v-else class=\"review-pane-main\" :style=\"reviewMainStyle\">\n <aside v-if=\"!isMobile\" class=\"review-pane-file-list\">\n <template v-for=\"node in visibleFileTreeNodes\" :key=\"node.treeKey\">\n <button\n v-if=\"node.kind === 'folder'\"\n type=\"button\"\n class=\"review-pane-tree-folder\"\n :style=\"treeIndentStyle(node.depth)\"\n :data-expanded=\"isFolderExpanded(node.id)\"\n @click=\"toggleFolder(node.id)\"\n >\n <span class=\"review-pane-tree-caret\" :data-expanded=\"isFolderExpanded(node.id)\"></span>\n <span class=\"review-pane-tree-folder-name\">{{ node.name }}</span>\n <span class=\"review-pane-tree-folder-count\">{{ node.fileCount }}</span>\n </button>\n\n <button\n v-else\n type=\"button\"\n class=\"review-pane-file review-pane-tree-file\"\n :style=\"treeIndentStyle(node.depth)\"\n :data-active=\"selectedFile?.id === node.file.id\"\n :title=\"node.file.path\"\n @click=\"selectFile(node.file.id)\"\n >\n <span class=\"review-pane-file-meta-row\">\n <span class=\"review-pane-file-main\">\n <span class=\"review-pane-file-op\" :data-operation=\"node.file.operation\">{{ formatOperation(node.file.operation) }}</span>\n <span class=\"review-pane-file-path\">\n {{ node.name }}\n <template v-if=\"node.file.previousPath\"> ← {{ fileBaseName(node.file.previousPath) }}</template>\n </span>\n </span>\n <span class=\"review-pane-file-delta\">\n <span class=\"review-pane-delta-add\">+{{ node.file.addedLineCount }}</span>\n <span class=\"review-pane-delta-separator\">/</span>\n <span class=\"review-pane-delta-remove\">-{{ node.file.removedLineCount }}</span>\n </span>\n </span>\n </button>\n </template>\n </aside>\n\n <div\n v-if=\"!isMobile\"\n class=\"review-pane-resizer\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize file list\"\n @pointerdown=\"onResizerPointerDown\"\n ></div>\n\n <section class=\"review-pane-diff\">\n <template v-if=\"selectedFile\">\n <div class=\"review-pane-file-header\">\n <div class=\"review-pane-file-header-main\">\n <p class=\"review-pane-file-title\">\n {{ selectedFile.path }}\n <template v-if=\"selectedFile.previousPath\"> ← {{ selectedFile.previousPath }}</template>\n </p>\n <p class=\"review-pane-file-subtitle\">\n {{ formatOperation(selectedFile.operation) }} · +{{ selectedFile.addedLineCount }} / -{{ selectedFile.removedLineCount }}\n </p>\n </div>\n <div v-if=\"showRowActions\" class=\"review-pane-row-actions\">\n <button\n v-for=\"action in fileActions\"\n :key=\"`${selectedFile.id}:${action.value}`\"\n type=\"button\"\n class=\"review-pane-row-button\"\n :disabled=\"isApplyingAction\"\n @click=\"applyFileAction(action.value, selectedFile)\"\n >\n {{ action.label }}\n </button>\n </div>\n </div>\n\n <div v-if=\"selectedFile.hunks.length === 0\" class=\"review-pane-raw-diff\">\n <pre>{{ selectedFile.diff || 'No unified diff available.' }}</pre>\n </div>\n\n <div v-else class=\"review-pane-hunks\">\n <article\n v-for=\"hunk in selectedFile.hunks\"\n :key=\"hunk.id\"\n :ref=\"(element) => bindHunkRef(hunk.id, element)\"\n class=\"review-pane-hunk\"\n :data-active=\"selectedHunkId === hunk.id\"\n @click=\"selectedHunkId = hunk.id\"\n >\n <div class=\"review-pane-hunk-header\">\n <div>\n <p class=\"review-pane-hunk-title\">{{ hunk.header }}</p>\n <p class=\"review-pane-hunk-meta\">+{{ hunk.addedLineCount }} / -{{ hunk.removedLineCount }}</p>\n </div>\n <div v-if=\"showRowActions\" class=\"review-pane-row-actions\">\n <button\n v-for=\"action in hunkActions\"\n :key=\"`${hunk.id}:${action.value}`\"\n type=\"button\"\n class=\"review-pane-row-button\"\n :disabled=\"isApplyingAction\"\n @click=\"applyHunkAction(action.value, hunk)\"\n >\n {{ action.label }}\n </button>\n </div>\n </div>\n\n <div class=\"review-pane-lines\">\n <div\n v-for=\"line in hunk.lines\"\n :key=\"line.key\"\n class=\"review-pane-line\"\n :data-kind=\"line.kind\"\n >\n <span class=\"review-pane-line-number\">{{ line.oldLine ?? '' }}</span>\n <span class=\"review-pane-line-number\">{{ line.newLine ?? '' }}</span>\n <span class=\"review-pane-line-marker\">{{ lineMarker(line.kind) }}</span>\n <code class=\"review-pane-line-code\">{{ line.text || ' ' }}</code>\n </div>\n </div>\n </article>\n </div>\n </template>\n </section>\n </div>\n </template>\n </div>\n\n <div v-else class=\"review-pane-findings\">\n <div v-if=\"currentReviewResult?.summary\" class=\"review-pane-summary-card\">\n <p class=\"review-pane-summary-title\">Summary</p>\n <pre class=\"review-pane-summary-text\">{{ currentReviewResult.summary }}</pre>\n </div>\n\n <div v-if=\"currentReviewResult?.findings.length\" class=\"review-pane-findings-list\">\n <button\n v-for=\"finding in currentReviewResult.findings\"\n :key=\"finding.id\"\n type=\"button\"\n class=\"review-pane-finding\"\n @click=\"openFinding(finding)\"\n >\n <span class=\"review-pane-finding-title\">{{ finding.title }}</span>\n <span v-if=\"finding.absolutePath\" class=\"review-pane-finding-location\">\n {{ formatFindingLocation(finding) }}\n </span>\n <span v-if=\"finding.body\" class=\"review-pane-finding-body\">{{ finding.body }}</span>\n </button>\n </div>\n\n <div v-else class=\"review-pane-empty\">\n <p class=\"review-pane-empty-title\">No structured findings yet</p>\n <p class=\"review-pane-empty-text\">\n {{ currentReviewResult?.summary ? 'The latest review only returned summary text.' : 'Run review to populate this pane.' }}\n </p>\n </div>\n </div>\n\n <Transition name=\"review-pane-sheet\">\n <div\n v-if=\"isMobile && isFileSheetOpen && snapshot?.files.length\"\n class=\"review-pane-sheet-backdrop\"\n @click=\"isFileSheetOpen = false\"\n >\n <div class=\"review-pane-sheet\" @click.stop>\n <div class=\"review-pane-sheet-handle\" aria-hidden=\"true\"></div>\n <div class=\"review-pane-sheet-header\">\n <p class=\"review-pane-sheet-title\">Changed files</p>\n <p class=\"review-pane-sheet-count\">{{ snapshot.files.length }}</p>\n </div>\n <div class=\"review-pane-sheet-list\">\n <template v-for=\"node in visibleFileTreeNodes\" :key=\"`sheet:${node.treeKey}`\">\n <button\n v-if=\"node.kind === 'folder'\"\n type=\"button\"\n class=\"review-pane-tree-folder review-pane-tree-folder-sheet\"\n :style=\"treeIndentStyle(node.depth)\"\n :data-expanded=\"isFolderExpanded(node.id)\"\n @click=\"toggleFolder(node.id)\"\n >\n <span class=\"review-pane-tree-caret\" :data-expanded=\"isFolderExpanded(node.id)\"></span>\n <span class=\"review-pane-tree-folder-name\">{{ node.name }}</span>\n <span class=\"review-pane-tree-folder-count\">{{ node.fileCount }}</span>\n </button>\n\n <button\n v-else\n type=\"button\"\n class=\"review-pane-file review-pane-tree-file\"\n :style=\"treeIndentStyle(node.depth)\"\n :data-active=\"selectedFile?.id === node.file.id\"\n :title=\"node.file.path\"\n @click=\"selectFile(node.file.id)\"\n >\n <span class=\"review-pane-file-meta-row\">\n <span class=\"review-pane-file-main\">\n <span class=\"review-pane-file-op\" :data-operation=\"node.file.operation\">{{ formatOperation(node.file.operation) }}</span>\n <span class=\"review-pane-file-path\">\n {{ node.name }}\n <template v-if=\"node.file.previousPath\"> ← {{ fileBaseName(node.file.previousPath) }}</template>\n </span>\n </span>\n <span class=\"review-pane-file-delta\">\n <span class=\"review-pane-delta-add\">+{{ node.file.addedLineCount }}</span>\n <span class=\"review-pane-delta-separator\">/</span>\n <span class=\"review-pane-delta-remove\">-{{ node.file.removedLineCount }}</span>\n </span>\n </span>\n </button>\n </template>\n </div>\n </div>\n </div>\n </Transition>\n </section>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport {\n applyReviewAction,\n getReviewSnapshot,\n getThreadReviewResult,\n initializeReviewGit,\n startThreadReview,\n subscribeCodexNotifications,\n type RpcNotification,\n} from '../../api/codexGateway'\nimport { useMobile } from '../../composables/useMobile'\nimport type {\n UiReviewAction,\n UiReviewFinding,\n UiReviewResult,\n UiReviewScope,\n UiReviewSnapshot,\n UiReviewTab,\n UiReviewWorkspaceView,\n UiReviewFile,\n UiReviewHunk,\n} from '../../types/codex'\nimport IconTablerX from '../icons/IconTablerX.vue'\n\nconst props = defineProps<{\n threadId: string\n cwd: string\n isThreadInProgress: boolean\n}>()\n\ndefineEmits<{\n close: []\n}>()\n\nconst { isMobile } = useMobile()\n\nconst activeTab = ref<UiReviewTab>('changes')\nconst activeScope = ref<UiReviewScope>('workspace')\nconst workspaceView = ref<UiReviewWorkspaceView>('unstaged')\nconst snapshot = ref<UiReviewSnapshot | null>(null)\nconst selectedBaseBranch = ref('')\nconst isSyncingBaseBranch = ref(false)\nconst selectedFileId = ref('')\nconst selectedHunkId = ref('')\nconst isFileSheetOpen = ref(false)\nconst isLoadingSnapshot = ref(false)\nconst isApplyingAction = ref(false)\nconst isRunningReview = ref(false)\nconst isInitializingGit = ref(false)\nconst snapshotError = ref('')\nconst reviewError = ref('')\nconst reviewStatusLabel = ref('')\nconst reviewResultsByKey = ref<Record<string, UiReviewResult | null>>({})\nconst pendingReviewKey = ref('')\nconst hunkRefs = new Map<string, HTMLElement>()\nlet stopNotifications: (() => void) | null = null\nlet stopResizeTracking: (() => void) | null = null\n\nconst reviewTabs = [\n { value: 'changes' as const, label: 'Changes' },\n { value: 'findings' as const, label: 'Findings' },\n]\n\ntype ReviewTreeFolderNode = {\n kind: 'folder'\n treeKey: string\n id: string\n name: string\n depth: number\n fileCount: number\n}\n\ntype ReviewTreeFileNode = {\n kind: 'file'\n treeKey: string\n id: string\n name: string\n depth: number\n file: UiReviewFile\n}\n\ntype ReviewTreeNode = ReviewTreeFolderNode | ReviewTreeFileNode\n\ntype MutableReviewTreeFile = {\n file: UiReviewFile\n name: string\n depth: number\n}\n\ntype MutableReviewTreeFolder = {\n id: string\n name: string\n depth: number\n folders: Map<string, MutableReviewTreeFolder>\n files: MutableReviewTreeFile[]\n}\n\nconst reviewKey = computed(() => `${activeScope.value}:${workspaceView.value}`)\nconst currentReviewResult = computed(() => reviewResultsByKey.value[reviewKey.value] ?? null)\nconst selectedFile = computed(() => snapshot.value?.files.find((file) => file.id === selectedFileId.value) ?? snapshot.value?.files[0] ?? null)\nconst folderExpansionState = ref<Record<string, boolean>>({})\n\nconst headerTitle = computed(() => {\n if (!snapshot.value?.isGitRepo) return 'Repository review'\n if (activeScope.value === 'workspace') {\n return workspaceView.value === 'staged' ? 'Staged changes' : 'Workspace changes'\n }\n return snapshot.value?.baseBranch ? `Against ${snapshot.value.baseBranch}` : 'Base branch'\n})\n\nconst canRunReview = computed(() => (\n props.threadId.trim().length > 0\n && props.cwd.trim().length > 0\n && snapshot.value?.isGitRepo === true\n && !props.isThreadInProgress\n && !(activeScope.value === 'baseBranch' && !snapshot.value?.baseBranch)\n))\n\nconst showBulkActions = computed(() => (\n activeScope.value === 'workspace'\n && snapshot.value?.isGitRepo === true\n && snapshot.value.files.length > 0\n))\n\nconst showRowActions = computed(() => showBulkActions.value && !isApplyingAction.value)\n\nconst bulkActions = computed(() => {\n if (workspaceView.value === 'staged') {\n return [{ value: 'unstage' as UiReviewAction, label: 'Unstage all' }]\n }\n return [\n { value: 'stage' as UiReviewAction, label: 'Stage all' },\n { value: 'revert' as UiReviewAction, label: 'Revert all' },\n ]\n})\n\nconst fileActions = computed(() => {\n if (workspaceView.value === 'staged') {\n return [{ value: 'unstage' as UiReviewAction, label: 'Unstage file' }]\n }\n return [\n { value: 'stage' as UiReviewAction, label: 'Stage file' },\n { value: 'revert' as UiReviewAction, label: 'Revert file' },\n ]\n})\n\nconst hunkActions = computed(() => {\n if (workspaceView.value === 'staged') {\n return [{ value: 'unstage' as UiReviewAction, label: 'Unstage hunk' }]\n }\n return [\n { value: 'stage' as UiReviewAction, label: 'Stage hunk' },\n { value: 'revert' as UiReviewAction, label: 'Revert hunk' },\n ]\n})\n\nconst reviewBannerText = computed(() => (\n reviewError.value\n || snapshotError.value\n || reviewStatusLabel.value\n))\nconst reviewBannerIsError = computed(() => Boolean(reviewError.value || snapshotError.value))\nconst REVIEW_FILE_LIST_WIDTH_KEY = 'codex-web-local.review-pane-file-list-width.v1'\nconst MIN_FILE_LIST_WIDTH = 220\nconst MAX_FILE_LIST_WIDTH = 420\nconst DEFAULT_FILE_LIST_WIDTH = 288\nconst fileListWidth = ref(loadFileListWidth())\n\nconst reviewMainStyle = computed<Record<string, string>>(() => {\n const style: Record<string, string> = {}\n if (!isMobile.value) {\n style['--review-file-list-width'] = `${fileListWidth.value}px`\n }\n return style\n})\n\nconst fileTreeData = computed(() => buildVisibleFileTree(snapshot.value?.files ?? [], folderExpansionState.value))\nconst visibleFileTreeNodes = computed(() => fileTreeData.value.nodes)\nconst fileTreeFolderIdsByFileId = computed(() => fileTreeData.value.folderIdsByFileId)\n\nfunction clampFileListWidth(value: number): number {\n if (!Number.isFinite(value)) return DEFAULT_FILE_LIST_WIDTH\n return Math.min(MAX_FILE_LIST_WIDTH, Math.max(MIN_FILE_LIST_WIDTH, Math.round(value)))\n}\n\nfunction loadFileListWidth(): number {\n if (typeof window === 'undefined') return DEFAULT_FILE_LIST_WIDTH\n const raw = window.localStorage.getItem(REVIEW_FILE_LIST_WIDTH_KEY)\n const parsed = raw ? Number(raw) : Number.NaN\n return clampFileListWidth(parsed)\n}\n\nfunction persistFileListWidth(value: number): void {\n if (typeof window === 'undefined') return\n window.localStorage.setItem(REVIEW_FILE_LIST_WIDTH_KEY, String(clampFileListWidth(value)))\n}\n\nfunction onResizerPointerDown(event: PointerEvent): void {\n if (isMobile.value) return\n event.preventDefault()\n stopResizeTracking?.()\n const startX = event.clientX\n const startWidth = fileListWidth.value\n\n const handleMove = (moveEvent: PointerEvent) => {\n fileListWidth.value = clampFileListWidth(startWidth + (moveEvent.clientX - startX))\n }\n\n const cleanup = () => {\n window.removeEventListener('pointermove', handleMove)\n window.removeEventListener('pointerup', handleUp)\n stopResizeTracking = null\n }\n\n const handleUp = () => {\n cleanup()\n persistFileListWidth(fileListWidth.value)\n }\n\n window.addEventListener('pointermove', handleMove)\n window.addEventListener('pointerup', handleUp)\n stopResizeTracking = cleanup\n}\n\nfunction lineMarker(kind: string): string {\n if (kind === 'add') return '+'\n if (kind === 'remove') return '-'\n if (kind === 'hunk') return '@@'\n return ' '\n}\n\nfunction fileBaseName(path: string): string {\n const segments = path.split('/').filter(Boolean)\n return segments[segments.length - 1] ?? path\n}\n\nfunction sortTreeEntries(left: string, right: string): number {\n return left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' })\n}\n\nfunction buildVisibleFileTree(\n files: UiReviewFile[],\n expansionState: Record<string, boolean>,\n): {\n nodes: ReviewTreeNode[]\n folderIdsByFileId: Record<string, string[]>\n} {\n const root: MutableReviewTreeFolder = {\n id: '',\n name: '',\n depth: -1,\n folders: new Map(),\n files: [],\n }\n const folderIdsByFileId: Record<string, string[]> = {}\n\n for (const file of files) {\n const segments = file.path.split('/').filter(Boolean)\n const fileName = segments.pop() ?? file.path\n let currentFolder = root\n let folderPath = ''\n const parentFolderIds: string[] = []\n\n for (const [index, segment] of segments.entries()) {\n folderPath = folderPath ? `${folderPath}/${segment}` : segment\n let nextFolder = currentFolder.folders.get(segment)\n if (!nextFolder) {\n nextFolder = {\n id: folderPath,\n name: segment,\n depth: index,\n folders: new Map(),\n files: [],\n }\n currentFolder.folders.set(segment, nextFolder)\n }\n currentFolder = nextFolder\n parentFolderIds.push(nextFolder.id)\n }\n\n currentFolder.files.push({\n file,\n name: fileName,\n depth: segments.length,\n })\n folderIdsByFileId[file.id] = parentFolderIds\n }\n\n const nodes: ReviewTreeNode[] = []\n const countFiles = (folder: MutableReviewTreeFolder): number => (\n folder.files.length + Array.from(folder.folders.values()).reduce((sum, child) => sum + countFiles(child), 0)\n )\n\n const visitFolder = (folder: MutableReviewTreeFolder) => {\n const childFolders = Array.from(folder.folders.values()).sort((left, right) => sortTreeEntries(left.name, right.name))\n const childFiles = [...folder.files].sort((left, right) => sortTreeEntries(left.name, right.name))\n\n for (const childFolder of childFolders) {\n nodes.push({\n kind: 'folder',\n treeKey: `folder:${childFolder.id}`,\n id: childFolder.id,\n name: childFolder.name,\n depth: childFolder.depth,\n fileCount: countFiles(childFolder),\n })\n if (expansionState[childFolder.id] !== false) {\n visitFolder(childFolder)\n }\n }\n\n for (const childFile of childFiles) {\n nodes.push({\n kind: 'file',\n treeKey: `file:${childFile.file.id}`,\n id: childFile.file.id,\n name: childFile.name,\n depth: childFile.depth,\n file: childFile.file,\n })\n }\n }\n\n visitFolder(root)\n return { nodes, folderIdsByFileId }\n}\n\nfunction isFolderExpanded(folderId: string): boolean {\n return folderExpansionState.value[folderId] !== false\n}\n\nfunction toggleFolder(folderId: string): void {\n folderExpansionState.value = {\n ...folderExpansionState.value,\n [folderId]: !isFolderExpanded(folderId),\n }\n}\n\nfunction expandFileAncestors(fileId: string): void {\n const folderIds = fileTreeFolderIdsByFileId.value[fileId] ?? []\n if (folderIds.length === 0) return\n const nextState = { ...folderExpansionState.value }\n let changed = false\n for (const folderId of folderIds) {\n if (nextState[folderId] === false) {\n nextState[folderId] = true\n changed = true\n }\n }\n if (changed) {\n folderExpansionState.value = nextState\n }\n}\n\nfunction treeIndentStyle(depth: number): Record<string, string> {\n const base = isMobile.value ? 8 : 10\n const step = isMobile.value ? 12 : 14\n return {\n paddingLeft: `${base + (depth * step)}px`,\n }\n}\n\nfunction formatOperation(operation: string): string {\n if (operation === 'add') return 'Added'\n if (operation === 'delete') return 'Deleted'\n if (operation === 'rename') return 'Renamed'\n return 'Modified'\n}\n\nfunction bindHunkRef(hunkId: string, element: unknown): void {\n if (!(element instanceof HTMLElement)) {\n hunkRefs.delete(hunkId)\n return\n }\n hunkRefs.set(hunkId, element)\n}\n\nfunction extractNotificationThreadId(notification: RpcNotification): string {\n const params = notification.params !== null && typeof notification.params === 'object' && !Array.isArray(notification.params)\n ? notification.params as Record<string, unknown>\n : null\n return typeof params?.threadId === 'string' ? params.threadId : ''\n}\n\nasync function loadSnapshot(): Promise<void> {\n if (!props.cwd.trim()) return\n isLoadingSnapshot.value = true\n snapshotError.value = ''\n try {\n const desiredBaseBranch = activeScope.value === 'baseBranch' ? selectedBaseBranch.value.trim() : ''\n const nextSnapshot = await getReviewSnapshot(\n props.cwd,\n activeScope.value,\n workspaceView.value,\n desiredBaseBranch || null,\n )\n if (nextSnapshot.baseBranchOptions.length > 0) {\n const normalizedBaseBranch = nextSnapshot.baseBranch ?? nextSnapshot.baseBranchOptions[0] ?? ''\n if (selectedBaseBranch.value !== normalizedBaseBranch) {\n isSyncingBaseBranch.value = true\n selectedBaseBranch.value = normalizedBaseBranch\n }\n } else {\n if (selectedBaseBranch.value !== '') {\n isSyncingBaseBranch.value = true\n selectedBaseBranch.value = ''\n }\n }\n snapshot.value = nextSnapshot\n const hasSelectedFile = nextSnapshot.files.some((file) => file.id === selectedFileId.value)\n if (!hasSelectedFile) {\n selectedFileId.value = nextSnapshot.files[0]?.id ?? ''\n selectedHunkId.value = nextSnapshot.files[0]?.hunks[0]?.id ?? ''\n }\n } catch (error) {\n snapshotError.value = error instanceof Error ? error.message : 'Failed to load review snapshot'\n } finally {\n isLoadingSnapshot.value = false\n }\n}\n\nasync function loadLatestReviewResult(): Promise<void> {\n if (!props.threadId.trim()) return\n try {\n const reviewState = await getThreadReviewResult(props.threadId)\n if (reviewState.result) {\n reviewResultsByKey.value = {\n ...reviewResultsByKey.value,\n [reviewKey.value]: reviewState.result,\n }\n }\n } catch {\n // Keep the pane usable even if thread history refresh fails.\n }\n}\n\nasync function reloadAll(): Promise<void> {\n await Promise.all([\n loadSnapshot(),\n loadLatestReviewResult(),\n ])\n}\n\nfunction selectFile(fileId: string): void {\n expandFileAncestors(fileId)\n selectedFileId.value = fileId\n const file = snapshot.value?.files.find((entry) => entry.id === fileId) ?? null\n selectedHunkId.value = file?.hunks[0]?.id ?? ''\n if (isMobile.value) {\n isFileSheetOpen.value = false\n }\n}\n\nasync function applyAction(action: UiReviewAction, level: 'all' | 'file' | 'hunk', patch = ''): Promise<void> {\n if (!snapshot.value) return\n isApplyingAction.value = true\n reviewError.value = ''\n try {\n const nextSnapshot = await applyReviewAction({\n cwd: props.cwd,\n scope: activeScope.value,\n workspaceView: workspaceView.value,\n action,\n level,\n patch,\n })\n snapshot.value = nextSnapshot\n const hasSelectedFile = nextSnapshot.files.some((file) => file.id === selectedFileId.value)\n if (!hasSelectedFile) {\n selectedFileId.value = nextSnapshot.files[0]?.id ?? ''\n selectedHunkId.value = nextSnapshot.files[0]?.hunks[0]?.id ?? ''\n }\n } catch (error) {\n reviewError.value = error instanceof Error ? error.message : 'Failed to apply review action'\n } finally {\n isApplyingAction.value = false\n }\n}\n\nasync function applyBulkAction(action: UiReviewAction): Promise<void> {\n await applyAction(action, 'all')\n}\n\nasync function applyFileAction(action: UiReviewAction, file: UiReviewFile): Promise<void> {\n await applyAction(action, 'file', file.diff)\n}\n\nasync function applyHunkAction(action: UiReviewAction, hunk: UiReviewHunk): Promise<void> {\n selectedHunkId.value = hunk.id\n await applyAction(action, 'hunk', hunk.patch)\n}\n\nasync function initializeGit(): Promise<void> {\n if (!props.cwd.trim()) return\n isInitializingGit.value = true\n reviewError.value = ''\n try {\n await initializeReviewGit(props.cwd)\n await loadSnapshot()\n } catch (error) {\n reviewError.value = error instanceof Error ? error.message : 'Failed to initialize Git'\n } finally {\n isInitializingGit.value = false\n }\n}\n\nasync function runReview(): Promise<void> {\n if (!canRunReview.value || isRunningReview.value) return\n reviewError.value = ''\n reviewStatusLabel.value = activeScope.value === 'workspace'\n ? 'Reviewing current changes'\n : `Reviewing against ${snapshot.value?.baseBranch ?? 'base branch'}`\n isRunningReview.value = true\n pendingReviewKey.value = reviewKey.value\n\n try {\n await startThreadReview(\n props.threadId,\n activeScope.value,\n workspaceView.value,\n selectedBaseBranch.value || (snapshot.value?.baseBranch ?? null),\n )\n } catch (error) {\n isRunningReview.value = false\n reviewStatusLabel.value = ''\n reviewError.value = error instanceof Error ? error.message : 'Failed to start review'\n }\n}\n\nfunction formatFindingLocation(finding: UiReviewFinding): string {\n if (!finding.absolutePath) return ''\n const lineSuffix = finding.startLine ? `:${finding.startLine}${finding.endLine && finding.endLine !== finding.startLine ? `-${finding.endLine}` : ''}` : ''\n return `${finding.absolutePath}${lineSuffix}`\n}\n\nfunction findMatchingHunk(file: UiReviewFile, finding: UiReviewFinding): UiReviewHunk | null {\n if (!finding.startLine) return file.hunks[0] ?? null\n for (const hunk of file.hunks) {\n if (hunk.newStart !== null) {\n const newEnd = hunk.newStart + Math.max(hunk.newLineCount, 1) - 1\n if (finding.startLine >= hunk.newStart && finding.startLine <= newEnd) {\n return hunk\n }\n }\n if (hunk.oldStart !== null) {\n const oldEnd = hunk.oldStart + Math.max(hunk.oldLineCount, 1) - 1\n if (finding.startLine >= hunk.oldStart && finding.startLine <= oldEnd) {\n return hunk\n }\n }\n }\n return file.hunks[0] ?? null\n}\n\nasync function scrollToHunk(hunkId: string): Promise<void> {\n await nextTick()\n const element = hunkRefs.get(hunkId)\n element?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n}\n\nasync function openFinding(finding: UiReviewFinding): Promise<void> {\n activeTab.value = 'changes'\n const file = snapshot.value?.files.find((entry) => (\n entry.absolutePath === finding.absolutePath\n || entry.previousAbsolutePath === finding.absolutePath\n )) ?? null\n if (!file) return\n\n expandFileAncestors(file.id)\n selectedFileId.value = file.id\n const matchedHunk = findMatchingHunk(file, finding)\n selectedHunkId.value = matchedHunk?.id ?? ''\n if (matchedHunk?.id) {\n await scrollToHunk(matchedHunk.id)\n }\n}\n\nfunction handleNotification(notification: RpcNotification): void {\n if (extractNotificationThreadId(notification) !== props.threadId) return\n const params = notification.params !== null && typeof notification.params === 'object' && !Array.isArray(notification.params)\n ? notification.params as Record<string, unknown>\n : null\n const item = params?.item !== null && typeof params?.item === 'object' && !Array.isArray(params.item)\n ? params.item as Record<string, unknown>\n : null\n const itemType = typeof item?.type === 'string' ? item.type : ''\n\n if (notification.method === 'item/started' && itemType === 'enteredReviewMode') {\n isRunningReview.value = true\n reviewStatusLabel.value = typeof item?.review === 'string' ? item.review : 'Review in progress'\n return\n }\n\n if (notification.method === 'item/completed' && itemType === 'exitedReviewMode') {\n const targetKey = pendingReviewKey.value || reviewKey.value\n isRunningReview.value = false\n reviewStatusLabel.value = ''\n void getThreadReviewResult(props.threadId)\n .then((reviewState) => {\n if (!reviewState.result) return\n reviewResultsByKey.value = {\n ...reviewResultsByKey.value,\n [targetKey]: reviewState.result,\n }\n activeTab.value = 'findings'\n })\n .catch((error) => {\n reviewError.value = error instanceof Error ? error.message : 'Failed to load review result'\n })\n .finally(() => {\n pendingReviewKey.value = ''\n })\n }\n}\n\nwatch(\n () => [props.threadId, props.cwd] as const,\n () => {\n selectedFileId.value = ''\n selectedHunkId.value = ''\n reviewResultsByKey.value = {}\n pendingReviewKey.value = ''\n reviewError.value = ''\n reviewStatusLabel.value = ''\n void reloadAll()\n },\n { immediate: true },\n)\n\nwatch(\n () => [activeScope.value, workspaceView.value] as const,\n () => {\n selectedFileId.value = ''\n selectedHunkId.value = ''\n reviewError.value = ''\n snapshotError.value = ''\n void loadSnapshot()\n },\n)\n\nwatch(selectedBaseBranch, (branch, previous) => {\n if (isSyncingBaseBranch.value) {\n isSyncingBaseBranch.value = false\n return\n }\n if (activeScope.value !== 'baseBranch') return\n if (!branch || branch === previous) return\n void loadSnapshot()\n})\n\nwatch(selectedFile, (file) => {\n if (!file) return\n expandFileAncestors(file.id)\n if (!file.hunks.some((hunk) => hunk.id === selectedHunkId.value)) {\n selectedHunkId.value = file.hunks[0]?.id ?? ''\n }\n})\n\nwatch(selectedHunkId, (hunkId) => {\n if (!hunkId) return\n void scrollToHunk(hunkId)\n})\n\nonMounted(() => {\n stopNotifications = subscribeCodexNotifications(handleNotification)\n})\n\nonBeforeUnmount(() => {\n if (stopNotifications) {\n stopNotifications()\n stopNotifications = null\n }\n stopResizeTracking?.()\n})\n</script>\n\n<style scoped>\n@reference \"tailwindcss\";\n\n.review-pane {\n @apply flex h-full min-h-0 min-w-0 flex-col overflow-hidden rounded-2xl border border-zinc-200 bg-white;\n}\n\n.review-pane.is-mobile {\n @apply fixed inset-0 z-40 rounded-none border-0;\n}\n\n.review-pane-header {\n @apply flex items-start justify-between gap-3 border-b border-zinc-200 px-3 py-2.5;\n}\n\n.review-pane-heading {\n @apply min-w-0;\n}\n\n.review-pane-eyebrow {\n @apply m-0 text-[11px] uppercase tracking-[0.12em] text-zinc-400;\n}\n\n.review-pane-title {\n @apply m-0 truncate text-sm font-medium text-zinc-900;\n}\n\n.review-pane-header-actions {\n @apply flex items-center gap-2;\n}\n\n.review-pane-close,\n.review-pane-mobile-files-button,\n.review-pane-refresh,\n.review-pane-run,\n.review-pane-bulk-button,\n.review-pane-row-button,\n.review-pane-primary-cta {\n @apply rounded-full border border-zinc-200 bg-white px-2.5 py-1.25 text-[11px] text-zinc-700 transition hover:bg-zinc-50 disabled:cursor-default disabled:opacity-50;\n}\n\n.review-pane-close {\n @apply flex h-7.5 w-7.5 items-center justify-center rounded-full p-0;\n}\n\n.review-pane-toolbar {\n @apply flex flex-col gap-2 border-b border-zinc-100 px-3 py-2.5;\n}\n\n.review-pane-toolbar-tabs {\n @apply min-w-0;\n}\n\n.review-pane-toolbar-controls {\n @apply flex flex-wrap items-center gap-2;\n}\n\n.review-pane-control-cluster {\n @apply flex min-w-0 items-center gap-1.5;\n}\n\n.review-pane-control-label {\n @apply shrink-0 text-[10px] font-medium uppercase tracking-[0.08em] text-zinc-400;\n}\n\n.review-pane-branch-select-wrap {\n @apply inline-flex min-w-[9rem] items-center rounded-full border border-zinc-200 bg-white px-2.5 py-1 shadow-sm;\n}\n\n.review-pane-branch-select {\n @apply w-full appearance-none bg-transparent text-[11px] font-medium text-zinc-700 outline-none;\n}\n\n.review-pane-segmented {\n @apply inline-flex min-w-0 items-center gap-1 rounded-full bg-zinc-100 p-1;\n}\n\n.review-pane-segmented-primary {\n @apply flex-1 bg-zinc-100/80;\n}\n\n.review-pane-segmented-button {\n @apply relative min-w-0 rounded-full border border-transparent px-2.5 py-1.25 text-[11px] font-medium text-zinc-500 transition-colors;\n}\n\n.review-pane-segmented-button::before {\n content: '';\n @apply mr-1.5 inline-block h-1.5 w-1.5 rounded-full bg-zinc-300 align-middle transition-colors;\n}\n\n.review-pane-segmented-button[data-active='true'] {\n @apply border-sky-200 bg-sky-600 text-white shadow-sm;\n}\n\n.review-pane-segmented-button[data-active='true']::before {\n @apply bg-white;\n}\n\n.review-pane-segmented-button:disabled {\n @apply opacity-45;\n}\n\n.review-pane-toolbar-actions {\n @apply flex shrink-0 items-center gap-1.5;\n}\n\n.review-pane-run {\n @apply border-emerald-600 bg-emerald-600 text-white hover:bg-emerald-700;\n}\n\n.review-pane-refresh {\n @apply border-amber-300 bg-amber-50 text-amber-900 hover:bg-amber-100;\n}\n\n.review-pane-banner {\n @apply mx-3 mt-2.5 rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800;\n}\n\n.review-pane-banner.is-error {\n @apply border-rose-200 bg-rose-50 text-rose-700;\n}\n\n.review-pane-meta {\n @apply flex flex-wrap items-center gap-1.5 px-3 pt-2.5 text-[11px] text-zinc-500;\n}\n\n.review-pane-meta span {\n @apply rounded-full bg-zinc-100 px-2 py-1;\n}\n\n.review-pane-summary-pill.review-pane-summary-pill-add {\n @apply bg-emerald-100 text-emerald-700;\n}\n\n.review-pane-summary-pill.review-pane-summary-pill-remove {\n @apply bg-rose-100 text-rose-700;\n}\n\n.review-pane-content,\n.review-pane-findings {\n @apply min-h-0 flex-1 overflow-hidden;\n}\n\n.review-pane-bulk-actions {\n @apply flex flex-nowrap gap-1.5 overflow-x-auto border-b border-zinc-100 px-3 py-2.5;\n}\n\n.review-pane-main {\n @apply grid h-full min-h-0 grid-cols-[var(--review-file-list-width,18rem)_0.5rem_minmax(0,1fr)];\n}\n\n.review-pane-file-list {\n @apply hidden min-w-0 overflow-y-auto border-r border-zinc-100 bg-zinc-50/60 p-2 md:flex md:flex-col md:gap-1.5;\n}\n\n.review-pane-tree-folder {\n @apply flex w-full items-center gap-1 rounded-lg border border-transparent px-2 py-1.5 text-left text-[12px] font-medium text-zinc-600 transition hover:bg-white hover:text-zinc-900;\n}\n\n.review-pane-tree-folder[data-expanded='false'] {\n @apply text-zinc-500;\n}\n\n.review-pane-tree-folder-sheet {\n @apply rounded-md bg-zinc-50/80;\n}\n\n.review-pane-tree-caret {\n @apply relative h-3.5 w-3.5 shrink-0;\n}\n\n.review-pane-tree-caret::before {\n content: '';\n @apply absolute left-1 top-1 h-0 w-0 border-y-[4px] border-l-[5px] border-y-transparent border-l-current transition-transform;\n}\n\n.review-pane-tree-caret[data-expanded='true']::before {\n transform: rotate(90deg);\n transform-origin: 2px 4px;\n}\n\n.review-pane-tree-folder-name {\n @apply min-w-0 flex-1 truncate;\n}\n\n.review-pane-tree-folder-count {\n @apply shrink-0 rounded-full bg-zinc-200 px-1.5 py-0.5 text-[10px] font-medium text-zinc-500;\n}\n\n.review-pane-resizer {\n @apply relative hidden cursor-col-resize bg-zinc-100 md:block;\n}\n\n.review-pane-resizer::before {\n content: '';\n @apply absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-zinc-300 transition-colors;\n}\n\n.review-pane-resizer:hover::before {\n @apply bg-sky-500;\n}\n\n.review-pane-file,\n.review-pane-finding {\n @apply flex w-full flex-col gap-0.75 rounded-xl border border-transparent px-2.5 py-2 text-left transition hover:border-zinc-200 hover:bg-white;\n}\n\n.review-pane-tree-file {\n @apply rounded-lg px-2 py-1.75;\n}\n\n.review-pane-file-meta-row {\n @apply flex items-start justify-between gap-2;\n}\n\n.review-pane-file-main {\n @apply flex min-w-0 items-center gap-1.5;\n}\n\n.review-pane-file[data-active='true'] {\n @apply border-zinc-300 bg-white shadow-sm;\n}\n\n.review-pane-file-op {\n @apply inline-flex w-fit rounded-full px-2 py-0.5 text-[10px] font-medium uppercase tracking-[0.08em];\n}\n\n.review-pane-file-op[data-operation='add'] {\n @apply bg-emerald-100 text-emerald-800;\n}\n\n.review-pane-file-op[data-operation='delete'] {\n @apply bg-rose-100 text-rose-700;\n}\n\n.review-pane-file-op[data-operation='rename'] {\n @apply bg-sky-100 text-sky-700;\n}\n\n.review-pane-file-op[data-operation='update'] {\n @apply bg-amber-100 text-amber-800;\n}\n\n.review-pane-file-path {\n @apply min-w-0 break-all text-sm text-zinc-800;\n}\n\n.review-pane-file-delta {\n @apply inline-flex shrink-0 items-center gap-1 whitespace-nowrap text-[11px];\n}\n\n.review-pane-delta-add {\n @apply text-emerald-600;\n}\n\n.review-pane-delta-remove {\n @apply text-rose-600;\n}\n\n.review-pane-delta-separator {\n @apply text-zinc-400;\n}\n\n.review-pane-diff {\n @apply min-h-0 overflow-y-auto px-3 py-3;\n}\n\n.review-pane-file-header,\n.review-pane-hunk {\n @apply rounded-2xl border border-zinc-200 bg-white;\n}\n\n.review-pane-file-header {\n @apply mb-3 flex flex-wrap items-start justify-between gap-2 px-3 py-2.5;\n}\n\n.review-pane-file-title {\n @apply m-0 break-all text-sm font-medium text-zinc-900;\n}\n\n.review-pane-file-subtitle,\n.review-pane-hunk-meta,\n.review-pane-finding-location {\n @apply m-0 text-[11px] text-zinc-500;\n}\n\n.review-pane-row-actions {\n @apply flex flex-wrap gap-1.5;\n}\n\n.review-pane-hunks {\n @apply flex flex-col gap-2.5;\n}\n\n.review-pane-hunk {\n @apply overflow-hidden;\n}\n\n.review-pane-hunk[data-active='true'] {\n @apply border-zinc-400 shadow-[0_0_0_1px_rgba(24,24,27,0.08)];\n}\n\n.review-pane-hunk-header {\n @apply flex flex-wrap items-start justify-between gap-2 border-b border-zinc-100 bg-zinc-50/70 px-3 py-2.5;\n}\n\n.review-pane-hunk-title {\n @apply m-0 font-mono text-xs text-zinc-800;\n}\n\n.review-pane-lines {\n @apply overflow-x-auto bg-zinc-950 px-0 py-0 font-mono text-xs text-zinc-100;\n}\n\n.review-pane-line {\n @apply grid min-w-max grid-cols-[3.5rem_3.5rem_1.5rem_minmax(0,1fr)] gap-0;\n}\n\n.review-pane-line-number {\n @apply px-2.5 py-1 text-right text-zinc-500;\n}\n\n.review-pane-line-marker {\n @apply px-2 py-1 text-center text-zinc-500;\n}\n\n.review-pane-line-code {\n @apply block px-2.5 py-1 whitespace-pre-wrap break-all;\n}\n\n.review-pane-line[data-kind='add'] {\n @apply bg-emerald-950/60 text-emerald-100;\n}\n\n.review-pane-line[data-kind='remove'] {\n @apply bg-rose-950/60 text-rose-100;\n}\n\n.review-pane-line[data-kind='add'] .review-pane-line-marker,\n.review-pane-line[data-kind='add'] .review-pane-line-code {\n @apply text-emerald-300;\n}\n\n.review-pane-line[data-kind='remove'] .review-pane-line-marker,\n.review-pane-line[data-kind='remove'] .review-pane-line-code {\n @apply text-rose-300;\n}\n\n.review-pane-line[data-kind='hunk'] {\n @apply bg-sky-950/70 text-sky-200;\n}\n\n.review-pane-line[data-kind='meta'] {\n @apply bg-zinc-900 text-zinc-400;\n}\n\n.review-pane-raw-diff {\n @apply overflow-x-auto rounded-2xl border border-zinc-200 bg-zinc-950 p-3 text-xs text-zinc-100;\n}\n\n.review-pane-raw-diff pre,\n.review-pane-summary-text {\n @apply m-0 whitespace-pre-wrap break-all font-mono;\n}\n\n.review-pane-summary-card {\n @apply mx-3 mt-3 rounded-2xl border border-zinc-200 bg-zinc-50 px-3 py-2.5;\n}\n\n.review-pane-summary-title {\n @apply m-0 mb-2 text-sm font-medium text-zinc-900;\n}\n\n.review-pane-findings-list {\n @apply flex h-full flex-col gap-2.5 overflow-y-auto px-3 py-3;\n}\n\n.review-pane-finding {\n @apply border-zinc-200 bg-white hover:border-zinc-300;\n}\n\n.review-pane-finding-title {\n @apply text-sm font-medium text-zinc-900;\n}\n\n.review-pane-finding-body {\n @apply text-sm text-zinc-600 whitespace-pre-wrap;\n}\n\n.review-pane-empty {\n @apply flex h-full min-h-0 flex-col items-center justify-center px-6 text-center;\n}\n\n.review-pane-empty-title {\n @apply m-0 text-sm font-medium text-zinc-900;\n}\n\n.review-pane-empty-text {\n @apply mt-2 max-w-sm text-sm text-zinc-500;\n}\n\n.review-pane-primary-cta {\n @apply mt-4 border-emerald-600 bg-emerald-600 text-white hover:bg-emerald-700;\n}\n\n.review-pane-sheet-backdrop {\n @apply fixed inset-0 z-50 bg-black/30;\n}\n\n.review-pane-sheet {\n @apply absolute inset-x-0 bottom-0 rounded-t-3xl bg-white px-4 pb-6 pt-3 shadow-2xl;\n}\n\n.review-pane-sheet-handle {\n @apply mx-auto mb-3 h-1.5 w-12 rounded-full bg-zinc-300;\n}\n\n.review-pane-sheet-header {\n @apply mb-3 flex items-center justify-between;\n}\n\n.review-pane-sheet-title {\n @apply m-0 text-sm font-medium text-zinc-900;\n}\n\n.review-pane-sheet-count {\n @apply m-0 rounded-full bg-zinc-100 px-2 py-1 text-[11px] text-zinc-500;\n}\n\n.review-pane-sheet-list {\n @apply flex max-h-[60vh] flex-col gap-2 overflow-y-auto pb-3;\n}\n\n.review-pane-sheet-enter-active,\n.review-pane-sheet-leave-active {\n transition: opacity 160ms ease;\n}\n\n.review-pane-sheet-enter-active .review-pane-sheet,\n.review-pane-sheet-leave-active .review-pane-sheet {\n transition: transform 200ms ease;\n}\n\n.review-pane-sheet-enter-from,\n.review-pane-sheet-leave-to {\n opacity: 0;\n}\n\n.review-pane-sheet-enter-from .review-pane-sheet,\n.review-pane-sheet-leave-to .review-pane-sheet {\n transform: translateY(16px);\n}\n\n@media (max-width: 767px) {\n .review-pane-header {\n @apply px-3 py-2;\n }\n\n .review-pane-eyebrow {\n @apply text-[10px];\n }\n\n .review-pane-title {\n @apply text-xs leading-5;\n }\n\n .review-pane-header-actions {\n @apply gap-1.5;\n }\n\n .review-pane-close,\n .review-pane-mobile-files-button,\n .review-pane-refresh,\n .review-pane-run {\n @apply px-2.5 py-1 text-[12px];\n }\n\n .review-pane-close {\n @apply h-7 w-7;\n }\n\n .review-pane-toolbar {\n @apply gap-1.5 px-3 py-2;\n }\n\n .review-pane-toolbar-controls {\n @apply grid grid-cols-1 gap-1.5;\n }\n\n .review-pane-control-cluster {\n @apply gap-1;\n }\n\n .review-pane-control-label {\n @apply text-[9px];\n }\n\n .review-pane-branch-select-wrap {\n @apply min-w-0 flex-1 px-2 py-0.75;\n }\n\n .review-pane-branch-select {\n @apply text-[12px];\n }\n\n .review-pane-segmented {\n @apply w-full justify-between gap-1 p-0.75;\n }\n\n .review-pane-segmented-button {\n @apply flex-1 px-2 py-1 text-[12px];\n }\n\n .review-pane-toolbar-actions {\n @apply w-auto gap-1;\n }\n\n .review-pane-refresh,\n .review-pane-run {\n @apply px-2.5 py-1 text-[12px];\n }\n\n .review-pane-banner {\n @apply mx-3 mt-2 px-2.5 py-1.5 text-xs;\n }\n\n .review-pane-meta {\n @apply gap-1 px-3 pt-2;\n }\n\n .review-pane-meta span {\n @apply px-1.75 py-0.75 text-[11px];\n }\n\n .review-pane-bulk-actions {\n @apply gap-1 px-3 py-2;\n }\n\n .review-pane-bulk-button {\n @apply px-2.5 py-1 text-[12px];\n }\n\n .review-pane-main {\n @apply block;\n }\n\n .review-pane-resizer {\n @apply hidden;\n }\n\n .review-pane-diff {\n @apply px-2 py-2.5;\n }\n\n .review-pane-file-header,\n .review-pane-hunk-header {\n @apply px-2.5 py-2;\n }\n\n .review-pane-sheet {\n @apply px-3 pb-4 pt-2.5;\n }\n\n .review-pane-sheet-handle {\n @apply mb-2 h-1.25 w-11;\n }\n\n .review-pane-sheet-header {\n @apply mb-2;\n }\n\n .review-pane-sheet-title {\n @apply text-xs;\n }\n\n .review-pane-sheet-count {\n @apply px-1.5 py-0.75 text-[10px];\n }\n\n .review-pane-sheet-list {\n @apply gap-1.5 pb-2;\n }\n\n .review-pane-sheet-list .review-pane-tree-folder {\n @apply gap-1 rounded-md px-2 py-1 text-[12px];\n }\n\n .review-pane-sheet-list .review-pane-tree-folder-count {\n @apply px-1.25 py-0.25 text-[9px];\n }\n\n .review-pane-sheet-list .review-pane-file {\n @apply gap-0.5 rounded-lg px-2 py-1.5;\n }\n\n .review-pane-sheet-list .review-pane-file-meta-row {\n @apply gap-1.5;\n }\n\n .review-pane-sheet-list .review-pane-file-op {\n @apply px-1.5 py-0.25 text-[9px];\n }\n\n .review-pane-sheet-list .review-pane-file-main {\n @apply gap-1;\n }\n\n .review-pane-sheet-list .review-pane-file-path {\n @apply text-[13px] leading-5;\n }\n\n .review-pane-sheet-list .review-pane-file-delta {\n @apply text-[11px];\n }\n}\n\n@media (min-width: 768px) {\n .review-pane-toolbar {\n @apply flex-row items-center gap-2.5;\n }\n\n .review-pane-toolbar-tabs {\n @apply flex-1;\n }\n\n .review-pane-toolbar-controls {\n @apply min-w-0 flex-nowrap;\n }\n\n .review-pane-control-cluster {\n @apply shrink-0;\n }\n\n .review-pane-toolbar-actions {\n @apply ml-auto;\n }\n}\n</style>\n"],"names":["REVIEW_FILE_LIST_WIDTH_KEY","MIN_FILE_LIST_WIDTH","MAX_FILE_LIST_WIDTH","DEFAULT_FILE_LIST_WIDTH","props","__props","isMobile","useMobile","activeTab","ref","activeScope","workspaceView","snapshot","selectedBaseBranch","isSyncingBaseBranch","selectedFileId","selectedHunkId","isFileSheetOpen","isLoadingSnapshot","isApplyingAction","isRunningReview","isInitializingGit","snapshotError","reviewError","reviewStatusLabel","reviewResultsByKey","pendingReviewKey","hunkRefs","stopNotifications","stopResizeTracking","reviewTabs","reviewKey","computed","currentReviewResult","selectedFile","_a","file","_b","folderExpansionState","headerTitle","canRunReview","showBulkActions","showRowActions","bulkActions","fileActions","hunkActions","reviewBannerText","reviewBannerIsError","fileListWidth","loadFileListWidth","reviewMainStyle","style","fileTreeData","buildVisibleFileTree","visibleFileTreeNodes","fileTreeFolderIdsByFileId","clampFileListWidth","value","raw","parsed","persistFileListWidth","onResizerPointerDown","event","startX","startWidth","handleMove","moveEvent","cleanup","handleUp","lineMarker","kind","fileBaseName","path","segments","sortTreeEntries","left","right","files","expansionState","root","folderIdsByFileId","fileName","currentFolder","folderPath","parentFolderIds","index","segment","nextFolder","nodes","countFiles","folder","sum","child","visitFolder","childFolders","childFiles","childFolder","childFile","isFolderExpanded","folderId","toggleFolder","expandFileAncestors","fileId","folderIds","nextState","changed","treeIndentStyle","depth","base","step","formatOperation","operation","bindHunkRef","hunkId","element","extractNotificationThreadId","notification","params","loadSnapshot","desiredBaseBranch","nextSnapshot","getReviewSnapshot","normalizedBaseBranch","_c","error","loadLatestReviewResult","reviewState","getThreadReviewResult","reloadAll","selectFile","entry","applyAction","action","level","patch","applyReviewAction","applyBulkAction","applyFileAction","applyHunkAction","hunk","initializeGit","initializeReviewGit","runReview","startThreadReview","formatFindingLocation","finding","lineSuffix","findMatchingHunk","newEnd","oldEnd","scrollToHunk","nextTick","openFinding","matchedHunk","handleNotification","item","itemType","targetKey","watch","branch","previous","onMounted","subscribeCodexNotifications","onBeforeUnmount","_createElementBlock","_normalizeClass","_unref","_createElementVNode","_hoisted_1","_hoisted_2","_cache","_hoisted_3","_toDisplayString","_hoisted_4","$emit","_createVNode","IconTablerX","_hoisted_5","_hoisted_6","_hoisted_7","_Fragment","_renderList","tab","$event","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_openBlock","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29","_hoisted_33","_hoisted_34","_hoisted_35","_hoisted_38","node","_normalizeStyle","_hoisted_41","_hoisted_42","_hoisted_44","_hoisted_45","_hoisted_46","_hoisted_47","_createTextVNode","_hoisted_48","_hoisted_49","_hoisted_50","_hoisted_51","_hoisted_52","_hoisted_53","_hoisted_54","_hoisted_55","_hoisted_56","_hoisted_57","_hoisted_58","_hoisted_59","_hoisted_61","_hoisted_62","_hoisted_63","_hoisted_64","_hoisted_65","_hoisted_66","line","_hoisted_68","_hoisted_69","_hoisted_70","_hoisted_71","_hoisted_36","_hoisted_37","_hoisted_31","_hoisted_32","_hoisted_30","_hoisted_72","_d","_hoisted_73","_hoisted_74","_e","_hoisted_75","_hoisted_77","_hoisted_78","_hoisted_79","_hoisted_80","_hoisted_81","_f","_Transition","_hoisted_82","_hoisted_83","_hoisted_84","_hoisted_87","_hoisted_88","_hoisted_90","_hoisted_91","_hoisted_92","_hoisted_93","_hoisted_94","_hoisted_95","_hoisted_96"],"mappings":"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"}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index.esm-mbv_PYjX.js","assets/index.esm-BilMXo9u.js","assets/index.esm-DtVW_dfU.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{d as le,o as l,c as r,g as se,a as o,t as h,h as b,s as ae,m as J,K as Pe,y as S,_ as ne,w as Fe,i as te,q as Ae,L as Oe,r as p,z as me,b as Ue,v as i,x as ge,M as Ge,F as X,f as we,N as xe,B as He,O as Ne,P as De}from"./index-Tdn545FN.js";const Re={class:"skill-card-top"},je=["src","alt"],Ke={key:1,class:"skill-card-avatar-fallback"},Be={class:"skill-card-info"},Me={class:"skill-card-header"},Je={class:"skill-card-name"},Ve={key:0,class:"skill-card-badge-disabled"},ze={key:1,class:"skill-card-badge"},We={class:"skill-card-owner"},qe={key:0,class:"skill-card-desc"},Qe={key:1,class:"skill-card-date"},Ze=le({__name:"SkillCard",props:{skill:{}},emits:["select"],setup(t){const L=t,u=S(()=>{const k=L.skill.path;return k?k.endsWith("/SKILL.md")?k.slice(0,-9):k:""});function I(){const k=u.value;k&&window.open(`/codex-local-browse${encodeURI(k)}`,"_blank","noopener,noreferrer")}const f=S(()=>{const k=L.skill.publishedAt;if(!k)return"";const y=new Date(k),$=Date.now()-k;return $<36e5?`${Math.floor($/6e4)}m ago`:$<864e5?`${Math.floor($/36e5)}h ago`:$<2592e6?`${Math.floor($/864e5)}d ago`:y.toLocaleDateString("en-US",{month:"short",day:"numeric"})});function _(k){const y=k.target;y.style.display="none"}return(k,y)=>(l(),r("button",{class:se(["skill-card",{"is-disabled":t.skill.installed&&t.skill.enabled===!1}]),type:"button",onClick:y[0]||(y[0]=C=>k.$emit("select",t.skill))},[o("div",Re,[t.skill.avatarUrl?(l(),r("img",{key:0,class:"skill-card-avatar",src:t.skill.avatarUrl,alt:t.skill.owner,loading:"lazy",onError:_},null,40,je)):(l(),r("div",Ke,h(t.skill.owner.charAt(0)),1)),o("div",Be,[o("div",Me,[o("span",Je,h(t.skill.displayName||t.skill.name),1),t.skill.installed&&t.skill.enabled===!1?(l(),r("span",Ve,"Disabled")):t.skill.installed?(l(),r("span",ze,"Installed")):b("",!0)]),o("span",We,h(t.skill.owner),1)]),t.skill.installed&&u.value?(l(),r("button",{key:2,class:"skill-card-browse",type:"button",title:"Browse files",onClick:ae(I,["stop"])},[J(Pe,{class:"skill-card-browse-icon"})])):b("",!0)]),t.skill.description?(l(),r("p",qe,h(t.skill.description),1)):b("",!0),f.value?(l(),r("span",Qe,h(f.value),1)):b("",!0)],2))}}),Se=ne(Ze,[["__scopeId","data-v-49ed14e0"]]),Ye={class:"sdm-panel"},Xe={class:"sdm-header"},es={class:"sdm-title-area"},ss=["src","alt"],ts={class:"sdm-title-col"},ls={class:"sdm-title-row"},as={class:"sdm-title"},ns={key:0,class:"sdm-badge-disabled"},os={class:"sdm-owner"},is={class:"sdm-body"},rs={key:0,class:"sdm-desc"},cs={key:1,class:"sdm-readme-loading"},us=["innerHTML"],ds=["href"],hs={class:"sdm-footer"},ks={class:"sdm-footer-actions"},vs=["disabled"],ps=["disabled"],bs=["disabled"],fs=le({__name:"SkillDetailModal",props:{skill:{},visible:{type:Boolean},isInstalling:{type:Boolean},isUninstalling:{type:Boolean}},emits:["close","install","uninstall","toggle-enabled"],setup(t,{emit:L}){const u=t,I=L,f=p(null),_=p(""),k=p(""),y=p(!1),C=S(()=>f.value??u.skill.enabled??!0),$=S(()=>u.isInstalling===!0||u.isUninstalling===!0),T=S(()=>_.value||u.skill.description),P=S(()=>{const d=u.skill.path;return d?d.endsWith("/SKILL.md")?d.slice(0,-9):d:""}),G=S(()=>{const d=k.value;if(!d)return"";const g=d.replace(/^---[\s\S]*?---\s*/,"");return F(g)});function F(d){return d.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/^### (.+)$/gm,"<h4>$1</h4>").replace(/^## (.+)$/gm,"<h3>$1</h3>").replace(/^# (.+)$/gm,"<h2>$1</h2>").replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>").replace(/`([^`]+)`/g,"<code>$1</code>").replace(/^- (.+)$/gm,"<li>$1</li>").replace(/(<li>.*<\/li>\n?)+/g,"<ul>$&</ul>").replace(/\n{2,}/g,"<br/><br/>").replace(/\n/g,"<br/>")}async function A(){if(!(!u.skill.owner||!u.skill.name)){y.value=!0,_.value="",k.value="";try{const d=new URLSearchParams({owner:u.skill.owner,name:u.skill.name});u.skill.installed&&d.set("installed","true"),u.skill.path&&d.set("path",u.skill.path);const g=await fetch(`/codex-api/skills-hub/readme?${d}`);if(!g.ok)return;const E=await g.json();k.value=E.content??"",_.value=E.description??""}catch{}finally{y.value=!1}}}Fe(()=>u.visible,d=>{d&&(f.value=null,_.value="",k.value="",A())});function O(){I("install",u.skill)}function x(){I("uninstall",u.skill)}function a(){const d=!C.value;f.value=d,I("toggle-enabled",u.skill,d)}function c(){const d=P.value;d&&window.open(`/codex-local-browse${encodeURI(d)}`,"_blank","noopener,noreferrer")}return(d,g)=>(l(),te(Oe,{to:"body"},[t.visible?(l(),r("div",{key:0,class:"sdm-overlay",onClick:g[1]||(g[1]=ae(E=>d.$emit("close"),["self"]))},[o("div",Ye,[o("div",Xe,[o("div",es,[t.skill.avatarUrl?(l(),r("img",{key:0,class:"sdm-avatar",src:t.skill.avatarUrl,alt:t.skill.owner,loading:"lazy"},null,8,ss)):b("",!0),o("div",ts,[o("div",ls,[o("h3",as,h(t.skill.displayName||t.skill.name),1),t.skill.installed&&!C.value?(l(),r("span",ns,"Disabled")):b("",!0)]),o("span",os,h(t.skill.owner),1)])]),o("button",{class:"sdm-close",type:"button","aria-label":"Close",onClick:g[0]||(g[0]=E=>d.$emit("close"))},[J(Ae,{class:"sdm-close-icon"})])]),o("div",is,[T.value?(l(),r("p",rs,h(T.value),1)):b("",!0),y.value?(l(),r("div",cs,"Loading skill contents...")):k.value?(l(),r("div",{key:2,class:"sdm-readme",innerHTML:G.value},null,8,us)):b("",!0),o("a",{class:"sdm-link",href:t.skill.url,target:"_blank",rel:"noopener noreferrer"},"View on GitHub",8,ds)]),o("div",hs,[o("div",ks,[t.skill.installed?(l(),r("button",{key:0,class:"sdm-btn sdm-btn-danger",type:"button",disabled:$.value,onClick:x},h(u.isUninstalling?"Uninstalling...":"Uninstall"),9,vs)):(l(),r("button",{key:1,class:"sdm-btn sdm-btn-primary",type:"button",disabled:$.value,onClick:O},h(u.isInstalling?"Installing...":"Install"),9,ps)),t.skill.installed?(l(),r("button",{key:2,class:"sdm-btn sdm-btn-secondary",type:"button",disabled:$.value,onClick:a},h(C.value?"Disable":"Enable"),9,bs)):b("",!0),t.skill.installed&&P.value?(l(),r("button",{key:3,class:"sdm-btn sdm-btn-secondary",type:"button",onClick:c}," Browse files ")):b("",!0)])])])])):b("",!0)]))}}),ys=ne(fs,[["__scopeId","data-v-c758a719"]]),ms={apiKey:"AIzaSyAf0CIHBZ-wEQJ8CCUUWo1Wl9P7typ_ZPI",authDomain:"gptcall-416910.firebaseapp.com",projectId:"gptcall-416910",storageBucket:"gptcall-416910.appspot.com",messagingSenderId:"99275526699",appId:"1:99275526699:web:3b623e1e2996108b52106e"};let ee=null;function gs(){return ee||(ee=Promise.all([me(()=>import("./index.esm-mbv_PYjX.js"),__vite__mapDeps([0,1])),me(()=>import("./index.esm-DtVW_dfU.js"),__vite__mapDeps([2,1]))])),ee}function ws(t){const L=p(null),u=p(""),I=p(""),f=p(""),_=p({loggedIn:!1,githubUsername:"",repoOwner:"",repoName:"",configured:!1,startup:{inProgress:!1,mode:"idle",branch:"main",lastAction:"not-started",lastRunAtIso:"",lastSuccessAtIso:"",lastError:""}}),k=S(()=>f.value==="pull"),y=S(()=>f.value==="push"),C=S(()=>f.value==="startup-sync"),$=S(()=>f.value!=="");async function T(){try{const a=await fetch("/codex-api/skills-sync/status");if(!a.ok)return;const c=await a.json();c.data&&(_.value=c.data)}catch{}}async function P(){try{const a=await fetch("/codex-api/skills-sync/github/start-login",{method:"POST"}),c=await a.json();if(!a.ok||!c.data)throw new Error("Failed to start GitHub login");L.value=c.data;const d=30,g=Math.max((c.data.interval??5)*1e3,3e3);let E=!1;for(let D=0;D<d;D++){await new Promise(j=>setTimeout(j,g));const H=await fetch("/codex-api/skills-sync/github/complete-login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({deviceCode:c.data.device_code})}),N=await H.json();if(!H.ok)throw new Error(N.error||"Failed to complete GitHub login");if(N.ok){E=!0;break}if(!N.pending)throw new Error(N.error||"Failed to complete GitHub login")}if(!E)throw new Error("GitHub login timed out. Please retry.");L.value=null,await T(),t.showToast("GitHub login successful")}catch(a){t.showToast(a instanceof Error?a.message:"Failed GitHub login","error")}}async function G(){try{const[a,c]=await gs(),{getApp:d,getApps:g,initializeApp:E}=a,{getAuth:D,GithubAuthProvider:H,signInWithPopup:N}=c,j=g().length>0?d():E(ms),V=D(j),R=new H;R.addScope("repo");const U=await N(V,R),K=H.credentialFromResult(U),z=(K==null?void 0:K.accessToken)??"";if(!z)throw new Error("GitHub access token missing from Firebase login");const W=await fetch("/codex-api/skills-sync/github/token-login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:z})}),q=await W.json();if(!W.ok||!q.ok)throw new Error(q.error||"Failed to login with GitHub token");await T(),t.showToast("GitHub login successful")}catch(a){const c=a instanceof Error?a.message:"Failed Firebase GitHub login";t.showToast(c,"error")}}async function F(){I.value="",u.value="pull-started",f.value="pull";try{const a=await fetch("/codex-api/skills-sync/pull",{method:"POST"}),c=await a.json();if(!a.ok||!c.ok)throw new Error(c.error||"Failed to pull synced skills");await t.onPulled(),u.value="pull-success",t.showToast(_.value.loggedIn?"Pulled skills from private sync repo":"Pulled skills from upstream repo")}catch(a){const c=a instanceof Error?a.message:"Failed to pull sync";I.value=c,u.value="pull-failed",t.showToast(c,"error")}finally{f.value=""}}async function A(){I.value="",u.value="push-started",f.value="push";try{const a=await fetch("/codex-api/skills-sync/push",{method:"POST"}),c=await a.json();if(!a.ok||!c.ok)throw new Error(c.error||"Failed to push synced skills");u.value="push-success",t.showToast("Pushed skills to private sync repo")}catch(a){const c=a instanceof Error?a.message:"Failed to push sync";I.value=c,u.value="push-failed",t.showToast(c,"error")}finally{f.value=""}}async function O(){I.value="",u.value="startup-sync-started",f.value="startup-sync";try{const a=await fetch("/codex-api/skills-sync/startup-sync",{method:"POST"}),c=await a.json();if(!a.ok||!c.ok)throw new Error(c.error||"Failed to run startup sync");await t.onPulled(),await T(),u.value="startup-sync-success",t.showToast("Startup sync completed")}catch(a){const c=a instanceof Error?a.message:"Failed startup sync";I.value=c,u.value="startup-sync-failed",t.showToast(c,"error")}finally{f.value=""}}async function x(){try{const a=await fetch("/codex-api/skills-sync/github/logout",{method:"POST"}),c=await a.json();if(!a.ok||!c.ok)throw new Error(c.error||"Failed to logout GitHub");await T(),t.showToast("Logged out from GitHub")}catch(a){t.showToast(a instanceof Error?a.message:"Failed to logout GitHub","error")}}return{deviceLogin:L,isPullInFlight:k,isPushInFlight:y,isStartupSyncInFlight:C,isSyncActionInFlight:$,loadSyncStatus:T,logoutGithub:x,pullSkillsSync:F,pushSkillsSync:A,startupSkillsSync:O,startGithubFirebaseLogin:G,startGithubLogin:P,syncActionError:I,syncActionStatus:u,syncStatus:_}}const Ss={class:"skills-hub"},_s={class:"skills-sync-panel"},$s={class:"skills-sync-header"},Is=["href"],Cs={key:1,class:"skills-sync-badge"},Ts={key:2,class:"skills-sync-badge"},Es={class:"skills-sync-meta"},Ls={key:0,class:"skills-sync-error"},Ps={key:1,class:"skills-sync-meta"},Fs={key:2,class:"skills-sync-error"},As={key:3,class:"skills-sync-device"},Os=["href"],Us={class:"skills-sync-actions"},Gs=["disabled"],xs=["disabled"],Hs=["disabled"],Ns=["disabled"],Ds={key:1,class:"skills-hub-section"},Rs={class:"skills-hub-section-title"},js={key:0,class:"skills-hub-grid"},Ks={class:"skills-hub-toolbar"},Bs={class:"skills-hub-search-wrap"},Ms=["onKeyup"],Js={key:0,class:"skills-hub-count"},Vs={class:"skills-hub-section"},zs={key:0,class:"skills-hub-loading"},Ws={key:1,class:"skills-hub-error"},qs={key:0,class:"skills-hub-grid"},Qs={key:1,class:"skills-hub-empty"},Z="codex-web-local.skills-hub.cache.v1",Zs=le({__name:"SkillsHub",emits:["skills-changed"],setup(t,{emit:L}){const u={name:"",owner:"",description:"",url:"",installed:!1},I=p(null),f=p(""),_=p(""),k=p("date"),y=p([]),C=p([]),$=p(0),T=p(!1),P=p(""),G=p(!0),F=p(!1),A=p(u),O=p(null),x=p(""),a=p(!1),c=p(!1);let d=null;const g=L,E=S(()=>k.value==="date"?"Newest":"A-Z"),D=S(()=>{var s;return((s=O.value)==null?void 0:s.type)==="error"?"skills-hub-toast-error":"skills-hub-toast-success"}),H=S(()=>`${A.value.owner}/${A.value.name}`),N=S(()=>a.value&&x.value===H.value),j=S(()=>c.value&&x.value===H.value),V=S(()=>{if(!w.value.configured)return"";const s=w.value.repoOwner.trim(),e=w.value.repoName.trim();return!s||!e?"":`https://github.com/${encodeURIComponent(s)}/${encodeURIComponent(e)}`}),R=S(()=>{const s=f.value.toLowerCase().trim();return s?C.value.filter(e=>e.name.toLowerCase().includes(s)||e.owner.toLowerCase().includes(s)||(e.displayName??"").toLowerCase().includes(s)):C.value});function U(s,e="success"){O.value={text:s,type:e},d&&clearTimeout(d),d=setTimeout(()=>{O.value=null},3e3)}function K(){k.value=k.value==="date"?"name":"date",B(_.value)}function z(s){return`${k.value}::${s.trim().toLowerCase()}`}function W(s){var e;if(typeof window>"u")return null;try{const n=window.localStorage.getItem(Z);return n?((e=JSON.parse(n).byKey)==null?void 0:e[s])??null:null}catch{return null}}function q(s,e){if(!(typeof window>"u"))try{const n=window.localStorage.getItem(Z),m=(n?JSON.parse(n):{}).byKey??{};m[s]=e,window.localStorage.setItem(Z,JSON.stringify({byKey:m}))}catch{}}function oe(){if(!(typeof window>"u"))try{window.localStorage.removeItem(Z)}catch{}}function ie(s){const e=s.installed??[];C.value=e;const n=new Set(e.map(v=>v.name));y.value=s.data.map(v=>{if(v.installed||n.has(v.name)){const m=e.find(M=>M.name===v.name);return{...v,installed:!0,path:(m==null?void 0:m.path)??v.path,enabled:(m==null?void 0:m.enabled)??v.enabled}}return v}).filter(v=>!v.installed),$.value=s.total}async function B(s){const e=s.trim();_.value=e;const n=z(e),v=W(n);v&&ie(v),T.value=!v,P.value="";try{const m=new URLSearchParams;e&&m.set("q",e),m.set("limit","100"),m.set("sort",k.value);const M=await fetch(`/codex-api/skills-hub?${m}`);if(!M.ok)throw new Error(`HTTP ${M.status}`);const ye=await M.json();ie(ye),q(n,ye)}catch(m){P.value=m instanceof Error?m.message:"Failed to load skills"}finally{T.value=!1}}function re(){B(f.value)}function ce(s){A.value=s,F.value=!0}async function _e(s){x.value=`${s.owner}/${s.name}`,a.value=!0;try{const n=await(await fetch("/codex-api/skills-hub/install",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({owner:s.owner,name:s.name})})).json();if(!n.ok)throw new Error(n.error||"Install failed");const v={...s,installed:!0,path:n.path,enabled:!0};C.value=[...C.value,v],y.value=y.value.filter(m=>m.name!==s.name),A.value=v,U(`${s.displayName||s.name} skill installed`),F.value=!1,oe(),g("skills-changed")}catch(e){U(e instanceof Error?e.message:"Failed to install skill","error")}finally{a.value=!1}}async function $e(s){x.value=`${s.owner}/${s.name}`,c.value=!0;try{const n=await(await fetch("/codex-api/skills-hub/uninstall",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:s.name,path:s.path})})).json();if(!n.ok)throw new Error(n.error||"Uninstall failed");C.value=C.value.filter(v=>v.name!==s.name),s.owner!=="local"&&(y.value=[...y.value,{...s,installed:!1,path:void 0,enabled:void 0}]),U(`${s.displayName||s.name} skill uninstalled`),F.value=!1,oe(),g("skills-changed")}catch(e){U(e instanceof Error?e.message:"Failed to uninstall skill","error")}finally{c.value=!1}}async function Ie(s,e){try{if(!(await fetch("/codex-api/rpc",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({method:"skills/config/write",params:{path:s.path,enabled:e}})})).ok)throw new Error("Failed to update skill");await fetch("/codex-api/skills-sync/push",{method:"POST"}),U(`${s.displayName||s.name} skill ${e?"enabled":"disabled"}`),await B(_.value)}catch(n){U(n instanceof Error?n.message:"Failed to update skill","error")}}const{deviceLogin:Y,isPullInFlight:Ce,isPushInFlight:Te,isStartupSyncInFlight:Ee,isSyncActionInFlight:Q,loadSyncStatus:Le,logoutGithub:ue,pullSkillsSync:de,pushSkillsSync:he,startupSkillsSync:ke,startGithubFirebaseLogin:ve,startGithubLogin:pe,syncActionError:be,syncActionStatus:fe,syncStatus:w}=ws({showToast:U,onPulled:async()=>{await B(_.value),g("skills-changed")}});return Ue(()=>{B(""),Le()}),(s,e)=>(l(),r("div",Ss,[e[12]||(e[12]=o("div",{class:"skills-hub-header"},[o("h2",{class:"skills-hub-title"},"Skills Hub"),o("p",{class:"skills-hub-subtitle"},"Browse and discover skills from the OpenClaw community")],-1)),o("div",_s,[o("div",$s,[e[9]||(e[9]=o("strong",null,"Skills Sync (GitHub)",-1)),i(w).configured&&V.value?(l(),r("a",{key:0,class:"skills-sync-badge skills-sync-badge-link",href:V.value,target:"_blank",rel:"noopener noreferrer"}," Connected: "+h(i(w).repoOwner)+"/"+h(i(w).repoName),9,Is)):i(w).loggedIn?(l(),r("span",Cs,"Logged in as "+h(i(w).githubUsername),1)):(l(),r("span",Ts,"Not connected"))]),o("div",Es,[o("span",null,"Startup: "+h(i(w).startup.mode),1),o("span",null,"Branch: "+h(i(w).startup.branch),1),o("span",null,"Action: "+h(i(w).startup.lastAction),1)]),i(w).startup.lastError?(l(),r("div",Ls,h(i(w).startup.lastError),1)):b("",!0),i(fe)?(l(),r("div",Ps,[o("span",null,"Manual sync: "+h(i(fe)),1)])):b("",!0),i(be)?(l(),r("div",Fs,h(i(be)),1)):b("",!0),i(Y)?(l(),r("div",As,[o("span",null,[e[10]||(e[10]=ge("Open ",-1)),o("a",{href:i(Y).verification_uri,target:"_blank",rel:"noreferrer"},"GitHub device login",8,Os),e[11]||(e[11]=ge(" and enter code:",-1))]),o("code",null,h(i(Y).user_code),1)])):b("",!0),o("div",Us,[i(w).loggedIn?b("",!0):(l(),r("button",{key:0,class:"skills-hub-sort",type:"button",onClick:e[0]||(e[0]=(...n)=>i(ve)&&i(ve)(...n))},"Login with GitHub")),i(w).loggedIn?b("",!0):(l(),r("button",{key:1,class:"skills-hub-sort",type:"button",onClick:e[1]||(e[1]=(...n)=>i(pe)&&i(pe)(...n))},"Device Login")),i(w).loggedIn?(l(),r("button",{key:2,class:"skills-hub-sort",type:"button",onClick:e[2]||(e[2]=(...n)=>i(ue)&&i(ue)(...n)),disabled:i(Q)},"Logout GitHub",8,Gs)):b("",!0),o("button",{class:"skills-hub-sort",type:"button",onClick:e[3]||(e[3]=(...n)=>i(ke)&&i(ke)(...n)),disabled:i(Q)},h(i(Ee)?"Syncing...":"Startup Sync"),9,xs),o("button",{class:"skills-hub-sort",type:"button",onClick:e[4]||(e[4]=(...n)=>i(de)&&i(de)(...n)),disabled:i(Q)},h(i(Ce)?"Pulling...":"Pull"),9,Hs),i(w).loggedIn?(l(),r("button",{key:3,class:"skills-hub-sort",type:"button",onClick:e[5]||(e[5]=(...n)=>i(he)&&i(he)(...n)),disabled:!i(w).configured||i(Q)},h(i(Te)?"Pushing...":"Push"),9,Ns)):b("",!0)])]),O.value?(l(),r("div",{key:0,class:se(["skills-hub-toast",D.value])},h(O.value.text),3)):b("",!0),R.value.length>0?(l(),r("div",Ds,[o("button",{class:"skills-hub-section-toggle",type:"button",onClick:e[6]||(e[6]=n=>G.value=!G.value)},[o("span",Rs,"Installed ("+h(R.value.length)+")",1),J(Ge,{class:se(["skills-hub-section-chevron",{"is-open":G.value}])},null,8,["class"])]),G.value?(l(),r("div",js,[(l(!0),r(X,null,we(R.value,n=>(l(),te(Se,{key:n.name,skill:n,onSelect:v=>ce(v)},null,8,["skill","onSelect"]))),128))])):b("",!0)])):b("",!0),o("div",Ks,[o("div",Bs,[J(xe,{class:"skills-hub-search-icon"}),He(o("input",{ref_key:"searchRef",ref:I,"onUpdate:modelValue":e[7]||(e[7]=n=>f.value=n),class:"skills-hub-search",type:"text",placeholder:"Search skills... (e.g. flight, docker, react)",onKeyup:De(ae(re,["prevent"]),["enter"])},null,40,Ms),[[Ne,f.value]]),o("button",{class:"skills-hub-search-btn",type:"button",onClick:re},"Search"),$.value>0?(l(),r("span",Js,h($.value)+" skills",1)):b("",!0)]),o("button",{class:"skills-hub-sort",type:"button",onClick:K},h(E.value),1)]),o("div",Vs,[T.value?(l(),r("div",zs,"Loading skills...")):P.value?(l(),r("div",Ws,h(P.value),1)):(l(),r(X,{key:2},[y.value.length>0?(l(),r("div",qs,[(l(!0),r(X,null,we(y.value,n=>(l(),te(Se,{key:n.url,skill:n,onSelect:v=>ce(v)},null,8,["skill","onSelect"]))),128))])):_.value.trim()?(l(),r("div",Qs,'No skills found for "'+h(_.value)+'"',1)):b("",!0)],64))]),J(ys,{skill:A.value,visible:F.value,"is-installing":N.value,"is-uninstalling":j.value,onClose:e[8]||(e[8]=n=>F.value=!1),onInstall:_e,onUninstall:$e,onToggleEnabled:Ie},null,8,["skill","visible","is-installing","is-uninstalling"])]))}}),Xs=ne(Zs,[["__scopeId","data-v-77953f50"]]);export{Xs as default};
|
|
2
|
+
import{d as le,o as l,c as r,h as se,a as o,t as h,f as b,s as ae,m as J,K as Pe,y as S,_ as ne,w as Fe,i as te,q as Ae,L as Oe,r as p,z as me,b as Ue,v as i,x as ge,M as Ge,F as X,g as we,N as xe,B as He,O as Ne,P as De}from"./index-C4y0SuPN.js";const Re={class:"skill-card-top"},je=["src","alt"],Ke={key:1,class:"skill-card-avatar-fallback"},Be={class:"skill-card-info"},Me={class:"skill-card-header"},Je={class:"skill-card-name"},Ve={key:0,class:"skill-card-badge-disabled"},ze={key:1,class:"skill-card-badge"},We={class:"skill-card-owner"},qe={key:0,class:"skill-card-desc"},Qe={key:1,class:"skill-card-date"},Ze=le({__name:"SkillCard",props:{skill:{}},emits:["select"],setup(t){const L=t,u=S(()=>{const k=L.skill.path;return k?k.endsWith("/SKILL.md")?k.slice(0,-9):k:""});function I(){const k=u.value;k&&window.open(`/codex-local-browse${encodeURI(k)}`,"_blank","noopener,noreferrer")}const f=S(()=>{const k=L.skill.publishedAt;if(!k)return"";const y=new Date(k),$=Date.now()-k;return $<36e5?`${Math.floor($/6e4)}m ago`:$<864e5?`${Math.floor($/36e5)}h ago`:$<2592e6?`${Math.floor($/864e5)}d ago`:y.toLocaleDateString("en-US",{month:"short",day:"numeric"})});function _(k){const y=k.target;y.style.display="none"}return(k,y)=>(l(),r("button",{class:se(["skill-card",{"is-disabled":t.skill.installed&&t.skill.enabled===!1}]),type:"button",onClick:y[0]||(y[0]=C=>k.$emit("select",t.skill))},[o("div",Re,[t.skill.avatarUrl?(l(),r("img",{key:0,class:"skill-card-avatar",src:t.skill.avatarUrl,alt:t.skill.owner,loading:"lazy",onError:_},null,40,je)):(l(),r("div",Ke,h(t.skill.owner.charAt(0)),1)),o("div",Be,[o("div",Me,[o("span",Je,h(t.skill.displayName||t.skill.name),1),t.skill.installed&&t.skill.enabled===!1?(l(),r("span",Ve,"Disabled")):t.skill.installed?(l(),r("span",ze,"Installed")):b("",!0)]),o("span",We,h(t.skill.owner),1)]),t.skill.installed&&u.value?(l(),r("button",{key:2,class:"skill-card-browse",type:"button",title:"Browse files",onClick:ae(I,["stop"])},[J(Pe,{class:"skill-card-browse-icon"})])):b("",!0)]),t.skill.description?(l(),r("p",qe,h(t.skill.description),1)):b("",!0),f.value?(l(),r("span",Qe,h(f.value),1)):b("",!0)],2))}}),Se=ne(Ze,[["__scopeId","data-v-49ed14e0"]]),Ye={class:"sdm-panel"},Xe={class:"sdm-header"},es={class:"sdm-title-area"},ss=["src","alt"],ts={class:"sdm-title-col"},ls={class:"sdm-title-row"},as={class:"sdm-title"},ns={key:0,class:"sdm-badge-disabled"},os={class:"sdm-owner"},is={class:"sdm-body"},rs={key:0,class:"sdm-desc"},cs={key:1,class:"sdm-readme-loading"},us=["innerHTML"],ds=["href"],hs={class:"sdm-footer"},ks={class:"sdm-footer-actions"},vs=["disabled"],ps=["disabled"],bs=["disabled"],fs=le({__name:"SkillDetailModal",props:{skill:{},visible:{type:Boolean},isInstalling:{type:Boolean},isUninstalling:{type:Boolean}},emits:["close","install","uninstall","toggle-enabled"],setup(t,{emit:L}){const u=t,I=L,f=p(null),_=p(""),k=p(""),y=p(!1),C=S(()=>f.value??u.skill.enabled??!0),$=S(()=>u.isInstalling===!0||u.isUninstalling===!0),T=S(()=>_.value||u.skill.description),P=S(()=>{const d=u.skill.path;return d?d.endsWith("/SKILL.md")?d.slice(0,-9):d:""}),G=S(()=>{const d=k.value;if(!d)return"";const g=d.replace(/^---[\s\S]*?---\s*/,"");return F(g)});function F(d){return d.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/^### (.+)$/gm,"<h4>$1</h4>").replace(/^## (.+)$/gm,"<h3>$1</h3>").replace(/^# (.+)$/gm,"<h2>$1</h2>").replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>").replace(/`([^`]+)`/g,"<code>$1</code>").replace(/^- (.+)$/gm,"<li>$1</li>").replace(/(<li>.*<\/li>\n?)+/g,"<ul>$&</ul>").replace(/\n{2,}/g,"<br/><br/>").replace(/\n/g,"<br/>")}async function A(){if(!(!u.skill.owner||!u.skill.name)){y.value=!0,_.value="",k.value="";try{const d=new URLSearchParams({owner:u.skill.owner,name:u.skill.name});u.skill.installed&&d.set("installed","true"),u.skill.path&&d.set("path",u.skill.path);const g=await fetch(`/codex-api/skills-hub/readme?${d}`);if(!g.ok)return;const E=await g.json();k.value=E.content??"",_.value=E.description??""}catch{}finally{y.value=!1}}}Fe(()=>u.visible,d=>{d&&(f.value=null,_.value="",k.value="",A())});function O(){I("install",u.skill)}function x(){I("uninstall",u.skill)}function a(){const d=!C.value;f.value=d,I("toggle-enabled",u.skill,d)}function c(){const d=P.value;d&&window.open(`/codex-local-browse${encodeURI(d)}`,"_blank","noopener,noreferrer")}return(d,g)=>(l(),te(Oe,{to:"body"},[t.visible?(l(),r("div",{key:0,class:"sdm-overlay",onClick:g[1]||(g[1]=ae(E=>d.$emit("close"),["self"]))},[o("div",Ye,[o("div",Xe,[o("div",es,[t.skill.avatarUrl?(l(),r("img",{key:0,class:"sdm-avatar",src:t.skill.avatarUrl,alt:t.skill.owner,loading:"lazy"},null,8,ss)):b("",!0),o("div",ts,[o("div",ls,[o("h3",as,h(t.skill.displayName||t.skill.name),1),t.skill.installed&&!C.value?(l(),r("span",ns,"Disabled")):b("",!0)]),o("span",os,h(t.skill.owner),1)])]),o("button",{class:"sdm-close",type:"button","aria-label":"Close",onClick:g[0]||(g[0]=E=>d.$emit("close"))},[J(Ae,{class:"sdm-close-icon"})])]),o("div",is,[T.value?(l(),r("p",rs,h(T.value),1)):b("",!0),y.value?(l(),r("div",cs,"Loading skill contents...")):k.value?(l(),r("div",{key:2,class:"sdm-readme",innerHTML:G.value},null,8,us)):b("",!0),o("a",{class:"sdm-link",href:t.skill.url,target:"_blank",rel:"noopener noreferrer"},"View on GitHub",8,ds)]),o("div",hs,[o("div",ks,[t.skill.installed?(l(),r("button",{key:0,class:"sdm-btn sdm-btn-danger",type:"button",disabled:$.value,onClick:x},h(u.isUninstalling?"Uninstalling...":"Uninstall"),9,vs)):(l(),r("button",{key:1,class:"sdm-btn sdm-btn-primary",type:"button",disabled:$.value,onClick:O},h(u.isInstalling?"Installing...":"Install"),9,ps)),t.skill.installed?(l(),r("button",{key:2,class:"sdm-btn sdm-btn-secondary",type:"button",disabled:$.value,onClick:a},h(C.value?"Disable":"Enable"),9,bs)):b("",!0),t.skill.installed&&P.value?(l(),r("button",{key:3,class:"sdm-btn sdm-btn-secondary",type:"button",onClick:c}," Browse files ")):b("",!0)])])])])):b("",!0)]))}}),ys=ne(fs,[["__scopeId","data-v-c758a719"]]),ms={apiKey:"AIzaSyAf0CIHBZ-wEQJ8CCUUWo1Wl9P7typ_ZPI",authDomain:"gptcall-416910.firebaseapp.com",projectId:"gptcall-416910",storageBucket:"gptcall-416910.appspot.com",messagingSenderId:"99275526699",appId:"1:99275526699:web:3b623e1e2996108b52106e"};let ee=null;function gs(){return ee||(ee=Promise.all([me(()=>import("./index.esm-mbv_PYjX.js"),__vite__mapDeps([0,1])),me(()=>import("./index.esm-DtVW_dfU.js"),__vite__mapDeps([2,1]))])),ee}function ws(t){const L=p(null),u=p(""),I=p(""),f=p(""),_=p({loggedIn:!1,githubUsername:"",repoOwner:"",repoName:"",configured:!1,startup:{inProgress:!1,mode:"idle",branch:"main",lastAction:"not-started",lastRunAtIso:"",lastSuccessAtIso:"",lastError:""}}),k=S(()=>f.value==="pull"),y=S(()=>f.value==="push"),C=S(()=>f.value==="startup-sync"),$=S(()=>f.value!=="");async function T(){try{const a=await fetch("/codex-api/skills-sync/status");if(!a.ok)return;const c=await a.json();c.data&&(_.value=c.data)}catch{}}async function P(){try{const a=await fetch("/codex-api/skills-sync/github/start-login",{method:"POST"}),c=await a.json();if(!a.ok||!c.data)throw new Error("Failed to start GitHub login");L.value=c.data;const d=30,g=Math.max((c.data.interval??5)*1e3,3e3);let E=!1;for(let D=0;D<d;D++){await new Promise(j=>setTimeout(j,g));const H=await fetch("/codex-api/skills-sync/github/complete-login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({deviceCode:c.data.device_code})}),N=await H.json();if(!H.ok)throw new Error(N.error||"Failed to complete GitHub login");if(N.ok){E=!0;break}if(!N.pending)throw new Error(N.error||"Failed to complete GitHub login")}if(!E)throw new Error("GitHub login timed out. Please retry.");L.value=null,await T(),t.showToast("GitHub login successful")}catch(a){t.showToast(a instanceof Error?a.message:"Failed GitHub login","error")}}async function G(){try{const[a,c]=await gs(),{getApp:d,getApps:g,initializeApp:E}=a,{getAuth:D,GithubAuthProvider:H,signInWithPopup:N}=c,j=g().length>0?d():E(ms),V=D(j),R=new H;R.addScope("repo");const U=await N(V,R),K=H.credentialFromResult(U),z=(K==null?void 0:K.accessToken)??"";if(!z)throw new Error("GitHub access token missing from Firebase login");const W=await fetch("/codex-api/skills-sync/github/token-login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:z})}),q=await W.json();if(!W.ok||!q.ok)throw new Error(q.error||"Failed to login with GitHub token");await T(),t.showToast("GitHub login successful")}catch(a){const c=a instanceof Error?a.message:"Failed Firebase GitHub login";t.showToast(c,"error")}}async function F(){I.value="",u.value="pull-started",f.value="pull";try{const a=await fetch("/codex-api/skills-sync/pull",{method:"POST"}),c=await a.json();if(!a.ok||!c.ok)throw new Error(c.error||"Failed to pull synced skills");await t.onPulled(),u.value="pull-success",t.showToast(_.value.loggedIn?"Pulled skills from private sync repo":"Pulled skills from upstream repo")}catch(a){const c=a instanceof Error?a.message:"Failed to pull sync";I.value=c,u.value="pull-failed",t.showToast(c,"error")}finally{f.value=""}}async function A(){I.value="",u.value="push-started",f.value="push";try{const a=await fetch("/codex-api/skills-sync/push",{method:"POST"}),c=await a.json();if(!a.ok||!c.ok)throw new Error(c.error||"Failed to push synced skills");u.value="push-success",t.showToast("Pushed skills to private sync repo")}catch(a){const c=a instanceof Error?a.message:"Failed to push sync";I.value=c,u.value="push-failed",t.showToast(c,"error")}finally{f.value=""}}async function O(){I.value="",u.value="startup-sync-started",f.value="startup-sync";try{const a=await fetch("/codex-api/skills-sync/startup-sync",{method:"POST"}),c=await a.json();if(!a.ok||!c.ok)throw new Error(c.error||"Failed to run startup sync");await t.onPulled(),await T(),u.value="startup-sync-success",t.showToast("Startup sync completed")}catch(a){const c=a instanceof Error?a.message:"Failed startup sync";I.value=c,u.value="startup-sync-failed",t.showToast(c,"error")}finally{f.value=""}}async function x(){try{const a=await fetch("/codex-api/skills-sync/github/logout",{method:"POST"}),c=await a.json();if(!a.ok||!c.ok)throw new Error(c.error||"Failed to logout GitHub");await T(),t.showToast("Logged out from GitHub")}catch(a){t.showToast(a instanceof Error?a.message:"Failed to logout GitHub","error")}}return{deviceLogin:L,isPullInFlight:k,isPushInFlight:y,isStartupSyncInFlight:C,isSyncActionInFlight:$,loadSyncStatus:T,logoutGithub:x,pullSkillsSync:F,pushSkillsSync:A,startupSkillsSync:O,startGithubFirebaseLogin:G,startGithubLogin:P,syncActionError:I,syncActionStatus:u,syncStatus:_}}const Ss={class:"skills-hub"},_s={class:"skills-sync-panel"},$s={class:"skills-sync-header"},Is=["href"],Cs={key:1,class:"skills-sync-badge"},Ts={key:2,class:"skills-sync-badge"},Es={class:"skills-sync-meta"},Ls={key:0,class:"skills-sync-error"},Ps={key:1,class:"skills-sync-meta"},Fs={key:2,class:"skills-sync-error"},As={key:3,class:"skills-sync-device"},Os=["href"],Us={class:"skills-sync-actions"},Gs=["disabled"],xs=["disabled"],Hs=["disabled"],Ns=["disabled"],Ds={key:1,class:"skills-hub-section"},Rs={class:"skills-hub-section-title"},js={key:0,class:"skills-hub-grid"},Ks={class:"skills-hub-toolbar"},Bs={class:"skills-hub-search-wrap"},Ms=["onKeyup"],Js={key:0,class:"skills-hub-count"},Vs={class:"skills-hub-section"},zs={key:0,class:"skills-hub-loading"},Ws={key:1,class:"skills-hub-error"},qs={key:0,class:"skills-hub-grid"},Qs={key:1,class:"skills-hub-empty"},Z="codex-web-local.skills-hub.cache.v1",Zs=le({__name:"SkillsHub",emits:["skills-changed"],setup(t,{emit:L}){const u={name:"",owner:"",description:"",url:"",installed:!1},I=p(null),f=p(""),_=p(""),k=p("date"),y=p([]),C=p([]),$=p(0),T=p(!1),P=p(""),G=p(!0),F=p(!1),A=p(u),O=p(null),x=p(""),a=p(!1),c=p(!1);let d=null;const g=L,E=S(()=>k.value==="date"?"Newest":"A-Z"),D=S(()=>{var s;return((s=O.value)==null?void 0:s.type)==="error"?"skills-hub-toast-error":"skills-hub-toast-success"}),H=S(()=>`${A.value.owner}/${A.value.name}`),N=S(()=>a.value&&x.value===H.value),j=S(()=>c.value&&x.value===H.value),V=S(()=>{if(!w.value.configured)return"";const s=w.value.repoOwner.trim(),e=w.value.repoName.trim();return!s||!e?"":`https://github.com/${encodeURIComponent(s)}/${encodeURIComponent(e)}`}),R=S(()=>{const s=f.value.toLowerCase().trim();return s?C.value.filter(e=>e.name.toLowerCase().includes(s)||e.owner.toLowerCase().includes(s)||(e.displayName??"").toLowerCase().includes(s)):C.value});function U(s,e="success"){O.value={text:s,type:e},d&&clearTimeout(d),d=setTimeout(()=>{O.value=null},3e3)}function K(){k.value=k.value==="date"?"name":"date",B(_.value)}function z(s){return`${k.value}::${s.trim().toLowerCase()}`}function W(s){var e;if(typeof window>"u")return null;try{const n=window.localStorage.getItem(Z);return n?((e=JSON.parse(n).byKey)==null?void 0:e[s])??null:null}catch{return null}}function q(s,e){if(!(typeof window>"u"))try{const n=window.localStorage.getItem(Z),m=(n?JSON.parse(n):{}).byKey??{};m[s]=e,window.localStorage.setItem(Z,JSON.stringify({byKey:m}))}catch{}}function oe(){if(!(typeof window>"u"))try{window.localStorage.removeItem(Z)}catch{}}function ie(s){const e=s.installed??[];C.value=e;const n=new Set(e.map(v=>v.name));y.value=s.data.map(v=>{if(v.installed||n.has(v.name)){const m=e.find(M=>M.name===v.name);return{...v,installed:!0,path:(m==null?void 0:m.path)??v.path,enabled:(m==null?void 0:m.enabled)??v.enabled}}return v}).filter(v=>!v.installed),$.value=s.total}async function B(s){const e=s.trim();_.value=e;const n=z(e),v=W(n);v&&ie(v),T.value=!v,P.value="";try{const m=new URLSearchParams;e&&m.set("q",e),m.set("limit","100"),m.set("sort",k.value);const M=await fetch(`/codex-api/skills-hub?${m}`);if(!M.ok)throw new Error(`HTTP ${M.status}`);const ye=await M.json();ie(ye),q(n,ye)}catch(m){P.value=m instanceof Error?m.message:"Failed to load skills"}finally{T.value=!1}}function re(){B(f.value)}function ce(s){A.value=s,F.value=!0}async function _e(s){x.value=`${s.owner}/${s.name}`,a.value=!0;try{const n=await(await fetch("/codex-api/skills-hub/install",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({owner:s.owner,name:s.name})})).json();if(!n.ok)throw new Error(n.error||"Install failed");const v={...s,installed:!0,path:n.path,enabled:!0};C.value=[...C.value,v],y.value=y.value.filter(m=>m.name!==s.name),A.value=v,U(`${s.displayName||s.name} skill installed`),F.value=!1,oe(),g("skills-changed")}catch(e){U(e instanceof Error?e.message:"Failed to install skill","error")}finally{a.value=!1}}async function $e(s){x.value=`${s.owner}/${s.name}`,c.value=!0;try{const n=await(await fetch("/codex-api/skills-hub/uninstall",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:s.name,path:s.path})})).json();if(!n.ok)throw new Error(n.error||"Uninstall failed");C.value=C.value.filter(v=>v.name!==s.name),s.owner!=="local"&&(y.value=[...y.value,{...s,installed:!1,path:void 0,enabled:void 0}]),U(`${s.displayName||s.name} skill uninstalled`),F.value=!1,oe(),g("skills-changed")}catch(e){U(e instanceof Error?e.message:"Failed to uninstall skill","error")}finally{c.value=!1}}async function Ie(s,e){try{if(!(await fetch("/codex-api/rpc",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({method:"skills/config/write",params:{path:s.path,enabled:e}})})).ok)throw new Error("Failed to update skill");await fetch("/codex-api/skills-sync/push",{method:"POST"}),U(`${s.displayName||s.name} skill ${e?"enabled":"disabled"}`),await B(_.value)}catch(n){U(n instanceof Error?n.message:"Failed to update skill","error")}}const{deviceLogin:Y,isPullInFlight:Ce,isPushInFlight:Te,isStartupSyncInFlight:Ee,isSyncActionInFlight:Q,loadSyncStatus:Le,logoutGithub:ue,pullSkillsSync:de,pushSkillsSync:he,startupSkillsSync:ke,startGithubFirebaseLogin:ve,startGithubLogin:pe,syncActionError:be,syncActionStatus:fe,syncStatus:w}=ws({showToast:U,onPulled:async()=>{await B(_.value),g("skills-changed")}});return Ue(()=>{B(""),Le()}),(s,e)=>(l(),r("div",Ss,[e[12]||(e[12]=o("div",{class:"skills-hub-header"},[o("h2",{class:"skills-hub-title"},"Skills Hub"),o("p",{class:"skills-hub-subtitle"},"Browse and discover skills from the OpenClaw community")],-1)),o("div",_s,[o("div",$s,[e[9]||(e[9]=o("strong",null,"Skills Sync (GitHub)",-1)),i(w).configured&&V.value?(l(),r("a",{key:0,class:"skills-sync-badge skills-sync-badge-link",href:V.value,target:"_blank",rel:"noopener noreferrer"}," Connected: "+h(i(w).repoOwner)+"/"+h(i(w).repoName),9,Is)):i(w).loggedIn?(l(),r("span",Cs,"Logged in as "+h(i(w).githubUsername),1)):(l(),r("span",Ts,"Not connected"))]),o("div",Es,[o("span",null,"Startup: "+h(i(w).startup.mode),1),o("span",null,"Branch: "+h(i(w).startup.branch),1),o("span",null,"Action: "+h(i(w).startup.lastAction),1)]),i(w).startup.lastError?(l(),r("div",Ls,h(i(w).startup.lastError),1)):b("",!0),i(fe)?(l(),r("div",Ps,[o("span",null,"Manual sync: "+h(i(fe)),1)])):b("",!0),i(be)?(l(),r("div",Fs,h(i(be)),1)):b("",!0),i(Y)?(l(),r("div",As,[o("span",null,[e[10]||(e[10]=ge("Open ",-1)),o("a",{href:i(Y).verification_uri,target:"_blank",rel:"noreferrer"},"GitHub device login",8,Os),e[11]||(e[11]=ge(" and enter code:",-1))]),o("code",null,h(i(Y).user_code),1)])):b("",!0),o("div",Us,[i(w).loggedIn?b("",!0):(l(),r("button",{key:0,class:"skills-hub-sort",type:"button",onClick:e[0]||(e[0]=(...n)=>i(ve)&&i(ve)(...n))},"Login with GitHub")),i(w).loggedIn?b("",!0):(l(),r("button",{key:1,class:"skills-hub-sort",type:"button",onClick:e[1]||(e[1]=(...n)=>i(pe)&&i(pe)(...n))},"Device Login")),i(w).loggedIn?(l(),r("button",{key:2,class:"skills-hub-sort",type:"button",onClick:e[2]||(e[2]=(...n)=>i(ue)&&i(ue)(...n)),disabled:i(Q)},"Logout GitHub",8,Gs)):b("",!0),o("button",{class:"skills-hub-sort",type:"button",onClick:e[3]||(e[3]=(...n)=>i(ke)&&i(ke)(...n)),disabled:i(Q)},h(i(Ee)?"Syncing...":"Startup Sync"),9,xs),o("button",{class:"skills-hub-sort",type:"button",onClick:e[4]||(e[4]=(...n)=>i(de)&&i(de)(...n)),disabled:i(Q)},h(i(Ce)?"Pulling...":"Pull"),9,Hs),i(w).loggedIn?(l(),r("button",{key:3,class:"skills-hub-sort",type:"button",onClick:e[5]||(e[5]=(...n)=>i(he)&&i(he)(...n)),disabled:!i(w).configured||i(Q)},h(i(Te)?"Pushing...":"Push"),9,Ns)):b("",!0)])]),O.value?(l(),r("div",{key:0,class:se(["skills-hub-toast",D.value])},h(O.value.text),3)):b("",!0),R.value.length>0?(l(),r("div",Ds,[o("button",{class:"skills-hub-section-toggle",type:"button",onClick:e[6]||(e[6]=n=>G.value=!G.value)},[o("span",Rs,"Installed ("+h(R.value.length)+")",1),J(Ge,{class:se(["skills-hub-section-chevron",{"is-open":G.value}])},null,8,["class"])]),G.value?(l(),r("div",js,[(l(!0),r(X,null,we(R.value,n=>(l(),te(Se,{key:n.name,skill:n,onSelect:v=>ce(v)},null,8,["skill","onSelect"]))),128))])):b("",!0)])):b("",!0),o("div",Ks,[o("div",Bs,[J(xe,{class:"skills-hub-search-icon"}),He(o("input",{ref_key:"searchRef",ref:I,"onUpdate:modelValue":e[7]||(e[7]=n=>f.value=n),class:"skills-hub-search",type:"text",placeholder:"Search skills... (e.g. flight, docker, react)",onKeyup:De(ae(re,["prevent"]),["enter"])},null,40,Ms),[[Ne,f.value]]),o("button",{class:"skills-hub-search-btn",type:"button",onClick:re},"Search"),$.value>0?(l(),r("span",Js,h($.value)+" skills",1)):b("",!0)]),o("button",{class:"skills-hub-sort",type:"button",onClick:K},h(E.value),1)]),o("div",Vs,[T.value?(l(),r("div",zs,"Loading skills...")):P.value?(l(),r("div",Ws,h(P.value),1)):(l(),r(X,{key:2},[y.value.length>0?(l(),r("div",qs,[(l(!0),r(X,null,we(y.value,n=>(l(),te(Se,{key:n.url,skill:n,onSelect:v=>ce(v)},null,8,["skill","onSelect"]))),128))])):_.value.trim()?(l(),r("div",Qs,'No skills found for "'+h(_.value)+'"',1)):b("",!0)],64))]),J(ys,{skill:A.value,visible:F.value,"is-installing":N.value,"is-uninstalling":j.value,onClose:e[8]||(e[8]=n=>F.value=!1),onInstall:_e,onUninstall:$e,onToggleEnabled:Ie},null,8,["skill","visible","is-installing","is-uninstalling"])]))}}),Xs=ne(Zs,[["__scopeId","data-v-77953f50"]]);export{Xs as default};
|
|
3
|
+
//# sourceMappingURL=SkillsHub-Crvp1wow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":"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","names":["props","__props","skillDirPath","computed","p","onBrowse","dir","publishedLabel","ts","d","diff","onAvatarError","e","img","_createElementBlock","_normalizeClass","_cache","$event","$emit","_createElementVNode","_hoisted_1","_openBlock","_hoisted_3","_toDisplayString","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_createVNode","IconTablerFolder","_hoisted_10","_hoisted_11","emit","__emit","localEnabled","ref","localDescription","readmeContent","isLoadingReadme","effectiveEnabled","isActing","effectiveDescription","renderedReadme","raw","withoutFrontmatter","simpleMarkdown","md","fetchReadme","params","resp","data","watch","v","onInstall","onUninstall","onToggleEnabled","next","onBrowseFiles","_createBlock","_Teleport","_hoisted_2","IconTablerX","_hoisted_12","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","firebaseConfig","firebaseGithubAuthLoader","loadFirebaseGithubAuth","__vitePreload","useGithubSkillsSync","options","deviceLogin","syncActionStatus","syncActionError","syncActionInFlight","syncStatus","isPullInFlight","isPushInFlight","isStartupSyncInFlight","isSyncActionInFlight","loadSyncStatus","payload","startGithubLogin","startResp","startData","maxAttempts","waitMs","loggedIn","i","resolve","completeResp","completeData","startGithubFirebaseLogin","firebaseApp","firebaseAuth","getApp","getApps","initializeApp","getAuth","GithubAuthProvider","signInWithPopup","app","auth","provider","result","credential","token","error","message","pullSkillsSync","pushSkillsSync","startupSkillsSync","logoutGithub","SKILLS_HUB_CACHE_KEY","EMPTY_SKILL","searchRef","query","activeQuery","sortMode","browseSkills","installedSkills","totalCount","isLoading","isInstalledOpen","isDetailOpen","detailSkill","toast","actionSkillKey","isInstallActionInFlight","isUninstallActionInFlight","toastTimer","sortLabel","toastClass","_a","currentDetailSkillKey","isDetailInstalling","isDetailUninstalling","githubRepoUrl","owner","repo","filteredInstalled","q","s","showToast","text","type","toggleSort","fetchSkills","cacheKey","readCache","key","writeCache","byKey","clearCache","applySkillsPayload","inst","installedNames","local","normalizedQuery","cached","onSearchSubmit","openDetail","skill","handleInstall","installed","handleUninstall","handleToggleEnabled","enabled","onMounted","_unref","_hoisted_13","args","IconTablerChevronRight","_hoisted_20","_Fragment","_renderList","SkillCard","_hoisted_21","_hoisted_22","IconTablerSearch","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29","SkillDetailModal"],"ignoreList":[],"sources":["../../src/components/content/SkillCard.vue","../../src/components/content/SkillDetailModal.vue","../../src/composables/useGithubSkillsSync.ts","../../src/components/content/SkillsHub.vue"],"sourcesContent":["<template>\n <button\n class=\"skill-card\"\n type=\"button\"\n :class=\"{ 'is-disabled': skill.installed && skill.enabled === false }\"\n @click=\"$emit('select', skill)\"\n >\n <div class=\"skill-card-top\">\n <img\n v-if=\"skill.avatarUrl\"\n class=\"skill-card-avatar\"\n :src=\"skill.avatarUrl\"\n :alt=\"skill.owner\"\n loading=\"lazy\"\n @error=\"onAvatarError\"\n />\n <div class=\"skill-card-avatar-fallback\" v-else>{{ skill.owner.charAt(0) }}</div>\n <div class=\"skill-card-info\">\n <div class=\"skill-card-header\">\n <span class=\"skill-card-name\">{{ skill.displayName || skill.name }}</span>\n <span v-if=\"skill.installed && skill.enabled === false\" class=\"skill-card-badge-disabled\">Disabled</span>\n <span v-else-if=\"skill.installed\" class=\"skill-card-badge\">Installed</span>\n </div>\n <span class=\"skill-card-owner\">{{ skill.owner }}</span>\n </div>\n <button\n v-if=\"skill.installed && skillDirPath\"\n class=\"skill-card-browse\"\n type=\"button\"\n title=\"Browse files\"\n @click.stop=\"onBrowse\"\n >\n <IconTablerFolder class=\"skill-card-browse-icon\" />\n </button>\n </div>\n <p v-if=\"skill.description\" class=\"skill-card-desc\">{{ skill.description }}</p>\n <span v-if=\"publishedLabel\" class=\"skill-card-date\">{{ publishedLabel }}</span>\n </button>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport IconTablerFolder from '../icons/IconTablerFolder.vue'\n\nconst props = defineProps<{\n skill: {\n name: string\n owner: string\n description: string\n displayName?: string\n publishedAt?: number\n avatarUrl?: string\n url: string\n installed: boolean\n path?: string\n enabled?: boolean\n }\n}>()\n\ndefineEmits<{ select: [skill: unknown] }>()\n\nconst skillDirPath = computed(() => {\n const p = props.skill.path\n if (!p) return ''\n return p.endsWith('/SKILL.md') ? p.slice(0, -'/SKILL.md'.length) : p\n})\n\nfunction onBrowse(): void {\n const dir = skillDirPath.value\n if (!dir) return\n window.open(`/codex-local-browse${encodeURI(dir)}`, '_blank', 'noopener,noreferrer')\n}\n\nconst publishedLabel = computed(() => {\n const ts = props.skill.publishedAt\n if (!ts) return ''\n const d = new Date(ts)\n const now = Date.now()\n const diff = now - ts\n if (diff < 3600_000) return `${Math.floor(diff / 60_000)}m ago`\n if (diff < 86400_000) return `${Math.floor(diff / 3600_000)}h ago`\n if (diff < 2592000_000) return `${Math.floor(diff / 86400_000)}d ago`\n return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n})\n\nfunction onAvatarError(e: Event): void {\n const img = e.target as HTMLImageElement\n img.style.display = 'none'\n}\n</script>\n\n<style scoped>\n@reference \"tailwindcss\";\n\n.skill-card {\n @apply flex flex-col gap-1.5 rounded-xl border border-zinc-200 bg-white p-3 text-left transition hover:border-zinc-300 hover:shadow-sm cursor-pointer;\n}\n\n.skill-card.is-disabled {\n @apply opacity-50;\n}\n\n.skill-card-top {\n @apply flex items-start gap-2.5;\n}\n\n.skill-card-avatar {\n @apply w-8 h-8 rounded-full shrink-0 bg-zinc-100;\n}\n\n.skill-card-avatar-fallback {\n @apply w-8 h-8 rounded-full shrink-0 bg-zinc-200 text-zinc-500 flex items-center justify-center text-xs font-medium uppercase;\n}\n\n.skill-card-info {\n @apply flex flex-col gap-0.5 min-w-0 flex-1;\n}\n\n.skill-card-header {\n @apply flex items-center gap-2;\n}\n\n.skill-card-name {\n @apply text-sm font-medium text-zinc-900 truncate;\n}\n\n.skill-card-badge {\n @apply shrink-0 rounded-md border border-emerald-200 bg-emerald-50 px-1.5 py-0.5 text-[10px] font-medium text-emerald-700 leading-none;\n}\n\n.skill-card-badge-disabled {\n @apply shrink-0 rounded-md border border-zinc-200 bg-zinc-100 px-1.5 py-0.5 text-[10px] font-medium text-zinc-500 leading-none;\n}\n\n.skill-card-owner {\n @apply text-xs text-zinc-400;\n}\n\n.skill-card-browse {\n @apply shrink-0 ml-auto h-7 w-7 rounded-lg border-0 bg-transparent text-zinc-300 flex items-center justify-center transition hover:bg-zinc-100 hover:text-zinc-600;\n}\n\n.skill-card-browse-icon {\n @apply w-4 h-4;\n}\n\n.skill-card-desc {\n @apply m-0 text-xs text-zinc-500 line-clamp-2;\n}\n\n.skill-card-date {\n @apply text-[10px] text-zinc-300;\n}\n</style>\n","<template>\n <Teleport to=\"body\">\n <div v-if=\"visible\" class=\"sdm-overlay\" @click.self=\"$emit('close')\">\n <div class=\"sdm-panel\">\n <div class=\"sdm-header\">\n <div class=\"sdm-title-area\">\n <img\n v-if=\"skill.avatarUrl\"\n class=\"sdm-avatar\"\n :src=\"skill.avatarUrl\"\n :alt=\"skill.owner\"\n loading=\"lazy\"\n />\n <div class=\"sdm-title-col\">\n <div class=\"sdm-title-row\">\n <h3 class=\"sdm-title\">{{ skill.displayName || skill.name }}</h3>\n <span v-if=\"skill.installed && !effectiveEnabled\" class=\"sdm-badge-disabled\">Disabled</span>\n </div>\n <span class=\"sdm-owner\">{{ skill.owner }}</span>\n </div>\n </div>\n <button class=\"sdm-close\" type=\"button\" aria-label=\"Close\" @click=\"$emit('close')\">\n <IconTablerX class=\"sdm-close-icon\" />\n </button>\n </div>\n\n <div class=\"sdm-body\">\n <p v-if=\"effectiveDescription\" class=\"sdm-desc\">{{ effectiveDescription }}</p>\n\n <div v-if=\"isLoadingReadme\" class=\"sdm-readme-loading\">Loading skill contents...</div>\n <div v-else-if=\"readmeContent\" class=\"sdm-readme\" v-html=\"renderedReadme\"></div>\n\n <a class=\"sdm-link\" :href=\"skill.url\" target=\"_blank\" rel=\"noopener noreferrer\">View on GitHub</a>\n </div>\n\n <div class=\"sdm-footer\">\n <div class=\"sdm-footer-actions\">\n <button\n v-if=\"skill.installed\"\n class=\"sdm-btn sdm-btn-danger\"\n type=\"button\"\n :disabled=\"isActing\"\n @click=\"onUninstall\"\n >\n {{ props.isUninstalling ? 'Uninstalling...' : 'Uninstall' }}\n </button>\n <button\n v-else\n class=\"sdm-btn sdm-btn-primary\"\n type=\"button\"\n :disabled=\"isActing\"\n @click=\"onInstall\"\n >\n {{ props.isInstalling ? 'Installing...' : 'Install' }}\n </button>\n\n <button\n v-if=\"skill.installed\"\n class=\"sdm-btn sdm-btn-secondary\"\n type=\"button\"\n :disabled=\"isActing\"\n @click=\"onToggleEnabled\"\n >\n {{ effectiveEnabled ? 'Disable' : 'Enable' }}\n </button>\n\n <button\n v-if=\"skill.installed && skillDirPath\"\n class=\"sdm-btn sdm-btn-secondary\"\n type=\"button\"\n @click=\"onBrowseFiles\"\n >\n Browse files\n </button>\n </div>\n </div>\n </div>\n </div>\n </Teleport>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport IconTablerX from '../icons/IconTablerX.vue'\n\nexport type HubSkill = {\n name: string\n owner: string\n description: string\n displayName?: string\n publishedAt?: number\n avatarUrl?: string\n url: string\n installed: boolean\n path?: string\n enabled?: boolean\n}\n\nconst props = defineProps<{\n skill: HubSkill\n visible: boolean\n isInstalling?: boolean\n isUninstalling?: boolean\n}>()\n\nconst emit = defineEmits<{\n close: []\n install: [skill: HubSkill]\n uninstall: [skill: HubSkill]\n 'toggle-enabled': [skill: HubSkill, enabled: boolean]\n}>()\n\nconst localEnabled = ref<boolean | null>(null)\nconst localDescription = ref('')\nconst readmeContent = ref('')\nconst isLoadingReadme = ref(false)\n\nconst effectiveEnabled = computed(() => localEnabled.value ?? props.skill.enabled ?? true)\nconst isActing = computed(() => (props.isInstalling === true) || (props.isUninstalling === true))\nconst effectiveDescription = computed(() => localDescription.value || props.skill.description)\nconst skillDirPath = computed(() => {\n const p = props.skill.path\n if (!p) return ''\n return p.endsWith('/SKILL.md') ? p.slice(0, -'/SKILL.md'.length) : p\n})\n\nconst renderedReadme = computed(() => {\n const raw = readmeContent.value\n if (!raw) return ''\n const withoutFrontmatter = raw.replace(/^---[\\s\\S]*?---\\s*/, '')\n return simpleMarkdown(withoutFrontmatter)\n})\n\nfunction simpleMarkdown(md: string): string {\n const escaped = md\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n return escaped\n .replace(/^### (.+)$/gm, '<h4>$1</h4>')\n .replace(/^## (.+)$/gm, '<h3>$1</h3>')\n .replace(/^# (.+)$/gm, '<h2>$1</h2>')\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n .replace(/`([^`]+)`/g, '<code>$1</code>')\n .replace(/^- (.+)$/gm, '<li>$1</li>')\n .replace(/(<li>.*<\\/li>\\n?)+/g, '<ul>$&</ul>')\n .replace(/\\n{2,}/g, '<br/><br/>')\n .replace(/\\n/g, '<br/>')\n}\n\nasync function fetchReadme(): Promise<void> {\n if (!props.skill.owner || !props.skill.name) return\n isLoadingReadme.value = true\n localDescription.value = ''\n readmeContent.value = ''\n try {\n const params = new URLSearchParams({ owner: props.skill.owner, name: props.skill.name })\n if (props.skill.installed) params.set('installed', 'true')\n if (props.skill.path) params.set('path', props.skill.path)\n const resp = await fetch(`/codex-api/skills-hub/readme?${params}`)\n if (!resp.ok) return\n const data = (await resp.json()) as { content?: string; description?: string }\n readmeContent.value = data.content ?? ''\n localDescription.value = data.description ?? ''\n } catch {\n // silently fail\n } finally {\n isLoadingReadme.value = false\n }\n}\n\nwatch(() => props.visible, (v) => {\n if (v) {\n localEnabled.value = null\n localDescription.value = ''\n readmeContent.value = ''\n void fetchReadme()\n }\n})\n\nfunction onInstall(): void {\n emit('install', props.skill)\n}\n\nfunction onUninstall(): void {\n emit('uninstall', props.skill)\n}\n\nfunction onToggleEnabled(): void {\n const next = !effectiveEnabled.value\n localEnabled.value = next\n emit('toggle-enabled', props.skill, next)\n}\n\nfunction onBrowseFiles(): void {\n const dir = skillDirPath.value\n if (!dir) return\n window.open(`/codex-local-browse${encodeURI(dir)}`, '_blank', 'noopener,noreferrer')\n}\n</script>\n\n<style scoped>\n@reference \"tailwindcss\";\n\n.sdm-overlay {\n @apply fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/40;\n}\n\n.sdm-panel {\n @apply w-full max-w-lg max-h-[90vh] sm:max-h-[80vh] rounded-t-2xl sm:rounded-2xl bg-white shadow-xl flex flex-col overflow-hidden;\n}\n\n.sdm-header {\n @apply flex items-start justify-between gap-3 p-4 sm:p-5 pb-3 shrink-0;\n}\n\n.sdm-title-area {\n @apply flex items-center gap-3 min-w-0;\n}\n\n.sdm-avatar {\n @apply w-10 h-10 rounded-full shrink-0 bg-zinc-100;\n}\n\n.sdm-title-col {\n @apply flex flex-col gap-0.5 min-w-0;\n}\n\n.sdm-title-row {\n @apply flex items-center gap-2 min-w-0;\n}\n\n.sdm-title {\n @apply text-lg font-semibold text-zinc-900 m-0 truncate;\n}\n\n.sdm-badge-disabled {\n @apply shrink-0 rounded-md border border-zinc-200 bg-zinc-100 px-1.5 py-0.5 text-[10px] font-medium text-zinc-500 leading-none;\n}\n\n.sdm-owner {\n @apply text-xs text-zinc-400;\n}\n\n.sdm-close {\n @apply shrink-0 h-7 w-7 rounded-lg border-0 bg-transparent text-zinc-400 flex items-center justify-center transition hover:bg-zinc-100 hover:text-zinc-700;\n}\n\n.sdm-close-icon {\n @apply w-4 h-4;\n}\n\n.sdm-body {\n @apply p-4 sm:p-5 pt-0 flex flex-col gap-3 overflow-y-auto flex-1 min-h-0;\n}\n\n.sdm-desc {\n @apply m-0 text-sm text-zinc-600 leading-relaxed;\n}\n\n.sdm-readme-loading {\n @apply text-xs text-zinc-400;\n}\n\n.sdm-readme {\n @apply text-xs text-zinc-700 leading-relaxed border-t border-zinc-100 pt-3;\n}\n\n.sdm-readme :deep(h2) {\n @apply text-sm font-semibold text-zinc-800 mt-3 mb-1;\n}\n\n.sdm-readme :deep(h3) {\n @apply text-xs font-semibold text-zinc-700 mt-2 mb-1;\n}\n\n.sdm-readme :deep(h4) {\n @apply text-xs font-medium text-zinc-600 mt-2 mb-0.5;\n}\n\n.sdm-readme :deep(code) {\n @apply bg-zinc-100 rounded px-1 py-0.5 text-[11px] font-mono;\n}\n\n.sdm-readme :deep(ul) {\n @apply m-0 pl-4 list-disc;\n}\n\n.sdm-readme :deep(li) {\n @apply mb-0.5;\n}\n\n.sdm-readme :deep(strong) {\n @apply font-semibold;\n}\n\n.sdm-link {\n @apply text-xs text-blue-600 hover:text-blue-700 no-underline hover:underline shrink-0;\n}\n\n.sdm-footer {\n @apply p-4 sm:p-5 pt-3 border-t border-zinc-100 shrink-0;\n}\n\n.sdm-footer-actions {\n @apply flex items-center gap-2;\n}\n\n.sdm-btn {\n @apply rounded-lg px-3 py-1.5 text-sm font-medium transition border-0 disabled:opacity-50 disabled:cursor-not-allowed;\n}\n\n.sdm-btn-primary {\n @apply bg-zinc-900 text-white hover:bg-black;\n}\n\n.sdm-btn-danger {\n @apply bg-rose-600 text-white hover:bg-rose-700;\n}\n\n.sdm-btn-secondary {\n @apply bg-zinc-100 text-zinc-700 hover:bg-zinc-200;\n}\n</style>\n","import { computed, ref } from 'vue'\n\ntype ToastType = 'success' | 'error'\n\ntype SyncStartupStatus = {\n inProgress: boolean\n mode: string\n branch: string\n lastAction: string\n lastRunAtIso: string\n lastSuccessAtIso: string\n lastError: string\n}\n\nexport type SkillsSyncStatus = {\n loggedIn: boolean\n githubUsername: string\n repoOwner: string\n repoName: string\n configured: boolean\n startup: SyncStartupStatus\n}\n\ntype UseGithubSkillsSyncOptions = {\n showToast: (text: string, type?: ToastType) => void\n onPulled: () => Promise<void>\n}\n\nconst firebaseConfig = {\n apiKey: 'AIzaSyAf0CIHBZ-wEQJ8CCUUWo1Wl9P7typ_ZPI',\n authDomain: 'gptcall-416910.firebaseapp.com',\n projectId: 'gptcall-416910',\n storageBucket: 'gptcall-416910.appspot.com',\n messagingSenderId: '99275526699',\n appId: '1:99275526699:web:3b623e1e2996108b52106e',\n}\n\nlet firebaseGithubAuthLoader:\n Promise<[typeof import('firebase/app'), typeof import('firebase/auth')]> | null = null\n\nfunction loadFirebaseGithubAuth() {\n if (!firebaseGithubAuthLoader) {\n firebaseGithubAuthLoader = Promise.all([\n import('firebase/app'),\n import('firebase/auth'),\n ])\n }\n return firebaseGithubAuthLoader\n}\n\nexport function useGithubSkillsSync(options: UseGithubSkillsSyncOptions) {\n const deviceLogin = ref<{ device_code: string; user_code: string; verification_uri: string } | null>(null)\n const syncActionStatus = ref('')\n const syncActionError = ref('')\n const syncActionInFlight = ref<'pull' | 'push' | 'startup-sync' | ''>('')\n const syncStatus = ref<SkillsSyncStatus>({\n loggedIn: false,\n githubUsername: '',\n repoOwner: '',\n repoName: '',\n configured: false,\n startup: {\n inProgress: false,\n mode: 'idle',\n branch: 'main',\n lastAction: 'not-started',\n lastRunAtIso: '',\n lastSuccessAtIso: '',\n lastError: '',\n },\n })\n\n const isPullInFlight = computed(() => syncActionInFlight.value === 'pull')\n const isPushInFlight = computed(() => syncActionInFlight.value === 'push')\n const isStartupSyncInFlight = computed(() => syncActionInFlight.value === 'startup-sync')\n const isSyncActionInFlight = computed(() => syncActionInFlight.value !== '')\n\n async function loadSyncStatus(): Promise<void> {\n try {\n const resp = await fetch('/codex-api/skills-sync/status')\n if (!resp.ok) return\n const payload = (await resp.json()) as { data?: SkillsSyncStatus }\n if (payload.data) syncStatus.value = payload.data\n } catch {\n // best effort\n }\n }\n\n async function startGithubLogin(): Promise<void> {\n try {\n const startResp = await fetch('/codex-api/skills-sync/github/start-login', { method: 'POST' })\n const startData = (await startResp.json()) as { data?: { device_code: string; user_code: string; verification_uri: string; interval?: number } }\n if (!startResp.ok || !startData.data) throw new Error('Failed to start GitHub login')\n deviceLogin.value = startData.data\n const maxAttempts = 30\n const waitMs = Math.max((startData.data.interval ?? 5) * 1000, 3000)\n let loggedIn = false\n for (let i = 0; i < maxAttempts; i++) {\n await new Promise((resolve) => setTimeout(resolve, waitMs))\n const completeResp = await fetch('/codex-api/skills-sync/github/complete-login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ deviceCode: startData.data.device_code }),\n })\n const completeData = (await completeResp.json()) as { ok?: boolean; pending?: boolean; error?: string }\n if (!completeResp.ok) throw new Error(completeData.error || 'Failed to complete GitHub login')\n if (completeData.ok) {\n loggedIn = true\n break\n }\n if (!completeData.pending) throw new Error(completeData.error || 'Failed to complete GitHub login')\n }\n if (!loggedIn) throw new Error('GitHub login timed out. Please retry.')\n deviceLogin.value = null\n await loadSyncStatus()\n options.showToast('GitHub login successful')\n } catch (e) {\n options.showToast(e instanceof Error ? e.message : 'Failed GitHub login', 'error')\n }\n }\n\n async function startGithubFirebaseLogin(): Promise<void> {\n try {\n const [firebaseApp, firebaseAuth] = await loadFirebaseGithubAuth()\n const { getApp, getApps, initializeApp } = firebaseApp\n const { getAuth, GithubAuthProvider, signInWithPopup } = firebaseAuth\n const app = getApps().length > 0 ? getApp() : initializeApp(firebaseConfig)\n const auth = getAuth(app)\n const provider = new GithubAuthProvider()\n provider.addScope('repo')\n const result = await signInWithPopup(auth, provider)\n const credential = GithubAuthProvider.credentialFromResult(result)\n const token = credential?.accessToken ?? ''\n if (!token) {\n throw new Error('GitHub access token missing from Firebase login')\n }\n const resp = await fetch('/codex-api/skills-sync/github/token-login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token }),\n })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!resp.ok || !data.ok) {\n throw new Error(data.error || 'Failed to login with GitHub token')\n }\n await loadSyncStatus()\n options.showToast('GitHub login successful')\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed Firebase GitHub login'\n options.showToast(message, 'error')\n }\n }\n\n async function pullSkillsSync(): Promise<void> {\n syncActionError.value = ''\n syncActionStatus.value = 'pull-started'\n syncActionInFlight.value = 'pull'\n try {\n const resp = await fetch('/codex-api/skills-sync/pull', { method: 'POST' })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!resp.ok || !data.ok) throw new Error(data.error || 'Failed to pull synced skills')\n await options.onPulled()\n syncActionStatus.value = 'pull-success'\n options.showToast(syncStatus.value.loggedIn ? 'Pulled skills from private sync repo' : 'Pulled skills from upstream repo')\n } catch (e) {\n const message = e instanceof Error ? e.message : 'Failed to pull sync'\n syncActionError.value = message\n syncActionStatus.value = 'pull-failed'\n options.showToast(message, 'error')\n } finally {\n syncActionInFlight.value = ''\n }\n }\n\n async function pushSkillsSync(): Promise<void> {\n syncActionError.value = ''\n syncActionStatus.value = 'push-started'\n syncActionInFlight.value = 'push'\n try {\n const resp = await fetch('/codex-api/skills-sync/push', { method: 'POST' })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!resp.ok || !data.ok) throw new Error(data.error || 'Failed to push synced skills')\n syncActionStatus.value = 'push-success'\n options.showToast('Pushed skills to private sync repo')\n } catch (e) {\n const message = e instanceof Error ? e.message : 'Failed to push sync'\n syncActionError.value = message\n syncActionStatus.value = 'push-failed'\n options.showToast(message, 'error')\n } finally {\n syncActionInFlight.value = ''\n }\n }\n\n async function startupSkillsSync(): Promise<void> {\n syncActionError.value = ''\n syncActionStatus.value = 'startup-sync-started'\n syncActionInFlight.value = 'startup-sync'\n try {\n const resp = await fetch('/codex-api/skills-sync/startup-sync', { method: 'POST' })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!resp.ok || !data.ok) throw new Error(data.error || 'Failed to run startup sync')\n await options.onPulled()\n await loadSyncStatus()\n syncActionStatus.value = 'startup-sync-success'\n options.showToast('Startup sync completed')\n } catch (e) {\n const message = e instanceof Error ? e.message : 'Failed startup sync'\n syncActionError.value = message\n syncActionStatus.value = 'startup-sync-failed'\n options.showToast(message, 'error')\n } finally {\n syncActionInFlight.value = ''\n }\n }\n\n async function logoutGithub(): Promise<void> {\n try {\n const resp = await fetch('/codex-api/skills-sync/github/logout', { method: 'POST' })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!resp.ok || !data.ok) throw new Error(data.error || 'Failed to logout GitHub')\n await loadSyncStatus()\n options.showToast('Logged out from GitHub')\n } catch (e) {\n options.showToast(e instanceof Error ? e.message : 'Failed to logout GitHub', 'error')\n }\n }\n\n return {\n deviceLogin,\n isPullInFlight,\n isPushInFlight,\n isStartupSyncInFlight,\n isSyncActionInFlight,\n loadSyncStatus,\n logoutGithub,\n pullSkillsSync,\n pushSkillsSync,\n startupSkillsSync,\n startGithubFirebaseLogin,\n startGithubLogin,\n syncActionError,\n syncActionStatus,\n syncStatus,\n }\n}\n","<template>\n <div class=\"skills-hub\">\n <div class=\"skills-hub-header\">\n <h2 class=\"skills-hub-title\">Skills Hub</h2>\n <p class=\"skills-hub-subtitle\">Browse and discover skills from the OpenClaw community</p>\n </div>\n\n <div class=\"skills-sync-panel\">\n <div class=\"skills-sync-header\">\n <strong>Skills Sync (GitHub)</strong>\n <a\n v-if=\"syncStatus.configured && githubRepoUrl\"\n class=\"skills-sync-badge skills-sync-badge-link\"\n :href=\"githubRepoUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Connected: {{ syncStatus.repoOwner }}/{{ syncStatus.repoName }}\n </a>\n <span v-else-if=\"syncStatus.loggedIn\" class=\"skills-sync-badge\">Logged in as {{ syncStatus.githubUsername }}</span>\n <span v-else class=\"skills-sync-badge\">Not connected</span>\n </div>\n <div class=\"skills-sync-meta\">\n <span>Startup: {{ syncStatus.startup.mode }}</span>\n <span>Branch: {{ syncStatus.startup.branch }}</span>\n <span>Action: {{ syncStatus.startup.lastAction }}</span>\n </div>\n <div v-if=\"syncStatus.startup.lastError\" class=\"skills-sync-error\">\n {{ syncStatus.startup.lastError }}\n </div>\n <div v-if=\"syncActionStatus\" class=\"skills-sync-meta\">\n <span>Manual sync: {{ syncActionStatus }}</span>\n </div>\n <div v-if=\"syncActionError\" class=\"skills-sync-error\">\n {{ syncActionError }}\n </div>\n <div v-if=\"deviceLogin\" class=\"skills-sync-device\">\n <span>Open <a :href=\"deviceLogin.verification_uri\" target=\"_blank\" rel=\"noreferrer\">GitHub device login</a> and enter code:</span>\n <code>{{ deviceLogin.user_code }}</code>\n </div>\n <div class=\"skills-sync-actions\">\n <button v-if=\"!syncStatus.loggedIn\" class=\"skills-hub-sort\" type=\"button\" @click=\"startGithubFirebaseLogin\">Login with GitHub</button>\n <button v-if=\"!syncStatus.loggedIn\" class=\"skills-hub-sort\" type=\"button\" @click=\"startGithubLogin\">Device Login</button>\n <button v-if=\"syncStatus.loggedIn\" class=\"skills-hub-sort\" type=\"button\" @click=\"logoutGithub\" :disabled=\"isSyncActionInFlight\">Logout GitHub</button>\n <button class=\"skills-hub-sort\" type=\"button\" @click=\"startupSkillsSync\" :disabled=\"isSyncActionInFlight\">{{ isStartupSyncInFlight ? 'Syncing...' : 'Startup Sync' }}</button>\n <button class=\"skills-hub-sort\" type=\"button\" @click=\"pullSkillsSync\" :disabled=\"isSyncActionInFlight\">{{ isPullInFlight ? 'Pulling...' : 'Pull' }}</button>\n <button v-if=\"syncStatus.loggedIn\" class=\"skills-hub-sort\" type=\"button\" @click=\"pushSkillsSync\" :disabled=\"!syncStatus.configured || isSyncActionInFlight\">{{ isPushInFlight ? 'Pushing...' : 'Push' }}</button>\n </div>\n </div>\n\n <div v-if=\"toast\" class=\"skills-hub-toast\" :class=\"toastClass\">{{ toast.text }}</div>\n\n <div v-if=\"filteredInstalled.length > 0\" class=\"skills-hub-section\">\n <button class=\"skills-hub-section-toggle\" type=\"button\" @click=\"isInstalledOpen = !isInstalledOpen\">\n <span class=\"skills-hub-section-title\">Installed ({{ filteredInstalled.length }})</span>\n <IconTablerChevronRight class=\"skills-hub-section-chevron\" :class=\"{ 'is-open': isInstalledOpen }\" />\n </button>\n <div v-if=\"isInstalledOpen\" class=\"skills-hub-grid\">\n <SkillCard\n v-for=\"skill in filteredInstalled\"\n :key=\"skill.name\"\n :skill=\"skill\"\n @select=\"(skill) => openDetail(skill as HubSkill)\"\n />\n </div>\n </div>\n\n <div class=\"skills-hub-toolbar\">\n <div class=\"skills-hub-search-wrap\">\n <IconTablerSearch class=\"skills-hub-search-icon\" />\n <input\n ref=\"searchRef\"\n v-model=\"query\"\n class=\"skills-hub-search\"\n type=\"text\"\n placeholder=\"Search skills... (e.g. flight, docker, react)\"\n @keyup.enter.prevent=\"onSearchSubmit\"\n />\n <button class=\"skills-hub-search-btn\" type=\"button\" @click=\"onSearchSubmit\">Search</button>\n <span v-if=\"totalCount > 0\" class=\"skills-hub-count\">{{ totalCount }} skills</span>\n </div>\n <button class=\"skills-hub-sort\" type=\"button\" @click=\"toggleSort\">\n {{ sortLabel }}\n </button>\n </div>\n\n <div class=\"skills-hub-section\">\n <div v-if=\"isLoading\" class=\"skills-hub-loading\">Loading skills...</div>\n <div v-else-if=\"error\" class=\"skills-hub-error\">{{ error }}</div>\n <template v-else>\n <div v-if=\"browseSkills.length > 0\" class=\"skills-hub-grid\">\n <SkillCard\n v-for=\"skill in browseSkills\"\n :key=\"skill.url\"\n :skill=\"skill\"\n @select=\"(skill) => openDetail(skill as HubSkill)\"\n />\n </div>\n <div v-else-if=\"activeQuery.trim()\" class=\"skills-hub-empty\">No skills found for \"{{ activeQuery }}\"</div>\n </template>\n </div>\n\n <SkillDetailModal\n :skill=\"detailSkill\"\n :visible=\"isDetailOpen\"\n :is-installing=\"isDetailInstalling\"\n :is-uninstalling=\"isDetailUninstalling\"\n @close=\"isDetailOpen = false\"\n @install=\"handleInstall\"\n @uninstall=\"handleUninstall\"\n @toggle-enabled=\"handleToggleEnabled\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport IconTablerSearch from '../icons/IconTablerSearch.vue'\nimport IconTablerChevronRight from '../icons/IconTablerChevronRight.vue'\nimport SkillCard from './SkillCard.vue'\nimport SkillDetailModal, { type HubSkill } from './SkillDetailModal.vue'\nimport { useGithubSkillsSync } from '../../composables/useGithubSkillsSync'\n\nconst EMPTY_SKILL: HubSkill = { name: '', owner: '', description: '', url: '', installed: false }\nconst SKILLS_HUB_CACHE_KEY = 'codex-web-local.skills-hub.cache.v1'\ntype SkillsHubPayload = { data: HubSkill[]; installed?: HubSkill[]; total: number }\n\nconst searchRef = ref<HTMLInputElement | null>(null)\nconst query = ref('')\nconst activeQuery = ref('')\nconst sortMode = ref<'date' | 'name'>('date')\nconst browseSkills = ref<HubSkill[]>([])\nconst installedSkills = ref<HubSkill[]>([])\nconst totalCount = ref(0)\nconst isLoading = ref(false)\nconst error = ref('')\nconst isInstalledOpen = ref(true)\nconst isDetailOpen = ref(false)\nconst detailSkill = ref<HubSkill>(EMPTY_SKILL)\nconst toast = ref<{ text: string; type: 'success' | 'error' } | null>(null)\nconst actionSkillKey = ref('')\nconst isInstallActionInFlight = ref(false)\nconst isUninstallActionInFlight = ref(false)\nlet toastTimer: ReturnType<typeof setTimeout> | null = null\n\nconst emit = defineEmits<{\n 'skills-changed': []\n}>()\n\nconst sortLabel = computed(() => sortMode.value === 'date' ? 'Newest' : 'A-Z')\nconst toastClass = computed(() => toast.value?.type === 'error' ? 'skills-hub-toast-error' : 'skills-hub-toast-success')\nconst currentDetailSkillKey = computed(() => `${detailSkill.value.owner}/${detailSkill.value.name}`)\nconst isDetailInstalling = computed(() =>\n isInstallActionInFlight.value && actionSkillKey.value === currentDetailSkillKey.value,\n)\nconst isDetailUninstalling = computed(() =>\n isUninstallActionInFlight.value && actionSkillKey.value === currentDetailSkillKey.value,\n)\nconst githubRepoUrl = computed(() => {\n if (!syncStatus.value.configured) return ''\n const owner = syncStatus.value.repoOwner.trim()\n const repo = syncStatus.value.repoName.trim()\n if (!owner || !repo) return ''\n return `https://github.com/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`\n})\nconst filteredInstalled = computed(() => {\n const q = query.value.toLowerCase().trim()\n if (!q) return installedSkills.value\n return installedSkills.value.filter((s) =>\n s.name.toLowerCase().includes(q) ||\n s.owner.toLowerCase().includes(q) ||\n (s.displayName ?? '').toLowerCase().includes(q),\n )\n})\n\nfunction showToast(text: string, type: 'success' | 'error' = 'success'): void {\n toast.value = { text, type }\n if (toastTimer) clearTimeout(toastTimer)\n toastTimer = setTimeout(() => { toast.value = null }, 3000)\n}\n\nfunction toggleSort(): void {\n sortMode.value = sortMode.value === 'date' ? 'name' : 'date'\n void fetchSkills(activeQuery.value)\n}\n\nfunction cacheKey(q: string): string {\n return `${sortMode.value}::${q.trim().toLowerCase()}`\n}\n\nfunction readCache(key: string): SkillsHubPayload | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.localStorage.getItem(SKILLS_HUB_CACHE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw) as { byKey?: Record<string, SkillsHubPayload> }\n return parsed.byKey?.[key] ?? null\n } catch {\n return null\n }\n}\n\nfunction writeCache(key: string, payload: SkillsHubPayload): void {\n if (typeof window === 'undefined') return\n try {\n const raw = window.localStorage.getItem(SKILLS_HUB_CACHE_KEY)\n const parsed = raw ? (JSON.parse(raw) as { byKey?: Record<string, SkillsHubPayload> }) : {}\n const byKey = parsed.byKey ?? {}\n byKey[key] = payload\n window.localStorage.setItem(SKILLS_HUB_CACHE_KEY, JSON.stringify({ byKey }))\n } catch {\n // best-effort cache\n }\n}\n\nfunction clearCache(): void {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.removeItem(SKILLS_HUB_CACHE_KEY)\n } catch {\n // best-effort cache cleanup\n }\n}\n\nfunction applySkillsPayload(payload: SkillsHubPayload): void {\n const inst = payload.installed ?? []\n installedSkills.value = inst\n const installedNames = new Set(inst.map((s) => s.name))\n browseSkills.value = payload.data\n .map((s) => {\n if (s.installed || installedNames.has(s.name)) {\n const local = inst.find((i) => i.name === s.name)\n return { ...s, installed: true, path: local?.path ?? s.path, enabled: local?.enabled ?? s.enabled }\n }\n return s\n })\n .filter((s) => !s.installed)\n totalCount.value = payload.total\n}\n\nasync function fetchSkills(q: string): Promise<void> {\n const normalizedQuery = q.trim()\n activeQuery.value = normalizedQuery\n const key = cacheKey(normalizedQuery)\n const cached = readCache(key)\n if (cached) {\n applySkillsPayload(cached)\n }\n isLoading.value = !cached\n error.value = ''\n try {\n const params = new URLSearchParams()\n if (normalizedQuery) params.set('q', normalizedQuery)\n params.set('limit', '100')\n params.set('sort', sortMode.value)\n const resp = await fetch(`/codex-api/skills-hub?${params}`)\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`)\n const data = (await resp.json()) as SkillsHubPayload\n applySkillsPayload(data)\n writeCache(key, data)\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to load skills'\n } finally {\n isLoading.value = false\n }\n}\n\nfunction onSearchSubmit(): void {\n void fetchSkills(query.value)\n}\n\nfunction openDetail(skill: HubSkill): void {\n detailSkill.value = skill\n isDetailOpen.value = true\n}\n\nasync function handleInstall(skill: HubSkill): Promise<void> {\n actionSkillKey.value = `${skill.owner}/${skill.name}`\n isInstallActionInFlight.value = true\n try {\n const resp = await fetch('/codex-api/skills-hub/install', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ owner: skill.owner, name: skill.name }),\n })\n const data = (await resp.json()) as { ok?: boolean; error?: string; path?: string }\n if (!data.ok) throw new Error(data.error || 'Install failed')\n const installed = { ...skill, installed: true, path: data.path, enabled: true }\n installedSkills.value = [...installedSkills.value, installed]\n browseSkills.value = browseSkills.value.filter((s) => s.name !== skill.name)\n detailSkill.value = installed\n showToast(`${skill.displayName || skill.name} skill installed`)\n isDetailOpen.value = false\n clearCache()\n emit('skills-changed')\n } catch (e) {\n showToast(e instanceof Error ? e.message : 'Failed to install skill', 'error')\n } finally {\n isInstallActionInFlight.value = false\n }\n}\n\nasync function handleUninstall(skill: HubSkill): Promise<void> {\n actionSkillKey.value = `${skill.owner}/${skill.name}`\n isUninstallActionInFlight.value = true\n try {\n const resp = await fetch('/codex-api/skills-hub/uninstall', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name: skill.name, path: skill.path }),\n })\n const data = (await resp.json()) as { ok?: boolean; error?: string }\n if (!data.ok) throw new Error(data.error || 'Uninstall failed')\n installedSkills.value = installedSkills.value.filter((s) => s.name !== skill.name)\n if (skill.owner !== 'local') {\n browseSkills.value = [...browseSkills.value, { ...skill, installed: false, path: undefined, enabled: undefined }]\n }\n showToast(`${skill.displayName || skill.name} skill uninstalled`)\n isDetailOpen.value = false\n clearCache()\n emit('skills-changed')\n } catch (e) {\n showToast(e instanceof Error ? e.message : 'Failed to uninstall skill', 'error')\n } finally {\n isUninstallActionInFlight.value = false\n }\n}\n\nasync function handleToggleEnabled(skill: HubSkill, enabled: boolean): Promise<void> {\n try {\n const resp = await fetch('/codex-api/rpc', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ method: 'skills/config/write', params: { path: skill.path, enabled } }),\n })\n if (!resp.ok) throw new Error('Failed to update skill')\n await fetch('/codex-api/skills-sync/push', { method: 'POST' })\n showToast(`${skill.displayName || skill.name} skill ${enabled ? 'enabled' : 'disabled'}`)\n await fetchSkills(activeQuery.value)\n } catch (e) {\n showToast(e instanceof Error ? e.message : 'Failed to update skill', 'error')\n }\n}\n\nconst {\n deviceLogin,\n isPullInFlight,\n isPushInFlight,\n isStartupSyncInFlight,\n isSyncActionInFlight,\n loadSyncStatus,\n logoutGithub,\n pullSkillsSync,\n pushSkillsSync,\n startupSkillsSync,\n startGithubFirebaseLogin,\n startGithubLogin,\n syncActionError,\n syncActionStatus,\n syncStatus,\n} = useGithubSkillsSync({\n showToast,\n onPulled: async () => {\n await fetchSkills(activeQuery.value)\n emit('skills-changed')\n },\n})\n\nonMounted(() => {\n void fetchSkills('')\n void loadSyncStatus()\n})\n</script>\n\n<style scoped>\n@reference \"tailwindcss\";\n\n.skills-hub {\n @apply flex flex-col gap-3 sm:gap-4 p-3 sm:p-6 max-w-4xl mx-auto w-full overflow-y-auto h-full;\n}\n\n.skills-hub-header {\n @apply flex flex-col gap-1;\n}\n\n.skills-hub-title {\n @apply text-xl sm:text-2xl font-semibold text-zinc-900 m-0;\n}\n\n.skills-hub-subtitle {\n @apply text-sm text-zinc-500 m-0;\n}\n\n.skills-hub-toolbar {\n @apply flex flex-col sm:flex-row items-stretch sm:items-center gap-2;\n}\n\n.skills-hub-search-wrap {\n @apply flex-1 flex items-center gap-2 rounded-xl border border-zinc-200 bg-white px-3 py-2 transition focus-within:border-zinc-400 focus-within:shadow-sm;\n}\n\n.skills-hub-search-icon {\n @apply w-4 h-4 text-zinc-400 shrink-0;\n}\n\n.skills-hub-search {\n @apply flex-1 min-w-0 bg-transparent text-sm text-zinc-800 placeholder-zinc-400 outline-none border-none p-0;\n}\n\n.skills-hub-search-btn {\n @apply shrink-0 rounded-md border border-zinc-200 bg-white px-2 py-1 text-xs font-medium text-zinc-600 transition hover:bg-zinc-50 hover:border-zinc-300 cursor-pointer;\n}\n\n.skills-hub-count {\n @apply text-xs text-zinc-400 whitespace-nowrap;\n}\n\n.skills-hub-sort {\n @apply shrink-0 rounded-lg border border-zinc-200 bg-white px-2.5 py-1.5 text-xs font-medium text-zinc-600 transition hover:bg-zinc-50 hover:border-zinc-300 cursor-pointer;\n}\n\n.skills-sync-panel {\n @apply rounded-xl border border-zinc-200 bg-zinc-50 p-3 flex flex-col gap-2;\n}\n\n.skills-sync-header {\n @apply flex flex-wrap items-center gap-2 text-sm text-zinc-700;\n}\n\n.skills-sync-badge {\n @apply text-xs rounded-md border border-zinc-300 bg-white px-2 py-0.5;\n}\n\n.skills-sync-badge-link {\n @apply text-zinc-700 hover:text-zinc-900 hover:border-zinc-400;\n}\n\n.skills-sync-device {\n @apply text-xs text-zinc-600 flex items-center gap-2 flex-wrap;\n}\n\n.skills-sync-meta {\n @apply text-xs text-zinc-600 flex items-center gap-3 flex-wrap;\n}\n\n.skills-sync-error {\n @apply text-xs text-rose-700 bg-rose-50 border border-rose-200 rounded-md px-2 py-1;\n}\n\n.skills-sync-actions {\n @apply flex flex-wrap gap-2;\n}\n\n.skills-hub-toast {\n @apply rounded-lg px-3 py-2 text-sm font-medium;\n}\n\n.skills-hub-toast-success {\n @apply border border-emerald-200 bg-emerald-50 text-emerald-700;\n}\n\n.skills-hub-toast-error {\n @apply border border-rose-200 bg-rose-50 text-rose-700;\n}\n\n.skills-hub-section {\n @apply flex flex-col gap-2;\n}\n\n.skills-hub-section-toggle {\n @apply flex items-center gap-1.5 border-0 bg-transparent p-0 text-sm font-medium text-zinc-600 transition hover:text-zinc-900 cursor-pointer;\n}\n\n.skills-hub-section-title {\n @apply text-sm font-medium;\n}\n\n.skills-hub-section-chevron {\n @apply w-3.5 h-3.5 transition-transform;\n}\n\n.skills-hub-section-chevron.is-open {\n @apply rotate-90;\n}\n\n.skills-hub-grid {\n @apply grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3;\n}\n\n.skills-hub-loading {\n @apply text-sm text-zinc-400 py-8 text-center;\n}\n\n.skills-hub-error {\n @apply text-sm text-rose-600 py-4 text-center rounded-lg border border-rose-200 bg-rose-50;\n}\n\n.skills-hub-empty {\n @apply text-sm text-zinc-400 py-8 text-center;\n}\n</style>\n"],"file":"assets/SkillsHub-Crvp1wow.js"}
|