codexui-android 0.1.85 → 0.1.89
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/dist/assets/{ReviewPane-BYXF27M-.js → ReviewPane-CwRK6K44.js} +2 -2
- package/dist/assets/{ReviewPane-BYXF27M-.js.map → ReviewPane-CwRK6K44.js.map} +1 -1
- package/dist/assets/{SkillsHub-GpJ2Lrm3.js → SkillsHub-BIRLBCzr.js} +2 -2
- package/dist/assets/{SkillsHub-GpJ2Lrm3.js.map → SkillsHub-BIRLBCzr.js.map} +1 -1
- package/dist/assets/{ThreadConversation-BsN7bN3q.css → ThreadConversation-BKfO22g2.css} +1 -1
- package/dist/assets/ThreadConversation-DymdJ3Hs.js +40 -0
- package/dist/assets/ThreadConversation-DymdJ3Hs.js.map +1 -0
- package/dist/assets/index-Bn_MPBwp.js +92 -0
- package/dist/assets/index-Bn_MPBwp.js.map +1 -0
- package/dist/assets/{index-iN-aqOuT.css → index-C_knKlTI.css} +1 -1
- package/dist/index.html +2 -2
- package/dist-cli/{chunk-JQMCS7KJ.js → chunk-PUR7OUAG.js} +41 -36
- package/dist-cli/chunk-PUR7OUAG.js.map +1 -0
- package/dist-cli/index.js +724 -139
- package/dist-cli/index.js.map +1 -1
- package/dist-cli/instrument.js +1 -1
- package/package.json +10 -4
- package/dist/assets/ThreadConversation-BNp42k0F.js +0 -40
- package/dist/assets/ThreadConversation-BNp42k0F.js.map +0 -1
- package/dist/assets/index-CWwHJTfC.js +0 -92
- package/dist/assets/index-CWwHJTfC.js.map +0 -1
- package/dist-cli/chunk-JQMCS7KJ.js.map +0 -1
|
@@ -1,3 +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,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-CWwHJTfC.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-
|
|
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,L as Pe,y as S,_ as ne,w as Fe,i as te,q as Ae,M as Oe,r as p,z as me,b as Ue,v as i,x as ge,N as Ge,F as X,g as we,O as xe,C as He,P as Ne,Q as De}from"./index-Bn_MPBwp.js";const Re={class:"skill-card-top"},je=["src","alt"],Ke={key:1,class:"skill-card-avatar-fallback"},Me={class:"skill-card-info"},Be={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",Me,[o("div",Be,[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"},Ms={class:"skills-hub-search-wrap"},Bs=["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",M(_.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(B=>B.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 M(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 B=await fetch(`/codex-api/skills-hub?${m}`);if(!B.ok)throw new Error(`HTTP ${B.status}`);const ye=await B.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(){M(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 M(_.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 M(_.value),g("skills-changed")}});return Ue(()=>{M(""),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",Ms,[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,Bs),[[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-BIRLBCzr.js.map
|
|
@@ -1 +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-GpJ2Lrm3.js"}
|
|
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-BIRLBCzr.js"}
|