@runfusion/fusion 0.27.1 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/bin.js +3418 -864
  2. package/dist/client/assets/AgentDetailView-DSpIqBOs.css +1 -0
  3. package/dist/client/assets/{AgentDetailView-shgiiUb4.js → AgentDetailView-V32TpEe1.js} +3 -3
  4. package/dist/client/assets/{AgentsView-CpwqOVDz.js → AgentsView-CxITZE-U.js} +3 -3
  5. package/dist/client/assets/ChatView-7WPHyB_7.js +1 -0
  6. package/dist/client/assets/DevServerView-Diw4hjw0.js +1 -0
  7. package/dist/client/assets/{DirectoryPicker-C0kmRv0u.js → DirectoryPicker-CWSApoLU.js} +1 -1
  8. package/dist/client/assets/{DocumentsView-B94U9ijs.js → DocumentsView-1kr0dhsV.js} +1 -1
  9. package/dist/client/assets/{EvalsView-O_4YWy--.js → EvalsView-cDfXYiK6.js} +1 -1
  10. package/dist/client/assets/{ExperimentalAgentOnboardingModal-CkEiF85-.js → ExperimentalAgentOnboardingModal-lNSf6UtY.js} +1 -1
  11. package/dist/client/assets/{InsightsView-D-Qe0tRr.js → InsightsView-D2AjxOnu.js} +1 -1
  12. package/dist/client/assets/{MemoryView-CoRUmRvb.js → MemoryView-Dq4yjIPI.js} +1 -1
  13. package/dist/client/assets/{NodesView-DQzXjcLc.js → NodesView-Cpmkze7n.js} +4 -4
  14. package/dist/client/assets/{PiExtensionsManager-Dn1LmFbq.js → PiExtensionsManager-oi9FHT0y.js} +2 -2
  15. package/dist/client/assets/PluginManager-C4UoLv4U.css +1 -0
  16. package/dist/client/assets/PluginManager-o12KG4e8.js +1 -0
  17. package/dist/client/assets/ResearchView-C7uXon3o.css +1 -0
  18. package/dist/client/assets/ResearchView-C9bjVB7-.js +1 -0
  19. package/dist/client/assets/{SettingsModal-Bg1-3JO_.js → SettingsModal-CT01a5Rh.js} +1 -1
  20. package/dist/client/assets/SettingsModal-Cu93Jjho.js +31 -0
  21. package/dist/client/assets/{SetupWizardModal-DuzYPbuJ.js → SetupWizardModal-Agi3XNtZ.js} +1 -1
  22. package/dist/client/assets/{SkillsView-BIFoVNUf.js → SkillsView-DmZfbGLs.js} +1 -1
  23. package/dist/client/assets/{StashRecoveryView-C52KsV7f.js → StashRecoveryView-BwKJQaUW.js} +1 -1
  24. package/dist/client/assets/{TodoView-sS_mT0Y7.js → TodoView-uJRg4Cnl.js} +2 -2
  25. package/dist/client/assets/dashboard-view-C7Snlgow.js +21 -0
  26. package/dist/client/assets/dashboard-view-DqWnXEbq.css +1 -0
  27. package/dist/client/assets/{folder-open-B9cwJ-OX.js → folder-open-DU-Ec_iC.js} +1 -1
  28. package/dist/client/assets/index-CD0_dkME.css +1 -0
  29. package/dist/client/assets/index-DiewofJh.js +692 -0
  30. package/dist/client/assets/{star-BDn04UYV.js → star-Qi0GspVN.js} +1 -1
  31. package/dist/client/assets/{upload-zdPPycKQ.js → upload-Dx_l9Vbu.js} +1 -1
  32. package/dist/client/assets/{users-CPYZjK2g.js → users-3Qw_TMah.js} +1 -1
  33. package/dist/client/index.html +2 -2
  34. package/dist/client/version.json +1 -1
  35. package/dist/droid-cli/package.json +1 -1
  36. package/dist/extension.js +2628 -573
  37. package/dist/pi-claude-cli/package.json +1 -1
  38. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
  39. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/registration.test.ts +2 -4
  40. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/runtime-availability.test.ts +5 -2
  41. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/workflow-integration.test.ts +7 -4
  42. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  43. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  44. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  45. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  46. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  47. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  48. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  49. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  50. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  51. package/package.json +1 -1
  52. package/skill/fusion/references/engine-tools.md +1 -0
  53. package/skill/fusion/references/extension-tools.md +3 -1
  54. package/dist/client/assets/AgentDetailView-B7QRcHJH.css +0 -1
  55. package/dist/client/assets/ChatView-DyRBOIKL.js +0 -1
  56. package/dist/client/assets/DevServerView-Cdelj9-m.js +0 -1
  57. package/dist/client/assets/PluginManager-DtRQXia5.css +0 -1
  58. package/dist/client/assets/PluginManager-Y0fs-6No.js +0 -1
  59. package/dist/client/assets/ResearchView-BEI4ZSGs.css +0 -1
  60. package/dist/client/assets/ResearchView-CjOxKhdS.js +0 -1
  61. package/dist/client/assets/SettingsModal-DL7tjJQa.js +0 -31
  62. package/dist/client/assets/dashboard-view-BoTzlP8b.css +0 -1
  63. package/dist/client/assets/dashboard-view-MB-86hAu.js +0 -21
  64. package/dist/client/assets/index-BOjPRqEk.js +0 -692
  65. package/dist/client/assets/index-BmSEq8Rb.css +0 -1
@@ -1,14 +1,14 @@
1
- import{r as s,j as e}from"./vendor-react-K0fH_qHe.js";import{c as ze,aJ as ls,aK as os,aL as cs,aM as is,aN as Le,aO as be,aP as Ve,aQ as ye,aR as Oe,A as Fe,ap as ds,U as Ke,aS as Ue,aT as us,W as _e,u as ms,R as Ne,a as we,I as he,aU as Te,aV as hs,aW as fs,aX as $e,X as me,a8 as gs,a9 as xs,aY as De,F as ps,aZ as Ie,a_ as js,a$ as vs,b0 as bs,b1 as ys,b2 as _s,G as Ns}from"./index-BOjPRqEk.js";import{v as ks}from"./projectDetection-G3XuxD2X.js";import{U as Be}from"./upload-zdPPycKQ.js";import"./vendor-xterm-DzcZoU0P.js";/**
1
+ import{r as s,j as e}from"./vendor-react-K0fH_qHe.js";import{c as ze,aJ as ls,aK as os,aL as cs,aM as is,aN as Le,aO as be,aP as Ve,aQ as ye,aR as Fe,A as Oe,ap as ds,U as Ke,aS as Ue,aT as us,W as _e,u as ms,R as Ne,a as we,I as he,aU as Te,aV as hs,aW as fs,aX as gs,aY as $e,X as me,a8 as xs,a9 as ps,aZ as De,F as js,a_ as Ie,a$ as vs,b0 as bs,b1 as ys,b2 as _s,b3 as Ns,G as ks}from"./index-DiewofJh.js";import{v as Cs}from"./projectDetection-G3XuxD2X.js";import{U as Be}from"./upload-Dx_l9Vbu.js";import"./vendor-xterm-DzcZoU0P.js";/**
2
2
  * @license lucide-react v1.7.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
5
5
  * See the LICENSE file in the root directory of this source tree.
6
- */const Cs=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}],["path",{d:"M5 12.859a10 10 0 0 1 5.17-2.69",key:"1dl1wf"}],["path",{d:"M19 12.859a10 10 0 0 0-2.007-1.523",key:"4k23kn"}],["path",{d:"M2 8.82a15 15 0 0 1 4.177-2.643",key:"1grhjp"}],["path",{d:"M22 8.82a15 15 0 0 0-11.288-3.764",key:"z3jwby"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],Ss=ze("wifi-off",Cs);/**
6
+ */const Ss=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}],["path",{d:"M5 12.859a10 10 0 0 1 5.17-2.69",key:"1dl1wf"}],["path",{d:"M19 12.859a10 10 0 0 0-2.007-1.523",key:"4k23kn"}],["path",{d:"M2 8.82a15 15 0 0 1 4.177-2.643",key:"1grhjp"}],["path",{d:"M22 8.82a15 15 0 0 0-11.288-3.764",key:"z3jwby"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],ws=ze("wifi-off",Ss);/**
7
7
  * @license lucide-react v1.7.0 - ISC
8
8
  *
9
9
  * This source code is licensed under the ISC license.
10
10
  * See the LICENSE file in the root directory of this source tree.
11
- */const ws=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0",key:"dnpr2z"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0",key:"1x1e6c"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}]],Ms=ze("wifi",ws);function pe(t){const{lastSyncAt:r,remoteReachable:a,diff:i}=t,u=i.global.length+i.project.length;return r===null?{syncState:"never-synced",lastSyncAt:r,diffCount:0}:a?u>0?{syncState:"diff",lastSyncAt:r,diffCount:u}:{syncState:"synced",lastSyncAt:r,diffCount:0}:{syncState:"error",lastSyncAt:r,diffCount:u}}function ke(t){if(t===null)return"Never synced";const r=new Date(t);if(Number.isNaN(r.getTime()))return"Never synced";const i=Date.now()-r.getTime(),u=Math.floor(i/1e3),m=Math.floor(u/60),o=Math.floor(m/60),j=Math.floor(o/24);return m<1?"Synced just now":m<60?`Synced ${m}m ago`:o<24?`Synced ${o}h ago`:`Synced ${j}d ago`}function Ps(t){switch(t){case"synced":return"var(--color-success)";case"pending":return"var(--color-warning)";case"diff":return"var(--color-warning)";case"error":return"var(--color-error)";case"never-synced":return"var(--text-muted)"}}const Es=3e4;function Rs(){const[t,r]=s.useState({}),[a,i]=s.useState(!1),[u,m]=s.useState({}),[o,j]=s.useState(null),g=s.useRef(new Set),c=s.useRef(!1),N=s.useRef(null),w=s.useRef(null),v=s.useCallback(async(p,x)=>{try{const S=await ls(p);r(Z=>({...Z,[p]:S})),j(null)}catch(S){console.error(`Failed to fetch sync status for node ${p}:`,S),j(S instanceof Error?S.message:"Failed to fetch sync status")}},[]),d=s.useCallback(async()=>{const p=Array.from(g.current);if(p.length===0)return;N.current&&N.current.abort(),N.current=new AbortController;const x=!c.current;x&&i(!0),j(null);try{const S=await Promise.allSettled(p.map(H=>v(H,x)));c.current=!0,S.filter(H=>H.status==="rejected").length>0&&j("Some sync status requests failed")}catch(S){if(S instanceof Error&&S.name==="AbortError")return;j(S instanceof Error?S.message:"Failed to fetch sync status"),c.current=!0}finally{i(!1)}},[v]),M=s.useCallback(()=>{w.current&&clearInterval(w.current),w.current=setInterval(()=>{d()},Es)},[d]),_=s.useCallback(()=>{w.current&&(clearInterval(w.current),w.current=null)},[]);s.useEffect(()=>(d(),M(),()=>{_(),N.current&&N.current.abort()}),[d,M,_]);const l=s.useCallback(p=>{g.current.has(p)||(g.current.add(p),v(p,!c.current))},[v]),b=s.useCallback(p=>{g.current.delete(p),r(x=>{const S={...x};return delete S[p],S}),g.current.size===0&&_()},[_]),C=s.useCallback(async p=>{m(x=>({...x,[p]:!0})),j(null);try{const x=await os(p);return v(p,!1),!x.success&&x.error&&j(x.error),x}catch(x){const S=x instanceof Error?x.message:"Push settings failed";throw j(S),x}finally{m(x=>{const S={...x};return delete S[p],S})}},[v]),k=s.useCallback(async p=>{m(x=>({...x,[p]:!0})),j(null);try{const x=await cs(p);return v(p,!1),!x.success&&x.error&&j(x.error),x}catch(x){const S=x instanceof Error?x.message:"Pull settings failed";throw j(S),x}finally{m(x=>{const S={...x};return delete S[p],S})}},[v]),$=s.useCallback(async p=>{m(x=>({...x,[p]:!0})),j(null);try{return await is(p)}catch(x){const S=x instanceof Error?x.message:"Auth sync failed";throw j(S),x}finally{m(x=>{const S={...x};return delete S[p],S})}},[]),V=s.useCallback(p=>t[p]?.authMatch,[t]),A=s.useCallback(p=>t[p]?.authDiff,[t]);return{syncStatusMap:t,loading:a,actionLoading:u,error:o,refresh:d,trackNode:l,untrackNode:b,pushSettings:C,pullSettings:k,syncAuth:$,getAuthSyncState:V,getAuthProviders:A}}const As=1e4,Ls=1e3;function $s(){const[t,r]=s.useState([]),[a,i]=s.useState(!0),[u,m]=s.useState(null),o=s.useRef(null),j=s.useRef(0),g=s.useCallback(async()=>{try{m(null);const c=await Le();r(c.nodes)}catch(c){m(c instanceof Error?c.message:"Failed to fetch mesh state")}},[]);return s.useEffect(()=>{let c=!1;async function N(){i(!0);try{const v=await Le();c||(r(v.nodes),m(null))}catch(v){c||m(v instanceof Error?v.message:"Failed to fetch mesh state")}finally{c||i(!1)}}N();const w=()=>{if(document.visibilityState!=="visible")return;const v=Date.now();v-j.current<Ls||(j.current=v,g())};return document.addEventListener("visibilitychange",w),()=>{c=!0,document.removeEventListener("visibilitychange",w)}},[g]),s.useEffect(()=>{if(!a)return o.current=setInterval(()=>{g()},As),()=>{o.current&&(clearInterval(o.current),o.current=null)}},[a,g]),{meshState:t,loading:a,error:u,refresh:g}}const de={online:{label:"Online",color:"var(--color-success)",className:"node-card__status--online"},offline:{label:"Offline",color:"var(--color-error)",className:"node-card__status--offline"},connecting:{label:"Connecting",color:"var(--color-warning)",className:"node-card__status--connecting"},error:{label:"Error",color:"var(--color-error)",className:"node-card__status--error"},creating:{label:"Creating",color:"var(--color-warning)",className:"node-card__status--creating"},recreating:{label:"Recreating",color:"var(--color-warning)",className:"node-card__status--recreating"},deleting:{label:"Deleting",color:"var(--color-error)",className:"node-card__status--deleting"},running:{label:"Running",color:"var(--color-success)",className:"node-card__status--online"},stopped:{label:"Stopped",color:"var(--color-error)",className:"node-card__status--offline"},exited:{label:"Exited",color:"var(--color-error)",className:"node-card__status--offline"}},Ds={match:"var(--color-success)",differs:"var(--color-warning)","not-synced":"var(--text-muted)"};function Is(t,r){if(t==="match")return"Auth credentials match";if(t==="not-synced")return"Auth not synced";if(r&&Object.keys(r).length>0){const a=Object.entries(r).filter(([,i])=>i==="differs").map(([i])=>i);if(a.length>0)return`Auth credentials differ: ${a.join(", ")}`}return"Auth credentials differ"}function zs(t,r=42){return t.length<=r?t:`${t.slice(0,r-3)}...`}function Vs(t,r){const a=t.node,i=r.node;if(a.id!==i.id||a.name!==i.name||a.type!==i.type||a.url!==i.url||a.status!==i.status||a.maxConcurrent!==i.maxConcurrent||a.updatedAt!==i.updatedAt||t.isLoading!==r.isLoading)return!1;const u=t.managedDockerNode,m=r.managedDockerNode;if(!!u!=!!m||u&&m&&(u.id!==m.id||u.status!==m.status||u.imageTag!==m.imageTag||u.updatedAt!==m.updatedAt))return!1;const o=t.syncStatus,j=r.syncStatus;if(!(!o&&!j)){if(!o||!j)return!1;if(o.syncState!==j.syncState||o.lastSyncAt!==j.lastSyncAt||o.diffCount!==j.diffCount)return!1}if(t.authSyncState!==r.authSyncState)return!1;const g=t.authSyncProviders,c=r.authSyncProviders;if(g!==c){if(!g||!c)return!1;{const v=Object.keys(g),d=Object.keys(c);if(v.length!==d.length||v.some(M=>g[M]!==c[M]))return!1}}const N=be(t.projects,a),w=be(r.projects,i);return N===w}function Os({node:t,projects:r,onHealthCheck:a,onEdit:i,onRemove:u,isLoading:m=!1,syncStatus:o,authSyncState:j,authSyncProviders:g,managedDockerNode:c}){const[N,w]=s.useState(!1),v=de[t.status]??de.offline,d=c?de[c.status]??de.error:null,M=c?.hostConfig.type==="remote"?`Remote: ${c.hostConfig.host??"unknown"}`:"Local Docker",_=s.useMemo(()=>be(r,t),[r,t]),l=s.useCallback(()=>{i(t)},[i,t]),b=s.useCallback(V=>{V.stopPropagation(),a(t.id)},[a,t.id]),C=s.useCallback(V=>{V.stopPropagation(),i(t)},[i,t]),k=s.useCallback(V=>{if(V.stopPropagation(),!N){w(!0);return}u(t.id),w(!1)},[N,u,t.id]),$=s.useCallback(V=>{(V.key==="Enter"||V.key===" ")&&(V.preventDefault(),i(t))},[i,t]);return e.jsxs("article",{className:`node-card ${m?"node-card--loading":""}`,"data-node-id":t.id,role:"button",tabIndex:0,onClick:l,onKeyDown:$,children:[e.jsx("header",{className:"node-card__header",children:e.jsxs("div",{className:"node-card__title-wrap",children:[e.jsx("div",{className:"node-card__icon",children:e.jsx(Ve,{size:18})}),e.jsxs("div",{children:[e.jsx("h3",{className:"node-card__name",title:t.name,children:t.name}),e.jsxs("div",{className:"node-card__meta-row",children:[e.jsx("span",{className:"node-card__type-badge",children:t.type==="local"?"Local":"Remote"}),c&&e.jsxs("span",{className:"node-card__docker-badge",title:"Managed Docker node",children:[e.jsx(ye,{size:12,"aria-hidden":!0}),"Docker"]}),e.jsxs("span",{className:`node-card__status ${v.className}`,style:{color:v.color},"data-status":t.status,children:[e.jsx("span",{className:"node-card__status-indicator",style:{backgroundColor:v.color},"aria-hidden":!0}),v.label]}),c&&d&&e.jsxs("span",{className:`node-card__status ${d.className}`,style:{color:d.color},"data-status":c.status,children:[e.jsx("span",{className:"node-card__status-indicator",style:{backgroundColor:d.color},"aria-hidden":!0}),d.label]}),t.type==="remote"&&j&&e.jsx("span",{className:`node-card__auth-indicator node-card__auth-indicator--${j}`,title:Is(j,g),"aria-label":`Auth sync: ${j==="match"?"credentials match":j==="differs"?"credentials differ":"not synced"}`,style:{color:Ds[j]},children:e.jsx(Oe,{size:14})})]})]})]})}),e.jsxs("div",{className:"node-card__body",children:[t.type==="remote"&&t.url&&e.jsx("div",{className:"node-card__url",title:t.url,children:zs(t.url)}),c&&e.jsxs("div",{className:"node-card__docker-meta",children:[e.jsxs("span",{title:`${c.imageName}:${c.imageTag}`,children:[c.imageName,":",c.imageTag]}),e.jsx("span",{title:M,children:M})]}),e.jsxs("div",{className:"node-card__metrics",children:[e.jsxs("div",{className:"node-card__metric",children:[e.jsx("span",{className:"node-card__metric-label",children:"Projects"}),e.jsx("span",{className:"node-card__metric-value",children:_})]}),e.jsxs("div",{className:"node-card__metric",children:[e.jsx("span",{className:"node-card__metric-label",children:"Concurrency"}),e.jsx("span",{className:"node-card__metric-value",children:t.maxConcurrent})]})]}),t.type==="remote"&&o&&e.jsxs("div",{className:"node-card__sync","data-sync-state":o.syncState,"data-testid":"node-card-sync",children:[e.jsx("span",{className:"node-card__sync-dot",style:{backgroundColor:Ps(o.syncState)},"aria-hidden":!0}),e.jsx("span",{className:"node-card__sync-time",children:ke(o.lastSyncAt)})]})]}),e.jsxs("footer",{className:"node-card__actions",children:[e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:b,disabled:m,"aria-label":"Run node health check",title:"Health Check",children:[e.jsx(Fe,{size:14}),e.jsx("span",{children:"Health"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:C,disabled:m,"aria-label":"Edit node",title:"Edit",children:[e.jsx(ds,{size:14}),e.jsx("span",{children:"Edit"})]}),c&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Start node container",title:"Available after FN-3113",children:[e.jsx(Ke,{size:14}),e.jsx("span",{children:"Start"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Stop node container",title:"Available after FN-3113",children:[e.jsx(Ue,{size:14}),e.jsx("span",{children:"Stop"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Restart node container",title:"Available after FN-3113",children:[e.jsx(us,{size:14}),e.jsx("span",{children:"Restart"})]})]}),e.jsxs("button",{className:`btn btn-sm node-card__action node-card__action--remove ${N?"btn-danger is-armed":""}`,type:"button",onClick:k,disabled:m,"aria-label":N?"Confirm remove node":"Remove node",title:N?"Confirm remove":"Remove",children:[e.jsx(_e,{size:14}),e.jsx("span",{children:N?"Confirm":"Remove"})]})]})]})}const Fs=s.memo(Os,Vs),ce={online:"var(--success, var(--color-success))",offline:"var(--text-dim)",connecting:"var(--color-warning)",error:"var(--color-error)"},ue=28,Ks=12,Us=300,Ts=120;function Bs({nodes:t,className:r}){const a=s.useMemo(()=>t.find(c=>c.nodeType==="local")??t[0],[t]),i=s.useMemo(()=>t.filter(c=>c.nodeId!==a?.nodeId),[t,a?.nodeId]),u=s.useMemo(()=>Us+Math.max(0,i.length-4)*20,[i.length]),m=u/2,o=u/2,j=s.useMemo(()=>{const c=new Map;if(a&&c.set(a.nodeId,{x:m,y:o,node:a}),i.length>0){const N=Math.min(Ts,u/2-ue-10),w=2*Math.PI/i.length,v=-Math.PI/2;i.forEach((d,M)=>{const _=v+M*w;c.set(d.nodeId,{node:d,x:m+N*Math.cos(_),y:o+N*Math.sin(_)})})}return c},[m,o,a,i,u]),g=s.useMemo(()=>{const c=new Set,N=[];for(const w of t){const v=j.get(w.nodeId);if(v)for(const d of w.knownPeers){const M=j.get(d.peerNodeId);if(!M)continue;const _=[w.nodeId,d.peerNodeId].sort().join("::");c.has(_)||(c.add(_),N.push({key:_,from:v,to:M}))}}return N},[j,t]);return t.length===0?e.jsx("div",{className:`mesh-topology mesh-topology--empty ${r??""}`,children:e.jsx("div",{className:"mesh-topology__empty-state",children:e.jsx("p",{children:"No nodes to display"})})}):e.jsxs("div",{className:`mesh-topology ${r??""}`,children:[e.jsxs("svg",{className:"mesh-topology__svg",viewBox:`0 0 ${u} ${u}`,preserveAspectRatio:"xMidYMid meet","aria-label":"Node mesh topology visualization",children:[g.map(c=>e.jsx("line",{className:"mesh-topology__link mesh-topology__peer-line",x1:c.from.x,y1:c.from.y,x2:c.to.x,y2:c.to.y},c.key)),Array.from(j.values()).map(({node:c,x:N,y:w})=>e.jsxs("g",{className:"mesh-topology__node",transform:`translate(${N}, ${w})`,children:[e.jsx("circle",{className:"mesh-topology__node-circle",r:ue,fill:ce[c.status],"aria-label":`${c.nodeName} (${c.status})`}),e.jsx("text",{className:"mesh-topology__node-label",y:ue+Ks,textAnchor:"middle",children:c.nodeName.length>12?`${c.nodeName.slice(0,10)}…`:c.nodeName}),e.jsxs("g",{className:"mesh-topology__node-type",transform:`translate(0 ${-ue-10})`,children:[e.jsx("circle",{className:"mesh-topology__node-type-badge",r:"8"}),e.jsx("text",{className:"mesh-topology__node-type-text",textAnchor:"middle",dominantBaseline:"middle",children:c.nodeType==="local"?"L":"R"})]})]},c.nodeId))]}),e.jsxs("div",{className:"mesh-topology__legend",children:[e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:ce.online}}),e.jsx("span",{children:"Online"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:ce.offline}}),e.jsx("span",{children:"Offline"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:ce.connecting}}),e.jsx("span",{children:"Connecting"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:ce.error}}),e.jsx("span",{children:"Error"})]})]}),g.length===0&&e.jsx("p",{className:"mesh-topology__notice",children:"Peer-to-peer discovery data unavailable."})]})}const Hs=s.memo(Bs),Ce=1,Se=10;function qs(t){const r={projectMappings:{}};t.name.trim()||(r.name="Name is required"),t.type==="remote"&&!t.url?.trim()&&(r.url="URL is required for remote nodes"),(!Number.isFinite(t.maxConcurrent)||t.maxConcurrent<Ce||t.maxConcurrent>Se)&&(r.maxConcurrent=`Concurrency must be between ${Ce} and ${Se}`);for(const a of t.projectMappings){const i=ks(a.path);i.valid||(r.projectMappings[a.projectId]=i.error??"Path is invalid")}return r}function Ys({isOpen:t,onClose:r,onSubmit:a,onDiscoverRemoteProjects:i,addToast:u,projects:m}){ms(t);const[o,j]=s.useState(""),[g,c]=s.useState("local"),[N,w]=s.useState(""),[v,d]=s.useState(""),[M,_]=s.useState(2),[l,b]=s.useState("auto-generate"),[C,k]=s.useState({}),[$,V]=s.useState({projectMappings:{}}),[A,p]=s.useState(!1),[x,S]=s.useState("idle"),[Z,H]=s.useState(null),[Y,ee]=s.useState([]),U=s.useCallback(()=>{j(""),c("local"),w(""),d(""),_(2),b("auto-generate"),k({}),V({projectMappings:{}}),p(!1),S("idle"),H(null),ee([])},[]),X=s.useCallback(()=>{A||(U(),r())},[A,r,U]);s.useEffect(()=>{if(!t){U();return}const y=L=>{L.key==="Escape"&&(L.preventDefault(),X())};return document.addEventListener("keydown",y),()=>{document.removeEventListener("keydown",y)}},[X,t,U]);const G=s.useMemo(()=>({name:o.trim(),type:g,url:g==="remote"&&N.trim()||void 0,apiKey:g==="remote"&&l==="provide"&&v||void 0,maxConcurrent:M,apiKeyMode:l,projectMappings:Object.entries(C).map(([y,L])=>({projectId:y,path:L.trim()}))}),[v,l,M,o,C,g,N]),te=s.useCallback(async()=>{if(A||x==="loading")return;const y=N.trim();if(!y){V(L=>({...L,url:"URL is required for remote nodes"}));return}S("loading"),H(null);try{const L=await i({url:y,apiKey:l==="provide"&&v.trim().length>0?v:void 0});ee(L.projects),S("success"),k(D=>{if(Object.keys(D).length===0)return D;const K={...D};for(const ae of m){if(!(ae.id in K))continue;const ne=L.projects.filter(se=>se.name===ae.name);ne.length===1&&(K[ae.id]=ne[0].path)}return K})}catch(L){S("error"),ee([]),H(L instanceof Error?L.message:"Failed to discover remote projects")}},[v,l,x,A,i,m,N]);s.useEffect(()=>{if(g!=="remote"){S("idle"),H(null),ee([]);return}S("idle"),H(null),ee([])},[v,l,g,N]);const J=s.useCallback(async()=>{if(A)return;const y=qs(G);if(V(y),!(y.name||y.url||y.maxConcurrent||Object.keys(y.projectMappings).length>0)){if(G.type==="remote"&&x!=="success"){H("Discover remote projects before adding this node.");return}p(!0);try{await a(G),u(`Node "${G.name}" registered`,"success"),X()}catch(L){const D=L instanceof Error?L.message:"Failed to register node";u(D,"error")}finally{p(!1)}}},[u,X,x,G,A,a]),T=y=>{k(L=>{if(y.id in L){const{[y.id]:D,...K}=L;return K}if(g==="remote"&&x==="success"){const D=Y.filter(K=>K.name===y.name);return D.length===1?{...L,[y.id]:D[0].path}:{...L,[y.id]:""}}return{...L,[y.id]:y.path}})},E=(y,L)=>{k(D=>({...D,[y]:L}))};return t?e.jsx("div",{className:"modal-overlay open",onClick:X,children:e.jsxs("div",{className:"modal modal-md add-node-modal",onClick:y=>y.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Add Node",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Add Node"}),e.jsx("button",{className:"modal-close",onClick:X,disabled:A,"aria-label":"Close add node modal",children:"×"})]}),e.jsxs("div",{className:"modal-body add-node-modal__body",children:[e.jsx("p",{className:"add-node-modal__description",children:"Register an existing Fusion node by providing its connection details and concurrency settings."}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{className:"input",type:"text",value:o,onChange:y=>j(y.target.value),placeholder:"Build Machine",disabled:A,"aria-invalid":!!$.name,autoFocus:!0}),$.name&&e.jsx("span",{className:"form-error add-node-modal__error",children:$.name})]}),e.jsxs("div",{className:"add-node-modal__type-toggle",children:[e.jsx("button",{type:"button",className:`add-node-modal__type-btn ${g==="local"?"active":""}`,"data-type":"local",onClick:()=>c("local"),disabled:A,"aria-pressed":g==="local",children:"Local"}),e.jsx("button",{type:"button",className:`add-node-modal__type-btn ${g==="remote"?"active":""}`,"data-type":"remote",onClick:()=>c("remote"),disabled:A,"aria-pressed":g==="remote",children:"Remote"})]}),g==="remote"&&e.jsxs("div",{className:"add-node-modal__remote-fields","data-testid":"remote-fields-container","data-visible":!0,children:[e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Reachable URL / Hostname"}),e.jsx("input",{className:"input",type:"text",value:N,onChange:y=>w(y.target.value),placeholder:"https://node.example.com",disabled:A,"aria-invalid":!!$.url}),$.url&&e.jsx("span",{className:"form-error add-node-modal__error",children:$.url})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key Mode"}),e.jsxs("select",{className:"select",value:l,onChange:y=>b(y.target.value),disabled:A,children:[e.jsx("option",{value:"auto-generate",children:"Auto-generate"}),e.jsx("option",{value:"provide",children:"Provide key manually"})]})]}),l==="provide"&&e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:v,onChange:y=>d(y.target.value),placeholder:"Enter node API key",disabled:A})]}),e.jsxs("div",{className:"add-node-modal__discovery-actions",children:[e.jsx("button",{type:"button",className:"btn btn-sm",onClick:()=>void te(),disabled:A||x==="loading",children:x==="loading"?"Discovering...":"Discover Remote Projects"}),x==="success"&&e.jsx("span",{className:"add-node-modal__discovery-state","data-state":"success",children:Y.length>0?`Discovered ${Y.length} remote project${Y.length===1?"":"s"}.`:"No projects discovered on remote node."}),x==="error"&&Z&&e.jsx("span",{className:"form-error add-node-modal__error",children:Z}),x==="idle"&&e.jsx("span",{className:"add-node-modal__hint",children:"Discover remote projects before adding this node."})]}),x==="success"&&Y.length>0&&e.jsx("div",{className:"add-node-modal__discovered-list","aria-label":"Discovered remote projects",children:Y.map(y=>e.jsxs("div",{className:"card add-node-modal__discovered-card",children:[e.jsxs("div",{className:"add-node-modal__discovered-row",children:[e.jsx("strong",{children:y.name}),e.jsx("span",{className:"card-status-badge card-status-badge--in-review",children:y.status})]}),e.jsx("div",{className:"add-node-modal__hint",children:y.path})]},`${y.id}-${y.path}`))})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Max Concurrent"}),e.jsx("input",{className:"input",type:"number",min:Ce,max:Se,value:M,onChange:y=>_(Number(y.target.value)),disabled:A,"aria-invalid":!!$.maxConcurrent}),e.jsx("span",{className:"add-node-modal__hint",children:"Max simultaneous task agents (1–10)"}),$.maxConcurrent&&e.jsx("span",{className:"form-error add-node-modal__error",children:$.maxConcurrent})]}),e.jsxs("section",{className:"add-node-modal__projects","aria-label":"Project path mappings",children:[e.jsx("h4",{className:"add-node-modal__projects-title",children:"Attach Existing Projects"}),e.jsx("p",{className:"add-node-modal__hint",children:"Select existing projects to run on this node and provide the node-specific absolute path for each one."}),m.length===0?e.jsx("p",{className:"add-node-modal__hint",children:"No projects are currently registered."}):e.jsx("div",{className:"add-node-modal__project-list",children:m.map(y=>{const L=y.id in C,D=$.projectMappings[y.id];return e.jsxs("div",{className:"card add-node-modal__project-card",children:[e.jsxs("label",{className:"checkbox-label add-node-modal__project-toggle",children:[e.jsx("input",{type:"checkbox",checked:L,onChange:()=>T(y),disabled:A}),e.jsx("span",{children:y.name})]}),L&&e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Path on this node"}),g==="remote"&&x==="success"&&e.jsx("span",{className:"add-node-modal__hint",children:(()=>{const K=Y.filter(ae=>ae.name===y.name);return K.length===1?`Remote-authoritative path discovered: ${K[0].path}`:K.length>1?"Multiple remote projects matched this name. Enter the correct path manually.":"No exact remote name match. Enter this path manually."})()}),e.jsx("input",{className:"input",type:"text",value:C[y.id]??"",onChange:K=>E(y.id,K.target.value),disabled:A,placeholder:"/absolute/path/to/project","aria-invalid":!!D}),D&&e.jsx("span",{className:"form-error add-node-modal__error",children:D})]})]},y.id)})})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:X,disabled:A,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm","data-testid":"add-node-submit",onClick:J,disabled:A,children:A?"Adding...":"Add Node"})]})]})}):null}function Xs(){const[t,r]=s.useState([]),[a,i]=s.useState(!1),[u,m]=s.useState(null),[o,j]=s.useState(!1),[g,c]=s.useState(null),[N,w]=s.useState(!1),v=s.useCallback(async()=>{i(!0),m(null);try{const _=await fetch("/api/docker/contexts");if(!_.ok)throw new Error(`Failed to load Docker contexts (${_.status})`);const l=await _.json();return r(l),l}catch(_){const l=_ instanceof Error?_.message:String(_);throw m(l),_}finally{i(!1)}},[]),d=s.useCallback(async _=>{j(!0);try{const l=await fetch("/api/docker/test-connection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({hostConfig:_})});if(!l.ok)throw new Error(`Failed to test Docker connection (${l.status})`);const b=await l.json();return c(b),b}finally{j(!1)}},[]),M=s.useCallback(async()=>{w(!0);try{const _=await fetch("/api/docker/local-available");if(!_.ok)throw new Error(`Failed to check local Docker availability (${_.status})`);return await _.json()}finally{w(!1)}},[]);return{contexts:t,isLoadingContexts:a,contextsError:u,loadContexts:v,isTestingConnection:o,lastTestResult:g,testConnection:d,isCheckingLocal:N,checkLocalDocker:M}}function Js({value:t,onChange:r}){const[a,i]=s.useState(!!(t?.tlsCaPath||t?.tlsCertPath||t?.tlsKeyPath||t?.tlsVerify));s.useEffect(()=>{a||r({tlsVerify:void 0,tlsCaPath:void 0,tlsCertPath:void 0,tlsKeyPath:void 0})},[a,r]);const u=s.useMemo(()=>({tlsVerify:t?.tlsVerify??!0,tlsCaPath:t?.tlsCaPath??"",tlsCertPath:t?.tlsCertPath??"",tlsKeyPath:t?.tlsKeyPath??""}),[t]);return e.jsxs("div",{className:"docker-tls-config",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:a,onChange:m=>i(m.target.checked)}),"Use TLS"]}),a&&e.jsxs("div",{className:"docker-tls-config__fields",children:[e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-ca-path",children:"CA Certificate Path"}),e.jsx("input",{id:"docker-tls-ca-path",className:"input",value:u.tlsCaPath,onChange:m=>r({...u,tlsCaPath:m.target.value}),placeholder:"/etc/docker/ca.pem"})]}),e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-cert-path",children:"Client Certificate Path"}),e.jsx("input",{id:"docker-tls-cert-path",className:"input",value:u.tlsCertPath,onChange:m=>r({...u,tlsCertPath:m.target.value}),placeholder:"/etc/docker/cert.pem"})]}),e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-key-path",children:"Client Key Path"}),e.jsx("input",{id:"docker-tls-key-path",className:"input",value:u.tlsKeyPath,onChange:m=>r({...u,tlsKeyPath:m.target.value}),placeholder:"/etc/docker/key.pem"})]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:u.tlsVerify,onChange:m=>r({...u,tlsVerify:m.target.checked})}),"Verify TLS Certificate"]})]})]})}function Ws({value:t,onChange:r,onError:a}){const i=t?.context?"context":t?.host?"host":"local",[u,m]=s.useState(i),[o,j]=s.useState(t?.context??""),[g,c]=s.useState(t?.host??""),[N,w]=s.useState(null),{contexts:v,isLoadingContexts:d,contextsError:M,loadContexts:_,testConnection:l,isTestingConnection:b,lastTestResult:C,checkLocalDocker:k,isCheckingLocal:$}=Xs(),V=s.useMemo(()=>({tlsVerify:t?.tlsVerify,tlsCaPath:t?.tlsCaPath,tlsCertPath:t?.tlsCertPath,tlsKeyPath:t?.tlsKeyPath}),[t]);s.useEffect(()=>{if(u==="local"){r({});return}if(u==="context"){_().catch(p=>a?.(p instanceof Error?p.message:String(p))),r(o?{context:o}:{});return}r({host:g,...V})},[u]);const A=s.useCallback(p=>{u==="host"&&r({host:g,...p})},[g,u,r]);return e.jsxs("div",{className:"docker-target-selector",children:[e.jsxs("div",{className:"docker-target-selector__modes",role:"group","aria-label":"Docker target mode",children:[e.jsx("button",{type:"button",className:`btn btn-sm ${u==="local"?"docker-target-selector__mode-active":""}`,onClick:()=>{m("local"),k().then(p=>w(p.available?`Docker is available${p.version?` (${p.version})`:""}`:`Docker not found${p.error?`: ${p.error}`:""}`)).catch(p=>{const x=p instanceof Error?p.message:String(p);w(`Docker not found: ${x}`),a?.(x)})},children:"Local Docker"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="context"?"docker-target-selector__mode-active":""}`,onClick:()=>m("context"),children:"Docker Context"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="host"?"docker-target-selector__mode-active":""}`,onClick:()=>m("host"),children:"Remote Host"})]}),u==="local"&&N&&e.jsx("div",{className:"docker-target-selector__status",children:N}),u==="context"&&e.jsxs("div",{className:"docker-target-selector__panel",children:[e.jsxs("div",{className:"docker-target-selector__context-row",children:[e.jsxs("select",{className:"select",value:o,onChange:p=>{const x=p.target.value;j(x),r(x?{context:x}:{})},children:[e.jsx("option",{value:"",children:"Select context"}),v.map(p=>e.jsxs("option",{value:p.name,children:[p.name,p.isCurrentContext?" (current)":"",p.dockerHost?` — ${p.dockerHost}`:""]},p.name))]}),e.jsx("button",{type:"button",className:"btn btn-sm btn-icon",onClick:()=>void _(),disabled:d,"aria-label":"Refresh contexts",children:e.jsx(Ne,{size:14})})]}),M&&e.jsx("div",{className:"docker-target-selector__error",children:M})]}),u==="host"&&e.jsxs("div",{className:"docker-target-selector__panel",children:[e.jsxs("div",{className:"docker-target-selector__field",children:[e.jsx("label",{htmlFor:"docker-target-selector-host",children:"Docker Host"}),e.jsx("input",{id:"docker-target-selector-host",className:"input",placeholder:"tcp://host:2376",value:g,onChange:p=>{const x=p.target.value;c(x),r({host:x,...V})}})]}),e.jsx(Js,{value:V,onChange:A})]}),e.jsx("button",{type:"button",className:"btn btn-sm",onClick:()=>void l(u==="local"?void 0:u==="context"?{context:o}:{host:g,...V}),disabled:b||$,children:b?"Testing...":"Test Connection"}),C&&e.jsx("div",{className:C.success?"docker-target-selector__success":"docker-target-selector__error",children:C.success?`Connected${C.dockerVersion?` (Docker ${C.dockerVersion})`:""}`:C.error??"Connection failed"})]})}const je="http://localhost:4040";function Gs({isOpen:t,onClose:r,onSubmit:a,addToast:i}){const[u,m]=s.useState(""),[o,j]=s.useState({}),[g,c]=s.useState(je),[N,w]=s.useState("auto"),[v,d]=s.useState(""),[M,_]=s.useState(!1),[l,b]=s.useState(!1),[C,k]=s.useState(!0),[$,V]=s.useState(4096),[A,p]=s.useState(2),[x,S]=s.useState(!1),[Z,H]=s.useState("runfusion/fusion"),[Y,ee]=s.useState("latest"),[U,X]=s.useState([]),[G,te]=s.useState([]),[J,T]=s.useState({}),[E,y]=s.useState(!1),L=s.useCallback(()=>{m(""),j({}),c(je),w("auto"),d(""),_(!1),b(!1),k(!0),V(4096),p(2),S(!1),H("runfusion/fusion"),ee("latest"),X([]),te([]),T({}),y(!1)},[]),D=s.useCallback(()=>{E||(L(),r())},[r,L,E]);s.useEffect(()=>{if(!t){L();return}const h=F=>{F.key==="Escape"&&(F.preventDefault(),D())};return document.addEventListener("keydown",h),()=>document.removeEventListener("keydown",h)},[D,t,L]);const K=s.useMemo(()=>({nodeId:null,name:u.trim(),imageName:Z.trim()||"runfusion/fusion",imageTag:Y.trim()||"latest",hostConfig:{context:o.context?.trim()||void 0,host:o.host?.trim()||void 0,tlsVerify:o.tlsVerify,tlsCaPath:o.tlsCaPath?.trim()||void 0,tlsCertPath:o.tlsCertPath?.trim()||void 0,tlsKeyPath:o.tlsKeyPath?.trim()||void 0},envVars:Object.fromEntries(U.map(h=>[h.key.trim(),h.value]).filter(([h])=>!!h)),volumeMounts:G.map(h=>({hostPath:h.hostPath.trim(),containerPath:h.containerPath.trim(),mode:h.mode})).filter(h=>h.hostPath&&h.containerPath),resourceSizing:{memoryMB:$,cpus:A},extraClis:[M?"claude-cli":null,l?"droid-cli":null].filter(Boolean),persistentStorage:C,reachableUrl:g.trim()||null,apiKey:N==="manual"&&v.trim()||null}),[v,N,A,o,U,Z,Y,M,l,$,G,u,C,g]),ae=s.useCallback(()=>{X(h=>[...h,{key:"",value:""}])},[]),ne=s.useCallback((h,F)=>{X(B=>B.map((q,oe)=>oe===h?F:q))},[]),se=s.useCallback(h=>{X(F=>F.filter((B,q)=>q!==h))},[]),le=s.useCallback(()=>{te(h=>[...h,{hostPath:"",containerPath:"",mode:"rw"}])},[]),R=s.useCallback((h,F)=>{te(B=>B.map((q,oe)=>oe===h?F:q))},[]),I=s.useCallback(h=>{te(F=>F.filter((B,q)=>q!==h))},[]),W=s.useCallback(async()=>{if(E)return;const h={};if((!K.name||K.name.length>64)&&(h.name="Name is required and must be 64 characters or fewer"),K.reachableUrl||(h.reachableUrl="URL is required"),$<512&&(h.memoryMB="Memory must be at least 512 MB"),A<.5&&(h.cpus="CPUs must be at least 0.5"),T(h),!(Object.keys(h).length>0)){y(!0);try{await a(K),D()}catch{}finally{y(!1)}}},[D,A,K,$,a,E]);return t?e.jsx("div",{className:"modal-overlay open",onClick:D,children:e.jsxs("div",{className:"modal docker-onboarding",role:"dialog","aria-modal":"true","aria-label":"Docker node onboarding",onClick:h=>h.stopPropagation(),children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Provision Docker Node"}),e.jsx("button",{className:"modal-close",onClick:D,disabled:E,"aria-label":"Close onboarding modal",children:"×"})]}),e.jsxs("div",{className:"modal-body docker-onboarding__body",children:[e.jsxs("section",{className:"docker-onboarding__section",children:[e.jsx("h4",{className:"docker-onboarding__section-title",children:"Required Settings"}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Node Name"}),e.jsx("input",{className:"input",value:u,onChange:h=>m(h.target.value),disabled:E,placeholder:"my-docker-node",autoFocus:!0})]}),J.name&&e.jsx("div",{className:"form-error",children:J.name}),e.jsx(Ws,{value:o,onChange:j}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Reachable URL"}),e.jsx("input",{className:"input",value:g,onChange:h=>c(h.target.value),disabled:E,placeholder:je})]}),J.reachableUrl&&e.jsx("div",{className:"form-error",children:J.reachableUrl}),e.jsxs("div",{className:"docker-onboarding__radio-group",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:N==="auto",onChange:()=>w("auto"),disabled:E}),"Auto-generate"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:N==="manual",onChange:()=>w("manual"),disabled:E}),"Provide manually"]})]}),N==="manual"&&e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:v,onChange:h=>d(h.target.value),disabled:E,placeholder:"Enter API key"})]}),e.jsxs("div",{className:"docker-onboarding__checkbox-group",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:M,onChange:h=>_(h.target.checked),disabled:E}),"Claude CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:l,onChange:h=>b(h.target.checked),disabled:E}),"Droid CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:C,onChange:h=>k(h.target.checked),disabled:E}),"Keep data across container recreations"]})]}),e.jsxs("div",{className:"docker-onboarding__inline-fields",children:[e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Memory (MB)"}),e.jsx("input",{className:"input",type:"number",min:512,value:$,onChange:h=>V(Number(h.target.value)),disabled:E})]}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"CPUs"}),e.jsx("input",{className:"input",type:"number",min:.5,step:.5,value:A,onChange:h=>p(Number(h.target.value)),disabled:E})]})]}),J.memoryMB&&e.jsx("div",{className:"form-error",children:J.memoryMB}),J.cpus&&e.jsx("div",{className:"form-error",children:J.cpus})]}),e.jsxs("section",{className:"docker-onboarding__section",children:[e.jsxs("button",{type:"button",className:`docker-onboarding__advanced-toggle ${x?"is-expanded":""}`,onClick:()=>S(h=>!h),disabled:E,children:[e.jsx("span",{children:"Advanced"}),e.jsx(we,{})]}),e.jsx("div",{className:`docker-onboarding__advanced-content ${x?"is-expanded":""}`,children:e.jsxs("div",{children:[e.jsxs("div",{className:"docker-onboarding__inline-fields",children:[e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Image"}),e.jsx("input",{className:"input",value:Z,onChange:h=>H(h.target.value),disabled:E,placeholder:"runfusion/fusion"})]}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Tag"}),e.jsx("input",{className:"input",value:Y,onChange:h=>ee(h.target.value),disabled:E,placeholder:"latest"})]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Environment Variables"}),U.map((h,F)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--env",children:[e.jsx("input",{className:"input",placeholder:"KEY",value:h.key,disabled:E,onChange:B=>ne(F,{key:B.target.value,value:h.value})}),e.jsx("input",{className:"input",placeholder:"Value",value:h.value,disabled:E,onChange:B=>ne(F,{key:h.key,value:B.target.value})}),e.jsx("button",{type:"button",className:"btn btn-icon","aria-label":"Remove environment variable",onClick:()=>se(F),disabled:E,children:e.jsx(_e,{size:14})})]},`env-${F}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:ae,disabled:E,children:[e.jsx(he,{size:14}),"Add variable"]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Volume Mounts"}),G.map((h,F)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--mount",children:[e.jsx("input",{className:"input",placeholder:"Host path",value:h.hostPath,disabled:E,onChange:B=>R(F,{hostPath:B.target.value,containerPath:h.containerPath,mode:h.mode})}),e.jsx("input",{className:"input",placeholder:"Container path",value:h.containerPath,disabled:E,onChange:B=>R(F,{hostPath:h.hostPath,containerPath:B.target.value,mode:h.mode})}),e.jsxs("select",{className:"select",value:h.mode,disabled:E,onChange:B=>R(F,{hostPath:h.hostPath,containerPath:h.containerPath,mode:B.target.value==="ro"?"ro":"rw"}),children:[e.jsx("option",{value:"rw",children:"rw"}),e.jsx("option",{value:"ro",children:"ro"})]}),e.jsx("button",{type:"button",className:"btn btn-icon","aria-label":"Remove volume mount",onClick:()=>I(F),disabled:E,children:e.jsx(_e,{size:14})})]},`mount-${F}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:le,disabled:E,children:[e.jsx(he,{size:14}),"Add mount"]})]})]})})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn",onClick:D,disabled:E,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary",onClick:()=>void W(),disabled:E,children:E?"Creating...":"Create Docker Node"})]})]})}):null}function Zs({nodeId:t,entries:r,loading:a=!1,singleNode:i=!1}){const[u,m]=s.useState(!1),[o,j]=s.useState("all"),[g,c]=s.useState("all"),N=s.useCallback(()=>{m(l=>!l)},[]),w=s.useMemo(()=>{const l=new Set;for(const b of r)l.add(b.nodeName);return Array.from(l).sort()},[r]),v=s.useMemo(()=>{let l=[...r];return o!=="all"&&(l=l.filter(b=>b.direction===o)),!i&&g!=="all"&&(l=l.filter(b=>b.nodeName===g)),l.sort((b,C)=>{const k=new Date(b.timestamp).getTime();return new Date(C.timestamp).getTime()-k}),l},[r,o,g,i]),d=s.useCallback(l=>new Date(l).toLocaleString(),[]),M=s.useCallback(l=>{switch(l){case"success":return"settings-sync-log__badge--success";case"conflict":return"settings-sync-log__badge--conflict";case"error":return"settings-sync-log__badge--error";default:return""}},[]),_=s.useCallback(l=>{switch(l){case"success":return"Success";case"conflict":return"Conflict";case"error":return"Error";default:return l}},[]);return e.jsxs("div",{className:"settings-sync-log",children:[e.jsxs("button",{className:"settings-sync-log__header",type:"button",onClick:N,"aria-expanded":u,"data-testid":"settings-sync-log-header",children:[e.jsx(we,{size:16,className:`settings-sync-log__chevron ${u?"settings-sync-log__chevron--expanded":""}`}),e.jsxs("span",{children:[r.length," ",r.length===1?"entry":"entries"]})]}),u&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"settings-sync-log__filters",children:[e.jsxs("label",{children:["Direction:",e.jsxs("select",{value:o,onChange:l=>j(l.target.value),children:[e.jsx("option",{value:"all",children:"All"}),e.jsx("option",{value:"push",children:"Push"}),e.jsx("option",{value:"pull",children:"Pull"})]})]}),!i&&e.jsxs("label",{children:["Node:",e.jsxs("select",{value:g,onChange:l=>c(l.target.value),children:[e.jsx("option",{value:"all",children:"All Nodes"}),w.map(l=>e.jsx("option",{value:l,children:l},l))]})]})]}),a&&r.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"Loading..."}):v.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"No sync history available"}):e.jsx("div",{className:"settings-sync-log__list",children:v.map(l=>e.jsxs("div",{className:"settings-sync-log__entry",children:[e.jsx("span",{className:"settings-sync-log__entry-timestamp",children:d(l.timestamp)}),e.jsx("span",{className:"settings-sync-log__entry-direction",children:l.direction==="push"?e.jsx(Be,{size:14,"data-testid":"upload-icon"}):e.jsx(Te,{size:14,"data-testid":"download-icon"})}),e.jsx("span",{className:`settings-sync-log__entry-result ${M(l.result)}`,children:_(l.result)}),!i&&e.jsx("span",{className:"settings-sync-log__entry-node",children:l.nodeName}),l.details&&e.jsx("span",{className:"settings-sync-log__entry-details",title:l.details,children:l.details})]},l.id))})]})]})}function Qs(t,r){const a=typeof t=="string"?t:JSON.stringify(t,null,2),i=typeof r=="string"?r:JSON.stringify(r,null,2);if(a===i)return a;const u=a.split(`
11
+ */const Ms=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0",key:"dnpr2z"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0",key:"1x1e6c"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}]],Ps=ze("wifi",Ms);function pe(t){const{lastSyncAt:r,remoteReachable:a,diff:i}=t,u=i.global.length+i.project.length;return r===null?{syncState:"never-synced",lastSyncAt:r,diffCount:0}:a?u>0?{syncState:"diff",lastSyncAt:r,diffCount:u}:{syncState:"synced",lastSyncAt:r,diffCount:0}:{syncState:"error",lastSyncAt:r,diffCount:u}}function ke(t){if(t===null)return"Never synced";const r=new Date(t);if(Number.isNaN(r.getTime()))return"Never synced";const i=Date.now()-r.getTime(),u=Math.floor(i/1e3),m=Math.floor(u/60),o=Math.floor(m/60),j=Math.floor(o/24);return m<1?"Synced just now":m<60?`Synced ${m}m ago`:o<24?`Synced ${o}h ago`:`Synced ${j}d ago`}function Es(t){switch(t){case"synced":return"var(--color-success)";case"pending":return"var(--color-warning)";case"diff":return"var(--color-warning)";case"error":return"var(--color-error)";case"never-synced":return"var(--text-muted)"}}const Rs=3e4;function As(){const[t,r]=s.useState({}),[a,i]=s.useState(!1),[u,m]=s.useState({}),[o,j]=s.useState(null),g=s.useRef(new Set),c=s.useRef(!1),N=s.useRef(null),w=s.useRef(null),v=s.useCallback(async(p,x)=>{try{const S=await ls(p);r(Z=>({...Z,[p]:S})),j(null)}catch(S){console.error(`Failed to fetch sync status for node ${p}:`,S),j(S instanceof Error?S.message:"Failed to fetch sync status")}},[]),d=s.useCallback(async()=>{const p=Array.from(g.current);if(p.length===0)return;N.current&&N.current.abort(),N.current=new AbortController;const x=!c.current;x&&i(!0),j(null);try{const S=await Promise.allSettled(p.map(H=>v(H,x)));c.current=!0,S.filter(H=>H.status==="rejected").length>0&&j("Some sync status requests failed")}catch(S){if(S instanceof Error&&S.name==="AbortError")return;j(S instanceof Error?S.message:"Failed to fetch sync status"),c.current=!0}finally{i(!1)}},[v]),M=s.useCallback(()=>{w.current&&clearInterval(w.current),w.current=setInterval(()=>{d()},Rs)},[d]),_=s.useCallback(()=>{w.current&&(clearInterval(w.current),w.current=null)},[]);s.useEffect(()=>(d(),M(),()=>{_(),N.current&&N.current.abort()}),[d,M,_]);const l=s.useCallback(p=>{g.current.has(p)||(g.current.add(p),v(p,!c.current))},[v]),b=s.useCallback(p=>{g.current.delete(p),r(x=>{const S={...x};return delete S[p],S}),g.current.size===0&&_()},[_]),C=s.useCallback(async p=>{m(x=>({...x,[p]:!0})),j(null);try{const x=await os(p);return v(p,!1),!x.success&&x.error&&j(x.error),x}catch(x){const S=x instanceof Error?x.message:"Push settings failed";throw j(S),x}finally{m(x=>{const S={...x};return delete S[p],S})}},[v]),k=s.useCallback(async p=>{m(x=>({...x,[p]:!0})),j(null);try{const x=await cs(p);return v(p,!1),!x.success&&x.error&&j(x.error),x}catch(x){const S=x instanceof Error?x.message:"Pull settings failed";throw j(S),x}finally{m(x=>{const S={...x};return delete S[p],S})}},[v]),$=s.useCallback(async p=>{m(x=>({...x,[p]:!0})),j(null);try{return await is(p)}catch(x){const S=x instanceof Error?x.message:"Auth sync failed";throw j(S),x}finally{m(x=>{const S={...x};return delete S[p],S})}},[]),V=s.useCallback(p=>t[p]?.authMatch,[t]),A=s.useCallback(p=>t[p]?.authDiff,[t]);return{syncStatusMap:t,loading:a,actionLoading:u,error:o,refresh:d,trackNode:l,untrackNode:b,pushSettings:C,pullSettings:k,syncAuth:$,getAuthSyncState:V,getAuthProviders:A}}const Ls=1e4,$s=1e3;function Ds(){const[t,r]=s.useState([]),[a,i]=s.useState(!0),[u,m]=s.useState(null),o=s.useRef(null),j=s.useRef(0),g=s.useCallback(async()=>{try{m(null);const c=await Le();r(c.nodes)}catch(c){m(c instanceof Error?c.message:"Failed to fetch mesh state")}},[]);return s.useEffect(()=>{let c=!1;async function N(){i(!0);try{const v=await Le();c||(r(v.nodes),m(null))}catch(v){c||m(v instanceof Error?v.message:"Failed to fetch mesh state")}finally{c||i(!1)}}N();const w=()=>{if(document.visibilityState!=="visible")return;const v=Date.now();v-j.current<$s||(j.current=v,g())};return document.addEventListener("visibilitychange",w),()=>{c=!0,document.removeEventListener("visibilitychange",w)}},[g]),s.useEffect(()=>{if(!a)return o.current=setInterval(()=>{g()},Ls),()=>{o.current&&(clearInterval(o.current),o.current=null)}},[a,g]),{meshState:t,loading:a,error:u,refresh:g}}const de={online:{label:"Online",color:"var(--color-success)",className:"node-card__status--online"},offline:{label:"Offline",color:"var(--color-error)",className:"node-card__status--offline"},connecting:{label:"Connecting",color:"var(--color-warning)",className:"node-card__status--connecting"},error:{label:"Error",color:"var(--color-error)",className:"node-card__status--error"},creating:{label:"Creating",color:"var(--color-warning)",className:"node-card__status--creating"},recreating:{label:"Recreating",color:"var(--color-warning)",className:"node-card__status--recreating"},deleting:{label:"Deleting",color:"var(--color-error)",className:"node-card__status--deleting"},running:{label:"Running",color:"var(--color-success)",className:"node-card__status--online"},stopped:{label:"Stopped",color:"var(--color-error)",className:"node-card__status--offline"},exited:{label:"Exited",color:"var(--color-error)",className:"node-card__status--offline"}},Is={match:"var(--color-success)",differs:"var(--color-warning)","not-synced":"var(--text-muted)"};function zs(t,r){if(t==="match")return"Auth credentials match";if(t==="not-synced")return"Auth not synced";if(r&&Object.keys(r).length>0){const a=Object.entries(r).filter(([,i])=>i==="differs").map(([i])=>i);if(a.length>0)return`Auth credentials differ: ${a.join(", ")}`}return"Auth credentials differ"}function Vs(t,r=42){return t.length<=r?t:`${t.slice(0,r-3)}...`}function Fs(t,r){const a=t.node,i=r.node;if(a.id!==i.id||a.name!==i.name||a.type!==i.type||a.url!==i.url||a.status!==i.status||a.maxConcurrent!==i.maxConcurrent||a.updatedAt!==i.updatedAt||t.isLoading!==r.isLoading)return!1;const u=t.managedDockerNode,m=r.managedDockerNode;if(!!u!=!!m||u&&m&&(u.id!==m.id||u.status!==m.status||u.imageTag!==m.imageTag||u.updatedAt!==m.updatedAt))return!1;const o=t.syncStatus,j=r.syncStatus;if(!(!o&&!j)){if(!o||!j)return!1;if(o.syncState!==j.syncState||o.lastSyncAt!==j.lastSyncAt||o.diffCount!==j.diffCount)return!1}if(t.authSyncState!==r.authSyncState)return!1;const g=t.authSyncProviders,c=r.authSyncProviders;if(g!==c){if(!g||!c)return!1;{const v=Object.keys(g),d=Object.keys(c);if(v.length!==d.length||v.some(M=>g[M]!==c[M]))return!1}}const N=be(t.projects,a),w=be(r.projects,i);return N===w}function Os({node:t,projects:r,onHealthCheck:a,onEdit:i,onRemove:u,isLoading:m=!1,syncStatus:o,authSyncState:j,authSyncProviders:g,managedDockerNode:c}){const[N,w]=s.useState(!1),v=de[t.status]??de.offline,d=c?de[c.status]??de.error:null,M=c?.hostConfig.type==="remote"?`Remote: ${c.hostConfig.host??"unknown"}`:"Local Docker",_=s.useMemo(()=>be(r,t),[r,t]),l=s.useCallback(()=>{i(t)},[i,t]),b=s.useCallback(V=>{V.stopPropagation(),a(t.id)},[a,t.id]),C=s.useCallback(V=>{V.stopPropagation(),i(t)},[i,t]),k=s.useCallback(V=>{if(V.stopPropagation(),!N){w(!0);return}u(t.id),w(!1)},[N,u,t.id]),$=s.useCallback(V=>{(V.key==="Enter"||V.key===" ")&&(V.preventDefault(),i(t))},[i,t]);return e.jsxs("article",{className:`node-card ${m?"node-card--loading":""}`,"data-node-id":t.id,role:"button",tabIndex:0,onClick:l,onKeyDown:$,children:[e.jsx("header",{className:"node-card__header",children:e.jsxs("div",{className:"node-card__title-wrap",children:[e.jsx("div",{className:"node-card__icon",children:e.jsx(Ve,{size:18})}),e.jsxs("div",{children:[e.jsx("h3",{className:"node-card__name",title:t.name,children:t.name}),e.jsxs("div",{className:"node-card__meta-row",children:[e.jsx("span",{className:"node-card__type-badge",children:t.type==="local"?"Local":"Remote"}),c&&e.jsxs("span",{className:"node-card__docker-badge",title:"Managed Docker node",children:[e.jsx(ye,{size:12,"aria-hidden":!0}),"Docker"]}),e.jsxs("span",{className:`node-card__status ${v.className}`,style:{color:v.color},"data-status":t.status,children:[e.jsx("span",{className:"node-card__status-indicator",style:{backgroundColor:v.color},"aria-hidden":!0}),v.label]}),c&&d&&e.jsxs("span",{className:`node-card__status ${d.className}`,style:{color:d.color},"data-status":c.status,children:[e.jsx("span",{className:"node-card__status-indicator",style:{backgroundColor:d.color},"aria-hidden":!0}),d.label]}),t.type==="remote"&&j&&e.jsx("span",{className:`node-card__auth-indicator node-card__auth-indicator--${j}`,title:zs(j,g),"aria-label":`Auth sync: ${j==="match"?"credentials match":j==="differs"?"credentials differ":"not synced"}`,style:{color:Is[j]},children:e.jsx(Fe,{size:14})})]})]})]})}),e.jsxs("div",{className:"node-card__body",children:[t.type==="remote"&&t.url&&e.jsx("div",{className:"node-card__url",title:t.url,children:Vs(t.url)}),c&&e.jsxs("div",{className:"node-card__docker-meta",children:[e.jsxs("span",{title:`${c.imageName}:${c.imageTag}`,children:[c.imageName,":",c.imageTag]}),e.jsx("span",{title:M,children:M})]}),e.jsxs("div",{className:"node-card__metrics",children:[e.jsxs("div",{className:"node-card__metric",children:[e.jsx("span",{className:"node-card__metric-label",children:"Projects"}),e.jsx("span",{className:"node-card__metric-value",children:_})]}),e.jsxs("div",{className:"node-card__metric",children:[e.jsx("span",{className:"node-card__metric-label",children:"Concurrency"}),e.jsx("span",{className:"node-card__metric-value",children:t.maxConcurrent})]})]}),t.type==="remote"&&o&&e.jsxs("div",{className:"node-card__sync","data-sync-state":o.syncState,"data-testid":"node-card-sync",children:[e.jsx("span",{className:"node-card__sync-dot",style:{backgroundColor:Es(o.syncState)},"aria-hidden":!0}),e.jsx("span",{className:"node-card__sync-time",children:ke(o.lastSyncAt)})]})]}),e.jsxs("footer",{className:"node-card__actions",children:[e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:b,disabled:m,"aria-label":"Run node health check",title:"Health Check",children:[e.jsx(Oe,{size:14}),e.jsx("span",{children:"Health"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:C,disabled:m,"aria-label":"Edit node",title:"Edit",children:[e.jsx(ds,{size:14}),e.jsx("span",{children:"Edit"})]}),c&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Start node container",title:"Available after FN-3113",children:[e.jsx(Ke,{size:14}),e.jsx("span",{children:"Start"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Stop node container",title:"Available after FN-3113",children:[e.jsx(Ue,{size:14}),e.jsx("span",{children:"Stop"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Restart node container",title:"Available after FN-3113",children:[e.jsx(us,{size:14}),e.jsx("span",{children:"Restart"})]})]}),e.jsxs("button",{className:`btn btn-sm node-card__action node-card__action--remove ${N?"btn-danger is-armed":""}`,type:"button",onClick:k,disabled:m,"aria-label":N?"Confirm remove node":"Remove node",title:N?"Confirm remove":"Remove",children:[e.jsx(_e,{size:14}),e.jsx("span",{children:N?"Confirm":"Remove"})]})]})]})}const Ks=s.memo(Os,Fs),ce={online:"var(--success, var(--color-success))",offline:"var(--text-dim)",connecting:"var(--color-warning)",error:"var(--color-error)"},ue=28,Us=12,Ts=300,Bs=120;function Hs({nodes:t,className:r}){const a=s.useMemo(()=>t.find(c=>c.nodeType==="local")??t[0],[t]),i=s.useMemo(()=>t.filter(c=>c.nodeId!==a?.nodeId),[t,a?.nodeId]),u=s.useMemo(()=>Ts+Math.max(0,i.length-4)*20,[i.length]),m=u/2,o=u/2,j=s.useMemo(()=>{const c=new Map;if(a&&c.set(a.nodeId,{x:m,y:o,node:a}),i.length>0){const N=Math.min(Bs,u/2-ue-10),w=2*Math.PI/i.length,v=-Math.PI/2;i.forEach((d,M)=>{const _=v+M*w;c.set(d.nodeId,{node:d,x:m+N*Math.cos(_),y:o+N*Math.sin(_)})})}return c},[m,o,a,i,u]),g=s.useMemo(()=>{const c=new Set,N=[];for(const w of t){const v=j.get(w.nodeId);if(v)for(const d of w.knownPeers){const M=j.get(d.peerNodeId);if(!M)continue;const _=[w.nodeId,d.peerNodeId].sort().join("::");c.has(_)||(c.add(_),N.push({key:_,from:v,to:M}))}}return N},[j,t]);return t.length===0?e.jsx("div",{className:`mesh-topology mesh-topology--empty ${r??""}`,children:e.jsx("div",{className:"mesh-topology__empty-state",children:e.jsx("p",{children:"No nodes to display"})})}):e.jsxs("div",{className:`mesh-topology ${r??""}`,children:[e.jsxs("svg",{className:"mesh-topology__svg",viewBox:`0 0 ${u} ${u}`,preserveAspectRatio:"xMidYMid meet","aria-label":"Node mesh topology visualization",children:[g.map(c=>e.jsx("line",{className:"mesh-topology__link mesh-topology__peer-line",x1:c.from.x,y1:c.from.y,x2:c.to.x,y2:c.to.y},c.key)),Array.from(j.values()).map(({node:c,x:N,y:w})=>e.jsxs("g",{className:"mesh-topology__node",transform:`translate(${N}, ${w})`,children:[e.jsx("circle",{className:"mesh-topology__node-circle",r:ue,fill:ce[c.status],"aria-label":`${c.nodeName} (${c.status})`}),e.jsx("text",{className:"mesh-topology__node-label",y:ue+Us,textAnchor:"middle",children:c.nodeName.length>12?`${c.nodeName.slice(0,10)}…`:c.nodeName}),e.jsxs("g",{className:"mesh-topology__node-type",transform:`translate(0 ${-ue-10})`,children:[e.jsx("circle",{className:"mesh-topology__node-type-badge",r:"8"}),e.jsx("text",{className:"mesh-topology__node-type-text",textAnchor:"middle",dominantBaseline:"middle",children:c.nodeType==="local"?"L":"R"})]})]},c.nodeId))]}),e.jsxs("div",{className:"mesh-topology__legend",children:[e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:ce.online}}),e.jsx("span",{children:"Online"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:ce.offline}}),e.jsx("span",{children:"Offline"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:ce.connecting}}),e.jsx("span",{children:"Connecting"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:ce.error}}),e.jsx("span",{children:"Error"})]})]}),g.length===0&&e.jsx("p",{className:"mesh-topology__notice",children:"Peer-to-peer discovery data unavailable."})]})}const qs=s.memo(Hs),Ce=1,Se=10;function Ys(t){const r={projectMappings:{}};t.name.trim()||(r.name="Name is required"),t.type==="remote"&&!t.url?.trim()&&(r.url="URL is required for remote nodes"),(!Number.isFinite(t.maxConcurrent)||t.maxConcurrent<Ce||t.maxConcurrent>Se)&&(r.maxConcurrent=`Concurrency must be between ${Ce} and ${Se}`);for(const a of t.projectMappings){const i=Cs(a.path);i.valid||(r.projectMappings[a.projectId]=i.error??"Path is invalid")}return r}function Xs({isOpen:t,onClose:r,onSubmit:a,onDiscoverRemoteProjects:i,addToast:u,projects:m}){ms(t);const[o,j]=s.useState(""),[g,c]=s.useState("local"),[N,w]=s.useState(""),[v,d]=s.useState(""),[M,_]=s.useState(2),[l,b]=s.useState("auto-generate"),[C,k]=s.useState({}),[$,V]=s.useState({projectMappings:{}}),[A,p]=s.useState(!1),[x,S]=s.useState("idle"),[Z,H]=s.useState(null),[Y,ee]=s.useState([]),U=s.useCallback(()=>{j(""),c("local"),w(""),d(""),_(2),b("auto-generate"),k({}),V({projectMappings:{}}),p(!1),S("idle"),H(null),ee([])},[]),X=s.useCallback(()=>{A||(U(),r())},[A,r,U]);s.useEffect(()=>{if(!t){U();return}const y=L=>{L.key==="Escape"&&(L.preventDefault(),X())};return document.addEventListener("keydown",y),()=>{document.removeEventListener("keydown",y)}},[X,t,U]);const G=s.useMemo(()=>({name:o.trim(),type:g,url:g==="remote"&&N.trim()||void 0,apiKey:g==="remote"&&l==="provide"&&v||void 0,maxConcurrent:M,apiKeyMode:l,projectMappings:Object.entries(C).map(([y,L])=>({projectId:y,path:L.trim()}))}),[v,l,M,o,C,g,N]),te=s.useCallback(async()=>{if(A||x==="loading")return;const y=N.trim();if(!y){V(L=>({...L,url:"URL is required for remote nodes"}));return}S("loading"),H(null);try{const L=await i({url:y,apiKey:l==="provide"&&v.trim().length>0?v:void 0});ee(L.projects),S("success"),k(D=>{if(Object.keys(D).length===0)return D;const K={...D};for(const ae of m){if(!(ae.id in K))continue;const ne=L.projects.filter(se=>se.name===ae.name);ne.length===1&&(K[ae.id]=ne[0].path)}return K})}catch(L){S("error"),ee([]),H(L instanceof Error?L.message:"Failed to discover remote projects")}},[v,l,x,A,i,m,N]);s.useEffect(()=>{if(g!=="remote"){S("idle"),H(null),ee([]);return}S("idle"),H(null),ee([])},[v,l,g,N]);const J=s.useCallback(async()=>{if(A)return;const y=Ys(G);if(V(y),!(y.name||y.url||y.maxConcurrent||Object.keys(y.projectMappings).length>0)){if(G.type==="remote"&&x!=="success"){H("Discover remote projects before adding this node.");return}p(!0);try{await a(G),u(`Node "${G.name}" registered`,"success"),X()}catch(L){const D=L instanceof Error?L.message:"Failed to register node";u(D,"error")}finally{p(!1)}}},[u,X,x,G,A,a]),T=y=>{k(L=>{if(y.id in L){const{[y.id]:D,...K}=L;return K}if(g==="remote"&&x==="success"){const D=Y.filter(K=>K.name===y.name);return D.length===1?{...L,[y.id]:D[0].path}:{...L,[y.id]:""}}return{...L,[y.id]:y.path}})},E=(y,L)=>{k(D=>({...D,[y]:L}))};return t?e.jsx("div",{className:"modal-overlay open",onClick:X,children:e.jsxs("div",{className:"modal modal-md add-node-modal",onClick:y=>y.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Add Node",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Add Node"}),e.jsx("button",{className:"modal-close",onClick:X,disabled:A,"aria-label":"Close add node modal",children:"×"})]}),e.jsxs("div",{className:"modal-body add-node-modal__body",children:[e.jsx("p",{className:"add-node-modal__description",children:"Register an existing Fusion node by providing its connection details and concurrency settings."}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{className:"input",type:"text",value:o,onChange:y=>j(y.target.value),placeholder:"Build Machine",disabled:A,"aria-invalid":!!$.name,autoFocus:!0}),$.name&&e.jsx("span",{className:"form-error add-node-modal__error",children:$.name})]}),e.jsxs("div",{className:"add-node-modal__type-toggle",children:[e.jsx("button",{type:"button",className:`add-node-modal__type-btn ${g==="local"?"active":""}`,"data-type":"local",onClick:()=>c("local"),disabled:A,"aria-pressed":g==="local",children:"Local"}),e.jsx("button",{type:"button",className:`add-node-modal__type-btn ${g==="remote"?"active":""}`,"data-type":"remote",onClick:()=>c("remote"),disabled:A,"aria-pressed":g==="remote",children:"Remote"})]}),g==="remote"&&e.jsxs("div",{className:"add-node-modal__remote-fields","data-testid":"remote-fields-container","data-visible":!0,children:[e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Reachable URL / Hostname"}),e.jsx("input",{className:"input",type:"text",value:N,onChange:y=>w(y.target.value),placeholder:"https://node.example.com",disabled:A,"aria-invalid":!!$.url}),$.url&&e.jsx("span",{className:"form-error add-node-modal__error",children:$.url})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key Mode"}),e.jsxs("select",{className:"select",value:l,onChange:y=>b(y.target.value),disabled:A,children:[e.jsx("option",{value:"auto-generate",children:"Auto-generate"}),e.jsx("option",{value:"provide",children:"Provide key manually"})]})]}),l==="provide"&&e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:v,onChange:y=>d(y.target.value),placeholder:"Enter node API key",disabled:A})]}),e.jsxs("div",{className:"add-node-modal__discovery-actions",children:[e.jsx("button",{type:"button",className:"btn btn-sm",onClick:()=>void te(),disabled:A||x==="loading",children:x==="loading"?"Discovering...":"Discover Remote Projects"}),x==="success"&&e.jsx("span",{className:"add-node-modal__discovery-state","data-state":"success",children:Y.length>0?`Discovered ${Y.length} remote project${Y.length===1?"":"s"}.`:"No projects discovered on remote node."}),x==="error"&&Z&&e.jsx("span",{className:"form-error add-node-modal__error",children:Z}),x==="idle"&&e.jsx("span",{className:"add-node-modal__hint",children:"Discover remote projects before adding this node."})]}),x==="success"&&Y.length>0&&e.jsx("div",{className:"add-node-modal__discovered-list","aria-label":"Discovered remote projects",children:Y.map(y=>e.jsxs("div",{className:"card add-node-modal__discovered-card",children:[e.jsxs("div",{className:"add-node-modal__discovered-row",children:[e.jsx("strong",{children:y.name}),e.jsx("span",{className:"card-status-badge card-status-badge--in-review",children:y.status})]}),e.jsx("div",{className:"add-node-modal__hint",children:y.path})]},`${y.id}-${y.path}`))})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Max Concurrent"}),e.jsx("input",{className:"input",type:"number",min:Ce,max:Se,value:M,onChange:y=>_(Number(y.target.value)),disabled:A,"aria-invalid":!!$.maxConcurrent}),e.jsx("span",{className:"add-node-modal__hint",children:"Max simultaneous task agents (1–10)"}),$.maxConcurrent&&e.jsx("span",{className:"form-error add-node-modal__error",children:$.maxConcurrent})]}),e.jsxs("section",{className:"add-node-modal__projects","aria-label":"Project path mappings",children:[e.jsx("h4",{className:"add-node-modal__projects-title",children:"Attach Existing Projects"}),e.jsx("p",{className:"add-node-modal__hint",children:"Select existing projects to run on this node and provide the node-specific absolute path for each one."}),m.length===0?e.jsx("p",{className:"add-node-modal__hint",children:"No projects are currently registered."}):e.jsx("div",{className:"add-node-modal__project-list",children:m.map(y=>{const L=y.id in C,D=$.projectMappings[y.id];return e.jsxs("div",{className:"card add-node-modal__project-card",children:[e.jsxs("label",{className:"checkbox-label add-node-modal__project-toggle",children:[e.jsx("input",{type:"checkbox",checked:L,onChange:()=>T(y),disabled:A}),e.jsx("span",{children:y.name})]}),L&&e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Path on this node"}),g==="remote"&&x==="success"&&e.jsx("span",{className:"add-node-modal__hint",children:(()=>{const K=Y.filter(ae=>ae.name===y.name);return K.length===1?`Remote-authoritative path discovered: ${K[0].path}`:K.length>1?"Multiple remote projects matched this name. Enter the correct path manually.":"No exact remote name match. Enter this path manually."})()}),e.jsx("input",{className:"input",type:"text",value:C[y.id]??"",onChange:K=>E(y.id,K.target.value),disabled:A,placeholder:"/absolute/path/to/project","aria-invalid":!!D}),D&&e.jsx("span",{className:"form-error add-node-modal__error",children:D})]})]},y.id)})})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:X,disabled:A,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm","data-testid":"add-node-submit",onClick:J,disabled:A,children:A?"Adding...":"Add Node"})]})]})}):null}function Js(){const[t,r]=s.useState([]),[a,i]=s.useState(!1),[u,m]=s.useState(null),[o,j]=s.useState(!1),[g,c]=s.useState(null),[N,w]=s.useState(!1),v=s.useCallback(async()=>{i(!0),m(null);try{const _=await fetch("/api/docker/contexts");if(!_.ok)throw new Error(`Failed to load Docker contexts (${_.status})`);const l=await _.json();return r(l),l}catch(_){const l=_ instanceof Error?_.message:String(_);throw m(l),_}finally{i(!1)}},[]),d=s.useCallback(async _=>{j(!0);try{const l=await fetch("/api/docker/test-connection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({hostConfig:_})});if(!l.ok)throw new Error(`Failed to test Docker connection (${l.status})`);const b=await l.json();return c(b),b}finally{j(!1)}},[]),M=s.useCallback(async()=>{w(!0);try{const _=await fetch("/api/docker/local-available");if(!_.ok)throw new Error(`Failed to check local Docker availability (${_.status})`);return await _.json()}finally{w(!1)}},[]);return{contexts:t,isLoadingContexts:a,contextsError:u,loadContexts:v,isTestingConnection:o,lastTestResult:g,testConnection:d,isCheckingLocal:N,checkLocalDocker:M}}function Ws({value:t,onChange:r}){const[a,i]=s.useState(!!(t?.tlsCaPath||t?.tlsCertPath||t?.tlsKeyPath||t?.tlsVerify));s.useEffect(()=>{a||r({tlsVerify:void 0,tlsCaPath:void 0,tlsCertPath:void 0,tlsKeyPath:void 0})},[a,r]);const u=s.useMemo(()=>({tlsVerify:t?.tlsVerify??!0,tlsCaPath:t?.tlsCaPath??"",tlsCertPath:t?.tlsCertPath??"",tlsKeyPath:t?.tlsKeyPath??""}),[t]);return e.jsxs("div",{className:"docker-tls-config",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:a,onChange:m=>i(m.target.checked)}),"Use TLS"]}),a&&e.jsxs("div",{className:"docker-tls-config__fields",children:[e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-ca-path",children:"CA Certificate Path"}),e.jsx("input",{id:"docker-tls-ca-path",className:"input",value:u.tlsCaPath,onChange:m=>r({...u,tlsCaPath:m.target.value}),placeholder:"/etc/docker/ca.pem"})]}),e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-cert-path",children:"Client Certificate Path"}),e.jsx("input",{id:"docker-tls-cert-path",className:"input",value:u.tlsCertPath,onChange:m=>r({...u,tlsCertPath:m.target.value}),placeholder:"/etc/docker/cert.pem"})]}),e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-key-path",children:"Client Key Path"}),e.jsx("input",{id:"docker-tls-key-path",className:"input",value:u.tlsKeyPath,onChange:m=>r({...u,tlsKeyPath:m.target.value}),placeholder:"/etc/docker/key.pem"})]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:u.tlsVerify,onChange:m=>r({...u,tlsVerify:m.target.checked})}),"Verify TLS Certificate"]})]})]})}function Gs({value:t,onChange:r,onError:a}){const i=t?.context?"context":t?.host?"host":"local",[u,m]=s.useState(i),[o,j]=s.useState(t?.context??""),[g,c]=s.useState(t?.host??""),[N,w]=s.useState(null),{contexts:v,isLoadingContexts:d,contextsError:M,loadContexts:_,testConnection:l,isTestingConnection:b,lastTestResult:C,checkLocalDocker:k,isCheckingLocal:$}=Js(),V=s.useMemo(()=>({tlsVerify:t?.tlsVerify,tlsCaPath:t?.tlsCaPath,tlsCertPath:t?.tlsCertPath,tlsKeyPath:t?.tlsKeyPath}),[t]);s.useEffect(()=>{if(u==="local"){r({});return}if(u==="context"){_().catch(p=>a?.(p instanceof Error?p.message:String(p))),r(o?{context:o}:{});return}r({host:g,...V})},[u]);const A=s.useCallback(p=>{u==="host"&&r({host:g,...p})},[g,u,r]);return e.jsxs("div",{className:"docker-target-selector",children:[e.jsxs("div",{className:"docker-target-selector__modes",role:"group","aria-label":"Docker target mode",children:[e.jsx("button",{type:"button",className:`btn btn-sm ${u==="local"?"docker-target-selector__mode-active":""}`,onClick:()=>{m("local"),k().then(p=>w(p.available?`Docker is available${p.version?` (${p.version})`:""}`:`Docker not found${p.error?`: ${p.error}`:""}`)).catch(p=>{const x=p instanceof Error?p.message:String(p);w(`Docker not found: ${x}`),a?.(x)})},children:"Local Docker"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="context"?"docker-target-selector__mode-active":""}`,onClick:()=>m("context"),children:"Docker Context"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="host"?"docker-target-selector__mode-active":""}`,onClick:()=>m("host"),children:"Remote Host"})]}),u==="local"&&N&&e.jsx("div",{className:"docker-target-selector__status",children:N}),u==="context"&&e.jsxs("div",{className:"docker-target-selector__panel",children:[e.jsxs("div",{className:"docker-target-selector__context-row",children:[e.jsxs("select",{className:"select",value:o,onChange:p=>{const x=p.target.value;j(x),r(x?{context:x}:{})},children:[e.jsx("option",{value:"",children:"Select context"}),v.map(p=>e.jsxs("option",{value:p.name,children:[p.name,p.isCurrentContext?" (current)":"",p.dockerHost?` — ${p.dockerHost}`:""]},p.name))]}),e.jsx("button",{type:"button",className:"btn btn-sm btn-icon",onClick:()=>void _(),disabled:d,"aria-label":"Refresh contexts",children:e.jsx(Ne,{size:14})})]}),M&&e.jsx("div",{className:"docker-target-selector__error",children:M})]}),u==="host"&&e.jsxs("div",{className:"docker-target-selector__panel",children:[e.jsxs("div",{className:"docker-target-selector__field",children:[e.jsx("label",{htmlFor:"docker-target-selector-host",children:"Docker Host"}),e.jsx("input",{id:"docker-target-selector-host",className:"input",placeholder:"tcp://host:2376",value:g,onChange:p=>{const x=p.target.value;c(x),r({host:x,...V})}})]}),e.jsx(Ws,{value:V,onChange:A})]}),e.jsx("button",{type:"button",className:"btn btn-sm",onClick:()=>void l(u==="local"?void 0:u==="context"?{context:o}:{host:g,...V}),disabled:b||$,children:b?"Testing...":"Test Connection"}),C&&e.jsx("div",{className:C.success?"docker-target-selector__success":"docker-target-selector__error",children:C.success?`Connected${C.dockerVersion?` (Docker ${C.dockerVersion})`:""}`:C.error??"Connection failed"})]})}const je="http://localhost:4040";function Zs({isOpen:t,onClose:r,onSubmit:a,addToast:i}){const[u,m]=s.useState(""),[o,j]=s.useState({}),[g,c]=s.useState(je),[N,w]=s.useState("auto"),[v,d]=s.useState(""),[M,_]=s.useState(!1),[l,b]=s.useState(!1),[C,k]=s.useState(!0),[$,V]=s.useState(4096),[A,p]=s.useState(2),[x,S]=s.useState(!1),[Z,H]=s.useState("runfusion/fusion"),[Y,ee]=s.useState("latest"),[U,X]=s.useState([]),[G,te]=s.useState([]),[J,T]=s.useState({}),[E,y]=s.useState(!1),L=s.useCallback(()=>{m(""),j({}),c(je),w("auto"),d(""),_(!1),b(!1),k(!0),V(4096),p(2),S(!1),H("runfusion/fusion"),ee("latest"),X([]),te([]),T({}),y(!1)},[]),D=s.useCallback(()=>{E||(L(),r())},[r,L,E]);s.useEffect(()=>{if(!t){L();return}const h=O=>{O.key==="Escape"&&(O.preventDefault(),D())};return document.addEventListener("keydown",h),()=>document.removeEventListener("keydown",h)},[D,t,L]);const K=s.useMemo(()=>({nodeId:null,name:u.trim(),imageName:Z.trim()||"runfusion/fusion",imageTag:Y.trim()||"latest",hostConfig:{context:o.context?.trim()||void 0,host:o.host?.trim()||void 0,tlsVerify:o.tlsVerify,tlsCaPath:o.tlsCaPath?.trim()||void 0,tlsCertPath:o.tlsCertPath?.trim()||void 0,tlsKeyPath:o.tlsKeyPath?.trim()||void 0},envVars:Object.fromEntries(U.map(h=>[h.key.trim(),h.value]).filter(([h])=>!!h)),volumeMounts:G.map(h=>({hostPath:h.hostPath.trim(),containerPath:h.containerPath.trim(),mode:h.mode})).filter(h=>h.hostPath&&h.containerPath),resourceSizing:{memoryMB:$,cpus:A},extraClis:[M?"claude-cli":null,l?"droid-cli":null].filter(Boolean),persistentStorage:C,reachableUrl:g.trim()||null,apiKey:N==="manual"&&v.trim()||null}),[v,N,A,o,U,Z,Y,M,l,$,G,u,C,g]),ae=s.useCallback(()=>{X(h=>[...h,{key:"",value:""}])},[]),ne=s.useCallback((h,O)=>{X(B=>B.map((q,oe)=>oe===h?O:q))},[]),se=s.useCallback(h=>{X(O=>O.filter((B,q)=>q!==h))},[]),le=s.useCallback(()=>{te(h=>[...h,{hostPath:"",containerPath:"",mode:"rw"}])},[]),R=s.useCallback((h,O)=>{te(B=>B.map((q,oe)=>oe===h?O:q))},[]),I=s.useCallback(h=>{te(O=>O.filter((B,q)=>q!==h))},[]),W=s.useCallback(async()=>{if(E)return;const h={};if((!K.name||K.name.length>64)&&(h.name="Name is required and must be 64 characters or fewer"),K.reachableUrl||(h.reachableUrl="URL is required"),$<512&&(h.memoryMB="Memory must be at least 512 MB"),A<.5&&(h.cpus="CPUs must be at least 0.5"),T(h),!(Object.keys(h).length>0)){y(!0);try{await a(K),D()}catch{}finally{y(!1)}}},[D,A,K,$,a,E]);return t?e.jsx("div",{className:"modal-overlay open",onClick:D,children:e.jsxs("div",{className:"modal docker-onboarding",role:"dialog","aria-modal":"true","aria-label":"Docker node onboarding",onClick:h=>h.stopPropagation(),children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Provision Docker Node"}),e.jsx("button",{className:"modal-close",onClick:D,disabled:E,"aria-label":"Close onboarding modal",children:"×"})]}),e.jsxs("div",{className:"modal-body docker-onboarding__body",children:[e.jsxs("section",{className:"docker-onboarding__section",children:[e.jsx("h4",{className:"docker-onboarding__section-title",children:"Required Settings"}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Node Name"}),e.jsx("input",{className:"input",value:u,onChange:h=>m(h.target.value),disabled:E,placeholder:"my-docker-node",autoFocus:!0})]}),J.name&&e.jsx("div",{className:"form-error",children:J.name}),e.jsx(Gs,{value:o,onChange:j}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Reachable URL"}),e.jsx("input",{className:"input",value:g,onChange:h=>c(h.target.value),disabled:E,placeholder:je})]}),J.reachableUrl&&e.jsx("div",{className:"form-error",children:J.reachableUrl}),e.jsxs("div",{className:"docker-onboarding__radio-group",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:N==="auto",onChange:()=>w("auto"),disabled:E}),"Auto-generate"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:N==="manual",onChange:()=>w("manual"),disabled:E}),"Provide manually"]})]}),N==="manual"&&e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:v,onChange:h=>d(h.target.value),disabled:E,placeholder:"Enter API key"})]}),e.jsxs("div",{className:"docker-onboarding__checkbox-group",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:M,onChange:h=>_(h.target.checked),disabled:E}),"Claude CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:l,onChange:h=>b(h.target.checked),disabled:E}),"Droid CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:C,onChange:h=>k(h.target.checked),disabled:E}),"Keep data across container recreations"]})]}),e.jsxs("div",{className:"docker-onboarding__inline-fields",children:[e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Memory (MB)"}),e.jsx("input",{className:"input",type:"number",min:512,value:$,onChange:h=>V(Number(h.target.value)),disabled:E})]}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"CPUs"}),e.jsx("input",{className:"input",type:"number",min:.5,step:.5,value:A,onChange:h=>p(Number(h.target.value)),disabled:E})]})]}),J.memoryMB&&e.jsx("div",{className:"form-error",children:J.memoryMB}),J.cpus&&e.jsx("div",{className:"form-error",children:J.cpus})]}),e.jsxs("section",{className:"docker-onboarding__section",children:[e.jsxs("button",{type:"button",className:`docker-onboarding__advanced-toggle ${x?"is-expanded":""}`,onClick:()=>S(h=>!h),disabled:E,children:[e.jsx("span",{children:"Advanced"}),e.jsx(we,{})]}),e.jsx("div",{className:`docker-onboarding__advanced-content ${x?"is-expanded":""}`,children:e.jsxs("div",{children:[e.jsxs("div",{className:"docker-onboarding__inline-fields",children:[e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Image"}),e.jsx("input",{className:"input",value:Z,onChange:h=>H(h.target.value),disabled:E,placeholder:"runfusion/fusion"})]}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Tag"}),e.jsx("input",{className:"input",value:Y,onChange:h=>ee(h.target.value),disabled:E,placeholder:"latest"})]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Environment Variables"}),U.map((h,O)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--env",children:[e.jsx("input",{className:"input",placeholder:"KEY",value:h.key,disabled:E,onChange:B=>ne(O,{key:B.target.value,value:h.value})}),e.jsx("input",{className:"input",placeholder:"Value",value:h.value,disabled:E,onChange:B=>ne(O,{key:h.key,value:B.target.value})}),e.jsx("button",{type:"button",className:"btn btn-icon","aria-label":"Remove environment variable",onClick:()=>se(O),disabled:E,children:e.jsx(_e,{size:14})})]},`env-${O}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:ae,disabled:E,children:[e.jsx(he,{size:14}),"Add variable"]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Volume Mounts"}),G.map((h,O)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--mount",children:[e.jsx("input",{className:"input",placeholder:"Host path",value:h.hostPath,disabled:E,onChange:B=>R(O,{hostPath:B.target.value,containerPath:h.containerPath,mode:h.mode})}),e.jsx("input",{className:"input",placeholder:"Container path",value:h.containerPath,disabled:E,onChange:B=>R(O,{hostPath:h.hostPath,containerPath:B.target.value,mode:h.mode})}),e.jsxs("select",{className:"select",value:h.mode,disabled:E,onChange:B=>R(O,{hostPath:h.hostPath,containerPath:h.containerPath,mode:B.target.value==="ro"?"ro":"rw"}),children:[e.jsx("option",{value:"rw",children:"rw"}),e.jsx("option",{value:"ro",children:"ro"})]}),e.jsx("button",{type:"button",className:"btn btn-icon","aria-label":"Remove volume mount",onClick:()=>I(O),disabled:E,children:e.jsx(_e,{size:14})})]},`mount-${O}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:le,disabled:E,children:[e.jsx(he,{size:14}),"Add mount"]})]})]})})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn",onClick:D,disabled:E,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary",onClick:()=>void W(),disabled:E,children:E?"Creating...":"Create Docker Node"})]})]})}):null}function Qs({nodeId:t,entries:r,loading:a=!1,singleNode:i=!1}){const[u,m]=s.useState(!1),[o,j]=s.useState("all"),[g,c]=s.useState("all"),N=s.useCallback(()=>{m(l=>!l)},[]),w=s.useMemo(()=>{const l=new Set;for(const b of r)l.add(b.nodeName);return Array.from(l).sort()},[r]),v=s.useMemo(()=>{let l=[...r];return o!=="all"&&(l=l.filter(b=>b.direction===o)),!i&&g!=="all"&&(l=l.filter(b=>b.nodeName===g)),l.sort((b,C)=>{const k=new Date(b.timestamp).getTime();return new Date(C.timestamp).getTime()-k}),l},[r,o,g,i]),d=s.useCallback(l=>new Date(l).toLocaleString(),[]),M=s.useCallback(l=>{switch(l){case"success":return"settings-sync-log__badge--success";case"conflict":return"settings-sync-log__badge--conflict";case"error":return"settings-sync-log__badge--error";default:return""}},[]),_=s.useCallback(l=>{switch(l){case"success":return"Success";case"conflict":return"Conflict";case"error":return"Error";default:return l}},[]);return e.jsxs("div",{className:"settings-sync-log",children:[e.jsxs("button",{className:"settings-sync-log__header",type:"button",onClick:N,"aria-expanded":u,"data-testid":"settings-sync-log-header",children:[e.jsx(we,{size:16,className:`settings-sync-log__chevron ${u?"settings-sync-log__chevron--expanded":""}`}),e.jsxs("span",{children:[r.length," ",r.length===1?"entry":"entries"]})]}),u&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"settings-sync-log__filters",children:[e.jsxs("label",{children:["Direction:",e.jsxs("select",{value:o,onChange:l=>j(l.target.value),children:[e.jsx("option",{value:"all",children:"All"}),e.jsx("option",{value:"push",children:"Push"}),e.jsx("option",{value:"pull",children:"Pull"})]})]}),!i&&e.jsxs("label",{children:["Node:",e.jsxs("select",{value:g,onChange:l=>c(l.target.value),children:[e.jsx("option",{value:"all",children:"All Nodes"}),w.map(l=>e.jsx("option",{value:l,children:l},l))]})]})]}),a&&r.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"Loading..."}):v.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"No sync history available"}):e.jsx("div",{className:"settings-sync-log__list",children:v.map(l=>e.jsxs("div",{className:"settings-sync-log__entry",children:[e.jsx("span",{className:"settings-sync-log__entry-timestamp",children:d(l.timestamp)}),e.jsx("span",{className:"settings-sync-log__entry-direction",children:l.direction==="push"?e.jsx(Be,{size:14,"data-testid":"upload-icon"}):e.jsx(Te,{size:14,"data-testid":"download-icon"})}),e.jsx("span",{className:`settings-sync-log__entry-result ${M(l.result)}`,children:_(l.result)}),!i&&e.jsx("span",{className:"settings-sync-log__entry-node",children:l.nodeName}),l.details&&e.jsx("span",{className:"settings-sync-log__entry-details",title:l.details,children:hs(l.details??"")})]},l.id))})]})]})}function et(t,r){const a=typeof t=="string"?t:JSON.stringify(t,null,2),i=typeof r=="string"?r:JSON.stringify(r,null,2);if(a===i)return a;const u=a.split(`
12
12
  `),m=i.split(`
13
13
  `),o=[],j=Math.max(u.length,m.length);for(let g=0;g<j;g++){const c=u[g],N=m[g];c!==void 0&&c!==N&&o.push(`- ${c}`),N!==void 0&&N!==c&&o.push(`+ ${N}`),c!==void 0&&c===N&&o.push(` ${c}`)}return o.join(`
14
- `)}function et({isOpen:t,onClose:r,onResolve:a,conflicts:i,localNodeName:u,remoteNodeName:m,addToast:o}){const[j,g]=s.useState({}),[c,N]=s.useState(!1);s.useEffect(()=>{const l={};for(const b of i)j[b.key]||(l[b.key]={resolution:"local"});Object.keys(l).length>0&&g(b=>({...b,...l}))},[i,j]),s.useEffect(()=>{if(!t)return;const l=b=>{b.key==="Escape"&&(b.preventDefault(),r())};return document.addEventListener("keydown",l),()=>document.removeEventListener("keydown",l)},[t,r]);const w=s.useCallback((l,b)=>{g(C=>{const k=C[l]??{};return b==="manual"?{...C,[l]:{resolution:"manual",manualValue:k.manualValue??JSON.stringify(i.find($=>$.key===l)?.localValue??null,null,2)}}:{...C,[l]:{resolution:b}}})},[i]),v=s.useCallback((l,b)=>{g(C=>({...C,[l]:{...C[l],resolution:"manual",manualValue:b}}))},[]),d=s.useCallback(l=>{const b={};for(const C of i)b[C.key]={resolution:l};g(b)},[i]),M=s.useCallback(async()=>{N(!0);try{const l=i.map(b=>{const C=j[b.key]??{resolution:"local"};let k;switch(C.resolution){case"remote":k=b.remoteValue;break;case"manual":try{k=JSON.parse(C.manualValue??"null")}catch{k=C.manualValue??null}break;case"local":default:k=b.localValue;break}return{key:b.key,value:k}});await a(l),o("Settings conflicts resolved successfully","success"),r()}catch(l){const b=l instanceof Error?l.message:"Failed to resolve conflicts";o(b,"error")}finally{N(!1)}},[o,i,r,a,j]),_=s.useMemo(()=>{const l={};for(const b of i)l[b.key]=Qs(b.localValue,b.remoteValue);return l},[i]);return!t||i.length===0?null:e.jsx("div",{className:"modal-overlay open",onClick:r,children:e.jsxs("div",{className:"modal modal-lg settings-sync-conflict-modal",onClick:l=>l.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Resolve Settings Conflicts",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Resolve Settings Conflicts"}),e.jsx("button",{className:"modal-close",onClick:r,"aria-label":"Close conflict modal",children:"×"})]}),e.jsxs("div",{className:"modal-body",children:[e.jsx("div",{className:"settings-sync-conflict-modal__conflict-list",children:i.map(l=>{const b=j[l.key]??{resolution:"local"},C=_[l.key];return e.jsxs("div",{className:"settings-sync-conflict-modal__conflict-item",children:[e.jsx("div",{className:"settings-sync-conflict-modal__key",children:l.key}),e.jsxs("div",{className:"settings-sync-conflict-modal__diff-panel",children:[e.jsxs("div",{className:"settings-sync-conflict-modal__diff-side",children:[e.jsx("div",{className:"settings-sync-conflict-modal__diff-label",children:u}),e.jsx("div",{className:"settings-sync-conflict-modal__diff-content",children:e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap"},children:C})})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__diff-side",children:[e.jsx("div",{className:"settings-sync-conflict-modal__diff-label",children:m}),e.jsx("div",{className:"settings-sync-conflict-modal__diff-content",children:e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap"},children:C})})]})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__resolution",children:[e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:b.resolution==="local",onChange:()=>w(l.key,"local")}),"Keep Local"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:b.resolution==="remote",onChange:()=>w(l.key,"remote")}),"Keep Remote"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:b.resolution==="manual",onChange:()=>w(l.key,"manual")}),"Merge Manually"]})]}),b.resolution==="manual"&&e.jsx("textarea",{className:"settings-sync-conflict-modal__manual-input",value:b.manualValue??"",onChange:k=>v(l.key,k.target.value),placeholder:"Enter JSON value..."})]},l.key)})}),e.jsxs("div",{className:"settings-sync-conflict-modal__bulk-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>d("local"),type:"button",children:"Resolve All: Keep Local"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>d("remote"),type:"button",children:"Resolve All: Keep Remote"})]})]}),e.jsxs("div",{className:"modal-actions settings-sync-conflict-modal__footer",children:[e.jsx("button",{className:"btn btn-sm",onClick:r,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm",onClick:M,disabled:c,children:c?"Resolving...":"Confirm"})]})]})})}const He=/(KEY|TOKEN|SECRET|PASSWORD)/i;function ve(t){if(!t)return"—";const r=new Date(t);return Number.isNaN(r.getTime())?"—":r.toLocaleString()}function st(t){if(!t)return"—";const r=new Date(t),a=Date.now();if(Number.isNaN(r.getTime())||r.getTime()>a)return"—";const i=Math.floor((a-r.getTime())/1e3);if(i<60)return`${i}s`;const u=Math.floor(i/60);if(u<60)return`${u}m`;const m=Math.floor(u/60);return m<24?`${m}h ${u%60}m`:`${Math.floor(m/24)}d ${m%24}h`}function tt(t){switch(t){case"synced":return"node-detail-modal__sync-dot--synced";case"diff":return"node-detail-modal__sync-dot--diff";case"error":return"node-detail-modal__sync-dot--error";case"pending":return"node-detail-modal__sync-dot--pending";case"never-synced":default:return"node-detail-modal__sync-dot--never"}}function at(t){return t==="running"?"success":t==="creating"||t==="recreating"||t==="restarting"?"warning":"error"}function nt(t){return t?`${t.charAt(0).toUpperCase()}${t.slice(1)}`:"Unknown"}function rt(t){if(!t)return"—";try{const r=new URL(t);return r.port?r.port:r.protocol==="https:"?"443":r.protocol==="http:"?"80":"—"}catch{return"—"}}function lt(t,r){return He.test(t)?"••••••••":r}function ot({isOpen:t,onClose:r,node:a,projects:i,onUpdate:u,onHealthCheck:m,addToast:o,syncStatus:j,onPushSettings:g,onPullSettings:c,onSyncAuth:N,syncHistory:w=[],onResolveConflicts:v,managedDockerNode:d,containerStatus:M,onFetchContainerStatus:_,onFetchLogs:l,onUpdateDockerConfig:b,onFetchDockerConfigDiff:C}){const k=s.useRef(!0),[$,V]=s.useState(!1),[A,p]=s.useState(""),[x,S]=s.useState(""),[Z,H]=s.useState(""),[Y,ee]=s.useState(2),[U,X]=s.useState(!1),[G,te]=s.useState(!1),[J,T]=s.useState(!1),[E,y]=s.useState(!1),[L,D]=s.useState(null),[K,ae]=s.useState(!1),[ne]=s.useState([]),[se,le]=s.useState(M),[R,I]=s.useState(!1),[W,h]=s.useState(!1),[F,B]=s.useState(""),[q,oe]=s.useState(!1),[fe,Me]=s.useState(!1),[f,O]=s.useState(a?.dockerConfig??null),[Pe,Ee]=s.useState({}),[ie,Re]=s.useState(!1),[qe,Ae]=s.useState(!1);s.useEffect(()=>(k.current=!0,()=>{k.current=!1}),[]),s.useEffect(()=>{le(M)},[M]),s.useEffect(()=>{if(!a||!t){V(!1),h(!1),B("");return}p(a.name),S(a.url??""),H(a.apiKey??""),ee(a.maxConcurrent),V(!1),O(a.dockerConfig??null),Me(!1),Ee({})},[t,a]),s.useEffect(()=>{if(!t)return;const n=P=>{P.key==="Escape"&&(P.preventDefault(),r())};return document.addEventListener("keydown",n),()=>document.removeEventListener("keydown",n)},[t,r]);const ge=s.useMemo(()=>a?hs(i,a):[],[a,i]),Ye=s.useMemo(()=>d?d.hostConfig.type==="remote"?d.hostConfig.host??"—":"Local Docker":"—",[d]),Xe=s.useMemo(()=>!d?.resourceSizing?.cpuLimit&&!d?.resourceSizing?.memoryLimit?"Default":`${d.resourceSizing?.cpuLimit??"Default CPU"} / ${d.resourceSizing?.memoryLimit??"Default memory"}`,[d]),Je=s.useCallback(async()=>{if(a)try{if(await m(a.id),!k.current)return;o(`Health check completed for ${a.name}`,"success")}catch(n){if(!k.current)return;const P=n instanceof Error?n.message:"Health check failed";o(P,"error")}},[o,a,m]),We=s.useCallback(async()=>{if(!(!a||!g)){D(null),te(!0);try{if(await g(a.id),!k.current)return;o("Settings pushed successfully","success")}catch(n){if(!k.current)return;const P=n instanceof Error?n.message:"Push settings failed";D(P),o(P,"error")}finally{k.current&&te(!1)}}},[o,a,g]),Ge=s.useCallback(async()=>{if(!(!a||!c)){D(null),T(!0);try{if(await c(a.id),!k.current)return;o("Settings pulled successfully","success")}catch(n){if(!k.current)return;const P=n instanceof Error?n.message:"Pull settings failed";D(P),o(P,"error")}finally{k.current&&T(!1)}}},[o,a,c]),Ze=s.useCallback(async()=>{if(!(!a||!N)){D(null),y(!0);try{if(await N(a.id),!k.current)return;o("Auth credentials synced successfully","success")}catch(n){if(!k.current)return;const P=n instanceof Error?n.message:"Auth sync failed";D(P),o(P,"error")}finally{k.current&&y(!1)}}},[o,a,N]),Qe=s.useCallback(()=>{D(null)},[]),es=s.useCallback(async()=>{if(!(!d||!_)){I(!0);try{const n=await _(d.id);if(!k.current)return;le(n)}catch(n){const P=n instanceof Error?n.message:"Failed to fetch container status";o(P,"error")}finally{k.current&&I(!1)}}},[o,d,_]),ss=s.useCallback(async()=>{if(!(!d||!l)){h(!0),oe(!0);try{const n=await l(d.id);if(!k.current)return;B(n)}catch(n){if(!k.current)return;B("");const P=n instanceof Error?n.message:"Failed to fetch container logs";o(P,"error")}finally{k.current&&oe(!1)}}},[o,d,l]),ts=s.useCallback(async()=>{if(!a||U)return;const n=A.trim();if(!n){o("Name is required","error");return}if(a.type==="remote"&&!x.trim()){o("URL is required for remote nodes","error");return}if(!Number.isFinite(Y)||Y<1){o("Concurrency must be at least 1","error");return}X(!0);try{await u(a.id,{name:n,url:a.type==="remote"&&x.trim()||void 0,apiKey:a.type==="remote"&&Z||void 0,maxConcurrent:Y}),o(`Updated ${n}`,"success"),V(!1)}catch(P){const Q=P instanceof Error?P.message:"Failed to update node";o(Q,"error")}finally{X(!1)}},[o,Z,U,Y,A,a,u,x]);s.useEffect(()=>{!a?.dockerConfig||!C||!t||C(a.id).then(n=>{k.current&&Ae(n.needsRecreate)}).catch(()=>{k.current&&Ae(!1)})},[t,a,C]);const as=s.useCallback(async()=>{if(!(!a||!f||!b||ie)){Re(!0);try{const n=await b(a.id,{image:f.image,volumeMounts:f.volumeMounts,environment:f.environment,resources:f.resources,host:f.host,extraClis:f.extraClis,persistence:f.persistence,containerName:f.containerName});if(!k.current)return;O(n),o("Docker config saved","success")}catch(n){if(!k.current)return;const P=n instanceof Error?n.message:"Failed to save Docker config";o(P,"error")}finally{k.current&&Re(!1)}}},[o,f,ie,a,b]),ns=s.useCallback(()=>{a&&(p(a.name),S(a.url??""),H(a.apiKey??""),ee(a.maxConcurrent),V(!1))},[a]);if(!t||!a)return null;const re=se?.status??d?.status,rs=at(re);return e.jsxs("div",{className:"modal-overlay open",onClick:r,children:[e.jsxs("div",{className:"modal modal-lg node-detail-modal",onClick:n=>n.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":`Node details for ${a.name}`,children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Node Details"}),e.jsx("button",{className:"modal-close",onClick:r,"aria-label":"Close node detail modal",children:"×"})]}),e.jsxs("div",{className:"modal-body node-detail-modal__body",children:[e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsxs("div",{className:"node-detail-modal__section-header",children:[e.jsx("h4",{children:"Overview"}),!$&&e.jsxs("button",{className:"btn btn-sm",onClick:()=>V(!0),children:[e.jsx(fs,{size:14}),"Edit"]})]}),e.jsxs("div",{className:"node-detail-modal__grid",children:[e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Name"}),$?e.jsx("input",{className:"input",value:A,onChange:n=>p(n.target.value),disabled:U}):e.jsx("strong",{children:a.name})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Type"}),e.jsx("strong",{children:a.type==="local"?"Local":"Remote"})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Status"}),e.jsx("strong",{children:a.status})]}),e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Max Concurrent"}),$?e.jsx("input",{className:"input",type:"number",min:1,max:10,value:Y,onChange:n=>ee(Number(n.target.value)),disabled:U}):e.jsx("strong",{children:a.maxConcurrent})]}),a.type==="remote"&&e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"URL"}),$?e.jsx("input",{className:"input",value:x,onChange:n=>S(n.target.value),disabled:U}):e.jsx("strong",{children:a.url??"—"})]}),e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"API Key"}),$?e.jsx("input",{className:"input",type:"password",value:Z,onChange:n=>H(n.target.value),placeholder:"Leave blank to keep unchanged",disabled:U}):e.jsx("strong",{children:a.apiKey?"••••••••":"Not configured"})]})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Created"}),e.jsx("strong",{children:ve(a.createdAt)})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Updated"}),e.jsx("strong",{children:ve(a.updatedAt)})]})]}),$&&e.jsxs("div",{className:"node-detail-modal__edit-actions",children:[e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:ts,disabled:U,children:[e.jsx($e,{size:14}),U?"Saving...":"Save"]}),e.jsxs("button",{className:"btn btn-sm",onClick:ns,disabled:U,children:[e.jsx(me,{size:14}),"Cancel"]})]})]}),e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsxs("h4",{children:[a.type==="local"?"Projects":"Assigned Projects"," (",ge.length,")"]}),ge.length===0?e.jsx("p",{className:"node-detail-modal__empty",children:a.type==="local"?"No projects are running on this node.":"No projects are assigned to this node."}):e.jsx("ul",{className:"node-detail-modal__project-list",children:ge.map(n=>e.jsxs("li",{className:"node-detail-modal__project-item",children:[e.jsx("span",{children:n.name}),e.jsx("code",{children:n.id})]},n.id))})]}),e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Health"}),e.jsxs("div",{className:"node-detail-modal__health-row",children:[e.jsxs("span",{children:["Status: ",e.jsx("strong",{children:a.status})]}),e.jsxs("span",{children:["Last check: ",e.jsx("strong",{children:ve(a.updatedAt)})]})]})]}),f&&e.jsxs("section",{className:"node-detail-modal__section node-detail-modal__docker-config",children:[e.jsxs("button",{className:"btn btn-sm node-detail-modal__docker-toggle",onClick:()=>Me(n=>!n),"aria-expanded":fe,children:[e.jsx(we,{size:14,className:fe?"node-detail-modal__docker-toggle-icon--expanded":""}),"Docker Configuration"]}),fe&&e.jsxs("div",{className:"node-detail-modal__docker-config-content",children:[e.jsx("div",{className:"node-detail-modal__grid",children:e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"Image"}),e.jsx("input",{className:"input",value:f.image,onChange:n=>O({...f,image:n.target.value})})]})}),e.jsxs("details",{children:[e.jsx("summary",{children:"Volume Mounts"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[f.volumeMounts.map((n,P)=>e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:n.hostPath,placeholder:"Host path",onChange:Q=>{const z=[...f.volumeMounts];z[P]={...z[P],hostPath:Q.target.value},O({...f,volumeMounts:z})}}),e.jsx("input",{className:"input",value:n.containerPath,placeholder:"Container path",onChange:Q=>{const z=[...f.volumeMounts];z[P]={...z[P],containerPath:Q.target.value},O({...f,volumeMounts:z})}}),e.jsxs("select",{className:"input",value:n.mode??"rw",onChange:Q=>{const z=[...f.volumeMounts];z[P]={...z[P],mode:Q.target.value},O({...f,volumeMounts:z})},children:[e.jsx("option",{value:"rw",children:"rw"}),e.jsx("option",{value:"ro",children:"ro"})]}),e.jsxs("select",{className:"input",value:n.type??"volume",onChange:Q=>{const z=[...f.volumeMounts];z[P]={...z[P],type:Q.target.value},O({...f,volumeMounts:z})},children:[e.jsx("option",{value:"volume",children:"volume"}),e.jsx("option",{value:"bind",children:"bind"})]}),e.jsx("button",{className:"btn btn-sm",onClick:()=>O({...f,volumeMounts:f.volumeMounts.filter((Q,z)=>z!==P)}),children:"Remove"})]},`${n.hostPath}-${n.containerPath}-${P}`)),e.jsx("button",{className:"btn btn-sm",onClick:()=>O({...f,volumeMounts:[...f.volumeMounts,{hostPath:"",containerPath:"",mode:"rw",type:"volume"}]}),children:"Add Mount"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Environment Variables"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[Object.entries(f.environment).map(([n,P])=>{const Q=He.test(n)&&!Pe[n];return e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:n,onChange:z=>{const xe={...f.environment};delete xe[n],xe[z.target.value]=P,O({...f,environment:xe})}}),e.jsx("input",{className:"input",value:Q?"***":String(P),onChange:z=>O({...f,environment:{...f.environment,[n]:z.target.value}})}),e.jsx("button",{className:"btn btn-sm",onClick:()=>Ee(z=>({...z,[n]:!z[n]})),children:Pe[n]?e.jsx(gs,{size:14}):e.jsx(xs,{size:14})}),e.jsx("button",{className:"btn btn-sm",onClick:()=>{const z={...f.environment};delete z[n],O({...f,environment:z})},children:"Remove"})]},n)}),e.jsx("button",{className:"btn btn-sm",onClick:()=>{const n=`NEW_VAR_${Object.keys(f.environment).length+1}`;O({...f,environment:{...f.environment,[n]:""}})},children:"Add Variable"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Resources"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",type:"number",placeholder:"Memory bytes (2 GB = 2147483648)",value:f.resources?.memoryBytes??"",onChange:n=>O({...f,resources:{...f.resources,memoryBytes:n.target.value?Number(n.target.value):void 0}})}),e.jsx("input",{className:"input",type:"number",placeholder:"CPU count",value:f.resources?.cpuCount??"",onChange:n=>O({...f,resources:{...f.resources,cpuCount:n.target.value?Number(n.target.value):void 0}})}),e.jsx("input",{className:"input",type:"number",placeholder:"PIDs limit",value:f.resources?.pidsLimit??"",onChange:n=>O({...f,resources:{...f.resources,pidsLimit:n.target.value?Number(n.target.value):void 0}})})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Host Config"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",placeholder:"Context name",value:f.host?.contextName??"",onChange:n=>O({...f,host:{...f.host,contextName:n.target.value}})}),e.jsx("input",{className:"input",placeholder:"Docker host URL",value:f.host?.dockerHost??"",onChange:n=>O({...f,host:{...f.host,dockerHost:n.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS CA cert path",value:f.host?.tlsCaCert??"",onChange:n=>O({...f,host:{...f.host,tlsCaCert:n.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS cert path",value:f.host?.tlsCert??"",onChange:n=>O({...f,host:{...f.host,tlsCert:n.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS key path",value:f.host?.tlsKey??"",onChange:n=>O({...f,host:{...f.host,tlsKey:n.target.value}})}),e.jsxs("label",{className:"node-detail-modal__checkbox",children:[e.jsx("input",{type:"checkbox",checked:f.host?.tlsVerify??!0,onChange:n=>O({...f,host:{...f.host,tlsVerify:n.target.checked}})}),"TLS verify"]})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Extra CLIs"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[(f.extraClis??[]).map((n,P)=>e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:n,onChange:Q=>{const z=[...f.extraClis??[]];z[P]=Q.target.value,O({...f,extraClis:z})}}),e.jsx("button",{className:"btn btn-sm",onClick:()=>O({...f,extraClis:(f.extraClis??[]).filter((Q,z)=>z!==P)}),children:"Remove"})]},`${n}-${P}`)),e.jsx("button",{className:"btn btn-sm",onClick:()=>O({...f,extraClis:[...f.extraClis??[],""]}),children:"Add CLI"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Persistence"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",placeholder:"Volume name",value:f.persistence?.volumeName??"",onChange:n=>O({...f,persistence:{...f.persistence,volumeName:n.target.value}})}),e.jsxs("label",{className:"node-detail-modal__checkbox",children:[e.jsx("input",{type:"checkbox",checked:f.persistence?.retainOnDelete??!1,onChange:n=>O({...f,persistence:{...f.persistence,retainOnDelete:n.target.checked}})}),"Retain on delete"]})]})]}),e.jsxs("div",{className:"node-detail-modal__docker-meta",children:[e.jsxs("span",{children:["Config v",f.configVersion," • Updated ",ke(f.lastUpdated??a.updatedAt)]}),qe&&e.jsx("span",{className:"node-detail-modal__docker-recreate",children:"Needs Recreate"})]}),e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>void as(),disabled:ie,children:[e.jsx($e,{size:14}),ie?"Saving...":"Save Docker Config"]})]})]}),d&&e.jsxs("section",{className:"node-detail-modal__section docker-management",children:[e.jsx("h4",{children:"Docker Management"}),e.jsxs("div",{className:"docker-management__status-card",children:[e.jsxs("div",{className:"docker-management__status-row",children:[e.jsx("span",{className:`docker-management__status-dot docker-management__status-dot--${rs}`,"aria-hidden":!0}),e.jsx("strong",{children:nt(re)}),(re==="creating"||re==="recreating"||re==="restarting")&&e.jsx(De,{size:14,className:"spin","aria-hidden":!0})]}),e.jsxs("div",{className:"docker-management__status-meta",children:[re==="running"&&e.jsxs("span",{children:["Uptime: ",st(se?.startedAt)]}),re!=="running"&&se?.exitCode!==void 0&&e.jsxs("span",{children:["Exit code: ",se.exitCode]}),(se?.error||d.errorMessage)&&e.jsx("span",{children:se?.error??d.errorMessage})]}),e.jsx("button",{className:"btn btn-sm",onClick:()=>void es(),disabled:!_||R,children:R?"Refreshing...":"Refresh Status"})]}),e.jsxs("div",{className:"node-detail-modal__grid docker-management__info-grid",children:[e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Image"}),e.jsx("strong",{children:e.jsxs("code",{children:[d.imageName,":",d.imageTag]})})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Container ID"}),e.jsx("strong",{children:e.jsx("code",{children:d.containerId?d.containerId.slice(0,12):"—"})})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Host"}),e.jsx("strong",{children:Ye})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Persistent Storage"}),e.jsx("strong",{children:d.persistentStorage?"Yes":"No"})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Port"}),e.jsx("strong",{children:rt(d.reachableUrl)})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Resource Sizing"}),e.jsx("strong",{children:Xe})]})]}),e.jsxs("div",{className:"docker-management__actions",children:[e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx(Ke,{size:14}),"Start"]}),e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx(Ue,{size:14}),"Stop"]}),e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx(De,{size:14}),"Restart"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>void ss(),disabled:!l,children:[e.jsx(ps,{size:14}),"View Logs"]})]}),W&&e.jsxs("div",{className:"docker-management__log-viewer",children:[e.jsxs("div",{className:"docker-management__log-viewer-header",children:[e.jsx("strong",{children:"Container Logs"}),e.jsx("button",{className:"btn-icon",onClick:()=>h(!1),"aria-label":"Close logs",children:e.jsx(me,{size:14})})]}),q?e.jsx("p",{children:"Fetching logs..."}):e.jsx("pre",{children:F.trim()||"No logs available"})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Environment Variables"}),e.jsx("dl",{className:"docker-management__env-list",children:Object.entries(d.envVars).map(([n,P])=>e.jsxs("div",{children:[e.jsx("dt",{children:n}),e.jsx("dd",{children:lt(n,P)})]},n))})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Volume Mounts"}),e.jsx("ul",{className:"docker-management__mounts-list",children:d.volumeMounts.map(n=>e.jsxs("li",{children:[e.jsxs("span",{children:[n.hostPath," → ",n.containerPath]}),n.readOnly&&e.jsx("span",{className:"node-card__type-badge",children:"Read-only"})]},`${n.hostPath}:${n.containerPath}`))})]})]}),a.type==="remote"&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Settings Sync"}),j&&e.jsxs("div",{className:"node-detail-modal__sync-status",children:[e.jsx("span",{className:`node-detail-modal__sync-dot ${tt(j.syncState)}`,"aria-hidden":!0}),e.jsxs("span",{children:["Last sync: ",e.jsx("strong",{children:j.lastSyncAt?ke(j.lastSyncAt):"Never synced"})]}),j.diffCount>0&&e.jsxs("span",{className:"node-detail-modal__sync-diff",children:["Differences: ",e.jsx("strong",{children:j.diffCount})]})]}),e.jsxs("div",{className:"node-detail-modal__sync-actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:We,disabled:G||!g,children:[e.jsx(Be,{size:14}),G?"Pushing...":"Push Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:Ge,disabled:J||!c,children:[e.jsx(Te,{size:14}),J?"Pulling...":"Pull Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:Ze,disabled:E||!N,children:[e.jsx(Oe,{size:14}),E?"Syncing...":"Sync Auth"]})]}),L&&e.jsxs("div",{className:"node-detail-modal__sync-error",children:[e.jsx("span",{children:L}),e.jsx("button",{className:"node-detail-modal__sync-error-dismiss",onClick:Qe,"aria-label":"Dismiss error",children:e.jsx(me,{size:14})})]})]}),a.type==="remote"&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Sync History"}),e.jsx(Zs,{nodeId:a.id,entries:w,singleNode:!0})]})]}),e.jsxs("div",{className:"modal-actions node-detail-modal__actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:Je,children:[e.jsx(Fe,{size:14}),"Health Check"]}),e.jsx("button",{className:"btn btn-sm",onClick:r,children:"Close"})]})]}),a.type==="remote"&&e.jsx(et,{isOpen:K,onClose:()=>ae(!1),onResolve:v??(async()=>{}),conflicts:ne,localNodeName:"Local",remoteNodeName:a.name,addToast:o})]})}const ct=15e3,it=1e3;function dt(){const[t,r]=s.useState([]),[a,i]=s.useState(!0),[u,m]=s.useState(null),o=s.useRef(null),j=s.useRef(0),g=s.useCallback(async()=>{try{m(null);const v=await Ie();r(v)}catch(v){m(v instanceof Error?v.message:"Failed to fetch managed Docker nodes")}},[]);s.useEffect(()=>{let v=!1;async function d(){i(!0);try{const _=await Ie();v||(r(_),m(null))}catch(_){v||m(_ instanceof Error?_.message:"Failed to fetch managed Docker nodes")}finally{v||i(!1)}}d();const M=()=>{if(document.visibilityState!=="visible")return;const _=Date.now();_-j.current<it||(j.current=_,g())};return document.addEventListener("visibilitychange",M),()=>{v=!0,document.removeEventListener("visibilitychange",M)}},[g]),s.useEffect(()=>{if(!a)return o.current=setInterval(()=>{g()},ct),()=>{o.current&&(clearInterval(o.current),o.current=null)}},[a,g]);const c=s.useCallback(async v=>js(v),[]),N=s.useCallback(async(v,d)=>(await vs(v,d)).logs,[]),w=s.useCallback(async v=>{const d=await bs(v),M={...d,nodeId:d.nodeId??void 0,containerId:d.containerId??void 0,status:d.status,hostConfig:{type:d.hostConfig.host||d.hostConfig.context?"remote":"local",host:d.hostConfig.host,context:d.hostConfig.context},reachableUrl:d.reachableUrl??void 0,volumeMounts:d.volumeMounts.map(_=>({hostPath:_.hostPath,containerPath:_.containerPath,readOnly:_.mode==="ro"?!0:void 0})),persistentStorage:d.persistentStorage,resourceSizing:{cpuLimit:d.resourceSizing.cpus!==void 0?String(d.resourceSizing.cpus):void 0,memoryLimit:d.resourceSizing.memoryMB!==void 0?`${d.resourceSizing.memoryMB}MB`:void 0},errorMessage:d.errorMessage??void 0};return r(_=>[..._,M]),M},[]);return{dockerNodes:t,loading:a,error:u,refresh:g,getContainerStatus:c,getLogs:N,create:w}}function xt({addToast:t,onClose:r}){const{nodes:a,loading:i,error:u,refresh:m,register:o,update:j,unregister:g,healthCheck:c,patchDockerConfig:N,fetchDockerDiff:w,discoverRemoteProjects:v}=ys(),{projects:d,refresh:M}=_s(),{meshState:_,loading:l,error:b}=$s(),{syncStatusMap:C,pushSettings:k,pullSettings:$,syncAuth:V,trackNode:A,getAuthSyncState:p,getAuthProviders:x}=Rs(),{dockerNodes:S,loading:Z,refresh:H,getContainerStatus:Y,getLogs:ee,create:U}=dt(),[X,G]=s.useState(!1),[te,J]=s.useState(!1),[T,E]=s.useState(null);s.useEffect(()=>{const R=a.filter(I=>I.type==="remote");for(const I of R)A(I.id)},[a,A]),s.useEffect(()=>{if(!T)return;const R=a.find(I=>I.id===T.id)??null;E(R)},[a,T]);const y=s.useMemo(()=>{const R=a.length,I=a.filter(q=>q.status==="online").length,W=a.filter(q=>q.status==="offline"||q.status==="error").length,h=a.filter(q=>q.type==="remote").length,F=a.filter(q=>q.type==="remote"&&C[q.id]&&pe(C[q.id]).syncState==="synced").length,B=S.length;return{total:R,online:I,offline:W,remote:h,synced:F,docker:B}},[S.length,a,C]),L=s.useCallback(async R=>{await o(R),await M()},[M,o]),D=s.useCallback(async R=>{try{await U(R),t(`Docker node "${R.name}" created`,"success"),J(!1)}catch(I){const W=I instanceof Error?I.message:"Failed to create Docker node";throw t(W,"error"),I}},[t,U]),K=s.useMemo(()=>{const R=new Map;for(const I of S)I.nodeId&&R.set(I.nodeId,I);return R},[S]),ae=s.useCallback(async()=>{try{await Promise.all([m(),H()])}catch{t("Failed to refresh nodes","error")}},[t,m,H]),ne=s.useCallback(async R=>{try{await c(R),t("Node health check complete","success")}catch(I){const W=I instanceof Error?I.message:"Health check failed";t(W,"error")}},[t,c]),se=s.useCallback(async R=>{try{await g(R),t("Node removed","success"),T?.id===R&&E(null)}catch(I){const W=I instanceof Error?I.message:"Failed to remove node";t(W,"error")}},[t,T?.id,g]),le=s.useCallback(async(R,I)=>{await j(R,I)},[j]);return e.jsxs("div",{className:"nodes-view","data-testid":"nodes-view",children:[e.jsxs("div",{className:"nodes-view-header",children:[e.jsxs("div",{className:"nodes-view-title",children:[e.jsxs("h2",{children:[e.jsx(Ve,{size:20}),"Nodes"]}),e.jsxs("span",{className:"nodes-view-count",children:[a.length," registered"]})]}),e.jsxs("div",{className:"nodes-view-actions",children:[e.jsx("button",{className:"btn-icon nodes-view-close",onClick:r,"aria-label":"Close nodes view",children:e.jsx(me,{size:16})}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>void ae(),disabled:i||Z,children:[e.jsx(Ne,{size:14,className:i?"spin":""}),"Refresh"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>G(!0),children:[e.jsx(he,{size:14}),"Add Node"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>J(!0),title:"Add a managed Docker node",children:[e.jsx(ye,{size:14}),"Add Docker Node"]})]})]}),e.jsxs("div",{className:"nodes-view-stats",children:[e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-total",children:[e.jsx("span",{children:"Total"}),e.jsx("strong",{children:y.total})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--online","data-testid":"nodes-stat-online",children:[e.jsxs("span",{children:[e.jsx(Ms,{size:14})," Online"]}),e.jsx("strong",{children:y.online})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--offline","data-testid":"nodes-stat-offline",children:[e.jsxs("span",{children:[e.jsx(Ss,{size:14})," Offline"]}),e.jsx("strong",{children:y.offline})]}),e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-remote",children:[e.jsxs("span",{children:[e.jsx(Ns,{size:14})," Remote"]}),e.jsx("strong",{children:y.remote})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--synced","data-testid":"nodes-stat-synced",children:[e.jsxs("span",{children:[e.jsx(Ne,{size:14})," Synced"]}),e.jsx("strong",{children:y.synced})]}),e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-docker",children:[e.jsxs("span",{children:[e.jsx(ye,{size:14})," Docker"]}),e.jsx("strong",{children:y.docker})]})]}),(u||b)&&e.jsx("div",{className:"nodes-view-error",children:u??b}),!l&&_.length>0&&e.jsxs("section",{className:"nodes-view-topology","aria-label":"Mesh Topology",children:[e.jsx("h3",{className:"nodes-view-section-title",children:"Mesh Topology"}),e.jsx(Hs,{nodes:_})]}),i?e.jsx("div",{className:"nodes-view-grid",children:Array.from({length:4}).map((R,I)=>e.jsx("div",{className:"node-card node-card--loading","aria-hidden":!0},I))}):a.length===0?e.jsxs("div",{className:"nodes-view-empty",children:[e.jsx("p",{children:"No nodes are registered yet."}),e.jsxs("button",{className:"btn btn-primary",onClick:()=>G(!0),children:[e.jsx(he,{size:14}),"Add First Node"]})]}):e.jsx("div",{className:"nodes-view-grid",children:a.map(R=>{const I=R.type==="remote"&&C[R.id]?pe(C[R.id]):void 0;return e.jsx(Fs,{node:R,projects:d,onHealthCheck:W=>{ne(W)},onEdit:W=>E(W),onRemove:W=>{se(W)},isLoading:i,syncStatus:I,authSyncState:R.type==="remote"?p(R.id):void 0,authSyncProviders:R.type==="remote"?x(R.id):void 0,managedDockerNode:K.get(R.id)},R.id)})}),e.jsx(Ys,{isOpen:X,onClose:()=>G(!1),onSubmit:L,onDiscoverRemoteProjects:v,addToast:t,projects:d}),e.jsx(Gs,{isOpen:te,onClose:()=>J(!1),onSubmit:D,addToast:t}),e.jsx(ot,{isOpen:T!==null,onClose:()=>E(null),node:T,projects:d,onUpdate:le,onHealthCheck:ne,addToast:t,syncStatus:T?.type==="remote"&&T&&C[T.id]?pe(C[T.id]):void 0,onPushSettings:k,onPullSettings:$,onSyncAuth:V,managedDockerNode:T?K.get(T.id):void 0,onFetchContainerStatus:Y,onFetchLogs:ee,onUpdateDockerConfig:N,onFetchDockerConfigDiff:w})]})}export{xt as NodesView};
14
+ `)}function st({isOpen:t,onClose:r,onResolve:a,conflicts:i,localNodeName:u,remoteNodeName:m,addToast:o}){const[j,g]=s.useState({}),[c,N]=s.useState(!1);s.useEffect(()=>{const l={};for(const b of i)j[b.key]||(l[b.key]={resolution:"local"});Object.keys(l).length>0&&g(b=>({...b,...l}))},[i,j]),s.useEffect(()=>{if(!t)return;const l=b=>{b.key==="Escape"&&(b.preventDefault(),r())};return document.addEventListener("keydown",l),()=>document.removeEventListener("keydown",l)},[t,r]);const w=s.useCallback((l,b)=>{g(C=>{const k=C[l]??{};return b==="manual"?{...C,[l]:{resolution:"manual",manualValue:k.manualValue??JSON.stringify(i.find($=>$.key===l)?.localValue??null,null,2)}}:{...C,[l]:{resolution:b}}})},[i]),v=s.useCallback((l,b)=>{g(C=>({...C,[l]:{...C[l],resolution:"manual",manualValue:b}}))},[]),d=s.useCallback(l=>{const b={};for(const C of i)b[C.key]={resolution:l};g(b)},[i]),M=s.useCallback(async()=>{N(!0);try{const l=i.map(b=>{const C=j[b.key]??{resolution:"local"};let k;switch(C.resolution){case"remote":k=b.remoteValue;break;case"manual":try{k=JSON.parse(C.manualValue??"null")}catch{k=C.manualValue??null}break;case"local":default:k=b.localValue;break}return{key:b.key,value:k}});await a(l),o("Settings conflicts resolved successfully","success"),r()}catch(l){const b=l instanceof Error?l.message:"Failed to resolve conflicts";o(b,"error")}finally{N(!1)}},[o,i,r,a,j]),_=s.useMemo(()=>{const l={};for(const b of i)l[b.key]=et(b.localValue,b.remoteValue);return l},[i]);return!t||i.length===0?null:e.jsx("div",{className:"modal-overlay open",onClick:r,children:e.jsxs("div",{className:"modal modal-lg settings-sync-conflict-modal",onClick:l=>l.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Resolve Settings Conflicts",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Resolve Settings Conflicts"}),e.jsx("button",{className:"modal-close",onClick:r,"aria-label":"Close conflict modal",children:"×"})]}),e.jsxs("div",{className:"modal-body",children:[e.jsx("div",{className:"settings-sync-conflict-modal__conflict-list",children:i.map(l=>{const b=j[l.key]??{resolution:"local"},C=_[l.key];return e.jsxs("div",{className:"settings-sync-conflict-modal__conflict-item",children:[e.jsx("div",{className:"settings-sync-conflict-modal__key",children:l.key}),e.jsxs("div",{className:"settings-sync-conflict-modal__diff-panel",children:[e.jsxs("div",{className:"settings-sync-conflict-modal__diff-side",children:[e.jsx("div",{className:"settings-sync-conflict-modal__diff-label",children:u}),e.jsx("div",{className:"settings-sync-conflict-modal__diff-content",children:e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap"},children:C})})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__diff-side",children:[e.jsx("div",{className:"settings-sync-conflict-modal__diff-label",children:m}),e.jsx("div",{className:"settings-sync-conflict-modal__diff-content",children:e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap"},children:C})})]})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__resolution",children:[e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:b.resolution==="local",onChange:()=>w(l.key,"local")}),"Keep Local"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:b.resolution==="remote",onChange:()=>w(l.key,"remote")}),"Keep Remote"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:b.resolution==="manual",onChange:()=>w(l.key,"manual")}),"Merge Manually"]})]}),b.resolution==="manual"&&e.jsx("textarea",{className:"settings-sync-conflict-modal__manual-input",value:b.manualValue??"",onChange:k=>v(l.key,k.target.value),placeholder:"Enter JSON value..."})]},l.key)})}),e.jsxs("div",{className:"settings-sync-conflict-modal__bulk-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>d("local"),type:"button",children:"Resolve All: Keep Local"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>d("remote"),type:"button",children:"Resolve All: Keep Remote"})]})]}),e.jsxs("div",{className:"modal-actions settings-sync-conflict-modal__footer",children:[e.jsx("button",{className:"btn btn-sm",onClick:r,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm",onClick:M,disabled:c,children:c?"Resolving...":"Confirm"})]})]})})}const He=/(KEY|TOKEN|SECRET|PASSWORD)/i;function ve(t){if(!t)return"—";const r=new Date(t);return Number.isNaN(r.getTime())?"—":r.toLocaleString()}function tt(t){if(!t)return"—";const r=new Date(t),a=Date.now();if(Number.isNaN(r.getTime())||r.getTime()>a)return"—";const i=Math.floor((a-r.getTime())/1e3);if(i<60)return`${i}s`;const u=Math.floor(i/60);if(u<60)return`${u}m`;const m=Math.floor(u/60);return m<24?`${m}h ${u%60}m`:`${Math.floor(m/24)}d ${m%24}h`}function at(t){switch(t){case"synced":return"node-detail-modal__sync-dot--synced";case"diff":return"node-detail-modal__sync-dot--diff";case"error":return"node-detail-modal__sync-dot--error";case"pending":return"node-detail-modal__sync-dot--pending";case"never-synced":default:return"node-detail-modal__sync-dot--never"}}function nt(t){return t==="running"?"success":t==="creating"||t==="recreating"||t==="restarting"?"warning":"error"}function rt(t){return t?`${t.charAt(0).toUpperCase()}${t.slice(1)}`:"Unknown"}function lt(t){if(!t)return"—";try{const r=new URL(t);return r.port?r.port:r.protocol==="https:"?"443":r.protocol==="http:"?"80":"—"}catch{return"—"}}function ot(t,r){return He.test(t)?"••••••••":r}function ct({isOpen:t,onClose:r,node:a,projects:i,onUpdate:u,onHealthCheck:m,addToast:o,syncStatus:j,onPushSettings:g,onPullSettings:c,onSyncAuth:N,syncHistory:w=[],onResolveConflicts:v,managedDockerNode:d,containerStatus:M,onFetchContainerStatus:_,onFetchLogs:l,onUpdateDockerConfig:b,onFetchDockerConfigDiff:C}){const k=s.useRef(!0),[$,V]=s.useState(!1),[A,p]=s.useState(""),[x,S]=s.useState(""),[Z,H]=s.useState(""),[Y,ee]=s.useState(2),[U,X]=s.useState(!1),[G,te]=s.useState(!1),[J,T]=s.useState(!1),[E,y]=s.useState(!1),[L,D]=s.useState(null),[K,ae]=s.useState(!1),[ne]=s.useState([]),[se,le]=s.useState(M),[R,I]=s.useState(!1),[W,h]=s.useState(!1),[O,B]=s.useState(""),[q,oe]=s.useState(!1),[fe,Me]=s.useState(!1),[f,F]=s.useState(a?.dockerConfig??null),[Pe,Ee]=s.useState({}),[ie,Re]=s.useState(!1),[qe,Ae]=s.useState(!1);s.useEffect(()=>(k.current=!0,()=>{k.current=!1}),[]),s.useEffect(()=>{le(M)},[M]),s.useEffect(()=>{if(!a||!t){V(!1),h(!1),B("");return}p(a.name),S(a.url??""),H(a.apiKey??""),ee(a.maxConcurrent),V(!1),F(a.dockerConfig??null),Me(!1),Ee({})},[t,a]),s.useEffect(()=>{if(!t)return;const n=P=>{P.key==="Escape"&&(P.preventDefault(),r())};return document.addEventListener("keydown",n),()=>document.removeEventListener("keydown",n)},[t,r]);const ge=s.useMemo(()=>a?fs(i,a):[],[a,i]),Ye=s.useMemo(()=>d?d.hostConfig.type==="remote"?d.hostConfig.host??"—":"Local Docker":"—",[d]),Xe=s.useMemo(()=>!d?.resourceSizing?.cpuLimit&&!d?.resourceSizing?.memoryLimit?"Default":`${d.resourceSizing?.cpuLimit??"Default CPU"} / ${d.resourceSizing?.memoryLimit??"Default memory"}`,[d]),Je=s.useCallback(async()=>{if(a)try{if(await m(a.id),!k.current)return;o(`Health check completed for ${a.name}`,"success")}catch(n){if(!k.current)return;const P=n instanceof Error?n.message:"Health check failed";o(P,"error")}},[o,a,m]),We=s.useCallback(async()=>{if(!(!a||!g)){D(null),te(!0);try{if(await g(a.id),!k.current)return;o("Settings pushed successfully","success")}catch(n){if(!k.current)return;const P=n instanceof Error?n.message:"Push settings failed";D(P),o(P,"error")}finally{k.current&&te(!1)}}},[o,a,g]),Ge=s.useCallback(async()=>{if(!(!a||!c)){D(null),T(!0);try{if(await c(a.id),!k.current)return;o("Settings pulled successfully","success")}catch(n){if(!k.current)return;const P=n instanceof Error?n.message:"Pull settings failed";D(P),o(P,"error")}finally{k.current&&T(!1)}}},[o,a,c]),Ze=s.useCallback(async()=>{if(!(!a||!N)){D(null),y(!0);try{if(await N(a.id),!k.current)return;o("Auth credentials synced successfully","success")}catch(n){if(!k.current)return;const P=n instanceof Error?n.message:"Auth sync failed";D(P),o(P,"error")}finally{k.current&&y(!1)}}},[o,a,N]),Qe=s.useCallback(()=>{D(null)},[]),es=s.useCallback(async()=>{if(!(!d||!_)){I(!0);try{const n=await _(d.id);if(!k.current)return;le(n)}catch(n){const P=n instanceof Error?n.message:"Failed to fetch container status";o(P,"error")}finally{k.current&&I(!1)}}},[o,d,_]),ss=s.useCallback(async()=>{if(!(!d||!l)){h(!0),oe(!0);try{const n=await l(d.id);if(!k.current)return;B(n)}catch(n){if(!k.current)return;B("");const P=n instanceof Error?n.message:"Failed to fetch container logs";o(P,"error")}finally{k.current&&oe(!1)}}},[o,d,l]),ts=s.useCallback(async()=>{if(!a||U)return;const n=A.trim();if(!n){o("Name is required","error");return}if(a.type==="remote"&&!x.trim()){o("URL is required for remote nodes","error");return}if(!Number.isFinite(Y)||Y<1){o("Concurrency must be at least 1","error");return}X(!0);try{await u(a.id,{name:n,url:a.type==="remote"&&x.trim()||void 0,apiKey:a.type==="remote"&&Z||void 0,maxConcurrent:Y}),o(`Updated ${n}`,"success"),V(!1)}catch(P){const Q=P instanceof Error?P.message:"Failed to update node";o(Q,"error")}finally{X(!1)}},[o,Z,U,Y,A,a,u,x]);s.useEffect(()=>{!a?.dockerConfig||!C||!t||C(a.id).then(n=>{k.current&&Ae(n.needsRecreate)}).catch(()=>{k.current&&Ae(!1)})},[t,a,C]);const as=s.useCallback(async()=>{if(!(!a||!f||!b||ie)){Re(!0);try{const n=await b(a.id,{image:f.image,volumeMounts:f.volumeMounts,environment:f.environment,resources:f.resources,host:f.host,extraClis:f.extraClis,persistence:f.persistence,containerName:f.containerName});if(!k.current)return;F(n),o("Docker config saved","success")}catch(n){if(!k.current)return;const P=n instanceof Error?n.message:"Failed to save Docker config";o(P,"error")}finally{k.current&&Re(!1)}}},[o,f,ie,a,b]),ns=s.useCallback(()=>{a&&(p(a.name),S(a.url??""),H(a.apiKey??""),ee(a.maxConcurrent),V(!1))},[a]);if(!t||!a)return null;const re=se?.status??d?.status,rs=nt(re);return e.jsxs("div",{className:"modal-overlay open",onClick:r,children:[e.jsxs("div",{className:"modal modal-lg node-detail-modal",onClick:n=>n.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":`Node details for ${a.name}`,children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Node Details"}),e.jsx("button",{className:"modal-close",onClick:r,"aria-label":"Close node detail modal",children:"×"})]}),e.jsxs("div",{className:"modal-body node-detail-modal__body",children:[e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsxs("div",{className:"node-detail-modal__section-header",children:[e.jsx("h4",{children:"Overview"}),!$&&e.jsxs("button",{className:"btn btn-sm",onClick:()=>V(!0),children:[e.jsx(gs,{size:14}),"Edit"]})]}),e.jsxs("div",{className:"node-detail-modal__grid",children:[e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Name"}),$?e.jsx("input",{className:"input",value:A,onChange:n=>p(n.target.value),disabled:U}):e.jsx("strong",{children:a.name})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Type"}),e.jsx("strong",{children:a.type==="local"?"Local":"Remote"})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Status"}),e.jsx("strong",{children:a.status})]}),e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Max Concurrent"}),$?e.jsx("input",{className:"input",type:"number",min:1,max:10,value:Y,onChange:n=>ee(Number(n.target.value)),disabled:U}):e.jsx("strong",{children:a.maxConcurrent})]}),a.type==="remote"&&e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"URL"}),$?e.jsx("input",{className:"input",value:x,onChange:n=>S(n.target.value),disabled:U}):e.jsx("strong",{children:a.url??"—"})]}),e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"API Key"}),$?e.jsx("input",{className:"input",type:"password",value:Z,onChange:n=>H(n.target.value),placeholder:"Leave blank to keep unchanged",disabled:U}):e.jsx("strong",{children:a.apiKey?"••••••••":"Not configured"})]})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Created"}),e.jsx("strong",{children:ve(a.createdAt)})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Updated"}),e.jsx("strong",{children:ve(a.updatedAt)})]})]}),$&&e.jsxs("div",{className:"node-detail-modal__edit-actions",children:[e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:ts,disabled:U,children:[e.jsx($e,{size:14}),U?"Saving...":"Save"]}),e.jsxs("button",{className:"btn btn-sm",onClick:ns,disabled:U,children:[e.jsx(me,{size:14}),"Cancel"]})]})]}),e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsxs("h4",{children:[a.type==="local"?"Projects":"Assigned Projects"," (",ge.length,")"]}),ge.length===0?e.jsx("p",{className:"node-detail-modal__empty",children:a.type==="local"?"No projects are running on this node.":"No projects are assigned to this node."}):e.jsx("ul",{className:"node-detail-modal__project-list",children:ge.map(n=>e.jsxs("li",{className:"node-detail-modal__project-item",children:[e.jsx("span",{children:n.name}),e.jsx("code",{children:n.id})]},n.id))})]}),e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Health"}),e.jsxs("div",{className:"node-detail-modal__health-row",children:[e.jsxs("span",{children:["Status: ",e.jsx("strong",{children:a.status})]}),e.jsxs("span",{children:["Last check: ",e.jsx("strong",{children:ve(a.updatedAt)})]})]})]}),f&&e.jsxs("section",{className:"node-detail-modal__section node-detail-modal__docker-config",children:[e.jsxs("button",{className:"btn btn-sm node-detail-modal__docker-toggle",onClick:()=>Me(n=>!n),"aria-expanded":fe,children:[e.jsx(we,{size:14,className:fe?"node-detail-modal__docker-toggle-icon--expanded":""}),"Docker Configuration"]}),fe&&e.jsxs("div",{className:"node-detail-modal__docker-config-content",children:[e.jsx("div",{className:"node-detail-modal__grid",children:e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"Image"}),e.jsx("input",{className:"input",value:f.image,onChange:n=>F({...f,image:n.target.value})})]})}),e.jsxs("details",{children:[e.jsx("summary",{children:"Volume Mounts"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[f.volumeMounts.map((n,P)=>e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:n.hostPath,placeholder:"Host path",onChange:Q=>{const z=[...f.volumeMounts];z[P]={...z[P],hostPath:Q.target.value},F({...f,volumeMounts:z})}}),e.jsx("input",{className:"input",value:n.containerPath,placeholder:"Container path",onChange:Q=>{const z=[...f.volumeMounts];z[P]={...z[P],containerPath:Q.target.value},F({...f,volumeMounts:z})}}),e.jsxs("select",{className:"input",value:n.mode??"rw",onChange:Q=>{const z=[...f.volumeMounts];z[P]={...z[P],mode:Q.target.value},F({...f,volumeMounts:z})},children:[e.jsx("option",{value:"rw",children:"rw"}),e.jsx("option",{value:"ro",children:"ro"})]}),e.jsxs("select",{className:"input",value:n.type??"volume",onChange:Q=>{const z=[...f.volumeMounts];z[P]={...z[P],type:Q.target.value},F({...f,volumeMounts:z})},children:[e.jsx("option",{value:"volume",children:"volume"}),e.jsx("option",{value:"bind",children:"bind"})]}),e.jsx("button",{className:"btn btn-sm",onClick:()=>F({...f,volumeMounts:f.volumeMounts.filter((Q,z)=>z!==P)}),children:"Remove"})]},`${n.hostPath}-${n.containerPath}-${P}`)),e.jsx("button",{className:"btn btn-sm",onClick:()=>F({...f,volumeMounts:[...f.volumeMounts,{hostPath:"",containerPath:"",mode:"rw",type:"volume"}]}),children:"Add Mount"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Environment Variables"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[Object.entries(f.environment).map(([n,P])=>{const Q=He.test(n)&&!Pe[n];return e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:n,onChange:z=>{const xe={...f.environment};delete xe[n],xe[z.target.value]=P,F({...f,environment:xe})}}),e.jsx("input",{className:"input",value:Q?"***":String(P),onChange:z=>F({...f,environment:{...f.environment,[n]:z.target.value}})}),e.jsx("button",{className:"btn btn-sm",onClick:()=>Ee(z=>({...z,[n]:!z[n]})),children:Pe[n]?e.jsx(xs,{size:14}):e.jsx(ps,{size:14})}),e.jsx("button",{className:"btn btn-sm",onClick:()=>{const z={...f.environment};delete z[n],F({...f,environment:z})},children:"Remove"})]},n)}),e.jsx("button",{className:"btn btn-sm",onClick:()=>{const n=`NEW_VAR_${Object.keys(f.environment).length+1}`;F({...f,environment:{...f.environment,[n]:""}})},children:"Add Variable"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Resources"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",type:"number",placeholder:"Memory bytes (2 GB = 2147483648)",value:f.resources?.memoryBytes??"",onChange:n=>F({...f,resources:{...f.resources,memoryBytes:n.target.value?Number(n.target.value):void 0}})}),e.jsx("input",{className:"input",type:"number",placeholder:"CPU count",value:f.resources?.cpuCount??"",onChange:n=>F({...f,resources:{...f.resources,cpuCount:n.target.value?Number(n.target.value):void 0}})}),e.jsx("input",{className:"input",type:"number",placeholder:"PIDs limit",value:f.resources?.pidsLimit??"",onChange:n=>F({...f,resources:{...f.resources,pidsLimit:n.target.value?Number(n.target.value):void 0}})})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Host Config"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",placeholder:"Context name",value:f.host?.contextName??"",onChange:n=>F({...f,host:{...f.host,contextName:n.target.value}})}),e.jsx("input",{className:"input",placeholder:"Docker host URL",value:f.host?.dockerHost??"",onChange:n=>F({...f,host:{...f.host,dockerHost:n.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS CA cert path",value:f.host?.tlsCaCert??"",onChange:n=>F({...f,host:{...f.host,tlsCaCert:n.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS cert path",value:f.host?.tlsCert??"",onChange:n=>F({...f,host:{...f.host,tlsCert:n.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS key path",value:f.host?.tlsKey??"",onChange:n=>F({...f,host:{...f.host,tlsKey:n.target.value}})}),e.jsxs("label",{className:"node-detail-modal__checkbox",children:[e.jsx("input",{type:"checkbox",checked:f.host?.tlsVerify??!0,onChange:n=>F({...f,host:{...f.host,tlsVerify:n.target.checked}})}),"TLS verify"]})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Extra CLIs"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[(f.extraClis??[]).map((n,P)=>e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:n,onChange:Q=>{const z=[...f.extraClis??[]];z[P]=Q.target.value,F({...f,extraClis:z})}}),e.jsx("button",{className:"btn btn-sm",onClick:()=>F({...f,extraClis:(f.extraClis??[]).filter((Q,z)=>z!==P)}),children:"Remove"})]},`${n}-${P}`)),e.jsx("button",{className:"btn btn-sm",onClick:()=>F({...f,extraClis:[...f.extraClis??[],""]}),children:"Add CLI"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Persistence"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",placeholder:"Volume name",value:f.persistence?.volumeName??"",onChange:n=>F({...f,persistence:{...f.persistence,volumeName:n.target.value}})}),e.jsxs("label",{className:"node-detail-modal__checkbox",children:[e.jsx("input",{type:"checkbox",checked:f.persistence?.retainOnDelete??!1,onChange:n=>F({...f,persistence:{...f.persistence,retainOnDelete:n.target.checked}})}),"Retain on delete"]})]})]}),e.jsxs("div",{className:"node-detail-modal__docker-meta",children:[e.jsxs("span",{children:["Config v",f.configVersion," • Updated ",ke(f.lastUpdated??a.updatedAt)]}),qe&&e.jsx("span",{className:"node-detail-modal__docker-recreate",children:"Needs Recreate"})]}),e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>void as(),disabled:ie,children:[e.jsx($e,{size:14}),ie?"Saving...":"Save Docker Config"]})]})]}),d&&e.jsxs("section",{className:"node-detail-modal__section docker-management",children:[e.jsx("h4",{children:"Docker Management"}),e.jsxs("div",{className:"docker-management__status-card",children:[e.jsxs("div",{className:"docker-management__status-row",children:[e.jsx("span",{className:`docker-management__status-dot docker-management__status-dot--${rs}`,"aria-hidden":!0}),e.jsx("strong",{children:rt(re)}),(re==="creating"||re==="recreating"||re==="restarting")&&e.jsx(De,{size:14,className:"spin","aria-hidden":!0})]}),e.jsxs("div",{className:"docker-management__status-meta",children:[re==="running"&&e.jsxs("span",{children:["Uptime: ",tt(se?.startedAt)]}),re!=="running"&&se?.exitCode!==void 0&&e.jsxs("span",{children:["Exit code: ",se.exitCode]}),(se?.error||d.errorMessage)&&e.jsx("span",{children:se?.error??d.errorMessage})]}),e.jsx("button",{className:"btn btn-sm",onClick:()=>void es(),disabled:!_||R,children:R?"Refreshing...":"Refresh Status"})]}),e.jsxs("div",{className:"node-detail-modal__grid docker-management__info-grid",children:[e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Image"}),e.jsx("strong",{children:e.jsxs("code",{children:[d.imageName,":",d.imageTag]})})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Container ID"}),e.jsx("strong",{children:e.jsx("code",{children:d.containerId?d.containerId.slice(0,12):"—"})})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Host"}),e.jsx("strong",{children:Ye})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Persistent Storage"}),e.jsx("strong",{children:d.persistentStorage?"Yes":"No"})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Port"}),e.jsx("strong",{children:lt(d.reachableUrl)})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Resource Sizing"}),e.jsx("strong",{children:Xe})]})]}),e.jsxs("div",{className:"docker-management__actions",children:[e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx(Ke,{size:14}),"Start"]}),e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx(Ue,{size:14}),"Stop"]}),e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx(De,{size:14}),"Restart"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>void ss(),disabled:!l,children:[e.jsx(js,{size:14}),"View Logs"]})]}),W&&e.jsxs("div",{className:"docker-management__log-viewer",children:[e.jsxs("div",{className:"docker-management__log-viewer-header",children:[e.jsx("strong",{children:"Container Logs"}),e.jsx("button",{className:"btn-icon",onClick:()=>h(!1),"aria-label":"Close logs",children:e.jsx(me,{size:14})})]}),q?e.jsx("p",{children:"Fetching logs..."}):e.jsx("pre",{children:O.trim()||"No logs available"})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Environment Variables"}),e.jsx("dl",{className:"docker-management__env-list",children:Object.entries(d.envVars).map(([n,P])=>e.jsxs("div",{children:[e.jsx("dt",{children:n}),e.jsx("dd",{children:ot(n,P)})]},n))})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Volume Mounts"}),e.jsx("ul",{className:"docker-management__mounts-list",children:d.volumeMounts.map(n=>e.jsxs("li",{children:[e.jsxs("span",{children:[n.hostPath," → ",n.containerPath]}),n.readOnly&&e.jsx("span",{className:"node-card__type-badge",children:"Read-only"})]},`${n.hostPath}:${n.containerPath}`))})]})]}),a.type==="remote"&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Settings Sync"}),j&&e.jsxs("div",{className:"node-detail-modal__sync-status",children:[e.jsx("span",{className:`node-detail-modal__sync-dot ${at(j.syncState)}`,"aria-hidden":!0}),e.jsxs("span",{children:["Last sync: ",e.jsx("strong",{children:j.lastSyncAt?ke(j.lastSyncAt):"Never synced"})]}),j.diffCount>0&&e.jsxs("span",{className:"node-detail-modal__sync-diff",children:["Differences: ",e.jsx("strong",{children:j.diffCount})]})]}),e.jsxs("div",{className:"node-detail-modal__sync-actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:We,disabled:G||!g,children:[e.jsx(Be,{size:14}),G?"Pushing...":"Push Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:Ge,disabled:J||!c,children:[e.jsx(Te,{size:14}),J?"Pulling...":"Pull Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:Ze,disabled:E||!N,children:[e.jsx(Fe,{size:14}),E?"Syncing...":"Sync Auth"]})]}),L&&e.jsxs("div",{className:"node-detail-modal__sync-error",children:[e.jsx("span",{children:L}),e.jsx("button",{className:"node-detail-modal__sync-error-dismiss",onClick:Qe,"aria-label":"Dismiss error",children:e.jsx(me,{size:14})})]})]}),a.type==="remote"&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Sync History"}),e.jsx(Qs,{nodeId:a.id,entries:w,singleNode:!0})]})]}),e.jsxs("div",{className:"modal-actions node-detail-modal__actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:Je,children:[e.jsx(Oe,{size:14}),"Health Check"]}),e.jsx("button",{className:"btn btn-sm",onClick:r,children:"Close"})]})]}),a.type==="remote"&&e.jsx(st,{isOpen:K,onClose:()=>ae(!1),onResolve:v??(async()=>{}),conflicts:ne,localNodeName:"Local",remoteNodeName:a.name,addToast:o})]})}const it=15e3,dt=1e3;function ut(){const[t,r]=s.useState([]),[a,i]=s.useState(!0),[u,m]=s.useState(null),o=s.useRef(null),j=s.useRef(0),g=s.useCallback(async()=>{try{m(null);const v=await Ie();r(v)}catch(v){m(v instanceof Error?v.message:"Failed to fetch managed Docker nodes")}},[]);s.useEffect(()=>{let v=!1;async function d(){i(!0);try{const _=await Ie();v||(r(_),m(null))}catch(_){v||m(_ instanceof Error?_.message:"Failed to fetch managed Docker nodes")}finally{v||i(!1)}}d();const M=()=>{if(document.visibilityState!=="visible")return;const _=Date.now();_-j.current<dt||(j.current=_,g())};return document.addEventListener("visibilitychange",M),()=>{v=!0,document.removeEventListener("visibilitychange",M)}},[g]),s.useEffect(()=>{if(!a)return o.current=setInterval(()=>{g()},it),()=>{o.current&&(clearInterval(o.current),o.current=null)}},[a,g]);const c=s.useCallback(async v=>vs(v),[]),N=s.useCallback(async(v,d)=>(await bs(v,d)).logs,[]),w=s.useCallback(async v=>{const d=await ys(v),M={...d,nodeId:d.nodeId??void 0,containerId:d.containerId??void 0,status:d.status,hostConfig:{type:d.hostConfig.host||d.hostConfig.context?"remote":"local",host:d.hostConfig.host,context:d.hostConfig.context},reachableUrl:d.reachableUrl??void 0,volumeMounts:d.volumeMounts.map(_=>({hostPath:_.hostPath,containerPath:_.containerPath,readOnly:_.mode==="ro"?!0:void 0})),persistentStorage:d.persistentStorage,resourceSizing:{cpuLimit:d.resourceSizing.cpus!==void 0?String(d.resourceSizing.cpus):void 0,memoryLimit:d.resourceSizing.memoryMB!==void 0?`${d.resourceSizing.memoryMB}MB`:void 0},errorMessage:d.errorMessage??void 0};return r(_=>[..._,M]),M},[]);return{dockerNodes:t,loading:a,error:u,refresh:g,getContainerStatus:c,getLogs:N,create:w}}function pt({addToast:t,onClose:r}){const{nodes:a,loading:i,error:u,refresh:m,register:o,update:j,unregister:g,healthCheck:c,patchDockerConfig:N,fetchDockerDiff:w,discoverRemoteProjects:v}=_s(),{projects:d,refresh:M}=Ns(),{meshState:_,loading:l,error:b}=Ds(),{syncStatusMap:C,pushSettings:k,pullSettings:$,syncAuth:V,trackNode:A,getAuthSyncState:p,getAuthProviders:x}=As(),{dockerNodes:S,loading:Z,refresh:H,getContainerStatus:Y,getLogs:ee,create:U}=ut(),[X,G]=s.useState(!1),[te,J]=s.useState(!1),[T,E]=s.useState(null);s.useEffect(()=>{const R=a.filter(I=>I.type==="remote");for(const I of R)A(I.id)},[a,A]),s.useEffect(()=>{if(!T)return;const R=a.find(I=>I.id===T.id)??null;E(R)},[a,T]);const y=s.useMemo(()=>{const R=a.length,I=a.filter(q=>q.status==="online").length,W=a.filter(q=>q.status==="offline"||q.status==="error").length,h=a.filter(q=>q.type==="remote").length,O=a.filter(q=>q.type==="remote"&&C[q.id]&&pe(C[q.id]).syncState==="synced").length,B=S.length;return{total:R,online:I,offline:W,remote:h,synced:O,docker:B}},[S.length,a,C]),L=s.useCallback(async R=>{await o(R),await M()},[M,o]),D=s.useCallback(async R=>{try{await U(R),t(`Docker node "${R.name}" created`,"success"),J(!1)}catch(I){const W=I instanceof Error?I.message:"Failed to create Docker node";throw t(W,"error"),I}},[t,U]),K=s.useMemo(()=>{const R=new Map;for(const I of S)I.nodeId&&R.set(I.nodeId,I);return R},[S]),ae=s.useCallback(async()=>{try{await Promise.all([m(),H()])}catch{t("Failed to refresh nodes","error")}},[t,m,H]),ne=s.useCallback(async R=>{try{await c(R),t("Node health check complete","success")}catch(I){const W=I instanceof Error?I.message:"Health check failed";t(W,"error")}},[t,c]),se=s.useCallback(async R=>{try{await g(R),t("Node removed","success"),T?.id===R&&E(null)}catch(I){const W=I instanceof Error?I.message:"Failed to remove node";t(W,"error")}},[t,T?.id,g]),le=s.useCallback(async(R,I)=>{await j(R,I)},[j]);return e.jsxs("div",{className:"nodes-view","data-testid":"nodes-view",children:[e.jsxs("div",{className:"nodes-view-header",children:[e.jsxs("div",{className:"nodes-view-title",children:[e.jsxs("h2",{children:[e.jsx(Ve,{size:20}),"Nodes"]}),e.jsxs("span",{className:"nodes-view-count",children:[a.length," registered"]})]}),e.jsxs("div",{className:"nodes-view-actions",children:[e.jsx("button",{className:"btn-icon nodes-view-close",onClick:r,"aria-label":"Close nodes view",children:e.jsx(me,{size:16})}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>void ae(),disabled:i||Z,children:[e.jsx(Ne,{size:14,className:i?"spin":""}),"Refresh"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>G(!0),children:[e.jsx(he,{size:14}),"Add Node"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>J(!0),title:"Add a managed Docker node",children:[e.jsx(ye,{size:14}),"Add Docker Node"]})]})]}),e.jsxs("div",{className:"nodes-view-stats",children:[e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-total",children:[e.jsx("span",{children:"Total"}),e.jsx("strong",{children:y.total})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--online","data-testid":"nodes-stat-online",children:[e.jsxs("span",{children:[e.jsx(Ps,{size:14})," Online"]}),e.jsx("strong",{children:y.online})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--offline","data-testid":"nodes-stat-offline",children:[e.jsxs("span",{children:[e.jsx(ws,{size:14})," Offline"]}),e.jsx("strong",{children:y.offline})]}),e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-remote",children:[e.jsxs("span",{children:[e.jsx(ks,{size:14})," Remote"]}),e.jsx("strong",{children:y.remote})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--synced","data-testid":"nodes-stat-synced",children:[e.jsxs("span",{children:[e.jsx(Ne,{size:14})," Synced"]}),e.jsx("strong",{children:y.synced})]}),e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-docker",children:[e.jsxs("span",{children:[e.jsx(ye,{size:14})," Docker"]}),e.jsx("strong",{children:y.docker})]})]}),(u||b)&&e.jsx("div",{className:"nodes-view-error",children:u??b}),!l&&_.length>0&&e.jsxs("section",{className:"nodes-view-topology","aria-label":"Mesh Topology",children:[e.jsx("h3",{className:"nodes-view-section-title",children:"Mesh Topology"}),e.jsx(qs,{nodes:_})]}),i?e.jsx("div",{className:"nodes-view-grid",children:Array.from({length:4}).map((R,I)=>e.jsx("div",{className:"node-card node-card--loading","aria-hidden":!0},I))}):a.length===0?e.jsxs("div",{className:"nodes-view-empty",children:[e.jsx("p",{children:"No nodes are registered yet."}),e.jsxs("button",{className:"btn btn-primary",onClick:()=>G(!0),children:[e.jsx(he,{size:14}),"Add First Node"]})]}):e.jsx("div",{className:"nodes-view-grid",children:a.map(R=>{const I=R.type==="remote"&&C[R.id]?pe(C[R.id]):void 0;return e.jsx(Ks,{node:R,projects:d,onHealthCheck:W=>{ne(W)},onEdit:W=>E(W),onRemove:W=>{se(W)},isLoading:i,syncStatus:I,authSyncState:R.type==="remote"?p(R.id):void 0,authSyncProviders:R.type==="remote"?x(R.id):void 0,managedDockerNode:K.get(R.id)},R.id)})}),e.jsx(Xs,{isOpen:X,onClose:()=>G(!1),onSubmit:L,onDiscoverRemoteProjects:v,addToast:t,projects:d}),e.jsx(Zs,{isOpen:te,onClose:()=>J(!1),onSubmit:D,addToast:t}),e.jsx(ct,{isOpen:T!==null,onClose:()=>E(null),node:T,projects:d,onUpdate:le,onHealthCheck:ne,addToast:t,syncStatus:T?.type==="remote"&&T&&C[T.id]?pe(C[T.id]):void 0,onPushSettings:k,onPullSettings:$,onSyncAuth:V,managedDockerNode:T?K.get(T.id):void 0,onFetchContainerStatus:Y,onFetchLogs:ee,onUpdateDockerConfig:N,onFetchDockerConfigDiff:w})]})}export{pt as NodesView};
@@ -1,6 +1,6 @@
1
- import{r as l,j as e}from"./vendor-react-K0fH_qHe.js";import{c as J,ev as Q,ew as V,ex as Y,R as $,ek as y,I as Z,a as T,b as ee,W as se,ey as L,cE as D,F as A,ez as ae,eA as te,eB as I,X as ie}from"./index-BOjPRqEk.js";import"./vendor-xterm-DzcZoU0P.js";/**
1
+ import{r as l,j as e}from"./vendor-react-K0fH_qHe.js";import{c as J,ey as Q,ez as V,eA as Y,R as $,en as y,I as Z,a as T,b as ee,W as se,eB as L,cH as D,F as A,eC as ae,eD as te,eE as I,X as ie}from"./index-DiewofJh.js";import"./vendor-xterm-DzcZoU0P.js";/**
2
2
  * @license lucide-react v1.7.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
5
5
  * See the LICENSE file in the root directory of this source tree.
6
- */const ne=[["path",{d:"M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z",key:"e79jfc"}],["circle",{cx:"13.5",cy:"6.5",r:".5",fill:"currentColor",key:"1okk4w"}],["circle",{cx:"17.5",cy:"10.5",r:".5",fill:"currentColor",key:"f64h9f"}],["circle",{cx:"6.5",cy:"12.5",r:".5",fill:"currentColor",key:"qy21gx"}],["circle",{cx:"8.5",cy:"7.5",r:".5",fill:"currentColor",key:"fotxhn"}]],W=J("palette",ne);function le(t){return t.replace(/-/g,"-")}function ce(t){return{"fusion-global":"Fusion Global","pi-global":"Pi Global","fusion-project":"Fusion Project","pi-project":"Pi Project",package:"Package"}[t]??t}function re(t){return t.startsWith("npm:")?"npm":t.startsWith("git:")?"git":"local"}function oe(t){return t.replace(/^(npm:|git:)/,"")}function pe({addToast:t,projectId:x}){const[c,B]=l.useState(null),[f,P]=l.useState(!0),[N,E]=l.useState(!1),[S,w]=l.useState(!1),[h,F]=l.useState(""),[G,M]=l.useState(new Set),[p,O]=l.useState([]),[b,C]=l.useState(!0),[_,R]=l.useState(!1),r=l.useCallback(async()=>{try{P(!0);const s=await Q();B(s)}catch(s){t(`Failed to load Pi settings: ${s instanceof Error?s.message:String(s)}`,"error")}finally{P(!1)}},[t]),m=l.useCallback(async()=>{try{C(!0);const s=await V(x);O(s.extensions)}catch(s){t(`Failed to load extensions: ${s instanceof Error?s.message:String(s)}`,"error")}finally{C(!1)}},[t,x]),q=l.useCallback(async s=>{try{R(!0);const i=s.enabled?[...p.filter(a=>a.enabled&&a.id!==s.id).map(a=>a.id),s.id]:p.filter(a=>a.enabled&&a.id!==s.id).map(a=>a.id);await Y(i,x),await m(),t(s.enabled?"Extension disabled":"Extension enabled","success")}catch(i){t(`Failed to update extension: ${i instanceof Error?i.message:String(i)}`,"error")}finally{R(!1)}},[p,x,m,t]);l.useEffect(()=>{r()},[r]),l.useEffect(()=>{m()},[m]);const K=s=>{M(i=>{const a=new Set(i);return a.has(s)?a.delete(s):a.add(s),a})},z=async()=>{if(!h.trim()){t("Please enter a package source","error");return}try{E(!0),await ae(h.trim()),t("Package installed successfully","success"),F(""),await r()}catch(s){t(`Failed to install package: ${s instanceof Error?s.message:String(s)}`,"error")}finally{E(!1)}},U=async()=>{try{w(!0),await te(x),await Promise.all([r(),m()]),t("Fusion skill reinstalled successfully","success")}catch(s){t(`Failed to reinstall Fusion skill: ${s instanceof Error?s.message:String(s)}`,"error")}finally{w(!1)}},X=async s=>{if(!c)return;const i=c.packages.filter(a=>(typeof a=="string"?a:a.source)!==s);try{await I({packages:i}),t("Package removed","success"),await r()}catch(a){t(`Failed to remove package: ${a instanceof Error?a.message:String(a)}`,"error")}},H=async(s,i)=>{if(!c)return;const a=c[s].filter(n=>n!==i);try{await I({[s]:a}),t(`${s.slice(0,-1)} removed`,"success"),await r()}catch(n){t(`Failed to update settings: ${n instanceof Error?n.message:String(n)}`,"error")}},u=(s,i,a)=>!c||c[a].length===0?null:e.jsxs("div",{className:"pi-ext-section",children:[e.jsxs("div",{className:"pi-ext-section-header",children:[e.jsx(i,{size:14}),e.jsx("span",{children:s}),e.jsx("span",{className:"pi-ext-count",children:c[a].length})]}),e.jsx("div",{className:"pi-ext-resource-list",children:c[a].map((n,g)=>e.jsxs("span",{className:"pi-ext-resource-tag",children:[e.jsx("span",{className:"pi-ext-resource-path",children:n}),e.jsx("button",{className:"btn-icon touch-target pi-ext-resource-remove",onClick:()=>H(a,n),title:`Remove ${n}`,"aria-label":`Remove ${n}`,children:e.jsx(ie,{})})]},g))})]});return e.jsxs("div",{className:"pi-ext-manager",children:[e.jsxs("div",{className:"pi-ext-manager-header",children:[e.jsx("h4",{className:"pi-ext-manager-title",children:"Pi Extensions"}),e.jsx("div",{className:"pi-ext-manager-actions",children:e.jsx("button",{className:"btn-icon",onClick:r,title:"Refresh",disabled:f,children:e.jsx($,{size:16,className:f?"spin":""})})})]}),f?e.jsx("div",{className:"loading-state",children:"Loading Pi settings…"}):c?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"pi-ext-add-form",children:[e.jsxs("div",{className:"pi-ext-add-form-row",children:[e.jsx("input",{type:"text",className:"input",placeholder:"npm:pi-extension-name or git:https://github.com/...",value:h,onChange:s=>F(s.target.value),onKeyDown:s=>{s.key==="Enter"&&(s.preventDefault(),z())},disabled:N}),e.jsxs("button",{className:"btn btn-primary",onClick:z,disabled:N||!h.trim(),children:[e.jsx(Z,{size:14}),N?"Installing…":"Add"]})]}),e.jsx("div",{className:"pi-ext-add-form-row",children:e.jsx("button",{className:"btn",onClick:U,disabled:S,children:S?"Reinstalling Fusion…":"Reinstall Fusion skill"})})]}),c.packages.length>0?e.jsx("div",{className:"pi-ext-package-list",children:c.packages.map((s,i)=>{const a=typeof s=="string"?s:s.source,n=re(a),g=oe(a),j=typeof s=="object"&&s!==null,v=j&&(s.extensions?.length??0)>0||(s.skills?.length??0)>0||(s.prompts?.length??0)>0||(s.themes?.length??0)>0,k=G.has(i);return e.jsxs("div",{className:"pi-ext-package-card",children:[e.jsxs("div",{className:"pi-ext-package-header",children:[j&&v?e.jsx("button",{className:"pi-ext-expand-btn",onClick:()=>K(i),"aria-expanded":k,children:k?e.jsx(T,{size:14}):e.jsx(ee,{size:14})}):e.jsx("span",{className:"pi-ext-expand-placeholder"}),e.jsx("span",{className:`pi-ext-source-badge pi-ext-source-badge--${n}`,children:n}),e.jsx("span",{className:"pi-ext-package-source",children:g}),e.jsxs("div",{className:"pi-ext-package-actions",children:[j&&v&&e.jsxs("span",{className:"pi-ext-filter-hint",children:[s.extensions?.length??0," ext,"," ",s.skills?.length??0," skill,"," ",s.prompts?.length??0," prompt,"," ",s.themes?.length??0," theme"]}),e.jsx("button",{className:"btn-icon touch-target pi-ext-remove-btn",onClick:()=>X(a),title:"Remove package","aria-label":`Remove package ${g}`,children:e.jsx(se,{size:14})})]})]}),j&&v&&k&&e.jsxs("div",{className:"pi-ext-filter-list",children:[s.extensions?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(L,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Extensions:"}),s.extensions.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.skills?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(D,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Skills:"}),s.skills.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.prompts?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(A,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Prompts:"}),s.prompts.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.themes?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(W,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Themes:"}),s.themes.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null]})]},i)})}):e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"No packages configured."}),e.jsx("p",{className:"text-muted",children:"Add a package source above to get started."})]}),e.jsxs("div",{className:"pi-ext-top-level",children:[u("Extensions",L,"extensions"),u("Skills",D,"skills"),u("Prompts",A,"prompts"),u("Themes",W,"themes")]})]}):e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"Failed to load Pi settings."})]}),e.jsxs("div",{className:"pi-ext-discovered-section",children:[e.jsxs("div",{className:"pi-ext-discovered-header",children:[e.jsx("h4",{children:"Discovered Extensions"}),e.jsx("button",{className:"btn-icon",onClick:m,disabled:b,title:"Refresh extensions",children:e.jsx($,{className:b?"spin":""})})]}),e.jsx("p",{className:"pi-ext-description",children:"Installed extensions resolved from packages and configured paths."}),b?e.jsx("div",{className:"loading-state",children:"Loading extensions…"}):p.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"No extensions discovered."})]}):e.jsx("div",{className:"pi-ext-list",children:p.map(s=>e.jsxs("div",{className:"pi-ext-item",children:[e.jsxs("div",{className:"pi-ext-item-content",children:[e.jsxs("div",{className:"pi-ext-info",children:[e.jsx("span",{className:"pi-ext-name",children:s.name}),e.jsx("span",{className:`pi-ext-source-badge pi-ext-source-badge--${le(s.source)}`,children:ce(s.source)})]}),e.jsx("span",{className:"pi-ext-path",children:s.path})]}),e.jsx("div",{className:"pi-ext-actions",children:e.jsxs("label",{className:"toggle-switch",children:[e.jsx("input",{type:"checkbox",checked:s.enabled,onChange:()=>void q(s),disabled:_,"aria-label":`Toggle ${s.name}`}),e.jsx("span",{className:"toggle-slider"})]})})]},s.id))})]})]})}export{pe as PiExtensionsManager};
6
+ */const ne=[["path",{d:"M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z",key:"e79jfc"}],["circle",{cx:"13.5",cy:"6.5",r:".5",fill:"currentColor",key:"1okk4w"}],["circle",{cx:"17.5",cy:"10.5",r:".5",fill:"currentColor",key:"f64h9f"}],["circle",{cx:"6.5",cy:"12.5",r:".5",fill:"currentColor",key:"qy21gx"}],["circle",{cx:"8.5",cy:"7.5",r:".5",fill:"currentColor",key:"fotxhn"}]],W=J("palette",ne);function le(t){return t.replace(/-/g,"-")}function ce(t){return{"fusion-global":"Fusion Global","pi-global":"Pi Global","fusion-project":"Fusion Project","pi-project":"Pi Project",package:"Package"}[t]??t}function re(t){return t.startsWith("npm:")?"npm":t.startsWith("git:")?"git":"local"}function oe(t){return t.replace(/^(npm:|git:)/,"")}function pe({addToast:t,projectId:x}){const[c,B]=l.useState(null),[f,P]=l.useState(!0),[N,E]=l.useState(!1),[S,w]=l.useState(!1),[h,F]=l.useState(""),[G,M]=l.useState(new Set),[p,O]=l.useState([]),[b,C]=l.useState(!0),[_,R]=l.useState(!1),r=l.useCallback(async()=>{try{P(!0);const s=await Q();B(s)}catch(s){t(`Failed to load Pi settings: ${s instanceof Error?s.message:String(s)}`,"error")}finally{P(!1)}},[t]),m=l.useCallback(async()=>{try{C(!0);const s=await V(x);O(s.extensions)}catch(s){t(`Failed to load extensions: ${s instanceof Error?s.message:String(s)}`,"error")}finally{C(!1)}},[t,x]),q=l.useCallback(async s=>{try{R(!0);const i=s.enabled?[...p.filter(a=>a.enabled&&a.id!==s.id).map(a=>a.id),s.id]:p.filter(a=>a.enabled&&a.id!==s.id).map(a=>a.id);await Y(i,x),await m(),t(s.enabled?"Extension disabled":"Extension enabled","success")}catch(i){t(`Failed to update extension: ${i instanceof Error?i.message:String(i)}`,"error")}finally{R(!1)}},[p,x,m,t]);l.useEffect(()=>{r()},[r]),l.useEffect(()=>{m()},[m]);const H=s=>{M(i=>{const a=new Set(i);return a.has(s)?a.delete(s):a.add(s),a})},z=async()=>{if(!h.trim()){t("Please enter a package source","error");return}try{E(!0),await ae(h.trim()),t("Package installed successfully","success"),F(""),await r()}catch(s){t(`Failed to install package: ${s instanceof Error?s.message:String(s)}`,"error")}finally{E(!1)}},K=async()=>{try{w(!0),await te(x),await Promise.all([r(),m()]),t("Fusion skill reinstalled successfully","success")}catch(s){t(`Failed to reinstall Fusion skill: ${s instanceof Error?s.message:String(s)}`,"error")}finally{w(!1)}},U=async s=>{if(!c)return;const i=c.packages.filter(a=>(typeof a=="string"?a:a.source)!==s);try{await I({packages:i}),t("Package removed","success"),await r()}catch(a){t(`Failed to remove package: ${a instanceof Error?a.message:String(a)}`,"error")}},X=async(s,i)=>{if(!c)return;const a=c[s].filter(n=>n!==i);try{await I({[s]:a}),t(`${s.slice(0,-1)} removed`,"success"),await r()}catch(n){t(`Failed to update settings: ${n instanceof Error?n.message:String(n)}`,"error")}},u=(s,i,a)=>!c||c[a].length===0?null:e.jsxs("div",{className:"pi-ext-section",children:[e.jsxs("div",{className:"pi-ext-section-header",children:[e.jsx(i,{size:14}),e.jsx("span",{children:s}),e.jsx("span",{className:"pi-ext-count",children:c[a].length})]}),e.jsx("div",{className:"pi-ext-resource-list",children:c[a].map((n,g)=>e.jsxs("span",{className:"pi-ext-resource-tag",children:[e.jsx("span",{className:"pi-ext-resource-path",children:n}),e.jsx("button",{className:"btn-icon touch-target pi-ext-resource-remove",onClick:()=>X(a,n),title:`Remove ${n}`,"aria-label":`Remove ${n}`,children:e.jsx(ie,{})})]},g))})]});return e.jsxs("div",{className:"pi-ext-manager",children:[e.jsxs("div",{className:"pi-ext-manager-header",children:[e.jsx("h4",{className:"pi-ext-manager-title",children:"Pi Extensions"}),e.jsx("div",{className:"pi-ext-manager-actions",children:e.jsx("button",{className:"btn-icon",onClick:r,title:"Refresh",disabled:f,children:e.jsx($,{size:16,className:f?"spin":""})})})]}),f?e.jsx("div",{className:"loading-state",children:"Loading Pi settings…"}):c?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"pi-ext-add-form",children:[e.jsxs("div",{className:"pi-ext-add-form-row",children:[e.jsx("input",{type:"text",className:"input",placeholder:"npm:pi-extension-name or git:https://github.com/...",value:h,onChange:s=>F(s.target.value),onKeyDown:s=>{s.key==="Enter"&&(s.preventDefault(),z())},disabled:N}),e.jsxs("button",{className:"btn btn-primary",onClick:z,disabled:N||!h.trim(),children:[e.jsx(Z,{size:14}),N?"Installing…":"Add"]})]}),e.jsx("div",{className:"pi-ext-add-form-row",children:e.jsx("button",{className:"btn",onClick:K,disabled:S,children:S?"Reinstalling Fusion…":"Reinstall Fusion skill"})})]}),c.packages.length>0?e.jsx("div",{className:"pi-ext-package-list",children:c.packages.map((s,i)=>{const a=typeof s=="string"?s:s.source,n=re(a),g=oe(a),j=typeof s=="object"&&s!==null,v=j&&(s.extensions?.length??0)>0||(s.skills?.length??0)>0||(s.prompts?.length??0)>0||(s.themes?.length??0)>0,k=G.has(i);return e.jsxs("div",{className:"pi-ext-package-card",children:[e.jsxs("div",{className:"pi-ext-package-header",children:[j&&v?e.jsx("button",{className:"pi-ext-expand-btn",onClick:()=>H(i),"aria-expanded":k,children:k?e.jsx(T,{size:14}):e.jsx(ee,{size:14})}):e.jsx("span",{className:"pi-ext-expand-placeholder"}),e.jsx("span",{className:`pi-ext-source-badge pi-ext-source-badge--${n}`,children:n}),e.jsx("span",{className:"pi-ext-package-source",children:g}),e.jsxs("div",{className:"pi-ext-package-actions",children:[j&&v&&e.jsxs("span",{className:"pi-ext-filter-hint",children:[s.extensions?.length??0," ext,"," ",s.skills?.length??0," skill,"," ",s.prompts?.length??0," prompt,"," ",s.themes?.length??0," theme"]}),e.jsx("button",{className:"btn-icon touch-target pi-ext-remove-btn",onClick:()=>U(a),title:"Remove package","aria-label":`Remove package ${g}`,children:e.jsx(se,{size:14})})]})]}),j&&v&&k&&e.jsxs("div",{className:"pi-ext-filter-list",children:[s.extensions?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(L,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Extensions:"}),s.extensions.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.skills?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(D,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Skills:"}),s.skills.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.prompts?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(A,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Prompts:"}),s.prompts.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.themes?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(W,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Themes:"}),s.themes.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null]})]},i)})}):e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"No packages configured."}),e.jsx("p",{className:"text-muted",children:"Add a package source above to get started."})]}),e.jsxs("div",{className:"pi-ext-top-level",children:[u("Extensions",L,"extensions"),u("Skills",D,"skills"),u("Prompts",A,"prompts"),u("Themes",W,"themes")]})]}):e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"Failed to load Pi settings."})]}),e.jsxs("div",{className:"pi-ext-discovered-section",children:[e.jsxs("div",{className:"pi-ext-discovered-header",children:[e.jsx("h4",{children:"Discovered Extensions"}),e.jsx("button",{className:"btn-icon",onClick:m,disabled:b,title:"Refresh extensions",children:e.jsx($,{className:b?"spin":""})})]}),e.jsx("p",{className:"pi-ext-description",children:"Installed extensions resolved from packages and configured paths."}),b?e.jsx("div",{className:"loading-state",children:"Loading extensions…"}):p.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"No extensions discovered."})]}):e.jsx("div",{className:"pi-ext-list",children:p.map(s=>e.jsxs("div",{className:"pi-ext-item",children:[e.jsxs("div",{className:"pi-ext-item-content",children:[e.jsxs("div",{className:"pi-ext-info",children:[e.jsx("span",{className:"pi-ext-name",children:s.name}),e.jsx("span",{className:`pi-ext-source-badge pi-ext-source-badge--${le(s.source)}`,children:ce(s.source)})]}),e.jsx("span",{className:"pi-ext-path",children:s.path})]}),e.jsx("div",{className:"pi-ext-actions",children:e.jsxs("label",{className:"toggle-switch",children:[e.jsx("input",{type:"checkbox",checked:s.enabled,onChange:()=>void q(s),disabled:_,"aria-label":`Toggle ${s.name}`}),e.jsx("span",{className:"toggle-slider"})]})})]},s.id))})]})]})}export{pe as PiExtensionsManager};
@@ -0,0 +1 @@
1
+ .plugin-manager,.plugin-manager-detail{display:flex;flex-direction:column;gap:var(--space-lg);padding-inline:var(--space-xl);padding-block:var(--space-md)}.plugin-manager-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--space-sm);border-bottom:var(--btn-border-width) solid var(--border);gap:var(--space-sm)}.plugin-manager-header-title{font-size:var(--font-size-sm, .85rem);font-weight:600;color:var(--text);flex:1}.plugin-manager-actions{display:flex;gap:var(--space-sm);align-items:center}.plugin-install-form{display:flex;flex-direction:column;gap:var(--space-sm);padding:var(--space-lg);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);background:var(--surface)}.plugin-install-hint{margin:0;font-size:var(--font-size-sm, .85rem);color:var(--text-secondary, var(--text-muted));line-height:1.45}.plugin-install-hint code{padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-sm);background:color-mix(in srgb,var(--text-muted) 12%,transparent);font-size:.85em}.plugin-install-actions{display:flex;gap:var(--space-sm);justify-content:flex-end}.plugin-list{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-item{display:flex;align-items:center;justify-content:space-between;padding:var(--space-md) var(--space-lg);background:var(--surface);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);transition:border-color var(--transition-fast)}.plugin-item:hover{border-color:var(--text-dim)}.plugin-info{display:flex;align-items:center;gap:var(--space-sm);min-width:0;flex:1}.plugin-copy{display:flex;flex-direction:column;gap:var(--space-xs);min-width:0}.plugin-copy-header{display:flex;align-items:center;gap:var(--space-sm);min-width:0;flex-wrap:wrap}.plugin-name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.plugin-version{font-size:var(--font-size-sm, .85rem)}.plugin-state-badge{display:inline-flex;align-items:center;padding:var(--space-xs) var(--space-sm);border-radius:var(--radius-pill);font-size:var(--font-size-3xs, .7rem);font-weight:600;text-transform:uppercase;letter-spacing:.04em;background:color-mix(in srgb,currentColor 12%,transparent)}.plugin-actions{display:flex;align-items:center;gap:var(--space-xs);flex-shrink:0}.plugin-manager-detail-header,.plugin-detail-title{display:flex;align-items:center;gap:var(--space-md);flex-wrap:wrap}.plugin-detail-title-copy{display:flex;flex-direction:column;gap:var(--space-xs);min-width:0}.plugin-detail-name{margin:0}.plugin-error-text{margin:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--font-size-xs, .8rem);font-family:var(--font-mono);color:var(--color-error);background:color-mix(in srgb,var(--color-error) 10%,transparent);border-radius:var(--radius-sm);padding:var(--space-xs) var(--space-sm)}.plugin-error-text--detail{max-width:min(100%,32rem);overflow:visible;text-overflow:clip;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.plugin-detail-content{display:flex;flex-direction:column;gap:var(--space-lg)}.plugin-detail-card{background:var(--surface);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);padding:var(--space-lg);display:flex;flex-direction:column;gap:var(--space-md)}.plugin-description{font-size:var(--font-size-md, .95rem);color:var(--text-secondary, var(--text-muted));line-height:1.5}.plugin-detail-meta-row{display:flex;align-items:center;gap:var(--space-xs);font-size:var(--font-size-base, .9rem);color:var(--text-muted)}.plugin-homepage{flex-wrap:wrap;align-items:flex-start}.plugin-homepage a{display:inline-flex;align-items:center;gap:var(--space-xs);color:var(--color-info);font-size:var(--font-size-sm, .85rem);flex-wrap:wrap;overflow-wrap:anywhere}.plugin-detail-section-heading{margin:0;padding:0;border:0;font-size:var(--font-size-md, .95rem)}.plugin-settings-form{display:flex;flex-direction:column;gap:var(--space-lg);margin-top:var(--space-xs)}.plugin-settings-form .form-group{padding:0;margin:0}.plugin-settings-group{display:flex;flex-direction:column;gap:var(--space-md)}.plugin-settings-group-heading{margin:0;font-size:var(--font-size-xs, .8rem);font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--text-muted)}.plugin-settings-array{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-settings-array-item{display:flex;align-items:center;gap:var(--space-sm)}.plugin-settings-array-item input{flex:1}.plugin-security-row{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm);flex-wrap:wrap}.plugin-security-results{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-security-header{display:flex;gap:var(--space-sm);align-items:center}.plugin-security-summary{margin:0}.plugin-security-badge{font-weight:700}.plugin-security-badge--warning,.plugin-security-badge--unavailable{color:var(--color-warning)}.plugin-security-badge--blocked,.plugin-security-badge--error{color:var(--color-error)}.plugin-security-findings{margin:0;padding-inline-start:var(--space-lg);display:flex;flex-direction:column;gap:var(--space-xs)}.plugin-detail-actions{display:flex;gap:var(--space-sm);padding-top:var(--space-md);border-top:var(--btn-border-width) solid var(--border);justify-content:flex-end}.plugin-manager .empty-state,.plugin-manager .loading-state,.plugin-manager .settings-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-sm);padding:var(--space-2xl);text-align:center;color:var(--text-muted)}.plugin-builtins-section{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-builtins-header{display:flex;flex-direction:column;gap:var(--space-xs)}.plugin-builtins-heading{margin:0;font-size:var(--font-size-md, .95rem)}.plugin-builtins-description{margin:0;font-size:var(--font-size-sm, .85rem);color:var(--text-muted)}.plugin-builtins-list{width:100%;display:flex;flex-direction:column;gap:var(--space-sm);margin-top:var(--space-sm)}.plugin-builtins-item{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm);padding:var(--space-sm) var(--space-md);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);background:var(--surface)}.plugin-builtins-meta{display:flex;align-items:center;gap:var(--space-sm);row-gap:var(--space-xs);flex-wrap:wrap;min-width:0}.plugin-builtins-name{color:var(--text);font-size:var(--font-size-base, .9rem);font-weight:500}.plugin-builtins-runtime-badge{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);background:var(--status-in-review-bg);color:var(--in-review);font-size:var(--font-size-2xs, .75rem);font-weight:600;text-transform:uppercase}.plugin-builtins-status{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);font-size:var(--font-size-2xs, .75rem);font-weight:600;text-transform:uppercase}.plugin-builtins-status--installed{background:var(--status-done-bg);color:var(--done)}.plugin-builtins-status--available{background:var(--status-todo-bg);color:var(--todo)}.plugin-builtins-setup-status{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);font-size:var(--font-size-2xs, .75rem);font-weight:600;text-transform:uppercase}.plugin-builtins-setup-status--warning{background:var(--status-in-review-bg);color:var(--color-warning)}.plugin-builtins-setup-status--ready{background:var(--status-done-bg);color:var(--color-success)}.plugin-builtins-setup-status--pending{background:var(--status-todo-bg);color:var(--color-warning)}.plugin-builtins-setup-status--deferred{background:var(--status-archived-bg);color:var(--text-muted)}.plugin-builtins-description-text{flex:1 1 100%;color:var(--text-muted);font-size:var(--font-size-sm, .85rem)}.plugin-builtins-metadata-only{color:var(--text-muted);font-size:var(--font-size-xs, .8rem);font-weight:600;text-transform:uppercase}.plugin-bundled-runtime-list{width:100%;display:flex;flex-direction:column;gap:var(--space-sm);margin-top:var(--space-sm)}.plugin-bundled-runtime-item{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm);padding:var(--space-sm) var(--space-md);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);background:var(--surface)}.plugin-bundled-runtime-meta{display:flex;align-items:center;gap:var(--space-sm);row-gap:var(--space-xs);flex-wrap:wrap;min-width:0}.plugin-bundled-runtime-name{color:var(--text);font-size:var(--font-size-base, .9rem);font-weight:500}.plugin-bundled-runtime-badge{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);background:var(--status-in-review-bg);color:var(--in-review);font-size:var(--font-size-2xs, .75rem);font-weight:600;text-transform:uppercase}.plugin-bundled-runtime-section{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-bundled-runtime-header{display:flex;flex-direction:column;gap:var(--space-xs)}.plugin-bundled-runtime-heading{margin:0;font-size:var(--font-size-md, .95rem)}.plugin-bundled-runtime-description{margin:0;font-size:var(--font-size-sm, .85rem);color:var(--text-muted)}.plugin-bundled-runtime-status{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);font-size:var(--font-size-2xs, .75rem);font-weight:600;text-transform:uppercase}@media(min-width:769px){.plugin-builtins-status,.plugin-bundled-runtime-status{margin-left:auto}}.plugin-bundled-runtime-status--installed{background:var(--status-done-bg);color:var(--done)}.plugin-bundled-runtime-status--available{background:var(--status-todo-bg);color:var(--todo)}@media(max-width:768px){.plugin-manager,.plugin-manager-detail{padding-inline:var(--space-sm)}.plugin-manager-detail-header{gap:var(--space-sm)}.plugin-detail-title{gap:var(--space-xs)}.plugin-detail-card{padding:var(--space-md);gap:var(--space-sm)}.plugin-list{gap:var(--space-xs)}.plugin-item{padding:var(--space-md);flex-direction:column;align-items:stretch;gap:var(--space-sm)}.plugin-info{width:100%;flex-wrap:wrap;row-gap:var(--space-xs)}.plugin-name{flex:1 1 100%;white-space:normal}.plugin-actions{width:100%;justify-content:flex-end;flex-wrap:wrap;gap:var(--space-sm)}.plugin-actions .btn-icon,.plugin-actions .toggle-switch{min-width:36px;min-height:36px}.plugin-actions .toggle-switch{display:inline-flex;align-items:center;justify-content:center}.plugin-security-row{align-items:flex-start}.plugin-detail-actions{flex-wrap:wrap;justify-content:flex-start}.plugin-detail-actions button{flex:1 1 auto;min-height:36px}.plugin-builtins-item,.plugin-bundled-runtime-item{flex-direction:column;align-items:stretch}.plugin-builtins-item .btn,.plugin-bundled-runtime-item .btn{min-height:36px}}