@augmentd-labs/canvas-web 2.26.21 → 2.26.22

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 (108) hide show
  1. package/dist/assets/{DocumentBodyEditor-CE2S2X6g.js → DocumentBodyEditor-8DNmOBS0.js} +1 -1
  2. package/dist/assets/{DocumentIcon-BS10SIP6.js → DocumentIcon-x299QNu_.js} +1 -1
  3. package/dist/assets/{EditorOverlay-BUToR_av.js → EditorOverlay-Cv1MaTok.js} +1 -1
  4. package/dist/assets/{FileForm-BxTixeDm.js → FileForm-DhQhVnoi.js} +1 -1
  5. package/dist/assets/{FiltersApplet-DNYAQP5F.js → FiltersApplet-gbu4I1fK.js} +1 -1
  6. package/dist/assets/{HomeFab-CpTZdNyY.js → HomeFab-CXaEEzXy.js} +2 -2
  7. package/dist/assets/{IdentityFields-C2MPM-Ab.js → IdentityFields-OPEWPlZs.js} +1 -1
  8. package/dist/assets/{LensApplet-CRa1j2WP.js → LensApplet-FifF2RUs.js} +1 -1
  9. package/dist/assets/LinkToCard-nR7Lal_o.js +1 -0
  10. package/dist/assets/{MarkdownEditor-7OqSDr7E.js → MarkdownEditor-CNY-VmKb.js} +2 -2
  11. package/dist/assets/{MessageComposerDialog-DzLhkHyh.js → MessageComposerDialog-gbD0Gu_b.js} +1 -1
  12. package/dist/assets/{NextShell-D9gvlKY2.js → NextShell-BIYs9fCL.js} +1 -1
  13. package/dist/assets/{NotesApplet-CFK86JYj.js → NotesApplet-D94iQKg6.js} +1 -1
  14. package/dist/assets/ObjectPropertiesCard-ChvWbOpZ.js +8 -0
  15. package/dist/assets/{ObjectPropertiesCard-C3jt40tM.css → ObjectPropertiesCard-JArcZXXv.css} +1 -1
  16. package/dist/assets/{PathPickerField-cVs5sZuG.js → PathPickerField-DBe7jykL.js} +1 -1
  17. package/dist/assets/{PickDocumentsCard-BlicxMqC.js → PickDocumentsCard-Bo224P4q.js} +1 -1
  18. package/dist/assets/{SketchApplet-D8mkYmK9.js → SketchApplet-ADVOnSLI.js} +1 -1
  19. package/dist/assets/{SketchEditor-zMrm1npg.js → SketchEditor-CkgRysCG.js} +5 -5
  20. package/dist/assets/{TerminalApplet-BO2HRp0z.js → TerminalApplet-CkO4uaE3.js} +1 -1
  21. package/dist/assets/{TextDocumentEditor-7V4aFL8i.js → TextDocumentEditor-COvsHJXH.js} +1 -1
  22. package/dist/assets/{TodosApplet-swdaBT-2.js → TodosApplet-Bpf1OIH4.js} +1 -1
  23. package/dist/assets/{TodosWidget-BXF8fYer.js → TodosWidget-BIYqPXUp.js} +1 -1
  24. package/dist/assets/{UploadProgressList-B2xCVzFR.js → UploadProgressList-BXJr87lI.js} +1 -1
  25. package/dist/assets/{_agentId_-CHdTYJFu.js → _agentId_-wN8YJoTZ.js} +0 -0
  26. package/dist/assets/{_contextId_-m9zn5qav.js → _contextId_-CoJraYnP.js} +1 -1
  27. package/dist/assets/{_workspaceName_-D0z9E9Uy.js → _workspaceName_-CXuIfG46.js} +1 -1
  28. package/dist/assets/{about-pl0DXTEt.js → about-CT6Ghfkb.js} +1 -1
  29. package/dist/assets/{add-B30V-yHb.js → add-BzgCpo5S.js} +1 -1
  30. package/dist/assets/{agents-Bc7_wscP.js → agents-DIJpWCkc.js} +1 -1
  31. package/dist/assets/{agents-ClK3Y2x0.js → agents-DlLq0q2s.js} +1 -1
  32. package/dist/assets/{alert-dialog-DIA-tAP5.js → alert-dialog-izlVMXfa.js} +1 -1
  33. package/dist/assets/{api-tokens-O5iFESo0.js → api-tokens-DDH7LMJ7.js} +1 -1
  34. package/dist/assets/{appearance-DLo_2WH0.js → appearance-D1EpKWaK.js} +1 -1
  35. package/dist/assets/{applet-target-DnTa2VAT.js → applet-target-ChiiXVdC.js} +1 -1
  36. package/dist/assets/{apps-DzFFo5D0.js → apps-DX8oWukQ.js} +1 -1
  37. package/dist/assets/{auth-CUdpxbLV.js → auth-HZNJB4wv.js} +1 -1
  38. package/dist/assets/backend-sync-progress-BeI9mDEX.js +7 -0
  39. package/dist/assets/{button-Bv4zA1qr.js → button-SgR3GVvV.js} +1 -1
  40. package/dist/assets/content-app-view-DS6Nrz6M.js +1 -0
  41. package/dist/assets/{context-D_K4g9SQ.js → context-mfIOB8dN.js} +1 -1
  42. package/dist/assets/{contexts-CgydYDn-2.js → contexts-CRWqFXzJ2.js} +1 -1
  43. package/dist/assets/{contexts-B6d6Axza.js → contexts-Ef3fHwv6.js} +1 -1
  44. package/dist/assets/{date-time-picker-BZaA3M9_.js → date-time-picker-B6mkWt-g.js} +1 -1
  45. package/dist/assets/{devices-D3-_UWM0.js → devices-C3gLuNDv.js} +1 -1
  46. package/dist/assets/{document-BG_mODBn.js → document-CX543Kcz.js} +1 -1
  47. package/dist/assets/document-list-SsF3jmuq.js +2 -0
  48. package/dist/assets/{embedding-m8SZH4oN.js → embedding-BHnBCyUx.js} +1 -1
  49. package/dist/assets/{embedding-Cjrxh6MN.js → embedding-CEGjemkq.js} +1 -1
  50. package/dist/assets/{form-panel-Dz96K7Lv.js → form-panel-C34lByEH.js} +1 -1
  51. package/dist/assets/{home-C5IxNq_5.js → home-3vhobmCu.js} +1 -1
  52. package/dist/assets/index-CiBgHujl.js +16 -0
  53. package/dist/assets/{inferd-config-editor-DagMmwW4.js → inferd-config-editor-BHUUFArV.js} +1 -1
  54. package/dist/assets/{key-bindings-DomR6ZNC.js → key-bindings-BNeY-GEg.js} +1 -1
  55. package/dist/assets/{label-CNoJOoe0.js → label-Dn4gCMcc.js} +1 -1
  56. package/dist/assets/{lazy-editor-Cbva8Pe2.js → lazy-editor-BSRnneOo.js} +2 -2
  57. package/dist/assets/{logs-CdbfY8_H.js → logs-Czn2sPeP.js} +1 -1
  58. package/dist/assets/{offline-BjMpUv3N.js → offline-CsRyFUg7.js} +1 -1
  59. package/dist/assets/{page-header-GtTr9Ff_.js → page-header-DkOHw0g-.js} +1 -1
  60. package/dist/assets/{public-share-DT9Tgn_Y.js → public-share-DXxVwHKT.js} +1 -1
  61. package/dist/assets/{query-debug-tVbB_8Z6.js → query-debug-BUUG3jpK.js} +1 -1
  62. package/dist/assets/{registry-BgnkvPjl.js → registry-DTWAywyJ.js} +2 -2
  63. package/dist/assets/{remotes-DR_iJClb.js → remotes-CdEU1Y5l.js} +1 -1
  64. package/dist/assets/{roles-DERIgujv.js → roles-BYHLwWCD.js} +1 -1
  65. package/dist/assets/roles-BpVWTMmp.js +2 -0
  66. package/dist/assets/settings-DH3ZiQLR.js +14 -0
  67. package/dist/assets/{settings-DhoHM9AU.js → settings-DNhy58i9.js} +1 -1
  68. package/dist/assets/{settings-BXOyceZ3.js → settings-DjnFAqIe.js} +1 -1
  69. package/dist/assets/{settings-B4LC1yCH.js → settings-jWfP7CP8.js} +1 -1
  70. package/dist/assets/{share-target-CpjVaYVl.js → share-target-CgHlnZLH.js} +1 -1
  71. package/dist/assets/{shared-B824Ibpc.js → shared-BMoiShd9.js} +1 -1
  72. package/dist/assets/{shared-1dpCuibu.js → shared-D2uRUNdC.js} +1 -1
  73. package/dist/assets/sketch-lazy-pbG2cCpW.js +2 -0
  74. package/dist/assets/{subtree-transfer-dialog-B7s4r78T.js → subtree-transfer-dialog-ChBI5llU.js} +1 -1
  75. package/dist/assets/{text-documents-D_zDMyhB.js → text-documents-BvDFBVnC.js} +1 -1
  76. package/dist/assets/ui-Byg1cZDr.js +41 -0
  77. package/dist/assets/{use-applet-docs-Cry2SFsG.js → use-applet-docs-o5NAUFBk.js} +1 -1
  78. package/dist/assets/{use-applet-target-mkjQujx6.js → use-applet-target-cO-FiyPQ.js} +1 -1
  79. package/dist/assets/{use-canvas-pins-CZBfEB5E.js → use-canvas-pins-Bth_Q--t.js} +2 -2
  80. package/dist/assets/{use-menu-BVkqNEIx.js → use-menu-BiJ8N7sS.js} +1 -1
  81. package/dist/assets/use-settings-back-CQauYjQ4.js +1 -0
  82. package/dist/assets/{use-sortable-data-DYIDDI8c.js → use-sortable-data-BW_0sXBs.js} +1 -1
  83. package/dist/assets/{use-workspace-identity-BVpGHkZq.js → use-workspace-identity-mgaofjsD.js} +1 -1
  84. package/dist/assets/{useAddTarget-CVRPElxz.js → useAddTarget-DUjaZdBQ.js} +1 -1
  85. package/dist/assets/{useAgentModels-ndLotMWH.js → useAgentModels-Sa4N8WFd.js} +1 -1
  86. package/dist/assets/{useDocumentThumbnail-C_rq2rXs.js → useDocumentThumbnail-DYLCC63L.js} +1 -1
  87. package/dist/assets/{useEditNoteFields-Bigs9ZG8.js → useEditNoteFields-zHEWwDfB.js} +1 -1
  88. package/dist/assets/{users-C9N-O1QA.js → users-CKz9kOoR.js} +1 -1
  89. package/dist/assets/{workspace-CJ1PWQ29.js → workspace-DvxC7pw1.js} +1 -1
  90. package/dist/assets/{workspace-area-menu-Ba_9bGKs.js → workspace-area-menu-UamvRNfd.js} +1 -1
  91. package/dist/assets/{workspace-content-gate-D8bzGD9i.js → workspace-content-gate-Be4tLYiB.js} +1 -1
  92. package/dist/assets/{workspace-import-form-D5JA8gzx.js → workspace-import-form-BRwc_oVg.js} +1 -1
  93. package/dist/assets/{workspaces-Dx5_N57M.js → workspaces-Da_hUIj3.js} +1 -1
  94. package/dist/assets/{workspaces-BBgdfAM_.js → workspaces-gQY1mczH.js} +1 -1
  95. package/dist/index.html +36 -36
  96. package/dist/sw.js +1 -1
  97. package/package.json +2 -2
  98. package/dist/assets/LinkToCard-BZwwE-Dv.js +0 -1
  99. package/dist/assets/ObjectPropertiesCard-DDcRLpsW.js +0 -8
  100. package/dist/assets/backend-sync-progress-DWR42uQV.js +0 -7
  101. package/dist/assets/content-app-view-BUi8HG1r.js +0 -1
  102. package/dist/assets/document-list-BiogH4tf.js +0 -2
  103. package/dist/assets/index-DtZS4JJ4.js +0 -16
  104. package/dist/assets/roles-DswT1Lv0.js +0 -2
  105. package/dist/assets/settings-CUzf6xEQ.js +0 -14
  106. package/dist/assets/sketch-lazy-Q58-vPF-.js +0 -2
  107. package/dist/assets/ui-BSqPn1LJ.js +0 -41
  108. package/dist/assets/use-settings-back-Dk-DJEUO.js +0 -1
@@ -0,0 +1,14 @@
1
+ import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{a as t,i as n,l as r,n as i,r as a,t as o,u as s}from"./code-editor-BOIngd5F.js";import{_t as c}from"./react-BHzhHVTZ.js";import{_ as l,g as u,v as d}from"./chunk-62JRHF6Z-vspAGgtK.js";import{Br as f,Ct as p,Ft as m,G as h,Gt as g,H as _,Hr as v,Ir as y,J as b,L as x,Mt as S,Nt as C,Ot as w,Pn as T,Sn as E,Tt as D,U as ee,Un as O,Ur as k,X as A,Xn as j,Yn as M,Yt as te,Z as N,Zt as ne,_n as P,_r as F,_t as I,bt as re,ct as ie,dr as L,ei as ae,fn as R,fr as z,ft as oe,hi as se,in as ce,jt as le,kn as ue,pr as de,pt as B,qn as fe,rt as pe,sn as me,st as he,vi as ge,wr as _e,yr as ve,zn as V,zt as H}from"./ui-Byg1cZDr.js";import{t as ye}from"./utils-2pl1SbIp.js";import{t as U}from"./button-SgR3GVvV.js";import{t as W}from"./input-BOtCd4HP.js";import{t as G}from"./label-Dn4gCMcc.js";import{c as be,l as xe,t as K}from"./api-CA6WiiYM.js";import{t as Se}from"./socket-CtH3EU9m.js";import{n as q}from"./use-toast-BlLDqSUO.js";import{t as Ce}from"./LinkToCard-nR7Lal_o.js";import{A as we,Bt as Te,Cn as Ee,Dt as De,E as Oe,F as ke,G as Ae,Gt as J,Jt as je,Kt as Me,L as Ne,Pt as Pe,R as Fe,Sn as Ie,Tn as Y,U as Le,Wt as Re,Zt as ze,_n as Be,an as Ve,bt as He,dn as X,et as Ue,f as We,gn as Ge,gt as Ke,hn as qe,i as Je,j as Ye,kt as Xe,ln as Ze,lt as Qe,nt as $e,on as et,ot as tt,p as nt,qt as rt,r as it,tt as at,un as ot,wt as st,yt as ct}from"./workspace-DvxC7pw1.js";import{a as lt,c as ut,i as dt,n as ft,o as pt,r as mt,s as ht,t as gt}from"./alert-dialog-izlVMXfa.js";import{t as _t}from"./use-mobile-Db-vWu9Y.js";import{t as vt}from"./useEscapeClose-DR_xKT_k.js";import{o as yt}from"./layer-style-Byl5LNFR.js";import{i as bt,t as xt}from"./color-DFEH4BfK.js";import{a as St,o as Ct,s as wt,t as Tt}from"./MessageComposerDialog-gbD0Gu_b.js";import{n as Et}from"./LinkToSidePanel-eCy3nW4R.js";import{n as Dt}from"./use-document-modal-PG8bQyIB.js";import{A as Ot,B as kt,D as At,E as jt,F as Mt,H as Nt,I as Pt,L as Ft,M as It,N as Lt,O as Rt,P as zt,R as Bt,S as Vt,T as Ht,U as Ut,V as Wt,b as Gt,d as Kt,f as qt,h as Jt,j as Yt,m as Xt,p as Zt,t as Qt,w as $t,x as en}from"./backend-sync-progress-BeI9mDEX.js";import{t as tn}from"./workspace-access-C9nZlBBZ.js";import{i as nn}from"./use-menu-BiJ8N7sS.js";import{t as rn}from"./use-theme-DaxpiICf.js";import{_t as an,at as on,it as sn,mt as cn,nt as ln,ot as un,rt as dn,st as fn,tt as pn,xt as mn}from"./index-CiBgHujl.js";import{n as hn}from"./page-header-DkOHw0g-.js";import{a as gn,c as _n,d as vn,f as yn,n as bn,o as xn,p as Sn,s as Cn,t as wn}from"./inferd-config-editor-BHUUFArV.js";import{n as Tn,t as En}from"./useSocketSubscription-ifW-vnlb.js";import{a as Dn,n as On,o as kn,r as An,t as jn}from"./devices-CAa1bRxk.js";import{a as Mn,i as Nn,n as Pn,s as Fn,t as In}from"./query-debug-BUUG3jpK.js";import{a as Ln,i as Rn,r as zn,t as Bn}from"./workspace-import-form-BRwc_oVg.js";var Z=e(s(),1),Vn=`workspace:backups`,Hn=e=>`${be.workspaces}/${encodeURIComponent(e)}/db/backups`,Un=e=>K.get(Hn(e)),Wn=e=>K.post(Hn(e),{action:`backup`}),Gn=(e,t)=>K.post(Hn(e),{action:`restore`,backupId:t,confirm:!0}),Kn=new Map;function qn(e,t,n){let r=JSON.stringify([t,n.id]),i=Kn.get(r);if(i)return i;let a=(async()=>{let r=n.action===`restore`,i=e({title:r?`Restoring workspace database`:`Backing up workspace database`,description:r?`Stopping the workspace and preserving its current database…`:`Copying and verifying all datasets…`});try{let e=n;for(;e.status===`running`;){let n=await Un(t),r=n.jobs.find(t=>t.id===e.id);if(!r)throw Error(`The server restarted. Refresh the backup list to check the result.`);e=r,window.dispatchEvent(new CustomEvent(Vn,{detail:{workspace:t,status:n}})),e.status===`running`&&await new Promise(e=>setTimeout(e,1500))}if(e.status===`failed`)throw Error(e.error||`Database operation failed`);i.done({title:r?`Workspace database restored`:`Workspace backup verified`,description:r?`The workspace is stopped. Start it when you are ready; search indexes will rebuild.`:`Documents, relations and virtual tree placements are saved.`})}catch(e){throw i.fail({title:`Workspace backup needs attention`,description:e instanceof Error?e.message:String(e),variant:`destructive`}),e}})();return Kn.set(r,a),a.finally(()=>Kn.delete(r)).catch(()=>{}),a}var Q=r(),Jn={startup:`Startup`,hourly:`Hourly`,manual:`Manual`,"pre-restore":`Before restore`};function Yn({workspaceId:e,onRestored:t}){let{showToast:n,showProgress:r}=q(),[i,a]=(0,Z.useState)(null),[o,s]=(0,Z.useState)(null),[c,l]=(0,Z.useState)(null),[u,d]=(0,Z.useState)(!1),f=(0,Z.useRef)(!1),p=(0,Z.useRef)(!1),m=(0,Z.useRef)(0),h=(0,Z.useCallback)(()=>{let t=++m.current;return Un(e).then(n=>{if(!(!f.current||t!==m.current)){a(n),s(null);for(let t of n.jobs.filter(e=>e.status===`running`))qn(r,e,t).catch(()=>{})}}).catch(e=>{f.current&&t===m.current&&s(e instanceof Error?e.message:String(e))})},[e,r]);(0,Z.useEffect)(()=>{f.current=!0,h();let t=t=>{let n=t.detail;n.workspace===e&&a(n.status)};return window.addEventListener(Vn,t),()=>{f.current=!1,window.removeEventListener(Vn,t)}},[e,h]);let g=async i=>{if(!p.current){p.current=!0,d(!0);try{let n=i?await Gn(e,i.id):await Wn(e);f.current&&a(e=>e&&{...e,jobs:[n,...e.jobs]}),await qn(r,e,n),i&&t()}catch(e){n({title:`Database operation did not complete`,description:e instanceof Error?e.message:String(e),variant:`destructive`})}finally{p.current=!1,f.current&&(d(!1),h())}}},_=u||!!i?.jobs.some(e=>e.status===`running`),v=i?.jobs[0];return(0,Q.jsxs)(`div`,{className:`min-w-0 space-y-5`,children:[(0,Q.jsxs)(`section`,{className:`space-y-4 rounded border p-4`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,Q.jsx)(se,{className:`h-5 w-5 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`font-semibold`,children:`Workspace checkpoints`}),(0,Q.jsxs)(U,{variant:`outline`,size:`sm`,className:`ml-auto`,disabled:_||!i||!!o,onClick:()=>void g(),children:[_?(0,Q.jsx)(R,{className:`mr-2 h-4 w-4 animate-spin`}):(0,Q.jsx)(se,{className:`mr-2 h-4 w-4`}),`Back up now`]})]}),(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Save a checkpoint before a bulk action. Each backup includes every document, relation and virtual tree placement in this workspace’s database.`}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap gap-x-5 gap-y-2 text-sm`,children:[(0,Q.jsxs)(`span`,{children:[(0,Q.jsx)(`strong`,{children:i?.retention.hourly??24}),` hourly backups`]}),(0,Q.jsxs)(`span`,{children:[(0,Q.jsx)(`strong`,{children:i?.retention.startup??14}),` startup backups`]}),(0,Q.jsx)(`span`,{children:`Manual checkpoints kept`})]}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Hourly backups run while the workspace is active. These checkpoints cover the database only; files in storage backends and workspace settings need their own backups.`})]}),(0,Q.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,Q.jsxs)(`h2`,{className:`text-sm font-semibold`,children:[`Verified checkpoints`,i?` · ${i.backups.length}`:``]}),(0,Q.jsx)(U,{variant:`ghost`,size:`icon`,className:`h-8 w-8`,onClick:()=>void h(),"aria-label":`Refresh backups`,children:(0,Q.jsx)(w,{className:`h-4 w-4`})})]}),o&&(0,Q.jsx)(`p`,{role:`alert`,className:`text-sm text-destructive`,children:o}),v&&(0,Q.jsx)(`p`,{role:`status`,className:`text-sm ${v.status===`failed`?`text-destructive`:`text-muted-foreground`}`,children:v.status===`running`?`Database operation in progress…`:v.status===`failed`?v.error:v.action===`restore`?`Database restored. The workspace is stopped and ready to start.`:`Backup complete and verified.`}),!i&&!o&&(0,Q.jsx)(`p`,{role:`status`,className:`text-sm text-muted-foreground`,children:`Loading backups…`}),i?.backups.length===0&&(0,Q.jsx)(`p`,{className:`rounded border border-dashed p-6 text-sm text-muted-foreground`,children:`No verified checkpoints yet. Create one before your next bulk action.`}),(0,Q.jsx)(`div`,{className:`space-y-2`,children:i?.backups.map(e=>(0,Q.jsxs)(`article`,{className:`flex min-w-0 flex-wrap items-center gap-3 rounded border p-3`,children:[(0,Q.jsx)(k,{className:`h-4 w-4 shrink-0 text-muted-foreground`,"aria-label":`Verified`}),(0,Q.jsxs)(`div`,{className:`min-w-0 flex-1 basis-40`,children:[(0,Q.jsx)(`p`,{className:`text-sm font-medium`,children:new Date(e.createdAt).toLocaleString()}),(0,Q.jsxs)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:[Jn[e.kind],` · `,Le(e.bytes),` · `,(e.datasets.documents?.count??0).toLocaleString(),` documents`]})]}),(0,Q.jsxs)(U,{variant:`outline`,size:`sm`,disabled:_,onClick:()=>l(e),children:[(0,Q.jsx)(D,{className:`mr-2 h-3.5 w-3.5`}),`Restore`]})]},e.id))}),(0,Q.jsx)(gt,{open:!!c,onOpenChange:e=>{e||l(null)},children:(0,Q.jsxs)(dt,{className:`max-w-[calc(100vw-2rem)] sm:max-w-lg`,children:[(0,Q.jsxs)(ht,{children:[(0,Q.jsx)(ut,{children:`Restore this workspace checkpoint?`}),(0,Q.jsxs)(lt,{children:[`The workspace will stop and its current database will be backed up first. Documents, relations and virtual tree placements will return to `,c?new Date(c.createdAt).toLocaleString():`the selected checkpoint`,`. Files in storage backends and workspace settings will stay as they are. The workspace stays stopped after restoration.`]})]}),(0,Q.jsxs)(pt,{children:[(0,Q.jsx)(mt,{children:`Cancel`}),(0,Q.jsx)(ft,{disabled:_,onClick:()=>{c&&g(c)},children:`Back up current & restore`})]})]})})]})}var Xn={collect:`Collect unused blobs`,verify:`Verify integrity`,compact:`Compact index`},Zn=e=>e.status===`queued`||e.status===`running`,Qn=e=>`${be.workspaces}/${encodeURIComponent(e)}/cache/maintenance`,$n=e=>K.get(Qn(e)),er=(e,t,n)=>K.post(Qn(e),{store:t,action:n,...n===`verify`?{confirm:!0}:{}});function tr(e){if(e.status===`failed`)return e.error||`Maintenance failed`;if(e.status===`queued`)return`Waiting for active storage operations…`;if(e.status===`running`)return e.action===`verify`?`Reading and checking stored content…`:`Processing the cache index…`;let t=e.result||{};return e.action===`compact`?`${(t.compactedCount||0).toLocaleString()} keys compacted`:e.action===`verify`?`${(t.verifiedContent||0).toLocaleString()} blobs verified · ${(t.badContentCount||0).toLocaleString()} corrupted blobs removed · ${Le(t.reclaimedSize||0)} reclaimed`:`${(t.reclaimedCount||0).toLocaleString()} unused blobs removed · ${Le(t.reclaimedSize||0)} reclaimed`}var nr=new Map,rr=`workspace:cache-maintenance`;function ir(e,t,n){let r=JSON.stringify([t,n.id]),i=nr.get(r);if(i)return i;let a=(async()=>{let r=e({title:Xn[n.action],description:`${n.store} · ${tr(n)}`});try{let e=n;for(;Zn(e);){let i=await $n(t),a=i.jobs.find(e=>e.id===n.id);if(!a)throw Error(`Maintenance receipt is no longer available. The server may have restarted; refresh the cache settings.`);if(e=a,window.dispatchEvent(new CustomEvent(rr,{detail:{workspace:t,status:i}})),!Zn(e))break;r.update({title:Xn[e.action],description:`${e.store} · ${tr(e)}`}),await new Promise(e=>setTimeout(e,2e3))}if(e.status===`failed`)throw Error(tr(e));r.done({title:`${Xn[e.action]} complete`,description:`${e.store} · ${tr(e)}`})}catch(e){throw r.fail({title:`Cache maintenance needs attention`,description:`${n.store} · ${e instanceof Error?e.message:String(e)}`,variant:`destructive`}),e}})();return nr.set(r,a),a.finally(()=>{nr.get(r)===a&&nr.delete(r)}).catch(()=>{}),a}function ar({workspaceId:e,children:t}){let{showToast:n,showProgress:r}=q(),[i,a]=(0,Z.useState)(null),[o,s]=(0,Z.useState)(`workspace:cache`),[c,l]=(0,Z.useState)(!1),[u,d]=(0,Z.useState)(!1),[f,p]=(0,Z.useState)(null),m=(0,Z.useRef)(!1),h=(0,Z.useRef)(!1),g=(0,Z.useCallback)(()=>$n(e).then(t=>{if(h.current){a(t),p(null);for(let n of t.jobs.filter(Zn))ir(r,e,n).catch(()=>{})}}).catch(e=>{h.current&&p(e instanceof Error?e.message:`Cannot load maintenance status`)}),[e,r]);(0,Z.useEffect)(()=>{h.current=!0,g();let t=t=>{let{workspace:n,status:r}=t.detail;n===e&&(a(r),p(null))};return window.addEventListener(rr,t),()=>{h.current=!1,window.removeEventListener(rr,t)}},[e,g]);let _=async t=>{if(!(m.current||i?.jobs.some(Zn))){m.current=!0,l(!0);try{let n=await er(e,o,t);h.current&&a(e=>e&&{...e,jobs:[n,...e.jobs.filter(e=>e.id!==n.id)]}),ir(r,e,n).catch(()=>{})}catch(e){n({title:`Could not start maintenance`,description:e instanceof Error?e.message:String(e),variant:`destructive`}),g()}finally{m.current=!1,h.current&&l(!1)}}},v=c||!!i?.jobs.some(Zn),y=i?.stores.find(e=>e.id===o),b=i?.jobs[0];return(0,Q.jsxs)(`section`,{className:`space-y-4 rounded-(--panel-radius) border p-4`,"aria-label":`Workspace cache maintenance`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-start justify-between gap-3`,children:[(0,Q.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Q.jsxs)(`h2`,{className:`flex items-center gap-2 text-sm font-semibold`,children:[(0,Q.jsx)(ve,{className:`h-4 w-4 shrink-0`}),`Workspace cache`]}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Thumbnails, cached resources and pending transfers. Maintenance runs only when requested.`})]}),t&&(0,Q.jsx)(`fieldset`,{disabled:v,children:t})]}),(0,Q.jsxs)(`div`,{className:`space-y-3 border-t pt-3`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,Q.jsx)(`label`,{htmlFor:`cache-maintenance-store`,className:`text-xs font-medium`,children:`Store`}),(0,Q.jsxs)(`select`,{id:`cache-maintenance-store`,value:o,onChange:e=>s(e.target.value),disabled:v,className:`min-w-0 max-w-full rounded-md border bg-background px-2 py-1.5 text-xs`,children:[(0,Q.jsx)(`option`,{value:`workspace:cache`,children:`workspace:cache`}),(0,Q.jsx)(`option`,{value:`workspace:data`,children:`workspace:data`})]}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`icon`,onClick:()=>void g(),"aria-label":`Refresh cache maintenance status`,className:`ml-auto h-8 w-8`,children:(0,Q.jsx)(w,{className:`h-3.5 w-3.5`})})]}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:o===`workspace:data`?`Managed document blobs. Referenced content is kept during collection.`:`Working cache, including content awaiting transfer. Referenced content is kept during collection.`}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:v||!y?.canCollect||!!f,onClick:()=>void _(`collect`),children:[(0,Q.jsx)(A,{className:`mr-2 h-3.5 w-3.5`}),`Collect unused blobs`]}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:v||!y?.canCompact||!!f,onClick:()=>void _(`compact`),children:[(0,Q.jsx)(se,{className:`mr-2 h-3.5 w-3.5`}),`Compact index`]}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:v||!y?.canVerify||!!f,onClick:()=>d(!0),children:[(0,Q.jsx)(oe,{className:`mr-2 h-3.5 w-3.5`}),`Verify integrity`]})]}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Collection removes unreferenced blobs. Compaction trims index history. Verification reads every blob and may remove corrupted content. Storage writes may wait during maintenance.`}),!i&&!f&&(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,role:`status`,children:`Loading maintenance status…`}),y&&!y.canCollect&&!y.canVerify&&!y.canCompact&&(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Update canvas-stored to enable maintenance for this store.`}),f&&(0,Q.jsx)(`p`,{className:`text-xs text-destructive`,role:`alert`,children:f}),b&&(0,Q.jsxs)(`div`,{className:`rounded-md bg-muted/50 p-3 text-xs`,role:`status`,"aria-live":`polite`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2 font-medium`,children:[Zn(b)&&(0,Q.jsx)(R,{className:`h-3.5 w-3.5 animate-spin`}),Xn[b.action],` · `,b.store]}),(0,Q.jsx)(`p`,{className:`mt-1 ${b.status===`failed`?`text-destructive`:`text-muted-foreground`}`,children:tr(b)}),(0,Q.jsx)(`p`,{className:`mt-1 text-muted-foreground`,children:b.finishedAt?`Finished ${new Date(b.finishedAt).toLocaleString()}`:`Requested ${new Date(b.createdAt).toLocaleString()}`})]})]}),(0,Q.jsx)(gt,{open:u,onOpenChange:d,children:(0,Q.jsxs)(dt,{className:`max-w-[calc(100vw-2rem)] sm:max-w-lg`,children:[(0,Q.jsxs)(ht,{children:[(0,Q.jsxs)(ut,{children:[`Verify `,o,`?`]}),(0,Q.jsx)(lt,{children:`This reads all stored content and can take a long time. Corrupted blobs and their index entries may be removed. Keep a recovery backup before running it. Storage writes may wait until it finishes.`})]}),(0,Q.jsxs)(pt,{children:[(0,Q.jsx)(mt,{children:`Cancel`}),(0,Q.jsx)(ft,{disabled:v,onClick:()=>void _(`verify`),children:`Verify and repair`})]})]})})]})}function or({workspaceId:e,backend:t,onSaved:n}){let r=t?.config||{},i=()=>({name:String(r.label||``),bucket:String(r.bucket||``),prefix:String(r.prefix||``),endpoint:String(r.endpoint||``),region:String(r.region||`us-east-1`),accessKeyId:``,secretAccessKey:``,sessionToken:``}),[a,o]=(0,Z.useState)(!1),[s,c]=(0,Z.useState)(i),[l,u]=(0,Z.useState)(r.forcePathStyle!==!1),[d,f]=(0,Z.useState)(r.readOnly===!0),[p,m]=(0,Z.useState)(r.watch===!0),[h,g]=(0,Z.useState)(!1),[_,v]=(0,Z.useState)(!1),{showToast:y}=q(),b=e=>({value:s[e],onChange:t=>c(n=>({...n,[e]:t.target.value}))});return a?(0,Q.jsxs)(`form`,{onSubmit:async r=>{r.preventDefault(),v(!0);try{let r={label:s.name.trim(),bucket:s.bucket.trim(),prefix:s.prefix.trim(),endpoint:s.endpoint.trim(),region:s.region.trim()||`us-east-1`,forcePathStyle:l,readOnly:d,watch:p,...Object.fromEntries([`accessKeyId`,`secretAccessKey`,`sessionToken`].flatMap(e=>{let t=s[e].trim();return t?[[e,t]]:[]})),...h?{sessionToken:``}:{}};t?await Ie(e,`s3`,t.address,r):await it(e,`s3`,r),o(!1),c(i()),y({title:t?`S3 backend updated`:`S3 backend added`,description:`Use Re-sync to check indexing progress, and filing policies to automate copies.`}),await n()}catch(e){y({title:`Could not connect S3`,description:e instanceof Error?e.message:String(e),variant:`destructive`})}finally{v(!1)}},className:`w-full space-y-3 rounded-lg border p-4`,children:[(0,Q.jsxs)(`h2`,{className:`flex items-center gap-2 text-sm font-semibold`,children:[(0,Q.jsx)(ve,{className:`h-4 w-4`}),t?`Edit S3 connection`:`Add S3-compatible bucket`]}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Amazon S3, MinIO, Garage, RustFS or Ceph RGW. Connect an existing bucket, optionally scoped to a prefix. Supports browsing, reads and filing copies; mirror policies and overwrites are unavailable.`}),(0,Q.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,Q.jsxs)(`label`,{className:`space-y-1 text-xs`,children:[`Name`,(0,Q.jsx)(W,{...b(`name`),required:!0,placeholder:`Accounting archive`})]}),(0,Q.jsxs)(`label`,{className:`space-y-1 text-xs`,children:[`Bucket`,(0,Q.jsx)(W,{...b(`bucket`),disabled:!!t,required:!0,placeholder:`accounting`})]}),(0,Q.jsxs)(`label`,{className:`space-y-1 text-xs`,children:[`Endpoint (blank for Amazon S3)`,(0,Q.jsx)(W,{...b(`endpoint`),disabled:!!t,type:`url`,placeholder:`https://s3.example.com`})]}),(0,Q.jsxs)(`label`,{className:`space-y-1 text-xs`,children:[`Region`,(0,Q.jsx)(W,{...b(`region`),required:!0,placeholder:`us-east-1`})]}),(0,Q.jsxs)(`label`,{className:`space-y-1 text-xs sm:col-span-2`,children:[`Prefix (optional)`,(0,Q.jsx)(W,{...b(`prefix`),disabled:!!t,placeholder:`Augmentd/Accounting`})]}),(0,Q.jsxs)(`label`,{className:`space-y-1 text-xs`,children:[`Access key ID`,(0,Q.jsx)(W,{...b(`accessKeyId`),required:!t,autoComplete:`off`,placeholder:t?`Leave blank to keep stored value`:``})]}),(0,Q.jsxs)(`label`,{className:`space-y-1 text-xs`,children:[`Secret access key`,(0,Q.jsx)(W,{...b(`secretAccessKey`),type:`password`,required:!t,autoComplete:`off`,placeholder:t?`Leave blank to keep stored value`:``})]}),(0,Q.jsxs)(`label`,{className:`space-y-1 text-xs sm:col-span-2`,children:[`Session token (optional)`,(0,Q.jsx)(W,{...b(`sessionToken`),disabled:h,type:`password`,autoComplete:`off`,placeholder:t?`Leave blank to keep stored value`:``})]}),t&&(0,Q.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:h,onChange:e=>g(e.target.checked)}),`Clear stored session token`]})]}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap gap-4 text-xs`,children:[(0,Q.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:l,onChange:e=>u(e.target.checked)}),`Path-style addressing`]}),(0,Q.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:d,onChange:e=>f(e.target.checked)}),`Read-only`]}),(0,Q.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:p,onChange:e=>m(e.target.checked)}),`Poll for changes`]})]}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Writable connections test conflict protection using a temporary object, then delete it. The key needs list/read/write access and delete access under .canvas-probe/. Initial indexing reads file bytes; polling can incur request and download charges. Upload limit: 5 GiB per file.`}),(0,Q.jsxs)(`div`,{className:`flex justify-end gap-2`,children:[(0,Q.jsx)(U,{type:`button`,variant:`ghost`,disabled:_,onClick:()=>{o(!1),c(i())},children:`Cancel`}),(0,Q.jsx)(U,{disabled:_,children:_?`Checking connection…`:`Save connection`})]})]}):(0,Q.jsxs)(U,{variant:`outline`,size:`sm`,onClick:()=>{c(i()),u(r.forcePathStyle!==!1),f(r.readOnly===!0),m(r.watch===!0),g(!1),o(!0)},children:[t?(0,Q.jsx)(H,{className:`mr-2 h-3.5 w-3.5`}):(0,Q.jsx)(C,{className:`mr-2 h-3.5 w-3.5`}),t?`Edit S3 connection`:`Add S3 bucket`]})}function sr({workspaceId:e,isActive:t,currentMode:n,onChanged:r}){let[i,a]=(0,Z.useState)(n===`workspace`?`workspace`:`secrets`),[o,s]=(0,Z.useState)(``),[c,l]=(0,Z.useState)(``),[u,d]=(0,Z.useState)(``),[f,p]=(0,Z.useState)(``),[m,h]=(0,Z.useState)(``),[g,_]=(0,Z.useState)(!1);return(0,Q.jsxs)(`section`,{className:`space-y-3 rounded-lg border p-4`,children:[(0,Q.jsx)(`h2`,{className:`font-semibold text-sm`,children:`Workspace protection`}),(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Protect service credentials with a PIN or password. Optionally require it to start the whole workspace. Files and document contents are not encrypted.`}),t&&(0,Q.jsx)(`p`,{className:`text-sm`,children:`Stop the workspace before changing protection.`}),(0,Q.jsxs)(`form`,{className:`space-y-3`,onSubmit:async t=>{if(t.preventDefault(),h(``),o!==c){h(`PIN/password confirmation does not match`);return}_(!0);try{let t=await K.put(`${be.workspaces}/${e}/protection`,{mode:i,passphrase:o,currentPassphrase:u||void 0});p(t.recoveryKey),s(``),l(``),d(``)}catch(e){h(e instanceof Error?e.message:`Could not save protection`)}finally{_(!1)}},children:[(0,Q.jsxs)(`label`,{className:`block text-sm space-y-1`,children:[`Protection mode`,(0,Q.jsxs)(`select`,{className:`block rounded border bg-background p-2 w-full`,value:i,onChange:e=>a(e.target.value),disabled:t||g,children:[(0,Q.jsx)(`option`,{value:`secrets`,children:`Secrets only — allow start without integrations`}),(0,Q.jsx)(`option`,{value:`workspace`,children:`Whole workspace — require PIN/password to start`})]})]}),(0,Q.jsxs)(`label`,{className:`block text-sm space-y-1`,children:[`Current PIN/password or recovery key (if already protected)`,(0,Q.jsx)(W,{type:`password`,autoComplete:`current-password`,value:u,onChange:e=>d(e.target.value),disabled:t||g})]}),(0,Q.jsxs)(`label`,{className:`block text-sm space-y-1`,children:[`New PIN/password`,(0,Q.jsx)(W,{type:`password`,autoComplete:`new-password`,minLength:4,maxLength:1024,required:!0,value:o,onChange:e=>s(e.target.value),disabled:t||g})]}),(0,Q.jsxs)(`label`,{className:`block text-sm space-y-1`,children:[`Confirm new PIN/password`,(0,Q.jsx)(W,{type:`password`,autoComplete:`new-password`,required:!0,value:c,onChange:e=>l(e.target.value),disabled:t||g})]}),(0,Q.jsx)(U,{disabled:t||g||!!f,children:g?`Saving…`:`Save protection`})]}),m&&(0,Q.jsx)(`p`,{role:`alert`,className:`text-sm text-destructive`,children:m}),f&&(0,Q.jsxs)(`div`,{className:`space-y-2 rounded border p-3`,children:[(0,Q.jsx)(`p`,{className:`text-sm`,children:`Save this recovery key now. It is shown only once and replaces any previous recovery key.`}),(0,Q.jsx)(`code`,{className:`block break-all select-all`,children:f}),(0,Q.jsx)(U,{variant:`outline`,onClick:()=>{p(``),r()},children:`I saved the recovery key`})]})]})}var cr=`h-8 rounded-md border bg-background px-2 text-sm outline-none focus:ring-2 focus:ring-ring`;function lr({title:e,hint:t,children:n}){let[r,i]=(0,Z.useState)(!1);return(0,Q.jsxs)(`section`,{className:`overflow-hidden rounded-lg border`,children:[(0,Q.jsxs)(`button`,{type:`button`,onClick:()=>i(e=>!e),"aria-expanded":r,className:`flex w-full items-center justify-between gap-4 px-4 py-3 text-left transition-colors hover:bg-muted/40`,children:[(0,Q.jsxs)(`span`,{className:`min-w-0`,children:[(0,Q.jsx)(`span`,{className:`block text-sm font-medium`,children:e}),(0,Q.jsx)(`span`,{className:`mt-0.5 block text-xs text-muted-foreground`,children:t})]}),(0,Q.jsx)(v,{className:ye(`h-4 w-4 shrink-0 text-muted-foreground transition-transform motion-reduce:transition-none`,r&&`rotate-180`)})]}),r&&(0,Q.jsx)(`div`,{className:`border-t p-4`,children:n})]})}function ur({workspaceId:e,workspaceName:t,refreshKey:n,onRefresh:r}){let{showToast:i}=q(),[a,o]=(0,Z.useState)(null),[s,c]=(0,Z.useState)(!1),l=(0,Z.useCallback)(()=>xn(e).then(e=>o(e.queue??null)).catch(()=>{}),[e]);(0,Z.useEffect)(()=>{l();let e=window.setInterval(()=>{o(e=>(e&&(e.pending>0||e.draining)&&l(),e))},3e3);return()=>window.clearInterval(e)},[l,n]);let u=async()=>{if(a){c(!0);try{let e=await Ze(!a.paused,t);i({title:e.paused?`Embedding paused`:`Embedding resumed`,description:e.paused?`${e.pending.toLocaleString()} job(s) held for this workspace. Resume any time; a restart also resumes.`:`${e.pending.toLocaleString()} job(s) draining.`}),await l()}catch(e){i({title:`Error`,description:e instanceof Error?e.message:`Failed to toggle the embedding queue (admin only)`,variant:`destructive`})}finally{c(!1)}}},d=!!a?.draining,f=a?a.paused?`bg-warning`:d?`bg-success`:`bg-muted-foreground/40`:`bg-muted-foreground/40`;return(0,Q.jsxs)(`section`,{className:`flex flex-wrap items-center justify-between gap-x-4 gap-y-2 rounded-lg border bg-muted/30 px-4 py-2.5`,children:[(0,Q.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2 text-sm`,children:[(0,Q.jsxs)(`span`,{className:`relative flex h-2 w-2 shrink-0`,"aria-hidden":!0,children:[d&&(0,Q.jsx)(`span`,{className:`absolute inline-flex h-full w-full animate-ping rounded-full bg-success opacity-60 motion-reduce:hidden`}),(0,Q.jsx)(`span`,{className:ye(`relative inline-flex h-2 w-2 rounded-full`,f)})]}),(0,Q.jsx)(`span`,{className:`font-medium`,children:a?a.pending>0?(0,Q.jsxs)(Q.Fragment,{children:[a.pending.toLocaleString(),` `,(0,Q.jsx)(`span`,{className:`font-normal text-muted-foreground`,children:`pending`})]}):a.paused?`Paused`:`Idle`:`Queue unavailable`}),a?.paused&&a.pending>0&&(0,Q.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`held`}),a?.ingestDisabled&&(0,Q.jsx)(`span`,{className:`rounded bg-warning-subtle px-1.5 py-0.5 text-[10px] font-medium text-warning`,title:`CANVAS_INFERD_INGEST_DISABLED=true: nothing new is enqueued; existing vectors still serve search`,children:`ingest disabled`})]}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-1`,children:[a&&(0,Q.jsx)(U,{type:`button`,size:`sm`,variant:`ghost`,disabled:s,onClick:u,title:a.paused?`Resume embedding; the held backlog drains`:`Pause after the current batch. Stops the CPU-heavy model inference; documents keep indexing and stay searchable by text.`,children:s?`…`:a.paused?`Resume`:`Pause`}),(0,Q.jsx)(U,{type:`button`,size:`sm`,variant:`ghost`,onClick:r,title:`Reload config, queue and tables`,children:(0,Q.jsx)(w,{className:`h-3.5 w-3.5`})})]})]})}function dr({workspaceId:e,tables:t,error:n,onChanged:r}){let{showToast:i}=q(),[a,o]=(0,Z.useState)(null),s=t.filter(e=>!e.active),c=async t=>{if(window.confirm(`Reclaim "${t.name}"?\n\nThis permanently deletes the vectors for ${t.model||`that model`} and its "already embedded" ledger. Switching back to it afterwards would cost a full re-embed rather than being instant.\n\nThis is the only irreversible step in the whole flow.`)){o(t.name);try{let n=await bn(e,t.name);if(!n.dropped)throw Error(n.error||`Failed to drop the vector table`);i({title:`Reclaimed`,description:`Dropped '${n.name||t.name}'.`}),r()}catch(e){i({title:`Error`,description:e instanceof Error?e.message:`Failed to reclaim`,variant:`destructive`})}finally{o(null)}}};return n?(0,Q.jsxs)(`section`,{className:`rounded-lg border border-destructive/40 bg-destructive-subtle p-4`,children:[(0,Q.jsx)(`p`,{className:`text-sm font-medium text-destructive`,children:`Vector tables unavailable`}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:n})]}):s.length===0?null:(0,Q.jsx)(lr,{title:`Previous models (${s.length})`,hint:`Kept on disk so switching back is instant. Reclaiming deletes their vectors for good (the only irreversible step here).`,children:(0,Q.jsx)(`div`,{className:`divide-y`,children:s.map(e=>(0,Q.jsxs)(`div`,{className:`flex items-center justify-between gap-4 py-2 first:pt-0 last:pb-0`,children:[(0,Q.jsxs)(`div`,{className:`min-w-0`,children:[(0,Q.jsxs)(`p`,{className:`truncate font-mono text-xs`,children:[e.model||`unknown model`,e.dim?` · ${e.dim}-d`:``]}),(0,Q.jsxs)(`p`,{className:`truncate font-mono text-[11px] text-muted-foreground`,children:[`└─ `,e.name]})]}),(0,Q.jsxs)(U,{type:`button`,size:`sm`,variant:`ghost`,className:`text-destructive hover:text-destructive`,disabled:a===e.name,onClick:()=>c(e),title:`Permanently delete this model's vectors and ledger`,children:[(0,Q.jsx)(A,{className:`mr-1.5 h-3.5 w-3.5`}),a===e.name?`Reclaiming…`:`Reclaim`]})]},e.name))})})}function fr({workspaceId:e,workspaceName:t,spaces:n,onDone:r}){let{showToast:i}=q(),[a,o]=(0,Z.useState)(``),[s,c]=(0,Z.useState)(``),[l,u]=(0,Z.useState)(!1),[d,f]=(0,Z.useState)(!1),[p,m]=(0,Z.useState)(!1);return(0,Q.jsxs)(lr,{title:`Fill a space`,hint:`Embed documents that are missing from a space. Narrow it to one subtree to try a model on a single project first.`,children:[(0,Q.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsx)(`label`,{className:`text-xs font-medium`,children:`Space`}),(0,Q.jsxs)(`select`,{className:`${cr} w-full`,value:a,onChange:e=>o(e.target.value),children:[(0,Q.jsx)(`option`,{value:``,children:`all spaces`}),n.map(e=>(0,Q.jsx)(`option`,{value:e,children:e},e))]})]}),(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsxs)(`label`,{className:`text-xs font-medium`,children:[`Limit to a path `,(0,Q.jsx)(`span`,{className:`font-normal text-muted-foreground`,children:`(optional)`})]}),(0,Q.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Q.jsx)(W,{value:s,onChange:e=>c(e.target.value),placeholder:`ctx://work/project or dir://photos`,className:`h-8 font-mono text-sm`}),(0,Q.jsx)(U,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>m(!0),title:`Pick a context or directory path from the tree`,children:(0,Q.jsx)(M,{className:`h-3.5 w-3.5`})})]})]})]}),(0,Q.jsxs)(`label`,{className:`mt-3 flex items-start gap-2 text-xs`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,className:`mt-0.5`,checked:l,onChange:e=>u(e.target.checked)}),(0,Q.jsxs)(`span`,{children:[`Re-embed documents that are already embedded`,l&&s.trim()&&(0,Q.jsx)(`span`,{className:`mt-1 block text-[11px] text-warning`,children:`Combined with a path limit this clears the WHOLE space, not just that subtree. A partial clear is not expressible in the ledger. Leave this off unless you mean to redo everything.`})]})]}),(0,Q.jsx)(U,{type:`button`,size:`sm`,className:`mt-3`,onClick:async()=>{f(!0);try{let t=await _n(e,{space:a||void 0,reindex:l,scope:s.trim()||void 0});t.ingestDisabled?i({title:`Ingest disabled`,description:`CANVAS_INFERD_INGEST_DISABLED=true: nothing was enqueued. Existing vectors still serve search.`,variant:`destructive`}):i({title:`Fill enqueued`,description:`${t.enqueued.toLocaleString()} document(s) queued${t.scope?` under ${t.scope}`:``}. The status line reports progress.`}),r()}catch(e){i({title:`Fill failed`,description:e instanceof Error?e.message:`Failed to enqueue`,variant:`destructive`})}finally{f(!1)}},disabled:d,children:d?`Queueing…`:`Start fill`}),p&&(0,Q.jsx)(Et,{onClose:()=>m(!1),children:(0,Q.jsx)(Ce,{sizeClassName:`h-full w-[380px] max-w-[90vw] max-md:w-full`,fixedWorkspaceName:t,multiple:!1,onClose:()=>m(!1),onConfirm:(e,t)=>{let n=e[0];if(n){let e=t.treeType===`directory`?`dir`:`ctx`;c(`${e}://${n.replace(/^\/+/,``)}`)}m(!1)}})})]})}var pr=[{provider:`blip`,model:`HuggingFaceTB/SmolVLM-500M-Instruct`,task:`instruct`,label:`SmolVLM 500M: prompted description (recommended)`,note:`~500 MB. Follows the prompt below, so architecture/materials/setting can be asked for.`},{provider:`blip`,model:`HuggingFaceTB/SmolVLM-256M-Instruct`,task:`instruct`,label:`SmolVLM 256M: prompted, smaller`,note:`~250 MB. Same prompting, weaker; for memory-tight boxes.`},{provider:`blip`,model:`onnx-community/Florence-2-base-ft`,task:`florence2`,label:`Florence-2 base: detailed caption`,note:`Detailed multi-sentence captions, but not steerable: no prompt, fixed task token.`},{provider:`blip`,model:`Xenova/vit-gpt2-image-captioning`,task:`caption`,label:`vit-gpt2: one short sentence (weakest)`,note:`The old default. Terse and repetition-prone; kept for continuity and low memory.`}],mr=`custom`,hr=[{value:`instruct`,label:`Instruct VLM (image + prompt)`},{value:`florence2`,label:`Florence-2 (task token)`},{value:`caption`,label:`Plain captioner (image only)`}],gr=`Describe this photograph in two or three sentences. If it shows a building or interior, name the architectural style, the main materials and the setting. Describe only what is visible.`;function _r({workspaceId:e,config:t,saving:n,onSave:r}){let{showToast:i}=q(),a=t.workspace||{},o=t.effective||{},s=pr[0],c=a.summarize?.image||o.summarize?.image||{},[l,u]=(0,Z.useState)(()=>o.summarize?.image?.enabled===!0||a.summarize?.image?.enabled===!0),[d,f]=(0,Z.useState)(()=>{let e=pr.find(e=>e.provider===(c.provider||s.provider)&&e.model===c.model);return e?`${e.provider}::${e.model}`:c.model?mr:`${s.provider}::${s.model}`}),[p,m]=(0,Z.useState)(()=>pr.some(e=>e.model===c.model)?``:c.model||``),[h,g]=(0,Z.useState)(()=>c.task||`instruct`),[_,v]=(0,Z.useState)(()=>c.prompt||``),[y,b]=(0,Z.useState)(!1),[x,S]=(0,Z.useState)(null),[C,w]=(0,Z.useState)(!1),[T,E]=(0,Z.useState)(!1),[D,ee]=(0,Z.useState)(!1),O=pr.find(e=>`${e.provider}::${e.model}`===d),k=d===mr,A=k?{provider:s.provider,model:p.trim(),task:h,label:`Custom`,note:``}:O||s,j=A.task===`instruct`,M=(0,Z.useCallback)(()=>xn(e).then(e=>S(e.summarize||null)).catch(()=>{}),[e]);(0,Z.useEffect)(()=>{M();let e=window.setInterval(()=>{S(e=>(e?.running&&M(),e))},2e3);return()=>window.clearInterval(e)},[M]);let te=async()=>{await r({...a,summarize:{...a.summarize||{},image:{enabled:l,provider:A.provider,model:A.model,task:A.task,...j&&_.trim()?{prompt:_.trim()}:{}}}}),b(!1)},N=async()=>{E(!0);try{S(await Sn(e)),i({title:`Stopping image summaries`,description:`The image being captioned finishes first; the rest are left for a later run.`})}catch(e){i({title:`Could not stop`,description:e instanceof Error?e.message:`Failed to stop`,variant:`destructive`})}finally{E(!1)}},ne=async(t=!1)=>{w(!0);try{let n=await yn(e,{force:t});S(n),i({title:t?`Rebuilding image summaries`:`Backfilling image summaries`,description:`${n.total.toLocaleString()} candidate image(s). First run downloads model weights.`})}catch(e){i({title:`Summaries failed to start`,description:e instanceof Error?e.message:`Failed to start`,variant:`destructive`})}finally{w(!1)}},P=o.summarize?.image?.enabled===!0,F=x?.running===!0,I=x?.errors?.[0]?.error;return(0,Q.jsx)(lr,{title:`Summaries`,hint:`Generate descriptions for indexed images (metadata.summary); searchable via full-text and dense search`,children:(0,Q.jsxs)(`div`,{className:`space-y-3`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,Q.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:l,disabled:n,onChange:e=>{u(e.target.checked),b(!0)}}),`Images`]}),(0,Q.jsxs)(`select`,{className:cr,value:d,disabled:n||!l,onChange:e=>{f(e.target.value),b(!0)},children:[pr.map(e=>(0,Q.jsx)(`option`,{value:`${e.provider}::${e.model}`,children:e.label},`${e.provider}::${e.model}`)),(0,Q.jsx)(`option`,{value:mr,children:`Custom model…`})]}),!k&&(0,Q.jsx)(`span`,{className:`font-mono text-xs text-muted-foreground`,children:A.model})]}),k&&(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,Q.jsx)(W,{className:`h-8 w-72 font-mono text-xs`,placeholder:`org/model-id (Transformers.js / ONNX)`,value:p,disabled:n||!l,onChange:e=>{m(e.target.value),b(!0)}}),(0,Q.jsx)(`select`,{className:cr,value:h,disabled:n||!l,onChange:e=>{g(e.target.value),b(!0)},children:hr.map(e=>(0,Q.jsx)(`option`,{value:e.value,children:e.label},e.value))})]}),l&&A.note&&(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:A.note}),l&&j&&(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsx)(`textarea`,{className:`w-full rounded-md border border-input bg-transparent p-2 text-xs outline-none focus-visible:ring-1 focus-visible:ring-ring`,rows:3,placeholder:gr,value:_,disabled:n,onChange:e=>{v(e.target.value),b(!0)}}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`What to ask of every image. Empty uses the default shown above. Captions are indexed, so ask for the words you would search by.`})]}),x&&(x.running||x.total>0||x.finishedAt)&&(0,Q.jsxs)(`p`,{className:ye(`text-xs`,x.aborted?`text-destructive`:`text-muted-foreground`),children:[F?`Running`:x.aborted?`Stopped`:x.cancelled?`Cancelled`:`Last run`,`: `,x.described,`/`,x.total,` described`,x.skipped?`, ${x.skipped} skipped`:``,x.failed?`, ${x.failed} failed`:``,x.aborted?`. ${x.abortedReason||`model worker died`}. Remaining images were not attempted; generate again to retry.`:x.cancelled?`. Stopped by you. The remaining images were left untouched; generate again to continue.`:I?`. ${I}`:``]}),(0,Q.jsxs)(`label`,{className:`flex items-start gap-2 text-xs`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,className:`mt-0.5`,checked:D,disabled:n||F,onChange:e=>ee(e.target.checked)}),(0,Q.jsxs)(`span`,{children:[`Re-caption images that already have a summary`,D&&(0,Q.jsxs)(`span`,{className:`mt-1 block text-[11px] text-warning`,children:[`Replaces every existing `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`metadata.summary`}),`, including ones written by a different model. Leave this off to fill in only what is missing.`]})]})]}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,Q.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Enable + save, then run. Only the local caption provider is wired today. Candidates come from `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`data/mime/image`}),`.`]}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,Q.jsx)(U,{size:`sm`,variant:`outline`,onClick:()=>void te(),disabled:n||!y,children:`Save summaries`}),(0,Q.jsx)(U,{size:`sm`,onClick:()=>void ne(D),disabled:n||y||C||F||!P,title:P?y?`Save config first`:D?`Re-caption every image, replacing existing summaries`:`Caption images that have no summary yet`:`Enable image summaries and save first`,children:F||C?`Running…`:D?`Rebuild`:`Backfill`}),F&&(0,Q.jsx)(U,{size:`sm`,variant:`destructive`,onClick:()=>void N(),disabled:T,title:`Finish the current image, then stop. The rest are left for later.`,children:T?`Stopping…`:`Stop`})]})]})]})})}function vr({workspaceId:e,workspaceName:t}){let{showToast:n}=q(),[r,i]=(0,Z.useState)(null),[a,o]=(0,Z.useState)([]),[s,c]=(0,Z.useState)(),[l,u]=(0,Z.useState)(!0),[d,f]=(0,Z.useState)(!1),[p,m]=(0,Z.useState)(null),[h,g]=(0,Z.useState)([]),[_,v]=(0,Z.useState)(0),y=(0,Z.useCallback)(()=>gn(e).then(e=>{i(e),m(null)}).catch(e=>m(e instanceof Error?e.message:`Failed to load the embedding config`)).finally(()=>u(!1)),[e]),b=(0,Z.useCallback)(()=>Cn(e).then(e=>{o(e.tables||[]),c(e.error)}).catch(e=>c(e instanceof Error?e.message:`Failed to list vector tables`)),[e]);(0,Z.useEffect)(()=>{y(),b()},[y,b]);let x=()=>{u(!0),y(),b(),v(e=>e+1)},S=async t=>{f(!0);try{let r=await vn(e,t);g(r.movedSpaces||[]),r.movedSpaces?.length?n({title:`Saved; fill to finish`,description:`${r.movedSpaces.join(`, `)} now targets a new model. The switch is live, but the new table is empty until you fill it.`}):r.applied===!1?n({title:`Saved; applies on next start`,description:`The workspace is not running, so the new spaces take effect when it starts.`}):n({title:`Saved`,description:`Embedding config updated.`}),await y(),await b()}catch(e){n({title:`Save failed`,description:e instanceof Error?e.message:`Failed to save`,variant:`destructive`})}finally{f(!1)}},C=async t=>{try{let r=await _n(e,{space:t});if(r.ingestDisabled){n({title:`Ingest disabled`,description:`CANVAS_INFERD_INGEST_DISABLED=true: nothing was enqueued. Existing vectors still serve search.`,variant:`destructive`});return}n({title:`Filling ${t}`,description:`${r.enqueued.toLocaleString()} document(s) queued. The status line reports progress.`}),g(e=>e.filter(e=>e!==t)),v(e=>e+1),b()}catch(e){n({title:`Fill failed`,description:e instanceof Error?e.message:`Failed to enqueue`,variant:`destructive`})}};if(l)return(0,Q.jsxs)(`div`,{className:`flex items-center gap-2 text-sm text-muted-foreground`,children:[(0,Q.jsx)(w,{className:`h-4 w-4 animate-spin`}),`Loading the embedding config…`]});if(p||!r)return(0,Q.jsxs)(`div`,{className:`space-y-3`,children:[(0,Q.jsxs)(`div`,{className:`rounded-lg border border-destructive/40 bg-destructive-subtle p-4 text-sm`,children:[(0,Q.jsx)(`p`,{className:`font-medium text-destructive`,children:`Embedding config unavailable`}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:p||`No config returned.`})]}),(0,Q.jsx)(U,{type:`button`,size:`sm`,variant:`outline`,onClick:x,children:`Try again`})]});let T=Object.keys(r.effective.spaces||{}).sort();return(0,Q.jsxs)(`div`,{className:`space-y-5`,children:[(0,Q.jsx)(ur,{workspaceId:e,workspaceName:t,refreshKey:_,onRefresh:x}),(0,Q.jsx)(wn,{value:r.workspace||{},effective:r.effective||{},inherited:r.inherited||{},resolvedSpaces:r.spaces,invalid:r.invalid,attention:h,onFill:C,saving:d,saveLabel:`Save embedding config`,onSave:S},JSON.stringify(r.workspace)),(0,Q.jsxs)(`div`,{className:`space-y-3`,children:[(0,Q.jsx)(_r,{workspaceId:e,config:r,saving:d,onSave:S},`summarize:${JSON.stringify(r.workspace?.summarize||{})}`),(0,Q.jsx)(fr,{workspaceId:e,workspaceName:t,spaces:T,onDone:()=>{b(),v(e=>e+1)}}),(0,Q.jsx)(dr,{workspaceId:e,tables:a,error:s,onChanged:()=>{b()}})]}),(0,Q.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Stored in this workspace's own `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`workspace.json`}),`, so it travels with the workspace. Settings resolve in layers: built-in, then server, then your defaults, then this workspace, which wins. Changes apply live, with no restart.`]})]})}function yr(e){let n=(e||``).split(`.`).pop()?.toLowerCase();switch(n){case`js`:case`mjs`:case`ts`:return[a({typescript:n===`ts`})];case`json`:return[i()];case`sh`:case`bash`:return[t.define(o)];default:return[]}}function br({value:e,onChange:t,path:r,className:i=``}){let a=(0,Z.useMemo)(()=>yr(r),[r]),{resolvedScheme:o}=rn();return(0,Q.jsx)(n,{value:e,onChange:t,extensions:a,theme:o===`dark`?`dark`:`light`,className:i,basicSetup:{lineNumbers:!0,foldGutter:!0,highlightActiveLine:!0,bracketMatching:!0,autocompletion:!1}})}var xr=[`pending`,`approved`,`declined`,`failed`,`expired`,`all`],Sr={pending:`text-warning dark:text-warning`,approved:`text-success dark:text-success`,declined:`text-muted-foreground`,failed:`text-destructive`,expired:`text-muted-foreground line-through`};function Cr(e){let t=Date.now()-new Date(e).getTime(),n=Math.floor(t/6e4);if(n<1)return`now`;if(n<60)return`${n}m`;let r=Math.floor(n/60);return r<24?`${r}h`:`${Math.floor(r/24)}d`}function wr(e,t){return t.split(`.`).reduce((e,t)=>typeof e==`object`&&e?e[t]:void 0,e)}function Tr(e){let t=e.split(`.`);return t.slice(Math.max(t.length-2,2)).join(`.`)}var Er=/(body|content|message|text|prompt)$/i;function Dr({workspaceId:e,id:t,children:n}){let{open:r}=Dt(),{showToast:i}=q(),[a,o]=(0,Z.useState)(!1),s=async n=>{n.stopPropagation(),o(!0);try{r(await $e(e,t),e)}catch(e){i({title:`Document ${t} not available`,description:e instanceof Error?e.message:void 0,variant:`destructive`})}finally{o(!1)}};return(0,Q.jsx)(`button`,{type:`button`,disabled:a,onClick:e=>void s(e),className:`text-primary underline-offset-2 hover:underline disabled:opacity-60`,title:`Open document ${t}`,children:n??`doc ${t}`})}function Or({workspaceId:e,text:t}){let n=t.split(/(\bdoc \d+\b)/);return(0,Q.jsx)(Q.Fragment,{children:n.map((t,n)=>{let r=/^doc (\d+)$/.exec(t);return r?(0,Q.jsx)(Dr,{workspaceId:e,id:Number(r[1])},n):(0,Q.jsx)(Z.Fragment,{children:t},n)})})}function kr({action:e,amend:t,onAmend:n}){return e.editable?.length?(0,Q.jsx)(`div`,{className:`space-y-2`,children:e.editable.map(r=>{let i=wr(e,r),a=typeof i==`string`,o=r in t?t[r]:i,s=a?String(o??``):JSON.stringify(o,null,2),c=a&&(Er.test(r)||s.length>80);return(0,Q.jsxs)(`label`,{className:`block text-xs`,children:[(0,Q.jsxs)(`span`,{className:`font-medium text-muted-foreground`,children:[Tr(r),(0,Q.jsx)(`span`,{className:`ml-1.5 font-mono text-[10px] opacity-60`,children:r}),r in t&&(0,Q.jsx)(`span`,{className:`ml-1.5 text-warning dark:text-warning`,children:`edited`})]}),c?(0,Q.jsx)(`textarea`,{className:`mt-1 w-full rounded-md border bg-background px-2 py-1.5 font-mono text-xs outline-none focus:ring-2 focus:ring-ring min-h-[100px]`,value:s,onChange:e=>n(r,e.target.value)}):(0,Q.jsx)(`input`,{className:`mt-1 w-full h-8 rounded-md border bg-background px-2 font-mono text-xs outline-none focus:ring-2 focus:ring-ring`,value:s,onChange:e=>{if(a){n(r,e.target.value);return}try{n(r,JSON.parse(e.target.value))}catch{n(r,e.target.value)}}})]},r)})}):null}function Ar({workspaceId:e,onPendingCount:t}){let{showToast:n}=q(),r=Tn(),[i,a]=(0,Z.useState)(`pending`),[o,s]=(0,Z.useState)([]),[c,l]=(0,Z.useState)(!0),[u,d]=(0,Z.useState)(new Set),[p,m]=(0,Z.useState)(null),[h,g]=(0,Z.useState)({}),[_,y]=(0,Z.useState)(!1),b=(0,Z.useCallback)(()=>Pt(e,{status:i===`all`?void 0:i,limit:200}).then(e=>{s(e),d(t=>new Set(e.filter(e=>e.status===`pending`&&t.has(e.actionId)).map(e=>e.actionId))),t?.(i===`pending`?e.length:e.filter(e=>e.status===`pending`).length)}).catch(()=>{n({title:`Error`,description:`Failed to load pending actions`,variant:`destructive`})}).finally(()=>l(!1)),[e,i]),S=(0,Z.useCallback)(async()=>{l(!0),await b()},[b]),[C,T]=(0,Z.useState)(`${e}|${i}`),E=`${e}|${i}`;E!==C&&(T(E),l(!0)),(0,Z.useEffect)(()=>{b()},[b]),En(r,`workspace:${e}`,(0,Z.useMemo)(()=>{let e=()=>{S()};return{"action.proposed":e,"action.approved":e,"action.declined":e,"action.failed":e}},[S]));let D=o.filter(e=>e.status===`pending`),ee=D.length>0&&D.every(e=>u.has(e.actionId)),O=()=>{d(ee?new Set:new Set(D.map(e=>e.actionId)))},A=e=>{d(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},j=(e,t,n)=>{g(r=>({...r,[e]:{...r[e],[t]:n}}))},M=async(t,r)=>{if(!(!t.length&&!r.length)){y(!0);try{let i=await Vt(e,{approve:t.map(e=>{let t=h[e];return t&&Object.keys(t).length?{actionId:e,amend:t}:e}),decline:r}),a=i.results.filter(e=>e.status===`error`||e.status===`failed`);a.length?n({title:`${a.length} of ${i.decided} failed`,description:a.map(e=>`${e.actionId}: ${e.error??`execution failed`}`).join(`; `),variant:`destructive`}):n({title:t.length&&r.length?`Decisions applied`:t.length?`Approved ${t.length}`:`Declined ${r.length}`,description:t.length?`Approved actions executed with their original provenance.`:void 0}),g(e=>{let n={...e};for(let e of[...t,...r])delete n[e];return n}),d(new Set),p&&[...t,...r].includes(p)&&m(null),await S()}catch(e){n({title:`Decision failed`,description:e instanceof Error?e.message:`Unknown error`,variant:`destructive`})}finally{y(!1)}}};return(0,Q.jsxs)(`div`,{className:`space-y-3`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center justify-between gap-2 flex-wrap`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(`select`,{className:`h-8 rounded-md border bg-background px-2 text-xs outline-none focus:ring-2 focus:ring-ring`,value:i,onChange:e=>a(e.target.value),children:xr.map(e=>(0,Q.jsx)(`option`,{value:e,children:e},e))}),(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,onClick:()=>void S(),title:`Reload`,children:(0,Q.jsx)(w,{className:`h-4 w-4`})})]}),u.size>0&&(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[u.size,` selected`]}),(0,Q.jsxs)(U,{size:`sm`,disabled:_,onClick:()=>void M([...u],[]),children:[(0,Q.jsx)(k,{className:`mr-1 h-3.5 w-3.5`}),` Approve`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`outline`,disabled:_,onClick:()=>void M([],[...u]),children:[(0,Q.jsx)(x,{className:`mr-1 h-3.5 w-3.5`}),` Decline`]})]})]}),(0,Q.jsx)(`div`,{className:`border rounded-lg overflow-auto max-h-[560px]`,children:c?(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground p-3`,children:`Loading...`}):o.length===0?(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground p-3`,children:i===`pending`?`Nothing waiting for review. Rules or hooks with approval enabled queue their actions here.`:`No ${i===`all`?``:i+` `}actions recorded.`}):(0,Q.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,Q.jsx)(`thead`,{className:`text-xs text-muted-foreground border-b sticky top-0 bg-background`,children:(0,Q.jsxs)(`tr`,{children:[(0,Q.jsx)(`th`,{className:`px-3 py-2 w-8`,children:D.length>0&&(0,Q.jsx)(`input`,{type:`checkbox`,checked:ee,onChange:O,title:`Select all pending`})}),(0,Q.jsx)(`th`,{className:`text-left font-medium px-3 py-2`,children:`Proposed action`}),(0,Q.jsx)(`th`,{className:`text-left font-medium px-3 py-2`,children:`Handler`}),(0,Q.jsx)(`th`,{className:`text-left font-medium px-3 py-2`,children:`Age`}),(0,Q.jsx)(`th`,{className:`text-left font-medium px-3 py-2`,children:`Status`}),(0,Q.jsx)(`th`,{className:`px-2 py-2 w-40`})]})}),(0,Q.jsx)(`tbody`,{children:o.map(t=>{let n=t.status===`pending`||t.status===`failed`,r=p===t.actionId,i=t.envelope?.payload?.document;return(0,Q.jsxs)(Z.Fragment,{children:[(0,Q.jsxs)(`tr`,{className:`border-b last:border-0 align-top hover:bg-muted/40 cursor-pointer`,onClick:()=>m(r?null:t.actionId),children:[(0,Q.jsx)(`td`,{className:`px-3 py-2`,onClick:e=>e.stopPropagation(),children:t.status===`pending`&&(0,Q.jsx)(`input`,{type:`checkbox`,checked:u.has(t.actionId),onChange:()=>A(t.actionId)})}),(0,Q.jsxs)(`td`,{className:`px-3 py-2`,children:[(0,Q.jsxs)(`span`,{className:`flex items-center gap-1 font-medium`,children:[r?(0,Q.jsx)(v,{className:`h-3 w-3 shrink-0`}):(0,Q.jsx)(f,{className:`h-3 w-3 shrink-0`}),t.title,t.editable?.length>0&&(0,Q.jsx)(`span`,{className:`text-[10px] uppercase tracking-wide rounded bg-muted-foreground/15 px-1 py-0.5 text-muted-foreground shrink-0`,children:`editable`})]}),t.summary&&(0,Q.jsx)(`span`,{className:`block text-xs text-muted-foreground pl-4`,children:(0,Q.jsx)(Or,{workspaceId:e,text:t.summary})})]}),(0,Q.jsxs)(`td`,{className:`px-3 py-2 font-mono text-xs whitespace-nowrap`,children:[(0,Q.jsxs)(`span`,{className:`text-muted-foreground`,children:[t.handlerType,`:`]}),t.handler,t.event&&(0,Q.jsxs)(`span`,{className:`block text-muted-foreground`,children:[t.event,i?.id!=null&&(0,Q.jsxs)(Q.Fragment,{children:[` · `,(0,Q.jsx)(Dr,{workspaceId:e,id:i.id})]})]})]}),(0,Q.jsx)(`td`,{className:`px-3 py-2 text-xs whitespace-nowrap`,title:t.ts,children:Cr(t.ts)}),(0,Q.jsxs)(`td`,{className:`px-3 py-2 text-xs`,children:[(0,Q.jsx)(`span`,{className:Sr[t.status],children:t.status}),t.amended&&(0,Q.jsx)(`span`,{className:`block text-muted-foreground`,children:`amended`}),t.decidedBy&&(0,Q.jsx)(`span`,{className:`block text-muted-foreground truncate max-w-[120px]`,title:t.decidedBy,children:t.decidedBy})]}),(0,Q.jsx)(`td`,{className:`px-2 py-2 whitespace-nowrap text-right`,onClick:e=>e.stopPropagation(),children:n&&(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(U,{size:`sm`,className:`h-7 px-2`,disabled:_,title:t.status===`failed`?`Retry (re-approve)`:`Approve and execute`,onClick:()=>void M([t.actionId],[]),children:(0,Q.jsx)(k,{className:`h-3.5 w-3.5`})}),t.status===`pending`&&(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,className:`h-7 px-2 text-muted-foreground`,disabled:_,title:`Decline`,onClick:()=>void M([],[t.actionId]),children:(0,Q.jsx)(x,{className:`h-3.5 w-3.5`})})]})})]}),r&&(0,Q.jsxs)(`tr`,{className:`border-b last:border-0 bg-muted/20`,children:[(0,Q.jsx)(`td`,{className:`px-3 py-3`}),(0,Q.jsxs)(`td`,{className:`px-3 py-3 space-y-3`,colSpan:5,children:[n&&t.editable?.length>0&&(0,Q.jsx)(kr,{action:t,amend:h[t.actionId]??{},onAmend:(e,n)=>j(t.actionId,e,n)}),(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`span`,{className:`text-xs font-medium text-muted-foreground`,children:`Actions on approve`}),(0,Q.jsx)(`pre`,{className:`mt-1 rounded border bg-background p-2 font-mono text-xs overflow-auto max-h-56`,children:JSON.stringify(t.actions,null,2)})]}),t.result&&t.result.length>0&&(0,Q.jsx)(`div`,{className:`text-xs font-mono`,children:t.result.map((e,t)=>(0,Q.jsxs)(`span`,{className:`mr-2 ${e.status===`error`?`text-destructive`:`text-muted-foreground`}`,children:[e.action,`:`,e.status,e.error?` (${e.error})`:``]},t))}),(0,Q.jsxs)(`p`,{className:`text-[11px] text-muted-foreground font-mono`,children:[t.actionId,` · origin `,t.provenance?.origin,t.provenance?.causedBy?` · causedBy ${t.provenance.causedBy}`:``,t.expiresAt?` · expires ${new Date(t.expiresAt).toLocaleString()}`:``]})]})]})]},t.actionId)})})]})})]})}var jr=`#!/usr/bin/env bash
2
+ # Called from a hook via: spawn('bash', [script, ...args])
3
+ set -euo pipefail
4
+ `;function Mr({workspaceId:e,prefillRule:t,onPrefillConsumed:n,initialSection:r,initialRunsHandler:i,initialRunId:a}){let{showToast:o}=q(),[s,c]=(0,Z.useState)(r??`rules`),[l,u]=(0,Z.useState)(t);t!==l&&(u(t),t&&c(`rules`));let[d,f]=(0,Z.useState)(!1),[h,g]=(0,Z.useState)([]),[_,y]=(0,Z.useState)(!0),[b,E]=(0,Z.useState)(null),[ee,k]=(0,Z.useState)(``),[j,M]=(0,Z.useState)(!1),[te,P]=(0,Z.useState)(!1),[F,I]=(0,Z.useState)(!1),[L,R]=(0,Z.useState)(null),[z,oe]=(0,Z.useState)(`document.inserted`),[se,de]=(0,Z.useState)(``),[B,fe]=(0,Z.useState)([]),[pe,me]=(0,Z.useState)(!1),[ge,_e]=(0,Z.useState)([]),[ve,V]=(0,Z.useState)(!1),[H,ye]=(0,Z.useState)(i||null),[G,be]=(0,Z.useState)(a?{runId:a,run:null}:null),K=e=>{be(t=>t?.runId===e?null:{runId:e,run:null})};(0,Z.useEffect)(()=>{if(!G||G.run||G.error)return;let t=!1;return Ot(e,G.runId).then(e=>{t||be(t=>t?.runId===e.runId?{runId:e.runId,run:e}:t)}).catch(e=>{t||be(t=>t?.runId===G.runId?{...t,error:e instanceof Error?e.message:`Failed to load run`}:t)}),()=>{t=!0}},[e,G]);let[Se,Ce]=(0,Z.useState)(null),[we,Te]=(0,Z.useState)(!1),[Ee,De]=(0,Z.useState)(0),[Oe,ke]=(0,Z.useState)(null),[Ae,J]=(0,Z.useState)(()=>jt()),[je,Me]=(0,Z.useState)(()=>String(jt())),Ne=()=>{let e=Nt(en(je));J(e),Me(String(e))};(0,Z.useEffect)(()=>{let t=!1;return Pt(e,{status:`pending`,limit:500}).then(e=>{t||De(e.length)}).catch(()=>{}),()=>{t=!0}},[e]),vt(()=>Te(!1),we);let Pe=async t=>{if(confirm(`Replay ${t.handlerType} "${t.handler}" for ${t.event} (docs ${(t.docIds||[]).join(`, `)})?`)){Ce(t.runId);try{let n=await Bt(e,t.runId);o({title:`Replayed`,description:`${t.handler} → ${n.status}`}),await Y()}catch(e){o({title:`Replay failed`,description:e instanceof Error?e.message:`Unknown error`,variant:`destructive`})}finally{Ce(null)}}},Fe=s===`scripts`?{list:Xt,get:Zt,save:Jt,del:qt}:{list:Mt,get:At,save:Wt,del:$t},Ie=s===`hooks`||s===`scripts`,Y=async()=>{if(s!==`rules`&&s!==`pending`){if(s===`runs`){try{y(!0),_e(await Ft(e,{limit:100,failed:ve||void 0,handler:H||void 0}))}catch{o({title:`Error`,description:`Failed to load hook runs`,variant:`destructive`})}finally{y(!1)}return}try{y(!0),g(await Fe.list(e))}catch{o({title:`Error`,description:`Failed to load ${s}`,variant:`destructive`})}finally{y(!1)}}};(0,Z.useEffect)(()=>{Y()},[e,s,ve,H]);let Le=e=>{e!==s&&(j&&!confirm(`Discard unsaved changes?`)||(c(e),E(null),k(``),M(!1),I(!1)))},Re=async t=>{if(!(j&&!confirm(`Discard unsaved changes?`)))try{E(t),k(await Fe.get(e,t)),M(!1)}catch{o({title:`Error`,description:`Failed to open ${t}`,variant:`destructive`})}},ze=async()=>{if(b)try{P(!0),await Fe.save(e,b,ee),M(!1),o({title:`Saved`,description:`${b} saved`}),await Y()}catch{o({title:`Error`,description:`Failed to save ${b}`,variant:`destructive`})}finally{P(!1)}},Be=async t=>{let n=zt(t);try{let r=await Ut(e,t,!n);b===t&&E(r),await Y(),o({title:n?`Disabled`:`Enabled`,description:r})}catch{o({title:`Error`,description:`Failed to toggle ${t}`,variant:`destructive`})}},Ve=async t=>{let n=It(t);if(!n||!zt(t)&&!confirm(`${t} is disabled. Run it anyway?`))return;let r=n.startsWith(`document.`);if(!(r&&!confirm(`Run ${t} against up to ${Ae} existing documents (event ${n})?`))){ke(t);try{if(r){let r=await Gt(e,{hookFile:t,event:n,limit:Ae});o({title:`Run finished`,description:`${r.processed} documents processed, ${r.failed} failed. Details in the Runs tab`,...r.failed?{variant:`destructive`}:{}})}else{let r=await kt(e,{hookFile:t,event:n});o({title:r.status===`error`?`Run failed`:`Run finished`,description:r.status===`error`?`${t}: ${r.error||`see the Runs tab`}`:`${t} → ${r.status}${r.durationMs==null?``:` in ${r.durationMs}ms`}`,...r.status===`error`?{variant:`destructive`}:{}})}}catch(e){o({title:`Run failed`,description:e instanceof Error?e.message:`Unknown error`,variant:`destructive`})}finally{ke(null)}}},He=async t=>{if(confirm(`Delete ${s===`hooks`?`hook`:`script`} ${t}?`))try{await Fe.del(e,t),b===t&&(E(null),k(``),M(!1)),await Y(),o({title:`Deleted`,description:`${t} deleted`})}catch{o({title:`Error`,description:`Failed to delete ${t}`,variant:`destructive`})}},X=async()=>{if(!L)try{R(await Rt(e))}catch{o({title:`Error`,description:`Failed to load hook metadata`,variant:`destructive`})}},Ue=async()=>{I(!F),s===`hooks`&&await X()},We=async()=>{f(!d),await X()},Ge=async()=>{let t=se.trim().replace(/^\/+/,``);if(!t){o({title:`Error`,description:`Give the script a name (e.g. on-image.sh)`,variant:`destructive`});return}try{me(!0),await Jt(e,t,jr),I(!1),de(``),await Y(),await Re(t)}catch{o({title:`Error`,description:`Failed to create script`,variant:`destructive`})}finally{me(!1)}},Ke=e=>{fe(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},qe=async()=>{if(!se.trim()){o({title:`Error`,description:`Give the hook a name`,variant:`destructive`});return}try{me(!0);let{path:t}=await Ht(e,{event:z,name:se.trim(),actions:B});I(!1),de(``),fe([]),await Y(),await Re(t),o({title:`Created`,description:`${t}. Edit the TODOs, then enable it`})}catch{o({title:`Error`,description:`Failed to create hook`,variant:`destructive`})}finally{me(!1)}},Je=s===`hooks`?Yt(h):{scripts:h},Ye=`${xe}/workspaces/${e}/git/internal`;return(0,Q.jsxs)(`div`,{className:`@container space-y-4`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-col gap-3 @3xl:flex-row @3xl:items-start @3xl:justify-between`,children:[(0,Q.jsxs)(`div`,{className:`min-w-0 @3xl:min-w-[18rem] @3xl:flex-1`,children:[(0,Q.jsx)(`h3`,{className:`text-lg font-medium`,children:`Automation`}),(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Rules are simple click-to-build automations (Outlook-style). Hooks are their programmable big brother; scripts are the shell helpers hooks spawn. Everything commits to the workspace git repo.`})]}),(0,Q.jsx)(`div`,{className:`flex min-w-0 flex-col gap-2 @3xl:max-w-[60%] @3xl:flex-row @3xl:items-center`,children:(0,Q.jsx)(`div`,{className:`w-full min-w-0 @3xl:w-auto`,children:(0,Q.jsx)(wt,{className:`border-b-0`,active:s,onChange:Le,tabs:[{id:`pending`,label:Ee>0?`Pending (${Ee})`:`Pending`,icon:ue},{id:`rules`,label:`Rules`,icon:he},{id:`hooks`,label:`Hooks`,icon:O},{id:`scripts`,label:`Scripts`,icon:N},{id:`runs`,label:`Runs`,icon:T}]})})})]}),(s===`runs`||Ie)&&(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center justify-end gap-2`,children:[s===`runs`&&(0,Q.jsxs)(Q.Fragment,{children:[H&&(0,Q.jsxs)(`button`,{type:`button`,className:`inline-flex items-center gap-1 rounded-full border bg-muted/50 px-2.5 py-1 font-mono text-xs hover:bg-muted`,title:`Showing runs of this rule / hook only — click to clear`,onClick:()=>ye(null),children:[H,` `,(0,Q.jsx)(x,{className:`h-3 w-3`})]}),(0,Q.jsxs)(`label`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground cursor-pointer`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:ve,onChange:e=>V(e.target.checked)}),`failed only`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`ghost`,onClick:Y,title:`Reload`,children:[(0,Q.jsx)(w,{className:`mr-1.5 h-4 w-4`}),` Reload`]})]}),Ie&&(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(U,{size:`sm`,variant:`ghost`,onClick:Y,title:`Reload`,children:[(0,Q.jsx)(w,{className:`mr-1.5 h-4 w-4`}),` Reload`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`ghost`,onClick:We,title:`Hook context API & classifier reference`,children:[(0,Q.jsx)(ae,{className:`mr-1.5 h-4 w-4`}),` Reference`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`outline`,onClick:Ue,children:[(0,Q.jsx)(C,{className:`mr-2 h-4 w-4`}),` `,s===`hooks`?`New Hook`:`New Script`]})]})]}),s===`pending`&&(0,Q.jsx)(Ar,{workspaceId:e,onPendingCount:De}),s===`rules`&&(0,Q.jsx)(Kt,{workspaceId:e,backfillLimit:Ae,onShowRuns:e=>{ye(e),Le(`runs`)},prefill:t,onPrefillConsumed:n,onOpenJson:async()=>{try{await At(e,`rules.json`)}catch{await Wt(e,`rules.json`,JSON.stringify({$schema:`canvas.hook-rules/v1`,rules:[]},null,2)+`
5
+ `).catch(()=>{})}c(`hooks`),Re(`rules.json`)}}),s===`runs`&&(0,Q.jsx)(`div`,{className:`border rounded-lg overflow-auto max-h-[560px]`,children:_?(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground p-3`,children:`Loading...`}):ge.length===0?(0,Q.jsxs)(`p`,{className:`text-sm text-muted-foreground p-3`,children:[`No runs recorded yet`,ve?` (failed only)`:``,`. Runs appear here every time a rule or hook executes.`]}):(0,Q.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,Q.jsx)(`thead`,{className:`text-xs text-muted-foreground border-b sticky top-0 bg-background`,children:(0,Q.jsxs)(`tr`,{children:[(0,Q.jsx)(`th`,{className:`text-left font-medium px-3 py-2`,children:`Time`}),(0,Q.jsx)(`th`,{className:`text-left font-medium px-3 py-2`,children:`Handler`}),(0,Q.jsx)(`th`,{className:`text-left font-medium px-3 py-2`,children:`Event`}),(0,Q.jsx)(`th`,{className:`text-left font-medium px-3 py-2`,children:`Docs`}),(0,Q.jsx)(`th`,{className:`text-right font-medium px-3 py-2`,children:`ms`}),(0,Q.jsx)(`th`,{className:`text-left font-medium px-3 py-2`,children:`Status`}),(0,Q.jsx)(`th`,{className:`px-2 py-2`})]})}),(0,Q.jsx)(`tbody`,{children:ge.map(e=>(0,Q.jsxs)(Z.Fragment,{children:[(0,Q.jsxs)(`tr`,{className:`border-b last:border-0 align-top hover:bg-muted/40`,children:[(0,Q.jsx)(`td`,{className:`px-3 py-1.5 whitespace-nowrap text-xs text-muted-foreground`,title:e.ts,children:new Date(e.ts).toLocaleString()}),(0,Q.jsxs)(`td`,{className:`px-3 py-1.5 font-mono text-xs`,children:[(0,Q.jsxs)(`span`,{className:`text-muted-foreground`,children:[e.handlerType,`:`]}),e.handler,e.origin!==`user`&&(0,Q.jsxs)(`span`,{className:`ml-1.5 text-[10px] uppercase tracking-wide rounded bg-muted-foreground/15 px-1 py-0.5 text-muted-foreground`,children:[e.origin,e.depth?` d${e.depth}`:``]})]}),(0,Q.jsxs)(`td`,{className:`px-3 py-1.5 font-mono text-xs`,children:[e.event,e.batch?` (batch)`:``]}),(0,Q.jsx)(`td`,{className:`px-3 py-1.5 font-mono text-xs`,children:(e.docIds||[]).join(`, `)}),(0,Q.jsx)(`td`,{className:`px-3 py-1.5 text-right font-mono text-xs`,children:e.durationMs}),(0,Q.jsxs)(`td`,{className:`px-3 py-1.5 text-xs`,children:[(0,Q.jsx)(`span`,{className:e.status===`error`?`text-destructive font-medium`:e.status===`skipped`?`text-muted-foreground`:`text-success dark:text-success`,children:e.status}),e.error&&(0,Q.jsx)(`span`,{className:`block text-destructive/80 font-mono break-all`,children:e.error}),e.skipReason&&(0,Q.jsx)(`span`,{className:`block text-muted-foreground`,children:e.skipReason}),e.actions&&e.actions.length>0&&(0,Q.jsx)(`span`,{className:`block text-muted-foreground font-mono`,children:e.actions.map(e=>`${e.action}:${e.status}`).join(` `)}),e.actions?.filter(e=>e.error).map((e,t)=>(0,Q.jsxs)(`span`,{className:`block break-all font-mono ${e.status===`error`?`text-destructive/80`:`text-muted-foreground`}`,children:[e.action,`: `,e.error]},t))]}),(0,Q.jsxs)(`td`,{className:`px-2 py-1.5 whitespace-nowrap`,children:[(0,Q.jsxs)(U,{size:`sm`,variant:`ghost`,className:`h-6 px-1.5 touch-target`,title:e.traceLines?`Execution log (${e.traceLines} lines)`:`Execution log`,onClick:()=>K(e.runId),children:[(0,Q.jsx)(re,{className:`h-3.5 w-3.5`}),(0,Q.jsx)(v,{className:`ml-0.5 h-3 w-3 transition-transform ${G?.runId===e.runId?`rotate-180`:``}`})]}),e.handlerType!==`dispatch`&&(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,className:`h-6 w-6 p-0 touch-target`,title:`Replay this run (reloads the document, re-runs the handler)`,disabled:Se!==null,onClick:()=>Pe(e),children:(0,Q.jsx)(D,{className:`h-3 w-3 ${Se===e.runId?`animate-spin`:``}`})})]})]}),G?.runId===e.runId&&(0,Q.jsx)(`tr`,{className:`border-b last:border-0 bg-muted/30`,children:(0,Q.jsx)(`td`,{colSpan:7,className:`px-3 py-2`,children:(0,Q.jsx)(Nr,{state:G})})})]},e.runId))})]})}),Ie&&d&&L&&(0,Q.jsxs)(`div`,{className:`border rounded-lg p-4 space-y-4 bg-muted/30 text-sm`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsxs)(`h4`,{className:`font-medium mb-2`,children:[`Hook context (`,(0,Q.jsx)(`span`,{className:`font-mono`,children:`ctx`}),`)`]}),(0,Q.jsx)(`div`,{className:`space-y-2`,children:L.contextApi?.map(e=>(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`code`,{className:`rounded bg-muted px-1 py-0.5 font-mono text-xs`,children:e.signature}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground mt-0.5`,children:e.description})]},e.name))})]}),(0,Q.jsxs)(`div`,{children:[(0,Q.jsxs)(`h4`,{className:`font-medium mb-2`,children:[`Classifier (`,(0,Q.jsx)(`span`,{className:`font-mono`,children:`const c = ctx.classify()`}),`)`]}),(0,Q.jsx)(`div`,{className:`flex flex-wrap gap-1`,children:L.classifier.predicates.map(e=>(0,Q.jsx)(`code`,{className:`rounded bg-muted px-1 py-0.5 font-mono text-xs`,children:e},e))}),(0,Q.jsxs)(`p`,{className:`text-xs text-muted-foreground mt-2`,children:[`Fields: `,L.classifier.fields.map(e=>(0,Q.jsxs)(`code`,{className:`font-mono`,children:[e,` `]},e)),`(never throws; all predicates are false for a null/unknown document).`]})]}),(0,Q.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Full reference: `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`docs/hooks.md`}),` in the server repo.`]})]}),F&&s===`scripts`&&(0,Q.jsxs)(`div`,{className:`border rounded-lg p-3 flex gap-2 items-center bg-muted/50`,children:[(0,Q.jsx)(W,{placeholder:`on-image.sh`,value:se,onChange:e=>de(e.target.value),className:`font-mono text-sm`}),(0,Q.jsx)(U,{size:`sm`,onClick:Ge,disabled:pe,children:`Create`}),(0,Q.jsx)(U,{size:`sm`,variant:`outline`,onClick:()=>I(!1),children:`Cancel`})]}),F&&s===`hooks`&&(0,Q.jsxs)(`div`,{className:`border rounded-lg p-4 space-y-3 bg-muted/50`,children:[(0,Q.jsxs)(`div`,{className:`grid grid-cols-1 md:grid-cols-2 gap-3`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`label`,{className:`text-xs font-semibold text-muted-foreground block mb-1`,children:`1. When (event)`}),(0,Q.jsx)(`select`,{className:`w-full h-9 rounded-md border bg-background px-2 font-mono text-sm outline-none focus:ring-2 focus:ring-ring`,value:z,onChange:e=>oe(e.target.value),children:(L?.events??[]).map(e=>(0,Q.jsxs)(`option`,{value:e.name,children:[e.name,`: `,e.description]},e.name))}),L&&(0,Q.jsxs)(`p`,{className:`text-xs text-muted-foreground mt-1 font-mono`,children:[`payload: `,L.events.find(e=>e.name===z)?.payload]}),(0,Q.jsx)(`label`,{className:`text-xs font-semibold text-muted-foreground block mt-3 mb-1`,children:`2. Name`}),(0,Q.jsx)(W,{placeholder:`my-hook`,value:se,onChange:e=>de(e.target.value),className:`font-mono text-sm`})]}),(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`label`,{className:`text-xs font-semibold text-muted-foreground block mb-1`,children:`3. Then (actions; pick any)`}),(0,Q.jsx)(`div`,{className:`space-y-1 max-h-52 overflow-auto pr-1`,children:(L?.actions??[]).map(e=>(0,Q.jsxs)(`label`,{className:`flex items-start gap-2 text-sm rounded px-2 py-1 hover:bg-muted cursor-pointer`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,className:`mt-1`,checked:B.includes(e.id),onChange:()=>Ke(e.id)}),(0,Q.jsxs)(`span`,{children:[(0,Q.jsx)(`span`,{className:`font-medium`,children:e.label}),(0,Q.jsx)(`span`,{className:`block text-xs text-muted-foreground`,children:e.description})]})]},e.id))})]})]}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(U,{size:`sm`,onClick:qe,disabled:pe||!L,children:pe?`Creating…`:`Create skeleton`}),(0,Q.jsx)(U,{size:`sm`,variant:`outline`,onClick:()=>I(!1),children:`Cancel`}),(0,Q.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Creates a disabled skeleton. Edit the TODOs, then enable it. Simple match→action automations can also go into `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`rules.json`}),` (no code).`]})]})]}),Ie&&(0,Q.jsxs)(`div`,{className:`grid grid-cols-1 md:grid-cols-3 gap-4`,children:[(0,Q.jsx)(`div`,{className:`md:col-span-1 border rounded-lg p-2 max-h-[480px] overflow-auto`,children:_?(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground p-2`,children:`Loading...`}):h.length===0?(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground p-2`,children:`No hooks yet.`}):Object.entries(Je).map(([e,t])=>(0,Q.jsxs)(`div`,{className:`mb-2`,children:[(0,Q.jsx)(`div`,{className:`text-xs font-semibold text-muted-foreground px-2 py-1`,children:e}),t.map(e=>{let t=zt(e.path);return(0,Q.jsxs)(`div`,{className:`flex items-center justify-between rounded px-2 py-1 cursor-pointer text-sm ${b===e.path?`bg-muted`:`hover:bg-muted/50`} ${t?``:`opacity-50`}`,onClick:()=>Re(e.path),children:[(0,Q.jsxs)(`span`,{className:`font-mono truncate flex items-center gap-1.5`,children:[e.path,s===`hooks`&&Lt(e.path)&&(0,Q.jsx)(`span`,{className:`text-[10px] uppercase tracking-wide rounded bg-muted-foreground/15 px-1 py-0.5 text-muted-foreground shrink-0`,children:`example`})]}),(0,Q.jsxs)(`div`,{className:`flex items-center`,children:[s===`hooks`&&It(e.path)&&(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,className:`h-6 w-6 p-0 touch-target`,title:It(e.path)?.startsWith(`document.`)?`Run against up to ${Ae} existing documents`:`Run now`,disabled:Oe!==null,onClick:t=>{t.stopPropagation(),Ve(e.path)},children:(0,Q.jsx)(m,{className:`h-3 w-3 ${Oe===e.path?`animate-pulse`:``}`})}),s===`hooks`&&(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,className:`h-6 w-6 p-0 touch-target`,title:t?`Disable`:`Enable`,onClick:t=>{t.stopPropagation(),Be(e.path)},children:t?(0,Q.jsx)(le,{className:`h-3 w-3`}):(0,Q.jsx)(S,{className:`h-3 w-3 text-muted-foreground`})}),(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,className:`h-6 w-6 p-0 text-destructive hover:text-destructive touch-target`,onClick:t=>{t.stopPropagation(),He(e.path)},children:(0,Q.jsx)(A,{className:`h-3 w-3`})})]})]},e.path)})]},e))}),(0,Q.jsx)(`div`,{className:we?`fixed inset-0 z-50 bg-background p-4 flex flex-col`:`md:col-span-2 border rounded-lg p-2 flex flex-col`,children:b?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(`div`,{className:`mb-2 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between`,children:[(0,Q.jsxs)(`span`,{className:`font-mono text-sm truncate`,children:[b,j?` •`:``]}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-1`,children:[s===`hooks`&&It(b)&&(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(U,{size:`sm`,variant:`outline`,className:`h-8`,title:zt(b)?`Disable this hook (renames it with a disabled- prefix)`:`Enable this hook (strips the prefix)`,onClick:()=>Be(b),children:zt(b)?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(le,{className:`mr-1.5 h-3.5 w-3.5`}),` Enabled`]}):(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(S,{className:`mr-1.5 h-3.5 w-3.5 text-muted-foreground`}),` Disabled`]})}),(0,Q.jsxs)(U,{size:`sm`,variant:`outline`,className:`h-8`,title:`Run this hook now`,disabled:Oe!==null||j,onClick:()=>Ve(b),children:[(0,Q.jsx)(m,{className:`mr-1.5 h-3.5 w-3.5 ${Oe===b?`animate-pulse`:``}`}),` Run`]})]}),(0,Q.jsxs)(U,{size:`sm`,onClick:ze,disabled:te||!j,children:[(0,Q.jsx)(p,{className:`mr-2 h-4 w-4`}),` Save`]}),(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,className:`h-8 w-8 p-0`,title:we?`Exit full screen (Esc)`:`Edit full screen`,onClick:()=>Te(!we),children:we?(0,Q.jsx)(ne,{className:`h-4 w-4`}):(0,Q.jsx)(ce,{className:`h-4 w-4`})})]})]}),(0,Q.jsx)(`div`,{className:`flex-1 overflow-auto rounded border text-sm ${we?``:`min-h-[400px] max-h-viewport-pane`}`,children:(0,Q.jsx)(br,{value:ee,path:b,onChange:e=>{k(e),M(!0)}})})]}):(0,Q.jsxs)(`div`,{className:`flex-1 flex items-center justify-center text-sm text-muted-foreground min-h-[400px]`,children:[`Select a `,s===`hooks`?`hook`:`script`,` to edit, or create a new one.`]})})]}),(s===`rules`||s===`hooks`)&&(0,Q.jsxs)(`label`,{className:`flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted-foreground`,title:`1–10000. Raise it when "Apply to existing items" reports fewer matches than you expect.`,children:[(0,Q.jsx)(ie,{className:`h-3.5 w-3.5 shrink-0`}),(0,Q.jsx)(`span`,{children:`Apply / run in batches of`}),(0,Q.jsx)(W,{type:`number`,min:1,max:1e4,step:100,value:je,onChange:e=>Me(e.target.value),onBlur:Ne,onKeyDown:e=>{e.key===`Enter`&&(Ne(),e.target.blur())},className:`h-7 w-20 px-2 text-xs`}),(0,Q.jsx)(`span`,{children:`documents per pass — the rule "Apply" button and manual hook runs stop after that many.`})]}),(0,Q.jsxs)(`p`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground`,children:[(0,Q.jsx)(O,{className:`h-3.5 w-3.5 shrink-0`}),(0,Q.jsxs)(`span`,{children:[`Everything here lives in the workspace git repo. Clone it with`,` `,(0,Q.jsxs)(`code`,{className:`break-all rounded bg-muted px-1 py-0.5 font-mono select-all`,children:[`git clone `,Ye.replace(`://`,`://canvas@`)]}),` `,`(password: a canvas API token; the `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`canvas@`}),` username is arbitrary but git needs one). Pushes hot-reload hooks.`]})]})]})}function Nr({state:e}){if(e.error)return(0,Q.jsx)(`p`,{className:`text-xs text-destructive`,children:e.error});if(!e.run)return(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Loading…`});let t=e.run,n={error:`text-destructive`,warn:`text-warning`,info:`text-foreground`,debug:`text-muted-foreground`};return(0,Q.jsxs)(`div`,{className:`space-y-2 text-xs`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-wrap gap-x-4 gap-y-1 text-muted-foreground`,children:[(0,Q.jsxs)(`span`,{children:[`run `,(0,Q.jsx)(`span`,{className:`font-mono`,children:t.runId})]}),(0,Q.jsxs)(`span`,{children:[`trigger `,(0,Q.jsx)(`span`,{className:`font-mono`,children:t.trigger})]}),t.eventId&&(0,Q.jsxs)(`span`,{children:[`event `,(0,Q.jsx)(`span`,{className:`font-mono`,children:t.eventId})]}),(0,Q.jsxs)(`span`,{children:[t.durationMs,` ms`]})]}),t.actions&&t.actions.length>0&&(0,Q.jsx)(`ol`,{className:`space-y-0.5 font-mono`,children:t.actions.map((e,t)=>(0,Q.jsxs)(`li`,{className:e.status===`error`?`text-destructive`:e.status===`skipped`?`text-muted-foreground`:``,children:[t+1,`. `,e.action,` → `,e.status,e.error?` — ${e.error}`:``]},t))}),t.trace&&t.trace.length>0?(0,Q.jsx)(`pre`,{className:`max-h-96 overflow-auto rounded-md border bg-background p-2 font-mono text-[11px] leading-relaxed whitespace-pre-wrap break-all`,children:t.trace.map((e,t)=>(0,Q.jsxs)(`span`,{className:`block ${n[e.level]||``}`,children:[(0,Q.jsxs)(`span`,{className:`text-muted-foreground`,children:[`+`,String(e.t).padStart(5,` `),`ms `]}),(0,Q.jsxs)(`span`,{className:`uppercase text-muted-foreground`,children:[e.level.padEnd(5,` `),` `]}),e.msg]},t))}):(0,Q.jsxs)(`p`,{className:`text-muted-foreground`,children:[`No execution log recorded for this run`,t.error?``:` (older runs predate tracing)`,`.`]})]})}function Pr({checked:e,disabled:t,onClick:n,label:r}){return(0,Q.jsx)(`button`,{type:`button`,role:`switch`,"aria-checked":e,"aria-label":r,disabled:t,onClick:n,className:`relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${e?`bg-primary`:`bg-muted-foreground/30`}`,children:(0,Q.jsx)(`span`,{className:`pointer-events-none inline-block h-4 w-4 rounded-full bg-background shadow transition-transform ${e?`translate-x-4`:`translate-x-0`}`})})}function Fr({children:e,title:t}){return(0,Q.jsx)(`span`,{title:t,className:`rounded border px-1 py-px text-[10px] uppercase tracking-wide text-muted-foreground`,children:e})}function Ir(e){let t=e.filter;return!t||!t.mime&&!t.filename&&(!t.schema||t.schema===`file`)?`Files`:t.mime===`image/*`&&!t.filename?`Images`:[t.schema&&t.schema!==`file`?t.schema:null,t.mime,t.filename].filter(Boolean).join(` · `)}function Lr({workspaceId:e}){let{policies:t,revision:n,status:r,loading:i,error:a,refresh:o}=ln(e),{showToast:s}=q(),[c,l]=(0,Z.useState)(null),[u,d]=(0,Z.useState)(null),[f,p]=(0,Z.useState)(``),h=(e,t)=>s({title:`Error`,description:e instanceof Error?e.message:t,variant:`destructive`}),g=async r=>{d(r.id);try{await fn(e,t.map(e=>e.id===r.id?{...e,enabled:!e.enabled}:e),n),pn(e)}catch(e){h(e,`Could not update the policy`)}finally{d(null)}},_=async r=>{if(window.confirm(`Delete the storage policy for "${r.path}"? Files already placed on the targets stay where they are.`)){d(r.id);try{await fn(e,t.filter(e=>e.id!==r.id),n),pn(e)}catch(e){h(e,`Could not delete the policy`)}finally{d(null)}}},v=async t=>{d(t.id),p(`Applying…`);try{let n=await sn(e,t.id,(e,t)=>p(`Applying… ${t.matched} planned, ${t.folders} folders`));s({title:n.failed?`Planning errors`:`Replication queued`,description:`${n.matched} documents and ${n.folders} folders planned${n.failed?`, ${n.failed} failed — see operation status`:``}.`,variant:n.failed?`destructive`:void 0}),window.dispatchEvent(new CustomEvent(`workspace:tree:refresh`))}catch(e){h(e,`Could not apply the policy`)}finally{d(null),p(``)}};return(0,Q.jsxs)(`div`,{className:`space-y-4`,children:[(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(fe,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Storage policies`})]}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`A mirror keeps filed documents on its targets and moves removed copies to Trash. A filing policy makes copies that stay on the targets. Uploads are kept locally until every required destination has been verified.`}),r?.error&&(0,Q.jsx)(`p`,{role:`alert`,className:`mt-2 text-xs text-destructive`,children:r.error.message}),r?.operations?.operations.filter(e=>e.state!==`complete`).map(t=>(0,Q.jsxs)(`div`,{className:`mt-2 rounded border p-2 text-xs`,children:[(0,Q.jsxs)(`p`,{children:[t.owner,`: `,t.state]}),(0,Q.jsx)(`p`,{children:t.error?.message}),t.outcomes.map(e=>(0,Q.jsxs)(`p`,{children:[e.backend,`:`,e.key,` — `,e.state]},`${e.backend}:${e.key}`)),t.state===`blocked`&&(0,Q.jsx)(U,{size:`sm`,variant:`outline`,onClick:()=>{un(e,t.id).then(o).catch(e=>h(e,`Retry failed`))},children:`Retry`})]},t.id)),a&&(0,Q.jsx)(`p`,{role:`alert`,className:`mt-2 text-xs text-destructive`,children:a})]}),i&&!t.length&&(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Loading…`}),!i&&!t.length&&(0,Q.jsxs)(`section`,{className:`rounded-lg border border-dashed p-6 text-center`,children:[(0,Q.jsx)(fe,{className:`mx-auto h-6 w-6 text-muted-foreground`}),(0,Q.jsx)(`p`,{className:`mt-2 text-sm`,children:`No storage policies yet.`}),(0,Q.jsxs)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:[`Right-click a folder in the context or directory tree and choose `,(0,Q.jsx)(`span`,{className:`font-medium text-foreground`,children:`Storage policy…`}),` to mirror it to a backend.`]})]}),t.map(e=>{let t=u===e.id;return(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4 ${e.enabled?``:`opacity-70`}`,children:[(0,Q.jsxs)(`div`,{className:`flex items-start justify-between gap-4`,children:[(0,Q.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,Q.jsx)(`h3`,{className:`break-all font-mono text-sm font-semibold`,children:e.path}),e.recursive&&(0,Q.jsx)(Fr,{children:`subfolders`}),(0,Q.jsx)(Fr,{children:Ir(e)}),!e.enabled&&(0,Q.jsx)(Fr,{children:`disabled`})]}),e.description&&(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:e.description}),(0,Q.jsx)(`ul`,{className:`mt-2 space-y-1`,children:e.targets.map((e,t)=>(0,Q.jsxs)(`li`,{className:`flex flex-wrap items-center gap-2 text-xs`,children:[(0,Q.jsx)(`span`,{className:`font-mono`,children:on(e)}),e.recursive&&(0,Q.jsx)(Fr,{children:`sub-path`}),e.recreateDirectories&&(0,Q.jsx)(Fr,{children:`folders`}),e.key&&(0,Q.jsx)(Fr,{title:e.key,children:`named`}),e.onConflict&&e.onConflict!==`error`&&(0,Q.jsx)(Fr,{children:e.onConflict})]},`${e.backend}:${t}`))}),t&&f&&(0,Q.jsx)(`p`,{role:`status`,className:`mt-2 text-xs text-muted-foreground`,children:f})]}),(0,Q.jsx)(Pr,{checked:e.enabled,disabled:t,onClick:()=>g(e),label:e.enabled?`Disable policy`:`Enable policy`})]}),(0,Q.jsxs)(`div`,{className:`mt-3 flex flex-wrap items-center gap-2`,children:[(0,Q.jsxs)(U,{variant:`outline`,size:`sm`,disabled:t,onClick:()=>l(e),children:[(0,Q.jsx)(H,{className:`mr-1 h-3.5 w-3.5`}),`Edit`]}),(0,Q.jsxs)(U,{variant:`outline`,size:`sm`,disabled:t||!e.enabled,onClick:()=>v(e),children:[(0,Q.jsx)(m,{className:`mr-1 h-3.5 w-3.5`}),`Apply to existing`]}),(0,Q.jsxs)(U,{variant:`ghost`,size:`sm`,onClick:()=>{o()},children:[(0,Q.jsx)(ge,{className:`mr-1 h-3.5 w-3.5`}),`Refresh status`]}),(0,Q.jsxs)(U,{variant:`ghost`,size:`sm`,disabled:t,className:`text-destructive`,onClick:()=>_(e),children:[(0,Q.jsx)(A,{className:`mr-1 h-3.5 w-3.5`}),`Delete`]})]})]},e.id)}),c&&(0,Q.jsx)(dn,{workspaceId:e,path:c.path,policy:c,onClose:()=>l(null),onSaved:()=>{pn(e),o()}})]})}var Rr=e=>`${be.workspaces}/${encodeURIComponent(e)}`;async function zr(e){return await K.get(`${Rr(e)}/mirrors`)||[]}async function Br(e,t){await K.delete(`${Rr(e)}/mirrors/${encodeURIComponent(t)}`)}async function Vr(e,t=10){return K.get(`${Rr(e)}/mirrors/protection?sample=${t}`)}async function Hr(e,t,n){return(await K.patch(`${Rr(e)}/mirrors/${encodeURIComponent(t)}`,n)).replica}async function Ur(e){return await K.get(`${Rr(e)}/sync/conflicts`)||[]}async function Wr(e,t,n){return K.post(`${Rr(e)}/sync/conflicts/${t}/resolve`,{keep:n})}var Gr=e=>{if(e==null||e===``)return`—`;let t=new Date(e);return Number.isNaN(t.getTime())?`—`:t.toLocaleString(void 0,{dateStyle:`medium`,timeStyle:`short`})},Kr=e=>e==null?`—`:e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KB`:e<1073741824?`${(e/1024/1024).toFixed(1)} MB`:`${(e/1024/1024/1024).toFixed(2)} GB`,qr=e=>e?e.slice(0,12):`—`;function Jr({mirror:e,now:t,busy:n,onForget:r,onRequired:i}){let a=e.mirror,o=!e.lastSeen||t-new Date(e.lastSeen).getTime()>6e5,s=e.replica?.role===`cache`||a.client===`fuse`,c=e.replica?.required===!0,l=a.direction||(a.client===`fuse`?`bi`:void 0);return(0,Q.jsx)(`div`,{className:`rounded-lg border p-3`,children:(0,Q.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,Q.jsxs)(`div`,{className:`flex items-start gap-3 min-w-0`,children:[(0,Q.jsx)(E,{className:`h-4 w-4 text-muted-foreground mt-0.5 shrink-0`}),(0,Q.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,Q.jsx)(`span`,{className:`text-sm font-medium`,children:e.name||e.deviceId}),a.client&&(0,Q.jsx)(`span`,{className:`rounded bg-muted px-1.5 py-0.5 text-[10px] font-mono uppercase`,children:a.client}),l&&(0,Q.jsx)(`span`,{className:`rounded bg-muted px-1.5 py-0.5 text-[10px] font-mono`,title:l===`pull`?`Backup target: the hub is the only writer`:l===`push`?`One-shot import: local is the only writer`:`Both ways`,children:l===`pull`?`⇣ pull`:l===`push`?`⇡ push`:`⇅ bi`}),c&&(0,Q.jsx)(`span`,{className:`rounded bg-emerald-500/15 px-1.5 py-0.5 text-[10px] font-mono text-emerald-700 dark:text-emerald-300`,title:`Counts toward protection`,children:`required`}),(0,Q.jsx)(`span`,{className:`rounded px-1.5 py-0.5 text-[10px] font-mono ${o?`bg-muted text-muted-foreground`:`bg-emerald-500/15 text-emerald-700 dark:text-emerald-300`}`,children:a.state||(o?`offline`:`online`)})]}),a.path&&(0,Q.jsx)(`p`,{className:`mt-0.5 text-[11px] font-mono text-muted-foreground truncate`,children:a.path}),(0,Q.jsxs)(`div`,{className:`mt-2 flex flex-wrap gap-x-4 gap-y-0.5 text-[11px] text-muted-foreground`,children:[(0,Q.jsxs)(`span`,{title:`Change-log entries the device has not applied yet`,children:[`lag `,e.lag??`—`]}),(0,Q.jsxs)(`span`,{title:`Local changes waiting to be pushed`,children:[`pending `,a.pending??0]}),(a.failed??0)>0&&(0,Q.jsxs)(`span`,{className:`text-destructive`,children:[`failed `,a.failed]}),(a.conflicts??0)>0&&(0,Q.jsxs)(`span`,{className:`text-amber-600 dark:text-amber-400`,children:[`conflicts `,a.conflicts]}),(a.skipped??0)>0&&(0,Q.jsxs)(`span`,{children:[`skipped `,a.skipped]}),(a.reverted??0)>0&&(0,Q.jsxs)(`span`,{title:`Local edits parked in .workspace/conflicts and replaced by the hub version`,children:[`reverted `,a.reverted]}),e.behind!=null&&(0,Q.jsxs)(`span`,{title:`Documents whose current version this device does not hold yet`,className:e.behind>0&&c?`text-amber-600 dark:text-amber-400`:``,children:[`behind `,e.behind]}),(0,Q.jsxs)(`span`,{children:[`last sync `,Gr(a.lastSync)]}),(0,Q.jsxs)(`span`,{children:[`seen `,Gr(e.lastSeen)]}),a.prefixes&&a.prefixes.length>0&&(0,Q.jsx)(`span`,{className:`font-mono`,children:a.prefixes.join(`, `)})]}),a.lastError&&(0,Q.jsx)(`p`,{className:`mt-1 text-[11px] text-destructive`,children:a.lastError}),(0,Q.jsxs)(`label`,{className:`mt-2 flex items-center gap-2 text-[11px] ${s?`text-muted-foreground`:``}`,title:s?`A cache never counts toward protection`:`A document is protected once every required replica holds its current version`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,className:`h-3 w-3`,checked:c,disabled:n||s,onChange:e=>i(e.target.checked)}),`required for protection`]})]})]}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,onClick:r,disabled:n,title:`Forget this mirror record (the device is not revoked)`,children:(0,Q.jsx)(A,{className:`h-3.5 w-3.5`})})]})})}function Yr({p:e}){let t=e.required.length===0,n=e.unprotected??0,r=t||n>0?B:oe,i=t?`text-muted-foreground`:n>0?`text-amber-600 dark:text-amber-400`:`text-emerald-700 dark:text-emerald-300`;return(0,Q.jsx)(`div`,{className:`rounded-lg border p-3`,children:(0,Q.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,Q.jsx)(r,{className:`h-4 w-4 mt-0.5 shrink-0 ${i}`}),(0,Q.jsxs)(`div`,{className:`min-w-0 flex-1 text-[11px]`,children:[t?(0,Q.jsxs)(`p`,{className:`text-sm`,children:[`No replica is marked `,(0,Q.jsx)(`span`,{className:`font-medium`,children:`required`}),` yet — nothing is counted as protected. Tick it on the device that must hold every document (the backup).`]}):(0,Q.jsxs)(`p`,{className:`text-sm`,children:[(0,Q.jsxs)(`span`,{className:`font-medium ${i}`,children:[e.protected??0,` of `,e.total-e.unversioned]}),` documents are held at their current version by every required replica`,n>0&&(0,Q.jsxs)(`span`,{className:i,children:[` · `,n,` not yet`]}),e.partial&&(0,Q.jsxs)(`span`,{className:`text-muted-foreground`,children:[` (first `,e.total,` checked)`]})]}),e.oldestUnprotected.length>0&&(0,Q.jsxs)(`ul`,{className:`mt-1 space-y-0.5 font-mono text-muted-foreground`,children:[e.oldestUnprotected.slice(0,5).map(e=>(0,Q.jsxs)(`li`,{className:`truncate`,title:`document ${e.docId} v${e.version}`,children:[e.key,` `,(0,Q.jsxs)(`span`,{className:`opacity-70`,children:[`· `,Gr(e.mtime)]})]},`${e.docId}:${e.key}`)),e.oldestUnprotected.length>5&&(0,Q.jsxs)(`li`,{children:[`… and `,e.oldestUnprotected.length-5,` more`]})]})]})]})})}function Xr({conflict:e,busy:t,onResolve:n}){let r=!e.hub;return(0,Q.jsxs)(`div`,{className:`rounded-lg border p-3 space-y-2`,children:[(0,Q.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,Q.jsx)(b,{className:`h-4 w-4 text-amber-500 mt-0.5 shrink-0`}),(0,Q.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Q.jsx)(`p`,{className:`text-sm font-medium font-mono break-all`,children:e.key}),(0,Q.jsxs)(`p`,{className:`text-[11px] text-muted-foreground`,children:[`from `,(0,Q.jsx)(`span`,{className:`font-medium`,children:e.deviceName||e.device||`unknown device`}),` · `,Gr(e.ts),e.mode===`rename`&&e.conflictKey&&(0,Q.jsxs)(Q.Fragment,{children:[` · already on disk as `,(0,Q.jsx)(`span`,{className:`font-mono`,children:e.conflictKey})]})]})]})]}),(0,Q.jsxs)(`div`,{className:`grid grid-cols-1 gap-2 text-[11px] sm:grid-cols-2`,children:[(0,Q.jsxs)(`div`,{className:`rounded border border-dashed p-2`,children:[(0,Q.jsx)(`div`,{className:`text-muted-foreground`,children:`Hub version (keeps the name)`}),e.hub?(0,Q.jsxs)(`div`,{className:`font-mono`,children:[qr(e.hub.sha256),` · `,Kr(e.hub.size),` · `,Gr(e.hub.mtime)]}):(0,Q.jsx)(`div`,{className:`italic text-muted-foreground`,children:`no file at this key any more`})]}),(0,Q.jsxs)(`div`,{className:`rounded border border-dashed p-2`,children:[(0,Q.jsx)(`div`,{className:`text-muted-foreground`,children:`Incoming version (the device’s)`}),(0,Q.jsxs)(`div`,{className:`font-mono`,children:[qr(e.incoming.sha256),` · `,Kr(e.incoming.size),` · `,Gr(e.incoming.mtime)]})]})]}),e.resolvable?(0,Q.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:t||r,onClick:()=>n(`hub`),title:`Discard the device's version`,children:`Keep hub`}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:t,onClick:()=>n(`incoming`),title:`The device's bytes replace the hub's; tags, relations and placements move over`,children:`Keep incoming`}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:t,onClick:()=>n(`both`),title:`Keep the hub file and add the device's version under a conflict name`,children:`Keep both`})]}):(0,Q.jsx)(`p`,{className:`text-[11px] text-muted-foreground`,children:`Dropbox-style conflict copy: both files are on disk, delete the one you do not need.`})]})}function Zr({workspaceId:e,workspaceUuid:t}){let{showToast:n}=q(),[r,i]=(0,Z.useState)([]),[a,o]=(0,Z.useState)([]),[s,c]=(0,Z.useState)(null),[l,u]=(0,Z.useState)(!1),[d,f]=(0,Z.useState)(null),[p,m]=(0,Z.useState)(0),h=(0,Z.useRef)(null),g=(0,Z.useCallback)(async(t=!1)=>{if(e){t||u(!0);try{let[t,n,r]=await Promise.all([zr(e),Ur(e),Vr(e).catch(()=>null)]);i(t),o(n),c(r),m(Date.now())}catch(e){t||n({title:`Error`,description:e instanceof Error?e.message:`Failed to load sync state`,variant:`destructive`})}finally{t||u(!1)}}},[e]);(0,Z.useEffect)(()=>{g()},[g]),(0,Z.useEffect)(()=>{if(!t)return;let e=`workspace:${t}`,n=()=>Se.emit(`subscribe`,{channel:e}),r=Se.on(`connect`,n);n();let i=e=>{let n=e;n?.workspaceId&&n.workspaceId!==t||(h.current&&clearTimeout(h.current),h.current=setTimeout(()=>{h.current=null,g(!0)},800))},a=[`backend.changed`,`sync.conflict.created`,`sync.conflict.resolved`].map(e=>Se.on(e,i));return()=>{r(),a.forEach(e=>e()),Se.emit(`unsubscribe`,{channel:e}),h.current&&=(clearTimeout(h.current),null)}},[t,g]);let _=async t=>{f(`mirror:${t}`);try{await Br(e,t),i(e=>e.filter(e=>e.deviceId!==t)),n({title:`Forgotten`,description:`Mirror record removed. The device keeps running and will report again if it still syncs this workspace.`})}catch(e){n({title:`Error`,description:e instanceof Error?e.message:`Failed to forget mirror`,variant:`destructive`})}finally{f(null)}},v=async(t,r)=>{f(`mirror:${t}`);try{let n=await Hr(e,t,{required:r});i(e=>e.map(e=>e.deviceId===t?{...e,replica:{role:n.role,required:n.required}}:e)),c(await Vr(e).catch(()=>null))}catch(e){n({title:`Error`,description:e instanceof Error?e.message:`Failed to update replica policy`,variant:`destructive`})}finally{f(null)}},y=async(t,r)=>{f(`conflict:${t.docId}`);try{let i=await Wr(e,t.docId,r);o(e=>e.filter(e=>e.docId!==t.docId));let a=r===`hub`?`kept the hub version`:r===`incoming`?`the device version now holds the name`:`kept both (${i.resultKey||`conflict copy`})`;n({title:`Resolved`,description:`${t.key}: ${a}`})}catch(e){n({title:`Error`,description:e instanceof Error?e.message:`Failed to resolve conflict`,variant:`destructive`})}finally{f(null)}};return(0,Q.jsxs)(`div`,{className:`space-y-6`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center justify-between gap-4`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Device mirrors`}),(0,Q.jsxs)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:[`Devices keeping this workspace’s files in sync (canvas-fuse `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`--mirror`}),`, canvas-edge). Lag counts change-log entries the device has not applied yet.`]})]}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>g(),disabled:l,children:[(0,Q.jsx)(w,{className:`mr-2 h-3.5 w-3.5 ${l?`animate-spin`:``}`}),`Refresh`]})]}),l&&r.length===0&&a.length===0?(0,Q.jsxs)(`div`,{className:`flex items-center gap-2 text-sm text-muted-foreground`,children:[(0,Q.jsx)(w,{className:`h-4 w-4 animate-spin`}),`Loading…`]}):r.length===0?(0,Q.jsxs)(`div`,{className:`rounded-lg border border-dashed p-4 text-xs text-muted-foreground`,children:[`No device mirrors this workspace yet. Mount it with `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`canvas-fuse mount -w <workspace> ~/Workspaces --mirror`}),` on a device and it shows up here.`]}):(0,Q.jsxs)(`div`,{className:`space-y-2`,children:[s&&(0,Q.jsx)(Yr,{p:s}),r.map(e=>(0,Q.jsx)(Jr,{mirror:e,now:p,busy:d===`mirror:${e.deviceId}`,onForget:()=>_(e.deviceId),onRequired:t=>v(e.deviceId,t)},e.deviceId))]}),(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Conflicts`}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`A device and the hub both changed the same file. The hub’s version keeps the filename; decide what happens to the device’s. Tags, relations and placements follow whichever version survives.`})]}),a.length===0?(0,Q.jsx)(`div`,{className:`rounded-lg border border-dashed p-4 text-xs text-muted-foreground`,children:`Nothing to resolve.`}):(0,Q.jsx)(`div`,{className:`space-y-2`,children:a.map(e=>(0,Q.jsx)(Xr,{conflict:e,busy:d===`conflict:${e.docId}`,onResolve:t=>y(e,t)},e.docId))})]})}function Qr({workspaceId:e,address:t,selectedChats:n=[],onSaved:r}){let[i,a]=(0,Z.useState)(n),[o,s]=(0,Z.useState)(!1),[c,l]=(0,Z.useState)(!1),[u,d]=(0,Z.useState)(null),[f,p]=(0,Z.useState)(``);return(0,Z.useEffect)(()=>{if(!c)return;let n=!1,r,i=async()=>{try{let r=await St(e,t);n||(d(r),p(``))}catch(e){n||p(e instanceof Error?e.message:`Connection unavailable`)}n||(r=setTimeout(()=>void i(),3e3))};return i(),()=>{n=!0,clearTimeout(r)}},[e,t,c]),(0,Q.jsxs)(`div`,{className:`mb-2`,children:[(0,Q.jsx)(U,{size:`sm`,variant:`outline`,onClick:()=>{c||a(n),l(!c)},children:c?`Close connection panel`:`Pair / connection status`}),c&&(0,Q.jsxs)(`div`,{className:`space-y-2 p-2 text-xs`,children:[(0,Q.jsx)(`p`,{children:f||u?.state||`Connecting…`}),(0,Q.jsx)(U,{size:`sm`,variant:`outline`,onClick:()=>{window.confirm(`Forget the current session and pair this account again?`)&&Ct(e,t).catch(e=>p(e.message))},children:`Pair again`}),u?.qr&&(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(`p`,{children:`WhatsApp → Settings → Linked devices → Link a device.`}),(0,Q.jsx)(`img`,{className:`max-w-full`,src:u.qr,width:256,height:256,alt:`WhatsApp device pairing QR code`})]}),u?.chats.length?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(`p`,{children:`Select conversations to synchronize into this workspace.`}),(0,Q.jsx)(`div`,{className:`max-h-48 overflow-auto`,children:u.chats.map(e=>(0,Q.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,disabled:o,checked:i.includes(e.id),onChange:t=>a(n=>t.target.checked?[...n,e.id]:n.filter(t=>t!==e.id))}),e.name,` · `,(0,Q.jsx)(`code`,{children:e.id})]},e.id))}),(0,Q.jsx)(U,{size:`sm`,disabled:o,onClick:async()=>{s(!0),p(``);try{await Ie(e,`whatsapp`,t,{chats:i}),await r?.()}catch(e){p(e instanceof Error?e.message:`Could not save conversations`)}finally{s(!1)}},children:o?`Saving…`:`Save conversations and sync`}),!i.length&&(0,Q.jsx)(`p`,{children:`No conversations selected; messages will not be synchronized.`})]}):null]})]})}async function $r(){return(await K.get(`/oauth/google/redirect-uri`)).redirectUri}async function ei(e){let t=window.open(`about:blank`,`canvas-google-oauth`,`popup=yes,width=540,height=720`),n;try{n=await K.post(`/oauth/google/start`,e)}catch(e){throw t?.close(),e}if(!t)throw Error(`Popup blocked — allow popups for this site and try again`);t.location.href=n.url;let r=new URL(xe,window.location.origin).origin;return new Promise((e,i)=>{let a=!1,o=e=>{a||(a=!0,window.removeEventListener(`message`,s),window.clearInterval(c),window.clearTimeout(l),e())},s=t=>{if(t.origin!==r)return;let a=t.data;!a||a.type!==`canvas:oauth`||a.provider!==`google`||a.state!==n.state||(a.ok&&a.refreshToken?o(()=>e({refreshToken:a.refreshToken,account:a.account})):o(()=>i(Error(a.error||`Google sign-in failed`))))};window.addEventListener(`message`,s);let c=window.setInterval(()=>{t.closed&&window.setTimeout(()=>o(()=>i(Error(`Sign-in window closed before finishing`))),500)},500),l=window.setTimeout(()=>o(()=>{t.close(),i(Error(`Sign-in timed out`))}),n.expiresInMs||6e5)})}function ti({clientId:e,clientSecret:t,scope:n,onLinked:r,disabled:i}){let[a,o]=(0,Z.useState)(!1),[s,l]=(0,Z.useState)(null),{showToast:u}=q(),d=e.trim().length>0&&t.trim().length>0;(0,Z.useEffect)(()=>{let e=!1;return $r().then(t=>{e||l(t)}).catch(()=>{}),()=>{e=!0}},[]);let f=async()=>{o(!0);try{let i=await ei({clientId:e.trim(),clientSecret:t.trim(),scope:n});r(i),u({title:`Google account linked`,description:i.account?`Refresh token obtained for ${i.account}`:`Refresh token obtained`})}catch(e){u({title:`Google sign-in failed`,description:e instanceof Error?e.message:String(e),variant:`destructive`})}finally{o(!1)}};return(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:i||a||!d,onClick:()=>{f()},title:d?`Open the Google consent screen and fill in the refresh token`:`Enter the OAuth client id and secret first`,children:[(0,Q.jsx)(c,{icon:`mdi:google`,width:14,height:14,className:`mr-2`}),a?`Waiting for Google…`:`Sign in with Google to get a refresh token`]}),s&&(0,Q.jsxs)(`p`,{className:`text-[11px] text-muted-foreground/80 break-all`,children:[`Register this redirect URI on the OAuth client (Google Cloud Console → Credentials → your Web application client → Authorized redirect URIs):`,` `,(0,Q.jsx)(`code`,{className:`select-all`,children:s})]})]})}var ni={git:{label:`Git repository`,icon:`mdi:git`,blurb:`Create a local repository or clone a remote into the workspace. Branches appear under Backends → Workspace → Git. Use Auto-Link on a branch or folder afterward to file its documents into a virtual tree.`,fields:[{key:`address`,label:`Repository name`,placeholder:`company-ui`,required:!0},{key:`url`,label:`Remote URL`,placeholder:`git@github.com:company/ui.git`,hint:`Leave empty for a local repository. Private repos use the server’s SSH keys or Git credential helper.`},{key:`branches`,label:`Branches to browse (empty means all)`,list:!0,placeholder:`main, jira-1234/myusername-int-qf`}]},github:{label:`GitHub Issues`,icon:`mdi:github`,blurb:`Issues from the listed github.com or GitHub Enterprise repos sync as todos. Token optional for public repos.`,fields:[{key:`address`,label:`Account label`,placeholder:`e.g. canvas-ui`,required:!0},{key:`baseUrl`,label:`GitHub Enterprise URL`,placeholder:`https://ghe.example.com`,hint:`Leave blank for github.com. Works with GitHub Enterprise Server (ghe.example.com) and GHE.com (yourco.ghe.com) — the web address is enough.`},{key:`token`,label:`Personal access token`,hint:`Optional for public repos; required for write-back and @me filters.`,secret:!0},{key:`repos`,label:`Repositories`,placeholder:`owner/repo, one per line`,hint:`owner/repo or the repo’s URL. Issues from each repo sync as todos.`,list:!0,required:!0},{key:`assignee`,label:`Only issues assigned to`,placeholder:`@me, a login, * or none`,hint:`Filters are applied by GitHub and combined with AND. Changing one re-syncs from scratch.`},{key:`creator`,label:`Only issues opened by`,placeholder:`@me or a login`},{key:`mentioned`,label:`Only issues mentioning`,placeholder:`@me or a login`},{key:`labels`,label:`Only issues with all of these labels`,placeholder:`one per line`,list:!0},{key:`openOnly`,label:`Skip closed issues on first import`,hint:`Issues closed later still sync as completed.`,bool:!0},{key:`writeBack`,label:`Manage issues from Canvas`,hint:`Create, edit and close issues. Needs a PAT with repo scope.`,bool:!0},{key:`pruneRemoved`,label:`Remove items deleted at the source`,hint:`After each sync, issues that no longer exist in the repo are dropped from the mirror (kept as location-less orphans until database retention GC; skipped whenever the repo can’t be fully listed).`,bool:!0}]},slack:{label:`Slack`,icon:`mdi:slack`,blurb:`Channel messages sync as message documents. Bot/user token with channels:read + channels:history.`,fields:[{key:`address`,label:`Workspace label`,placeholder:`e.g. acme`,required:!0},{key:`token`,label:`Token (xoxb-… / xoxp-…)`,secret:!0,required:!0},{key:`channels`,label:`Conversations (channel names or IDs; DM IDs need im:read + im:history)`,list:!0},{key:`directMessages`,label:`Include direct messages`,hint:`Requires im:read, im:history, mpim:read and mpim:history.`,bool:!0},{key:`writeBack`,label:`Allow sending from Canvas`,hint:`Token also needs chat:write.`,bool:!0},{key:`allowAgentSend`,label:`Allow agents with workspace write access to send`,bool:!0}]},whatsapp:{label:`WhatsApp (linked device)`,icon:`mdi:whatsapp`,blurb:`Pair your phone with Canvas, then choose conversations. Uses Baileys; separate from WhatsApp Business.`,fields:[{key:`address`,label:`Account label`,placeholder:`personal`,hint:`Any unique label; no phone number required. Keep it unchanged after pairing.`,required:!0},{key:`chats`,label:`Conversation IDs to sync (one per line; available after pairing)`,list:!0},{key:`writeBack`,label:`Allow sending from Canvas`,bool:!0},{key:`allowAgentSend`,label:`Allow agents with workspace write access to send`,bool:!0}]},gcal:{label:`Google Calendar`,icon:`mdi:calendar-month`,blurb:`Events sync as calendar entries (recurring series kept as series). Offline-access OAuth credentials.`,fields:[{key:`address`,label:`Account label`,placeholder:`e.g. me-gmail`,required:!0},{key:`clientId`,label:`OAuth client id`,required:!0},{key:`clientSecret`,label:`OAuth client secret`,secret:!0,required:!0},{key:`refreshToken`,label:`Refresh token`,secret:!0,required:!0,oauth:`google`,oauthScope:`calendar.readonly`},{key:`calendars`,label:`Calendar ids (empty = primary)`,list:!0}]},caldav:{label:`CalDAV`,icon:`mdi:calendar-sync`,blurb:`Any CalDAV endpoint (GroupOffice, Nextcloud, Radicale, SOGo…). Read-only by default; enable write-back to create events from Canvas.`,fields:[{key:`address`,label:`Account label`,placeholder:`e.g. groupoffice`,required:!0},{key:`url`,label:`CalDAV URL (calendar home or one calendar)`,placeholder:`https://host/caldav/user`,required:!0},{key:`username`,label:`Username`},{key:`password`,label:`Password`,secret:!0},{key:`calendars`,label:`Calendars (names, empty = all)`,list:!0},{key:`writeBack`,label:`Create events from Canvas`,hint:`Write-back into the CalDAV calendar.`,bool:!0}]},teams:{label:`MS Teams`,icon:`mdi:microsoft-teams`,blurb:`Channel messages via Microsoft Graph (app-only credentials, admin-consented ChannelMessage.Read.All).`,fields:[{key:`address`,label:`Tenant label`,placeholder:`e.g. corp`,required:!0},{key:`tenantId`,label:`Tenant id`,required:!0},{key:`clientId`,label:`Client id`,required:!0},{key:`clientSecret`,label:`Client secret`,secret:!0,required:!0},{key:`teams`,label:`Team ids (one per line)`,list:!0,required:!0}]}};function ri(e){return e.split(/[\n,]/).map(e=>e.trim()).filter(Boolean)}function ii({workspaceId:e}){let{showToast:t}=q(),[n,r]=(0,Z.useState)([]),[i,a]=(0,Z.useState)(null),[o,s]=(0,Z.useState)(null),[l,u]=(0,Z.useState)({}),[d,f]=(0,Z.useState)(!1),[p,m]=(0,Z.useState)(!1),h=(0,Z.useCallback)(async()=>{let t=await He(e).catch(()=>[]);r(t.filter(e=>e.kind===`connector`))},[e]),g=async()=>{m(!0);try{await h()}finally{m(!1)}};(0,Z.useEffect)(()=>{let t=!1;return He(e).then(e=>{t||r(e.filter(e=>e.kind===`connector`))}).catch(()=>{}),()=>{t=!0}},[e]);let _=n.some(e=>e.status===`syncing`);(0,Z.useEffect)(()=>{if(!_)return;let e=setInterval(()=>{h()},4e3);return()=>clearInterval(e)},[_,h]);let v=e=>{let t=ni[e.driver];if(!t)return;let n=e.config||{},r={};for(let i of t.fields){if(i.key===`address`){r.address=e.address;continue}if(i.secret)continue;if(i.key===`writeBack`){r.writeBack=n.readOnly===!1&&(![`slack`,`whatsapp`].includes(e.driver)||n.sendEnabled===!0)?`true`:``;continue}let t=n[i.key];if(i.list){r[i.key]=Array.isArray(t)?t.join(`
6
+ `):``;continue}t!=null&&(r[i.key]=String(t))}u(r),a(e.driver),s(e)},y=()=>{a(null),s(null),u({})},b=async()=>{if(!i)return;let n=ni[i],r={};for(let e of n.fields){let n=(l[e.key]||``).trim();if(!n){if(e.required&&!(o&&e.secret)){t({title:`${e.label} is required`,variant:`destructive`});return}o&&!e.secret&&(r[e.key]=e.list?[]:!e.bool&&``);continue}r[e.key]=e.list?ri(n):e.bool?n===`true`:n}n.fields.some(e=>e.key===`writeBack`)&&([`slack`,`whatsapp`].includes(i)&&(r.sendEnabled=r.writeBack===!0),r.readOnly=r.writeBack!==!0,delete r.writeBack),i===`git`&&(r.enabled=!0),f(!0);try{o?await Ie(e,o.driver,o.address,r):await it(e,i,r),y(),await h(),t({title:o?`Connector updated; sync restarted`:`Connector saved; first sync started`})}catch(e){t({title:`Failed to save connector`,description:e instanceof Error?e.message:String(e),variant:`destructive`})}finally{f(!1)}},S=async n=>{try{n.driver===`git`&&n.enabled===!1?await Ie(e,n.driver,n.address,{enabled:!0}):await qe(e,n.driver,n.address),t({title:`Syncing ${n.driver}/${n.address}`}),setTimeout(()=>{h()},1500)}catch(e){t({title:`Sync failed`,description:e instanceof Error?e.message:String(e),variant:`destructive`})}},T=async n=>{let r=n.driver===`git`?`Stop syncing ${n.address}? Its checkout and existing tree documents will stay available.`:`Remove ${n.driver}/${n.address}? Synced documents stay in the backends tree until purged.`;if(window.confirm(r))try{await rt(e,n.driver,n.address),await h()}catch(e){t({title:`Remove failed`,description:e instanceof Error?e.message:String(e),variant:`destructive`})}};return(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Connectors`}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Git repositories, GitHub issues, Slack, Google Calendar, CalDAV and MS Teams, polled into the backends tree (read-only unless write-back is enabled).`})]}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:p,onClick:()=>{g()},title:`Refresh`,children:(0,Q.jsx)(w,{className:`h-3.5 w-3.5 ${p?`animate-spin`:``}`})})]}),n.length>0&&(0,Q.jsx)(`div`,{className:`mt-3 space-y-2`,children:n.map(t=>{let n=ni[t.driver];return(0,Q.jsxs)(`div`,{className:`rounded-md border p-3`,children:[t.driver===`whatsapp`&&(0,Q.jsx)(Qr,{workspaceId:e,address:t.address,selectedChats:Array.isArray(t.config?.chats)?t.config.chats:[],onSaved:h}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,Q.jsx)(c,{icon:n?.icon||`mdi:cloud-sync`,width:16,height:16,className:`shrink-0`}),(0,Q.jsx)(`span`,{className:`text-sm font-medium`,children:n?.label||t.driver}),(0,Q.jsx)(`span`,{className:`font-mono text-xs text-muted-foreground`,children:t.address}),(0,Q.jsx)(`span`,{className:`flex-1`}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:t.status===`syncing`,onClick:()=>{S(t)},children:[(0,Q.jsx)(w,{className:`mr-1 h-3 w-3 ${t.status===`syncing`?`animate-spin`:``}`}),t.status===`syncing`?`Syncing`:t.driver===`git`&&t.enabled===!1?`Resume sync`:`Sync now`]}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,onClick:()=>v(t),title:`Edit connector`,children:(0,Q.jsx)(H,{className:`h-3.5 w-3.5`})}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,onClick:()=>{T(t)},disabled:t.driver===`git`&&t.enabled===!1,title:t.driver===`git`?`Stop syncing`:`Remove connector`,children:(0,Q.jsx)(A,{className:`h-3.5 w-3.5 text-destructive`})})]}),(0,Q.jsxs)(`div`,{className:`mt-1.5 flex flex-wrap gap-2 text-[11px] text-muted-foreground`,children:[(0,Q.jsxs)(`span`,{children:[`status: `,t.status]}),t.lastSyncAt&&(0,Q.jsxs)(`span`,{children:[`last sync: `,new Date(t.lastSyncAt).toLocaleString()]}),t.treePath&&(0,Q.jsx)(`span`,{className:`font-mono`,children:t.treePath})]}),t.lastError&&(0,Q.jsx)(`p`,{className:`mt-1 text-[11px] text-destructive`,children:t.lastError})]},`${t.driver}:${t.address}`)})}),i?(0,Q.jsxs)(`div`,{className:`mt-3 rounded-md border p-3`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(c,{icon:ni[i].icon,width:16,height:16}),(0,Q.jsx)(`span`,{className:`text-sm font-medium`,children:o?`Edit ${ni[i].label}`:ni[i].label}),o&&(0,Q.jsx)(`span`,{className:`font-mono text-xs text-muted-foreground`,children:o.address}),(0,Q.jsx)(`span`,{className:`flex-1`}),(0,Q.jsx)(`button`,{type:`button`,onClick:y,className:`p-1 text-muted-foreground hover:text-foreground`,"aria-label":`Cancel`,children:(0,Q.jsx)(x,{className:`h-3.5 w-3.5`})})]}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:ni[i].blurb}),(0,Q.jsx)(`div`,{className:`mt-2 grid gap-2 sm:grid-cols-2`,children:ni[i].fields.map(e=>(0,Q.jsxs)(`label`,{className:`text-xs ${e.list||e.bool?`sm:col-span-2`:``}`,children:[!e.bool&&(0,Q.jsx)(`span`,{className:`text-muted-foreground`,children:e.label}),e.bool?(0,Q.jsxs)(`span`,{className:`mt-1 flex items-center gap-2`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:l[e.key]===`true`,onChange:t=>u(n=>({...n,[e.key]:t.target.checked?`true`:``})),className:`h-4 w-4 accent-primary`}),(0,Q.jsx)(`span`,{children:e.label})]}):e.list?(0,Q.jsx)(`textarea`,{value:l[e.key]||``,onChange:t=>u(n=>({...n,[e.key]:t.target.value})),placeholder:e.placeholder,rows:2,className:`mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring`}):(0,Q.jsx)(W,{type:e.secret?`password`:`text`,value:l[e.key]||``,disabled:!!o&&e.key===`address`,onChange:t=>u(n=>({...n,[e.key]:t.target.value})),placeholder:o&&e.secret?`Leave blank to keep current`:e.placeholder,autoComplete:`off`,className:`mt-1 h-8 text-xs`}),e.hint&&(0,Q.jsx)(`span`,{className:`mt-0.5 block text-[11px] text-muted-foreground/80`,children:e.hint}),e.oauth===`google`&&(0,Q.jsx)(`span`,{className:`mt-1.5 block`,children:(0,Q.jsx)(ti,{clientId:l.clientId||``,clientSecret:l.clientSecret||``,scope:e.oauthScope||`calendar.readonly`,disabled:d,onLinked:({refreshToken:t,account:n})=>u(r=>({...r,[e.key]:t,address:(r.address||``).trim()||(n?n.replace(/[^a-z0-9]+/gi,`-`).replace(/^-|-$/g,``).toLowerCase():``)}))})})]},e.key))}),(0,Q.jsx)(U,{type:`button`,size:`sm`,className:`mt-3`,disabled:d,onClick:()=>{b()},children:d?`Saving…`:o?`Save changes & sync`:`Save & sync`})]}):(0,Q.jsx)(`div`,{className:`mt-3 flex flex-wrap gap-2`,children:Object.entries(ni).map(([e,t])=>(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>{a(e),u({})},children:[(0,Q.jsx)(C,{className:`mr-1 h-3 w-3`}),(0,Q.jsx)(c,{icon:t.icon,width:14,height:14,className:`mr-1`}),t.label]},e))})]})}var ai={host:``,port:993,tls:!0,allowSelfSigned:!0,user:``,password:``,pollInterval:6e4,initialSyncDays:180,smtp:{enabled:!1,host:``,port:587,secure:!1,user:``,password:``,from:``,sentFolder:`Sent`,appendSent:!1,allowAgentSend:!1}};function oi(e){let t=e.config||{};return{host:String(t.host||``),port:Number(t.port??993),tls:t.tls!==!1,allowSelfSigned:t.allowSelfSigned!==!1,user:String(t.user||e.address),password:``,pollInterval:Number(t.pollInterval??6e4),initialSyncDays:Number(t.initialSyncDays??180),smtp:{...ai.smtp,...t.smtp||{},password:``}}}function si(e,t,n,r){let{password:i,...a}=e.smtp;return{host:e.host.trim(),user:e.user.trim(),port:Number(e.port)||993,tls:e.tls,allowSelfSigned:e.allowSelfSigned,pollInterval:Number(e.pollInterval)||6e4,initialSyncDays:Math.max(0,Number(e.initialSyncDays)||0),smtp:{...a,...(!t||r)&&i?{password:i}:{}},...(!t||n)&&e.password?{password:e.password}:{}}}function ci({workspaceId:e,enabled:t}){let[n,r]=(0,Z.useState)([]),[i,a]=(0,Z.useState)([]);(0,Z.useEffect)(()=>{let t=!0;return He(e).then(e=>{t&&a(e.filter(e=>e.kind===`storage`))}).catch(()=>{}),()=>{t=!1}},[e]);let[o,s]=(0,Z.useState)(null),[c,l]=(0,Z.useState)(null),[u,d]=(0,Z.useState)(!1),[m,h]=(0,Z.useState)(ai),[g,_]=(0,Z.useState)(!1),[y,b]=(0,Z.useState)(!1),[x,S]=(0,Z.useState)(!1),C=(0,Z.useRef)(0),[T,E]=(0,Z.useState)([]),[D,ee]=(0,Z.useState)([`INBOX`]),[O,k]=(0,Z.useState)(!0),[M,te]=(0,Z.useState)(!1),[N,ne]=(0,Z.useState)(!1),[P,F]=(0,Z.useState)(null),{showToast:I,showProgress:re}=q(),ie=(0,Z.useMemo)(()=>[...n].sort((e,t)=>e.address.localeCompare(t.address)),[n]),L=(0,Z.useMemo)(()=>n.find(e=>e.address===c)||null,[n,c]),ae=(0,Z.useCallback)(()=>He(e,`imap`).then(e=>r(e)).catch(e=>{I({title:`Error`,description:e instanceof Error?e.message:`Failed to load IMAP accounts`,variant:`destructive`})}).finally(()=>k(!1)),[e,I]),z=(0,Z.useCallback)(async()=>{k(!0),await ae()},[ae]),[oe,se]=(0,Z.useState)(e);e!==oe&&(se(e),k(!0),l(null),d(!1),h(ai),E([]),ee([`INBOX`]),S(!1),_(!1),b(!1)),(0,Z.useEffect)(()=>{ae()},[ae]),(0,Z.useEffect)(()=>()=>{C.current++},[e]);let ce=(0,Z.useCallback)(async(t,n)=>{let r=++C.current;te(!0);try{let i=t?await ct(e,`imap`,t.address):await Ye(e,`imap`,{host:(n?.host||``).trim(),port:Number(n?.port)||993,tls:n?.tls!==!1,allowSelfSigned:n?.allowSelfSigned!==!1,user:(n?.user||``).trim(),password:n?.password||``,folder:`INBOX`});r===C.current&&E(i.filter(e=>e.selectable))}catch(e){r===C.current&&I({title:`IMAP Error`,description:e instanceof Error?e.message:`Failed to discover folders`,variant:`destructive`})}finally{r===C.current&&te(!1)}},[e,I]),le=()=>{C.current++,te(!1),d(!1),l(null),h(ai),E([]),ee([`INBOX`]),_(!1),b(!1),S(!1)},ue=e=>{if(!N){if(c===e.address){le();return}C.current++,te(!1),d(!1),l(e.address),h(oi(e)),E([]),ee((e.containers||[]).map(e=>e.name)),_(!1),b(!1),S(!1)}},de=()=>{le(),d(!0)},B=(e,t)=>{S(!0),h(n=>({...n,[e]:t}))},fe=e=>{S(!0),ee(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},pe=(0,Z.useMemo)(()=>{let e=new Map(T.map(e=>[e.path,e]));for(let t of D)e.has(t)||e.set(t,{name:t,path:t,delimiter:`/`,selectable:!0,attributes:[]});return[...e.values()].sort((e,t)=>e.path.localeCompare(t.path))},[T,D]),he=async()=>{ne(!0);try{let t=D.length?D:[`INBOX`],n=si(m,!!L,g,y);if(L){await Ie(e,`imap`,L.address,n);let r=new Set((L.containers||[]).map(e=>e.name)),i=t.filter(e=>!r.has(e)),a=[...r].filter(e=>!t.includes(e));i.length&&await Je(e,`imap`,L.address,i);for(let t of a)await je(e,`imap`,L.address,t)}else{await it(e,`imap`,{...n,password:m.password,folder:t[0]});let r=m.user.trim();t.length>1&&await Je(e,`imap`,r,t.slice(1))}await z(),d(!1),l(m.user.trim()),h({...m,password:``,smtp:{...m.smtp,password:``}}),_(!1),b(!1),S(!1),I({title:`Saved`,description:`IMAP account ${m.user.trim()} saved; sync runs in the background`})}catch(e){I({title:`Error`,description:e instanceof Error?e.message:`Failed to save IMAP account`,variant:`destructive`})}finally{ne(!1)}},ge=async()=>{if(!(!L||!window.confirm(`Delete IMAP account ${L.address} and all its folders?`))){F(`delete:${L.address}`);try{await rt(e,`imap`,L.address),await z(),le(),I({title:`Deleted`,description:`IMAP account ${L.address} deleted`})}catch(e){I({title:`Error`,description:e instanceof Error?e.message:`Failed to delete account`,variant:`destructive`})}finally{F(null)}}},_e=async(t,n)=>{F(`${t}:${n}`);try{t===`test`?await Be(e,`imap`,n):await Qt(re,e,`imap`,n,e=>{r(t=>t.map(t=>t.address===n?e:t))}),await z(),t===`test`&&I({title:`Success`,description:`${n} saved account test completed`})}catch(e){I({title:`Error`,description:e instanceof Error?e.message:`Failed to ${t}`,variant:`destructive`})}finally{F(null)}},ve=async(t,n)=>{F(`folder-sync:${t}:${n}`);try{await Ge(e,`imap`,t,n),await z(),I({title:`Sync started`,description:`${t} · ${n} · rescanning the saved lookback window`})}catch(e){I({title:`Error`,description:e instanceof Error?e.message:`Failed to sync folder`,variant:`destructive`})}finally{F(null)}},V=(0,Q.jsxs)(`form`,{className:`grid gap-3`,autoComplete:`off`,"aria-label":`IMAP account settings`,onSubmit:e=>e.preventDefault(),children:[(0,Q.jsxs)(`div`,{className:`grid gap-2`,children:[(0,Q.jsx)(G,{htmlFor:`imap-host`,children:`Host`}),(0,Q.jsx)(W,{id:`imap-host`,name:`imap-host`,autoComplete:`off`,value:m.host,readOnly:!!L,onChange:e=>B(`host`,e.target.value),placeholder:`imap.example.com`})]}),(0,Q.jsxs)(`div`,{className:`grid gap-2`,children:[(0,Q.jsx)(G,{htmlFor:`imap-user`,children:`User (account)`}),(0,Q.jsx)(W,{id:`imap-user`,name:`imap-username`,autoComplete:`section-imap username`,value:m.user,readOnly:!!L,onChange:e=>B(`user`,e.target.value),placeholder:`me@example.com`})]}),(0,Q.jsx)(`div`,{className:`grid gap-2`,children:!L||g?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(G,{htmlFor:`imap-password`,children:L?`New IMAP password`:`Password`}),(0,Q.jsx)(W,{id:`imap-password`,name:`imap-password`,type:`password`,autoComplete:`section-imap new-password`,"data-lpignore":`true`,"data-1p-ignore":`true`,value:m.password,onChange:e=>B(`password`,e.target.value),placeholder:L?`Leave blank to keep current password`:`Password`})]}):(0,Q.jsxs)(`div`,{className:`flex items-center gap-3 text-sm`,children:[(0,Q.jsx)(`span`,{className:`text-muted-foreground`,children:`Saved IMAP password`}),(0,Q.jsx)(U,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>_(!0),children:`Change IMAP password`})]})}),(0,Q.jsxs)(`div`,{className:`grid gap-2`,children:[(0,Q.jsx)(G,{htmlFor:`imap-port`,children:`Port`}),(0,Q.jsx)(W,{id:`imap-port`,type:`number`,value:String(m.port),onChange:e=>B(`port`,Number(e.target.value||993))})]}),(0,Q.jsxs)(`div`,{className:`grid gap-2`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,Q.jsx)(G,{children:`Folders to sync`}),(0,Q.jsxs)(U,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>void ce(L,m),disabled:M||!!L&&x||!L&&(!m.host.trim()||!m.user.trim()||!m.password),children:[M?(0,Q.jsx)(R,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(j,{className:`h-3 w-3`}),` Discover folders`]})]}),(0,Q.jsx)(`div`,{className:`max-h-56 overflow-y-auto rounded-md border p-2`,children:pe.length===0?(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:M?`Discovering folders…`:L?`No folders found on the server.`:`Fill in host, user and password, then discover the account’s folders. INBOX is synced by default.`}):(0,Q.jsx)(`div`,{className:`space-y-0.5`,children:pe.map(e=>{let t=e.path.split(e.delimiter||`/`).length-1,n=T.some(t=>t.path===e.path);return(0,Q.jsxs)(`label`,{className:`flex items-center gap-2 rounded px-2 py-1 text-xs hover:bg-accent/50`,style:{paddingLeft:`${8+t*14}px`},children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:D.includes(e.path),onChange:()=>fe(e.path)}),(0,Q.jsx)(`span`,{className:`font-mono truncate`,children:e.path}),!n&&T.length>0&&(0,Q.jsx)(`span`,{className:`text-muted-foreground`,children:`(subscribed, not on server)`})]},e.path)})})}),(0,Q.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[D.length,` folder`,D.length===1?``:`s`,` selected; each synced as its own container. Unchecking a synced folder removes it on Save.`]})]}),(0,Q.jsxs)(`fieldset`,{className:`rounded border p-3 space-y-3 sm:col-span-2`,children:[(0,Q.jsx)(`legend`,{className:`px-1 text-sm font-medium`,children:`Outgoing email (SMTP)`}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:!L||!m.smtp.enabled||N,onClick:()=>s(L.address),children:`Send test email`}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Save your SMTP settings before testing. The test uses the saved account.`}),o&&(0,Q.jsx)(Tt,{workspaceId:e,initialAccount:{driver:`imap`,address:o},testEmail:!0,onClose:()=>s(null)}),(0,Q.jsxs)(`label`,{className:`flex gap-2 text-xs`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:m.smtp.enabled,onChange:e=>B(`smtp`,{...m.smtp,enabled:e.target.checked})}),`Allow sending from Canvas`]}),(0,Q.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[[`host`,`user`,`from`,`sentFolder`].map(e=>(0,Q.jsxs)(`label`,{className:`text-xs`,children:[{host:`SMTP host`,user:`SMTP username`,from:`From address`,sentFolder:`Sent folder`}[e],(0,Q.jsx)(W,{name:`smtp-${e}`,value:m.smtp[e],autoComplete:e===`user`?`section-smtp username`:`off`,onChange:t=>B(`smtp`,{...m.smtp,[e]:t.target.value})})]},e)),m.smtp.enabled&&(!L||y)&&(0,Q.jsxs)(`label`,{className:`text-xs`,children:[`SMTP password (blank keeps current)`,(0,Q.jsx)(W,{name:`smtp-password`,type:`password`,autoComplete:`section-smtp new-password`,"data-lpignore":`true`,"data-1p-ignore":`true`,value:m.smtp.password,onChange:e=>B(`smtp`,{...m.smtp,password:e.target.value})})]}),L&&m.smtp.enabled&&!y&&(0,Q.jsx)(U,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>b(!0),children:`Change SMTP password`}),(0,Q.jsxs)(`label`,{className:`text-xs`,children:[`Port`,(0,Q.jsx)(W,{type:`number`,min:1,max:65535,value:m.smtp.port,onChange:e=>B(`smtp`,{...m.smtp,port:Number(e.target.value)})})]})]}),(0,Q.jsxs)(`label`,{className:`flex gap-2 text-xs`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:m.smtp.secure,onChange:e=>B(`smtp`,{...m.smtp,secure:e.target.checked})}),`Implicit TLS (usually port 465; otherwise STARTTLS is required)`]}),(0,Q.jsxs)(`label`,{className:`flex gap-2 text-xs`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:m.smtp.appendSent,onChange:e=>B(`smtp`,{...m.smtp,appendSent:e.target.checked})}),`Save a copy in the IMAP Sent folder (leave off if your provider does this automatically)`]}),(0,Q.jsxs)(`label`,{className:`flex gap-2 text-xs`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:m.smtp.allowAgentSend,onChange:e=>B(`smtp`,{...m.smtp,allowAgentSend:e.target.checked})}),`Allow agents with workspace write access to send`]})]}),(0,Q.jsxs)(`div`,{className:`grid gap-2`,children:[(0,Q.jsx)(G,{htmlFor:`imap-lookback`,children:`Sync lookback (days)`}),(0,Q.jsx)(W,{id:`imap-lookback`,type:`number`,min:`0`,value:String(m.initialSyncDays),onChange:e=>B(`initialSyncDays`,Number(e.target.value||0))}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Used for the first sync and every Resync. Set 0 to import the whole mailbox. Saving a wider window automatically fetches older mail; a narrower window keeps messages already imported.`})]}),(0,Q.jsxs)(`div`,{className:`grid gap-2`,children:[(0,Q.jsx)(G,{htmlFor:`imap-poll`,children:`Poll interval (ms)`}),(0,Q.jsx)(W,{id:`imap-poll`,type:`number`,value:String(m.pollInterval),onChange:e=>B(`pollInterval`,Number(e.target.value||6e4))})]}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap gap-4 text-xs`,children:[(0,Q.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:m.tls,onChange:e=>B(`tls`,e.target.checked)}),` TLS`]}),(0,Q.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:m.allowSelfSigned,onChange:e=>B(`allowSelfSigned`,e.target.checked)}),` Allow self-signed`]})]})]});return(0,Q.jsxs)(`div`,{className:`space-y-3 border-t pt-4`,children:[(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsx)(`h4`,{className:`text-sm font-medium`,children:`IMAP Accounts`}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Each account mirrors under /imap/<account> in the backends tree; its folders are synced as containers. Click an account to edit its settings.`})]}),!t&&(0,Q.jsx)(`p`,{className:`rounded-md border bg-muted/40 p-2 text-xs text-muted-foreground`,children:`Enable the IMAP service before polling starts. Accounts can still be configured while it is off.`}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,Q.jsxs)(U,{size:`sm`,variant:`outline`,onClick:u?le:de,children:[(0,Q.jsx)(me,{className:`h-3 w-3`}),` `,u?`Cancel`:`New Account`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`ghost`,onClick:z,disabled:O,children:[O?(0,Q.jsx)(R,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(w,{className:`h-3 w-3`}),` Refresh`]})]}),(0,Q.jsx)(`div`,{className:`rounded-md border`,children:(0,Q.jsx)(`div`,{className:`max-h-[32rem] overflow-y-auto p-2 space-y-1`,children:ie.length===0?(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No IMAP accounts configured.`}):ie.map(t=>{let n=c===t.address;return(0,Q.jsxs)(`div`,{className:`rounded px-2 py-2 text-xs transition-colors ${n?`bg-accent/40`:`hover:bg-accent/50`}`,children:[(0,Q.jsxs)(`button`,{onClick:()=>ue(t),className:`w-full text-left`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,Q.jsxs)(`span`,{className:`flex items-center gap-1.5 font-medium`,children:[n?(0,Q.jsx)(v,{className:`h-3 w-3 shrink-0`}):(0,Q.jsx)(f,{className:`h-3 w-3 shrink-0`}),t.address]}),(0,Q.jsx)(`span`,{className:`shrink-0 ${t.status===`error`?`text-destructive`:`text-muted-foreground`}`,children:t.status})]}),(0,Q.jsxs)(`div`,{className:`pl-[18px] text-muted-foreground`,children:[String(t.config?.host||``),` · `,(t.containers||[]).length,` folder(s)`]}),t.lastError&&(0,Q.jsxs)(`div`,{className:`pl-[18px] text-destructive`,children:[`error: `,t.lastError]})]}),n&&(0,Q.jsxs)(`div`,{className:`mt-2 space-y-3 border-t pt-2`,children:[(0,Q.jsx)(`div`,{className:`space-y-0.5`,children:(t.containers||[]).map(n=>(0,Q.jsxs)(`div`,{className:`flex items-center justify-between gap-2 rounded bg-background/60 px-2 py-1`,children:[(0,Q.jsx)(`span`,{className:`font-mono truncate`,children:n.name}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-1 shrink-0`,children:[(0,Q.jsx)(`button`,{title:`Resync folder`,className:`p-1 hover:bg-accent rounded`,onClick:()=>ve(t.address,n.name),disabled:!!P||x||N,children:P===`folder-sync:${t.address}:${n.name}`?(0,Q.jsx)(R,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(w,{className:`h-3 w-3`})}),(0,Q.jsx)(`button`,{title:`Remove folder`,className:`p-1 hover:bg-destructive/10 text-destructive rounded`,onClick:()=>je(e,`imap`,t.address,n.name).then(z).catch(e=>I({title:`Error`,description:e instanceof Error?e.message:`Failed to remove folder`,variant:`destructive`})),disabled:!!P,children:(0,Q.jsx)(A,{className:`h-3 w-3`})})]})]},n.name))}),(t.containers||[]).map(n=>(0,Q.jsxs)(`div`,{className:`rounded border p-2`,children:[(0,Q.jsx)(`p`,{className:`mb-2 font-medium`,children:n.name}),(0,Q.jsx)(Mn,{value:n.removalPolicy,backends:i,source:`imap:${n.mailboxId}`,destination:!1,onSave:async r=>{await Fn(e,t.address,n.name,r),await z()}})]},`removal:${n.name}`)),V,(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:x?`Save changes before testing, discovering folders or resyncing.`:`Test and Discover folders use the saved connection settings. Resync fetches the saved lookback window again.`}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,Q.jsxs)(U,{size:`sm`,onClick:he,disabled:N||!x,children:[N?(0,Q.jsx)(R,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(p,{className:`h-3 w-3`}),` Save`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`outline`,disabled:!!P||x||N,onClick:()=>_e(`test`,t.address),children:[P===`test:${t.address}`?(0,Q.jsx)(R,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(me,{className:`h-3 w-3`}),` Test`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`outline`,disabled:!!P||x||N,onClick:()=>_e(`sync`,t.address),children:[P===`sync:${t.address}`?(0,Q.jsx)(R,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(w,{className:`h-3 w-3`}),` Resync all`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`destructive`,disabled:!!P,onClick:ge,children:[P===`delete:${t.address}`?(0,Q.jsx)(R,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(A,{className:`h-3 w-3`}),` Delete`]})]})]})]},t.address)})})}),u&&(0,Q.jsxs)(`div`,{className:`space-y-3 rounded-md border p-3`,children:[(0,Q.jsx)(`h5`,{className:`text-xs font-medium`,children:`New IMAP account`}),V,(0,Q.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,Q.jsxs)(U,{size:`sm`,onClick:he,disabled:N,children:[N?(0,Q.jsx)(R,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(p,{className:`h-3 w-3`}),` Save`]}),(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,onClick:le,children:`Cancel`})]})]})]})}function li({workspaceId:e}){let[t,n]=(0,Z.useState)([]),[r,i]=(0,Z.useState)(!0),[a,o]=(0,Z.useState)(!1),[s,c]=(0,Z.useState)(``),[l,u]=(0,Z.useState)([`read`]),[d,f]=(0,Z.useState)(``),[p,m]=(0,Z.useState)(!0),[h,g]=(0,Z.useState)(!1),[_,v]=(0,Z.useState)(new Set),[y,b]=(0,Z.useState)(``),[x,S]=(0,Z.useState)(!1),{showToast:w}=q(),[T,E]=(0,Z.useState)(e);e!==T&&(E(e),i(!0));let D=(0,Z.useCallback)(()=>K.get(`/workspaces/${e}/tokens`).then(e=>{n((e||[]).map(e=>({...e,hash:e.hash||e.tokenHash||``})).filter(e=>e.hash))}).catch(()=>{w({title:`Error`,description:`Failed to load workspace tokens`,variant:`destructive`})}).finally(()=>{i(!1)}),[e,w]);(0,Z.useEffect)(()=>{D()},[D]);let ee=async()=>{if(!s.trim()){w({title:`Error`,description:`Token description is required`,variant:`destructive`});return}try{o(!0);let t=null;if(!p&&d){let e=new Date(d);e.setHours(23,59,59,999),t=e.toISOString()}let n={description:s,permissions:l,expiresAt:t},r=await K.post(`/workspaces/${e}/tokens`,n);r&&r.token&&b(r.token),c(``),u([`read`]),f(``),m(!0),g(!1),i(!0),await D(),w({title:`Success`,description:`Workspace sharing token created successfully`,variant:`default`})}catch{w({title:`Error`,description:`Failed to create token`,variant:`destructive`})}finally{o(!1)}},O=async t=>{try{await K.delete(`/workspaces/${e}/tokens/${t}`),w({title:`Success`,description:`Token revoked successfully`}),i(!0),await D()}catch{w({title:`Error`,description:`Failed to revoke token`,variant:`destructive`})}},j=e=>{let t=new Set(_);t.has(e)?t.delete(e):t.add(e),v(t)},M=e=>_.has(e)?e:`${e.substring(0,8)}...${e.substring(e.length-8)}`,te=async e=>{try{await navigator.clipboard.writeText(e),w({title:`Copied`,description:`Token hash copied to clipboard`})}catch{w({title:`Error`,description:`Failed to copy to clipboard`,variant:`destructive`})}};return(0,Q.jsxs)(`div`,{className:`space-y-4`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between`,children:[(0,Q.jsx)(`h3`,{className:`text-lg font-medium`,children:`Workspace Sharing Tokens`}),(0,Q.jsxs)(U,{onClick:()=>g(!h),size:`sm`,variant:`outline`,children:[(0,Q.jsx)(C,{className:`mr-2 h-4 w-4`}),`Create Token`]})]}),h&&(0,Q.jsxs)(`div`,{className:`border rounded-lg p-4 space-y-4 bg-muted/50`,children:[(0,Q.jsx)(W,{placeholder:`Token description (e.g., 'John's laptop')`,value:s,onChange:e=>c(e.target.value)}),(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`label`,{className:`text-sm font-medium mb-2 block`,children:`Permissions`}),(0,Q.jsx)(`div`,{className:`flex gap-2 flex-wrap`,children:[`read`,`write`,`admin`].map(e=>(0,Q.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:l.includes(e),onChange:t=>{t.target.checked?u([...l,e]):u(l.filter(t=>t!==e))}}),(0,Q.jsx)(`span`,{className:`text-sm`,children:e})]},e))})]}),(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`label`,{className:`text-sm font-medium mb-2 block`,children:`Token Expiry`}),(0,Q.jsxs)(`div`,{className:`space-y-3`,children:[(0,Q.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:p,onChange:e=>{m(e.target.checked),e.target.checked&&f(``)}}),(0,Q.jsx)(`span`,{className:`text-sm`,children:`Never expires`})]}),!p&&(0,Q.jsx)(W,{type:`date`,placeholder:`Expiry date`,value:d,onChange:e=>f(e.target.value),min:new Date().toISOString().split(`T`)[0]})]})]}),(0,Q.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Q.jsx)(U,{onClick:ee,disabled:a,children:`Create Token`}),(0,Q.jsx)(U,{variant:`outline`,onClick:()=>g(!1),children:`Cancel`})]})]}),y&&(0,Q.jsxs)(`div`,{className:`p-4 border rounded-md bg-muted/50`,children:[(0,Q.jsxs)(`div`,{className:`flex justify-between items-center mb-2`,children:[(0,Q.jsx)(`h3`,{className:`font-semibold`,children:`Your New Workspace Sharing Token`}),(0,Q.jsx)(U,{variant:`outline`,size:`sm`,onClick:async()=>{try{await navigator.clipboard.writeText(y),S(!0),w({title:`Success`,description:`Token copied to clipboard`}),setTimeout(()=>S(!1),2e3)}catch{w({title:`Error`,description:`Failed to copy token to clipboard`,variant:`destructive`})}},children:x?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(k,{className:`mr-2 h-4 w-4`}),`Copied`]}):(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(_e,{className:`mr-2 h-4 w-4`}),`Copy`]})})]}),(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground mb-2`,children:`Make sure to copy your workspace sharing token now. You won't be able to see it again!`}),(0,Q.jsx)(`div`,{className:`p-2 bg-background border rounded font-mono text-sm break-all outline-none focus:ring-2 focus:ring-ring`,children:y}),(0,Q.jsx)(U,{variant:`outline`,size:`sm`,className:`mt-2`,onClick:()=>b(``),children:`Dismiss`})]}),(0,Q.jsx)(ui,{workspaceId:e}),r?(0,Q.jsxs)(`div`,{className:`text-center py-4`,children:[(0,Q.jsx)(`div`,{className:`animate-spin rounded-full h-6 w-6 border-b-2 border-primary mx-auto touch-target`}),(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground mt-2`,children:`Loading tokens...`})]}):t.length===0?(0,Q.jsxs)(`div`,{className:`text-center py-8 text-muted-foreground`,children:[(0,Q.jsx)(`p`,{children:`No sharing tokens created yet.`}),(0,Q.jsx)(`p`,{className:`text-sm`,children:`Create a token to share this workspace with others.`})]}):(0,Q.jsx)(`div`,{className:`space-y-3`,children:t.map(e=>(0,Q.jsxs)(`div`,{className:`border rounded-lg p-3 flex items-center justify-between`,children:[(0,Q.jsxs)(`div`,{className:`flex-1`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2 mb-1`,children:[(0,Q.jsx)(`span`,{className:`font-medium text-sm`,children:e.description}),(0,Q.jsx)(`span`,{className:`text-xs bg-muted px-2 py-1 rounded`,children:e.permissions.join(`, `)})]}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,Q.jsx)(`span`,{className:`font-mono`,children:M(e.hash)}),(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,onClick:()=>j(e.hash),children:_.has(e.hash)?(0,Q.jsx)(z,{className:`h-3 w-3`}):(0,Q.jsx)(L,{className:`h-3 w-3`})}),(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,onClick:()=>te(e.hash),children:(0,Q.jsx)(_e,{className:`h-3 w-3`})})]}),(0,Q.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[`Created: `,new Date(e.createdAt).toLocaleDateString(),e.expiresAt?(0,Q.jsxs)(`span`,{children:[` • Expires: `,new Date(e.expiresAt).toLocaleDateString()]}):(0,Q.jsx)(`span`,{children:` • Never expires`})]})]}),(0,Q.jsx)(U,{size:`sm`,variant:`outline`,onClick:()=>O(e.hash),className:`text-destructive hover:text-destructive`,children:(0,Q.jsx)(A,{className:`h-4 w-4`})})]},e.hash))})]})}function ui({workspaceId:e}){let{showToast:t}=q(),[n,r]=(0,Z.useState)(``),[i,a]=(0,Z.useState)([`read`]),[o,s]=(0,Z.useState)(!1),[c,l]=(0,Z.useState)([]),u=(0,Z.useCallback)(()=>K.get(`/workspaces/${e}/shares`).then(e=>l(Array.isArray(e)?e:e?.emailShares||[])).catch(()=>{}),[e]);(0,Z.useEffect)(()=>{u()},[u]);let d=async()=>{if(n.trim())try{s(!0),await K.post(`/workspaces/${e}/shares`,{userEmail:n.trim(),permissions:i}),r(``),a([`read`]),await u(),t({title:`Success`,description:`Access granted`})}catch(e){t({title:`Error`,description:e instanceof Error?e.message:`Failed to grant access`,variant:`destructive`})}finally{s(!1)}},f=async n=>{try{await K.delete(`/workspaces/${e}/shares/${encodeURIComponent(n)}`),await u(),t({title:`Success`,description:`Access revoked`})}catch(e){t({title:`Error`,description:e instanceof Error?e.message:`Failed to revoke`,variant:`destructive`})}};return(0,Q.jsxs)(`div`,{className:`space-y-3`,children:[(0,Q.jsx)(`h4`,{className:`font-medium`,children:`Email-based Shares`}),(0,Q.jsxs)(`div`,{className:`flex gap-2 flex-wrap items-center`,children:[(0,Q.jsx)(W,{placeholder:`user@local.server`,value:n,onChange:e=>r(e.target.value),className:`max-w-xs`}),(0,Q.jsx)(`div`,{className:`flex gap-2`,children:[`read`,`write`,`admin`].map(e=>(0,Q.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:i.includes(e),onChange:t=>{t.target.checked?a([...i,e]):a(i.filter(t=>t!==e))}}),e]},e))}),(0,Q.jsx)(U,{size:`sm`,onClick:d,disabled:o,children:`Add`})]}),c.length>0&&(0,Q.jsx)(`div`,{className:`space-y-2`,children:c.map(e=>(0,Q.jsxs)(`div`,{className:`flex items-center justify-between border rounded p-2`,children:[(0,Q.jsxs)(`div`,{className:`text-sm`,children:[(0,Q.jsx)(`div`,{className:`font-medium`,children:e.userEmail}),(0,Q.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[e.permissions.join(`, `),e.grantedAt?` • ${new Date(e.grantedAt).toLocaleDateString()}`:``]})]}),(0,Q.jsx)(U,{size:`sm`,variant:`outline`,onClick:()=>f(e.userEmail),className:`text-destructive hover:text-destructive`,children:`Revoke`})]},e.userEmail))})]})}var di=[{value:`read`,label:`Read`,hint:`Browse and search`},{value:`write`,label:`Read & write`,hint:`Add, edit and organise documents`},{value:`admin`,label:`Admin`,hint:`Everything except sharing and deleting`}],fi=e=>e.includes(`admin`)?`admin`:e.includes(`write`)?`write`:`read`;function pi({workspaceId:e}){let{showToast:t}=q(),[n,r]=(0,Z.useState)(null),[i,a]=(0,Z.useState)(!0),[o,s]=(0,Z.useState)(null),[c,l]=(0,Z.useState)(`user`),[u,d]=(0,Z.useState)(``),[f,p]=(0,Z.useState)(`read`),[m,g]=(0,Z.useState)(e);e!==m&&(g(e),a(!0));let v=(0,Z.useCallback)(()=>De(e).then(r).catch(()=>r(null)).finally(()=>a(!1)),[e]);(0,Z.useEffect)(()=>{v()},[v]);let b=n?.isOwner===!0,x=n?.members??[],S=async n=>{n.preventDefault();let r=u.trim();if(r){s(`grant`);try{let n=await Qe(e,c===`user`?{email:r}:{group:r},[f]);d(``),await v();let i=c===`user`?r:`group ${r}`;t({title:`Shared`,description:n.type===`user`&&n.userExists===!1?`${i} has no account here yet — the share applies as soon as they sign in.`:`Access granted to ${i}.`})}catch(e){t({title:`Could not share`,description:e instanceof Error?e.message:`Unknown error`,variant:`destructive`})}finally{s(null)}}},C=async(n,r)=>{let i=`${n.type}:${n.principal}`;s(i);try{await Y(e,n.type,n.principal,[r]),await v()}catch(e){t({title:`Could not update`,description:e instanceof Error?e.message:`Unknown error`,variant:`destructive`})}finally{s(null)}},T=async n=>{let r=`${n.type}:${n.principal}`;s(r);try{await Ve(e,n.type,n.principal),await v(),t({title:`Access revoked`,description:`${n.principal} no longer has access.`})}catch(e){t({title:`Could not revoke`,description:e instanceof Error?e.message:`Unknown error`,variant:`destructive`})}finally{s(null)}};return n&&n.shareable===!1?(0,Q.jsx)(`div`,{className:`rounded-md border border-dashed p-4 text-sm text-muted-foreground`,children:`Your universe workspace is private and cannot be shared.`}):(0,Q.jsxs)(`div`,{className:`space-y-4`,children:[(0,Q.jsxs)(`div`,{className:`space-y-3`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:b?`People and groups who can open this workspace.`:`You are a member of this workspace; only its owner can change the list.`}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>{a(!0),v()},disabled:i,children:[(0,Q.jsx)(w,{className:ye(`mr-2 h-3.5 w-3.5`,i&&`animate-spin`)}),`Refresh`]})]}),i?(0,Q.jsx)(`div`,{className:`rounded-md border p-4 text-sm text-muted-foreground`,children:`Loading members…`}):x.length===0?(0,Q.jsx)(`div`,{className:`rounded-md border border-dashed p-4 text-sm text-muted-foreground`,children:`Not shared with anyone.`}):x.map(e=>{let t=`${e.type}:${e.principal}`,n=fi(e.permissions);return(0,Q.jsxs)(`div`,{className:`flex flex-col gap-2 rounded-md border p-3 sm:flex-row sm:items-center sm:justify-between`,children:[(0,Q.jsxs)(`div`,{className:`flex min-w-0 items-start gap-2`,children:[e.type===`group`?(0,Q.jsx)(_,{className:`mt-0.5 h-4 w-4 shrink-0 text-muted-foreground`}):(0,Q.jsx)(ee,{className:`mt-0.5 h-4 w-4 shrink-0 text-muted-foreground`}),(0,Q.jsxs)(`div`,{className:`min-w-0`,children:[(0,Q.jsx)(`div`,{className:`truncate text-sm font-medium`,title:e.principal,children:e.principal}),(0,Q.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[e.type===`group`?`Group`:`User`,e.description?` · ${e.description}`:``,e.grantedAt?` · since ${new Date(e.grantedAt).toLocaleDateString()}`:``]}),e.type===`user`&&e.userExists===!1&&(0,Q.jsxs)(`div`,{className:`mt-1 flex items-center gap-1 text-[11px] text-warning`,children:[(0,Q.jsx)(y,{className:`h-3 w-3`}),` No account yet — applies at first sign-in`]})]})]}),(0,Q.jsxs)(`div`,{className:`flex shrink-0 items-center gap-2`,children:[b?(0,Q.jsx)(`select`,{value:n,disabled:o===t,onChange:t=>C(e,t.target.value),className:`rounded-md border border-input bg-background px-2 py-1 text-xs focus:outline-none focus:ring-2 focus:ring-ring`,title:`Access level`,children:di.map(e=>(0,Q.jsx)(`option`,{value:e.value,children:e.label},e.value))}):(0,Q.jsx)(`span`,{className:`rounded-full bg-muted px-2 py-0.5 text-xs`,children:di.find(e=>e.value===n)?.label}),b&&(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,className:`text-destructive hover:text-destructive`,disabled:o===t,onClick:()=>T(e),title:`Revoke access`,children:(0,Q.jsx)(h,{className:`h-3.5 w-3.5`})})]})]},t)})]}),b&&(0,Q.jsxs)(`form`,{onSubmit:S,className:`space-y-3 rounded-md border p-3`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 text-sm font-medium`,children:[`Share with`,(0,Q.jsx)(`div`,{className:`inline-flex overflow-hidden rounded-md border`,children:[`user`,`group`].map(e=>(0,Q.jsx)(`button`,{type:`button`,onClick:()=>l(e),className:ye(`px-3 py-1 text-xs`,c===e?`bg-primary text-primary-foreground`:`bg-background text-muted-foreground hover:bg-muted`),children:e===`user`?`a person`:`a group`},e))})]}),(0,Q.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-[1fr_auto]`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(W,{type:c===`user`?`email`:`text`,value:u,onChange:e=>d(e.target.value),placeholder:c===`user`?`colleague@corp.tld`:`team-a or cn=team-a,ou=groups,dc=corp,dc=tld`,autoComplete:`off`}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:c===`user`?`The share is keyed by e-mail: it works even before the person signs in for the first time.`:`Directory group (LDAP/AD memberOf — full DN or just the CN) or a group an admin assigned to local accounts.`})]}),(0,Q.jsx)(`div`,{children:(0,Q.jsx)(`select`,{value:f,onChange:e=>p(e.target.value),className:`w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring`,title:di.find(e=>e.value===f)?.hint,children:di.map(e=>(0,Q.jsx)(`option`,{value:e.value,children:e.label},e.value))})})]}),(0,Q.jsx)(U,{type:`submit`,size:`sm`,disabled:o===`grant`||!u.trim(),children:o===`grant`?`Sharing…`:`Share workspace`})]})]})}function mi({workspaceId:e,workspaceName:t,isActive:n,onChanged:r}){let{showToast:i}=q(),[a,o]=(0,Z.useState)([]),[s,c]=(0,Z.useState)(!0),[l,u]=(0,Z.useState)(!1),[d,f]=(0,Z.useState)(!1),[p,m]=(0,Z.useState)(!1),[h,_]=(0,Z.useState)(null),[v,y]=(0,Z.useState)(!1),b=(0,Z.useCallback)(async()=>{try{o(await st(e))}catch{o([])}finally{c(!1)}},[e]);(0,Z.useEffect)(()=>{b()},[b]);let x=async()=>{if(!(n&&!window.confirm(`'${t}' is running. It has to be stopped to be exported — its index is only consistent on disk once it has flushed.\n\nStop it and export now?`))){u(!0);try{let t=await Fe(e,{stop:!0});f(t.stoppedWorkspace),i({title:`Workspace exported`,description:`${t.name} — ${Le(t.size)}`}),await b(),t.stoppedWorkspace&&r?.()}catch(e){i({title:`Export failed`,description:e instanceof Error?e.message:`Failed to export workspace`,variant:`destructive`})}finally{u(!1)}}},S=async()=>{m(!0);try{await X(e),f(!1),i({title:`Workspace started`,description:`'${t}' is running again.`}),r?.()}catch(e){i({title:`Error`,description:e instanceof Error?e.message:`Failed to start workspace`,variant:`destructive`})}finally{m(!1)}},C=async e=>{_(e.name);try{await ke(e.name)}catch(e){i({title:`Download failed`,description:e instanceof Error?e.message:`Failed to download archive`,variant:`destructive`})}finally{_(null)}},T=async e=>{if(window.confirm(`Delete ${e.name}? The archive is removed from the server; any copy you already downloaded is untouched.`)){_(e.name);try{await Oe(e.name),await b()}catch(e){i({title:`Error`,description:e instanceof Error?e.message:`Failed to delete archive`,variant:`destructive`})}finally{_(null)}}};return(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(g,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Export & Import`})]}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Export packs this workspace into a single compressed archive you can download and move elsewhere. The workspace is stopped first — an export of a running workspace would capture a half-written index.`}),(0,Q.jsxs)(`div`,{className:`mt-3 flex flex-wrap items-center gap-2`,children:[(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:l,onClick:x,children:[(0,Q.jsx)(g,{className:`mr-2 h-3.5 w-3.5 ${l?`animate-pulse`:``}`}),l?`Exporting…`:`Export Workspace`]}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:s||l,onClick:()=>void b(),children:[(0,Q.jsx)(w,{className:`mr-2 h-3.5 w-3.5 ${s?`animate-spin`:``}`}),`Refresh`]}),d&&(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:p,onClick:S,children:p?`Starting…`:`Start workspace again`})]}),(0,Q.jsxs)(`div`,{className:`mt-4 border-t pt-3`,children:[(0,Q.jsx)(`h3`,{className:`text-xs font-semibold uppercase tracking-wide text-muted-foreground`,children:`Archives`}),s?(0,Q.jsx)(`p`,{className:`mt-2 text-xs text-muted-foreground`,children:`Loading…`}):a.length===0?(0,Q.jsx)(`p`,{className:`mt-2 text-xs text-muted-foreground`,children:`No exports yet.`}):(0,Q.jsx)(`ul`,{className:`mt-2 space-y-2`,children:a.map(e=>(0,Q.jsxs)(`li`,{className:`flex flex-col gap-2 rounded-md border p-2 sm:flex-row sm:items-center sm:justify-between`,children:[(0,Q.jsxs)(`div`,{className:`min-w-0`,children:[(0,Q.jsx)(`div`,{className:`truncate font-mono text-xs`,children:e.name}),(0,Q.jsxs)(`div`,{className:`mt-0.5 text-[11px] text-muted-foreground`,children:[Le(e.size),` · `,new Date(e.createdAt).toLocaleString()]})]}),(0,Q.jsxs)(`div`,{className:`flex shrink-0 gap-2`,children:[(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:h===e.name,onClick:()=>C(e),title:`Download (${Le(e.size)})`,children:(0,Q.jsx)(F,{className:`h-3.5 w-3.5`})}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:h===e.name,onClick:()=>T(e),className:`text-destructive hover:text-destructive`,title:`Delete archive`,children:(0,Q.jsx)(A,{className:`h-3.5 w-3.5`})})]})]},e.name))})]}),(0,Q.jsxs)(`div`,{className:`mt-4 space-y-3 border-t pt-3`,children:[(0,Q.jsx)(`h3`,{className:`text-xs font-semibold uppercase tracking-wide text-muted-foreground`,children:`Import workspace`}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:l,onClick:()=>y(e=>!e),children:v?`Close import`:`Import workspace…`}),v&&(0,Q.jsx)(Bn,{disabled:l,onImported:()=>r?.()})]})]})}var hi={"workspace:home":{title:`Workspace Home`,description:`User-managed roaming home folder, exported through WebDAV and watched for changes. Mirrored into the backends tree.`},"workspace:data":{title:`Workspace Data`,description:`Managed content-addressable blob store (deduped, checksum-keyed). Opaque by design: the tree is the navigation; not meant for direct edits or export.`},imap:{title:`IMAP`,description:`Mailbox data source; ingested mail lands in the backends tree.`}},gi=[{id:`git`,title:`Git`,description:`Git-backed dotfile access.`},{id:`webdav`,title:`WebDAV`,description:`Mountable access to the workspace home backend.`},{id:`imapSync`,title:`IMAP Sync Worker`,description:`Background mailbox polling and ingestion.`}];function _i({checked:e,disabled:t,onClick:n}){return(0,Q.jsx)(`button`,{type:`button`,role:`switch`,"aria-checked":e,disabled:t,onClick:n,className:`relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${e?`bg-primary`:`bg-muted-foreground/30`}`,children:(0,Q.jsx)(`span`,{className:`pointer-events-none inline-block h-4 w-4 rounded-full bg-background shadow transition-transform ${e?`translate-x-4`:`translate-x-0`}`})})}function vi({workspaceId:e,onAdded:t}){let[n,r]=(0,Z.useState)(!1),[i,a]=(0,Z.useState)(``),[o,s]=(0,Z.useState)(``),[c,l]=(0,Z.useState)(!1),[u,d]=(0,Z.useState)(!1),[f,p]=(0,Z.useState)(!1),{showToast:m}=q(),h=()=>{a(``),s(``),l(!1),d(!1)};return n?(0,Q.jsxs)(`form`,{onSubmit:async n=>{if(n.preventDefault(),!(!i.trim()||!o.trim())){p(!0);try{await it(e,`file`,{name:i.trim(),path:o.trim(),watch:c,readOnly:u}),m({title:`Added`,description:`${i.trim()} mounted; initial scan running in the background`}),h(),r(!1),await t()}catch(e){m({title:`Error`,description:e instanceof Error?e.message:`Failed to add folder backend`,variant:`destructive`})}finally{p(!1)}}},className:`w-full space-y-3 rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(j,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Add local folder`})]}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Index a folder on the server host as a data source. Files stay in place; the index records them as device-scoped locations, so they remain addressable if the workspace moves to another server.`}),(0,Q.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsx)(`label`,{className:`text-xs font-medium`,children:`Name`}),(0,Q.jsx)(W,{value:i,onChange:e=>a(e.target.value),placeholder:`Financial Reports`,required:!0})]}),(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsx)(`label`,{className:`text-xs font-medium`,children:`Absolute path on server`}),(0,Q.jsx)(W,{value:o,onChange:e=>s(e.target.value),placeholder:`/mnt/data/reports`,required:!0,className:`font-mono`})]})]}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-6 text-[11px] text-muted-foreground`,children:[(0,Q.jsxs)(`label`,{className:`flex items-center gap-1.5`,children:[`watch`,(0,Q.jsx)(_i,{checked:c,onClick:()=>l(e=>!e)})]}),(0,Q.jsxs)(`label`,{className:`flex items-center gap-1.5`,title:`Never delete bytes in this folder. Destroy degrades to a reference drop. Recommended for folders managed outside Canvas.`,children:[`read-only`,(0,Q.jsx)(_i,{checked:u,onClick:()=>d(e=>!e)})]})]}),(0,Q.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Q.jsx)(U,{type:`submit`,size:`sm`,disabled:f||!i.trim()||!o.trim(),children:f?`Adding…`:`Add folder`}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,disabled:f,onClick:()=>{h(),r(!1)},children:`Cancel`})]})]}):(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>r(!0),children:[(0,Q.jsx)(j,{className:`mr-2 h-3.5 w-3.5`}),`Add local folder`]})}function yi({workspaceId:e,onAdded:t}){let[n,r]=(0,Z.useState)(!1),[i,a]=(0,Z.useState)({name:``,clientId:``,clientSecret:``,refreshToken:``,folderId:``}),[o,s]=(0,Z.useState)(!1),[l,u]=(0,Z.useState)(!1),[d,f]=(0,Z.useState)(!1),{showToast:p}=q(),m=e=>t=>a(n=>({...n,[e]:t.target.value})),h=()=>{a({name:``,clientId:``,clientSecret:``,refreshToken:``,folderId:``}),s(!1),u(!1)},g=i.name.trim()&&i.clientId.trim()&&i.clientSecret.trim()&&i.refreshToken.trim();return n?(0,Q.jsxs)(`form`,{onSubmit:async n=>{if(n.preventDefault(),g){f(!0);try{await it(e,`gdrive`,{name:i.name.trim(),clientId:i.clientId.trim(),clientSecret:i.clientSecret.trim(),refreshToken:i.refreshToken.trim(),folderId:i.folderId.trim()||`root`,watch:o,readOnly:l}),p({title:`Added`,description:`${i.name.trim()} connected; initial scan running in the background`}),h(),r(!1),await t()}catch(e){p({title:`Error`,description:e instanceof Error?e.message:`Failed to add Google Drive backend`,variant:`destructive`})}finally{f(!1)}}},className:`w-full space-y-3 rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(c,{icon:`mdi:google-drive`,width:16,height:16,className:`text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Add Google Drive`})]}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Index a Drive folder (or the whole My Drive) as a remote data source. Files are hashed from Drive metadata, uploads are queued through the workspace cache. Needs a Google OAuth client (Web application) — enter its id and secret, then sign in to obtain the offline-access refresh token, or paste one you already have.`}),(0,Q.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsx)(`label`,{className:`text-xs font-medium`,children:`Name`}),(0,Q.jsx)(W,{value:i.name,onChange:m(`name`),placeholder:`Work Drive`,required:!0})]}),(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsx)(`label`,{className:`text-xs font-medium`,children:`Folder id (empty = My Drive root)`}),(0,Q.jsx)(W,{value:i.folderId,onChange:m(`folderId`),placeholder:`1AbC…`,className:`font-mono`})]}),(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsx)(`label`,{className:`text-xs font-medium`,children:`OAuth client id`}),(0,Q.jsx)(W,{value:i.clientId,onChange:m(`clientId`),required:!0,className:`font-mono`})]}),(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsx)(`label`,{className:`text-xs font-medium`,children:`OAuth client secret`}),(0,Q.jsx)(W,{type:`password`,value:i.clientSecret,onChange:m(`clientSecret`),required:!0,className:`font-mono`,autoComplete:`off`})]}),(0,Q.jsxs)(`div`,{className:`space-y-1 sm:col-span-2`,children:[(0,Q.jsx)(`label`,{className:`text-xs font-medium`,children:`Refresh token`}),(0,Q.jsx)(W,{type:`password`,value:i.refreshToken,onChange:m(`refreshToken`),required:!0,className:`font-mono`,autoComplete:`off`,placeholder:`Sign in below, or paste an offline-access refresh token`}),(0,Q.jsx)(ti,{clientId:i.clientId,clientSecret:i.clientSecret,scope:l?`drive.readonly`:`drive`,disabled:d,onLinked:({refreshToken:e,account:t})=>a(n=>({...n,refreshToken:e,name:n.name.trim()||(t?`Drive (${t})`:n.name)}))})]})]}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-6 text-[11px] text-muted-foreground`,children:[(0,Q.jsxs)(`label`,{className:`flex items-center gap-1.5`,title:`Poll the Drive changes feed (every 60s) so edits made in Drive show up without a manual re-sync.`,children:[`watch`,(0,Q.jsx)(_i,{checked:o,onClick:()=>s(e=>!e)})]}),(0,Q.jsxs)(`label`,{className:`flex items-center gap-1.5`,title:`Never delete files in this Drive. Destroy degrades to a reference drop.`,children:[`read-only`,(0,Q.jsx)(_i,{checked:l,onClick:()=>u(e=>!e)})]})]}),(0,Q.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Q.jsx)(U,{type:`submit`,size:`sm`,disabled:d||!g,children:d?`Checking…`:`Add Drive`}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,disabled:d,onClick:()=>{h(),r(!1)},children:`Cancel`})]})]}):(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>r(!0),children:[(0,Q.jsx)(c,{icon:`mdi:google-drive`,width:14,height:14,className:`mr-2`}),`Add Google Drive`]})}function bi({workspaceId:e,backend:t}){let[n,r]=(0,Z.useState)(t.usage??null),[i,a]=(0,Z.useState)(!1),{showToast:o}=q();return(0,Q.jsxs)(`button`,{type:`button`,onClick:async()=>{a(!0);try{r(await Ae(e,t.driver,t.address))}catch(e){o({title:`Error`,description:e instanceof Error?e.message:`Failed to compute size`,variant:`destructive`})}finally{a(!1)}},disabled:i,className:`flex items-center gap-1.5 rounded border px-2 py-1 text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-50`,title:n?`Computed ${new Date(n.computedAt).toLocaleString()}; click to refresh`:`Compute on-disk size (walks the backend folder; may take a while)`,children:[(0,Q.jsx)(V,{className:`h-3 w-3 ${i?`animate-pulse`:``}`}),i?`Calculating…`:n?Le(n.bytes):`Size`]})}function xi({workspaceId:e}){let[t,n]=(0,Z.useState)(!1),{showToast:r}=q();return(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:t,onClick:async()=>{n(!0);try{let{removed:t}=await nt(e);r({title:`Thumbnail cache cleared`,description:`${t} cached thumbnail(s) removed`})}catch(e){r({title:`Error`,description:e instanceof Error?e.message:`Failed to clear thumbnail cache`,variant:`destructive`})}finally{n(!1)}},title:`Remove all cached thumbnails; they regenerate on demand`,children:[(0,Q.jsx)(A,{className:`mr-2 h-3.5 w-3.5 ${t?`animate-pulse`:``}`}),t?`Clearing…`:`Clear thumbnails`]})}function Si({workspaceId:e}){let[t,n]=(0,Z.useState)(null),[r,i]=(0,Z.useState)(!1),{showToast:a}=q();return(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center justify-between gap-4`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(V,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Disk usage`})]}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Total on-disk size of this workspace (index, blobs, home, cache): what an export or sync would move. Computed on demand; may take a while on large workspaces.`})]}),(0,Q.jsxs)(`div`,{className:`flex shrink-0 items-center gap-3`,children:[t&&(0,Q.jsx)(`span`,{className:`text-sm font-semibold`,children:Le(t.bytes)}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:r,onClick:async()=>{i(!0);try{n(await at(e))}catch(e){a({title:`Error`,description:e instanceof Error?e.message:`Failed to compute workspace size`,variant:`destructive`})}finally{i(!1)}},children:[(0,Q.jsx)(w,{className:`mr-2 h-3.5 w-3.5 ${r?`animate-spin`:``}`}),r?`Calculating…`:t?`Recalculate`:`Calculate size`]})]})]}),t&&(0,Q.jsx)(`div`,{className:`mt-3 grid grid-cols-2 gap-x-6 gap-y-1 border-t pt-3 sm:grid-cols-3`,children:Object.entries(t.breakdown).sort(([,e],[,t])=>t-e).map(([e,t])=>(0,Q.jsxs)(`div`,{className:`flex items-baseline justify-between gap-2 text-xs`,children:[(0,Q.jsx)(`span`,{className:`truncate font-mono text-muted-foreground`,children:e}),(0,Q.jsx)(`span`,{children:Le(t)})]},e))})]})}function Ci({backend:e,busy:t,onSave:n}){let r=e.config||{},i=Array.isArray(r.exclude)?r.exclude:[],a=Array.isArray(r.effectiveExclusions)?r.effectiveExclusions:[],o=Math.max(a.length-i.length,0),[s,c]=(0,Z.useState)(!1),[l,u]=(0,Z.useState)(i.join(`
7
+ `)),[d,f]=(0,Z.useState)(!1),p=i.join(`
8
+ `),[m,h]=(0,Z.useState)(p);return m!==p&&(h(p),u(p)),(0,Q.jsxs)(`div`,{className:`mt-3 border-t pt-3`,children:[(0,Q.jsxs)(`button`,{type:`button`,className:`text-[11px] font-medium text-muted-foreground hover:text-foreground`,onClick:()=>c(!s),children:[`Sync exclusions `,i.length>0?`(${i.length} custom`:`(defaults only`,o>0?`, ${o} default)`:`)`,` `,s?`▾`:`▸`]}),s&&(0,Q.jsxs)(`div`,{className:`mt-2 space-y-2`,children:[(0,Q.jsxs)(`p`,{className:`text-[11px] text-muted-foreground`,children:[`One glob pattern per line, e.g. `,(0,Q.jsx)(`code`,{className:`font-mono`,children:`**/node_modules/**`}),` or `,(0,Q.jsx)(`code`,{className:`font-mono`,children:`**/target/**`}),`. Defaults (dotfiles, node_modules, __pycache__, caches, …) always apply. Changes take effect immediately for the watcher; run Re-sync to also unlink already-mirrored entries.`]}),(0,Q.jsx)(`textarea`,{className:`w-full min-h-[80px] rounded-md border bg-background p-2 font-mono text-xs`,value:l,onChange:e=>u(e.target.value),placeholder:`**/node_modules/**
9
+ **/dist/**`,spellCheck:!1}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(U,{type:`button`,size:`sm`,disabled:t||l.split(`
10
+ `).map(e=>e.trim()).filter(Boolean).join(`
11
+ `)===i.join(`
12
+ `),onClick:()=>n(l.split(`
13
+ `).map(e=>e.trim()).filter(Boolean)),children:`Save exclusions`}),o>0&&(0,Q.jsxs)(`button`,{type:`button`,className:`text-[11px] text-muted-foreground hover:text-foreground`,onClick:()=>f(!d),children:[d?`Hide`:`Show`,` default patterns`]})]}),d&&(0,Q.jsx)(`pre`,{className:`max-h-40 overflow-auto rounded-md bg-muted/50 p-2 font-mono text-[11px] text-muted-foreground`,children:a.filter(e=>!i.includes(e)).join(`
14
+ `)})]})]})}function wi({device:e,linked:t,busy:n,onLink:r,onUnlink:i,onUpdate:a}){let[o,s]=(0,Z.useState)(!1),[c,l]=(0,Z.useState)(e.name),[u,d]=(0,Z.useState)(e.description??``),f=()=>{c.trim()&&(c!==e.name||u!==(e.description??``))&&a({name:c.trim(),description:u.trim()||void 0}),s(!1)},p=()=>{l(e.name),d(e.description??``),s(!1)};return(0,Q.jsx)(`section`,{className:`rounded-lg border p-4`,children:(0,Q.jsxs)(`div`,{className:`flex items-start justify-between gap-4`,children:[(0,Q.jsxs)(`div`,{className:`flex items-start gap-3 min-w-0`,children:[(0,Q.jsx)(te,{className:`h-4 w-4 text-muted-foreground mt-0.5 shrink-0`}),(0,Q.jsxs)(`div`,{className:`min-w-0`,children:[o?(0,Q.jsxs)(`div`,{className:`space-y-2`,children:[(0,Q.jsx)(W,{value:c,onChange:e=>l(e.target.value),className:`h-7 text-sm`,placeholder:`Device name`,autoFocus:!0,onKeyDown:e=>{e.key===`Enter`&&f(),e.key===`Escape`&&p()}}),(0,Q.jsx)(W,{value:u,onChange:e=>d(e.target.value),className:`h-7 text-xs`,placeholder:`Description (optional)`,onKeyDown:e=>{e.key===`Enter`&&f(),e.key===`Escape`&&p()}})]}):(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(`div`,{className:`text-sm font-medium`,children:e.name}),e.description&&(0,Q.jsx)(`div`,{className:`text-xs text-muted-foreground mt-0.5`,children:e.description})]}),(0,Q.jsxs)(`div`,{className:`mt-1.5 flex flex-wrap gap-x-3 gap-y-0.5 text-[11px] text-muted-foreground font-mono`,children:[(0,Q.jsx)(`span`,{children:e.deviceId}),e.platform&&(0,Q.jsx)(`span`,{children:e.platform}),e.arch&&(0,Q.jsx)(`span`,{children:e.arch}),e.type&&(0,Q.jsx)(`span`,{children:e.type})]})]})]}),(0,Q.jsxs)(`div`,{className:`flex shrink-0 items-center gap-2`,children:[o?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:f,disabled:!c.trim(),children:(0,Q.jsx)(k,{className:`h-3.5 w-3.5`})}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,onClick:p,children:(0,Q.jsx)(x,{className:`h-3.5 w-3.5`})})]}):(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,onClick:()=>s(!0),title:`Edit name/description`,children:(0,Q.jsx)(H,{className:`h-3.5 w-3.5`})}),t?(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:n,onClick:i,className:`text-destructive hover:text-destructive`,title:`Remove from workspace`,children:(0,Q.jsx)(h,{className:`h-3.5 w-3.5`})}):(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:n,onClick:r,title:`Link to workspace`,children:(0,Q.jsx)(P,{className:`h-3.5 w-3.5`})}),t&&(0,Q.jsx)(`span`,{className:`rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-medium text-primary`,children:`linked`})]})]})})}function Ti({allDevices:e,linkedDevices:t,isLoading:n,deviceBusy:r,onRefresh:i,onLink:a,onUnlink:o,onUpdate:s}){let c=new Set(t.map(e=>e.deviceId));return n?(0,Q.jsxs)(`div`,{className:`flex items-center gap-2 text-sm text-muted-foreground`,children:[(0,Q.jsx)(w,{className:`h-4 w-4 animate-spin`}),`Loading devices…`]}):(0,Q.jsxs)(`div`,{className:`space-y-4`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,Q.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[c.size,` of `,e.length,` registered device`,e.length===1?``:`s`,` linked to this workspace.`]}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:i,disabled:n,children:[(0,Q.jsx)(w,{className:`mr-2 h-3.5 w-3.5`}),`Refresh`]})]}),e.length===0?(0,Q.jsxs)(`div`,{className:`rounded-md border p-4 text-sm text-muted-foreground`,children:[`No devices registered. Devices register themselves on first connection via `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`POST /auth/devices/register`}),`.`]}):(0,Q.jsx)(`div`,{className:`space-y-2`,children:e.map(e=>(0,Q.jsx)(wi,{device:e,linked:c.has(e.deviceId),busy:r===e.deviceId,onLink:()=>a(e.deviceId),onUnlink:()=>o(e.deviceId),onUpdate:t=>s(e.deviceId,t)},e.deviceId))})]})}function $({label:e,value:t,mono:n=!1}){return(0,Q.jsxs)(`div`,{className:`flex items-start justify-between gap-4 py-2 border-b last:border-0`,children:[(0,Q.jsx)(`span`,{className:`text-xs text-muted-foreground shrink-0`,children:e}),(0,Q.jsx)(`span`,{className:`text-xs text-right ${n?`font-mono`:``}`,children:t??`—`})]})}function Ei({workspaceName:e,current:t,weights:n,floorMode:r,relativeMargin:i,onDone:a}){let[o,s]=(0,Z.useState)(t==null?``:String(t)),[c,l]=(0,Z.useState)(r??`relative`),[u,d]=(0,Z.useState)(i==null?`0.035`:String(i)),[f,p]=(0,Z.useState)(n?.fts==null?`2`:String(n.fts)),[m,h]=(0,Z.useState)(n?.dense==null?`1`:String(n.dense)),[g,_]=(0,Z.useState)(n?.image==null?`2`:String(n.image)),[v,y]=(0,Z.useState)(!1),[b,x]=(0,Z.useState)(null),[S,C]=(0,Z.useState)(In);return(0,Q.jsxs)(`div`,{className:`mt-3 space-y-3`,children:[(0,Q.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,Q.jsx)(`label`,{className:`text-xs text-muted-foreground`,children:`How photo relevance is decided`}),(0,Q.jsxs)(`select`,{value:c,onChange:e=>l(e.target.value),className:`h-8 rounded-md border border-input bg-transparent px-2 text-xs`,children:[(0,Q.jsx)(`option`,{value:`relative`,children:`Relative to the best match (recommended)`}),(0,Q.jsx)(`option`,{value:`absolute`,children:`Absolute distance cutoff`})]}),(0,Q.jsx)(`p`,{className:`text-[11px] text-muted-foreground`,children:`Photo-vs-text distances shift with every query and every model, so one fixed cutoff travels badly: a value tuned for one model keeps everything (or nothing) on the next. Relative anchors on the best match for THIS query and survives a re-embed.`})]}),c===`relative`&&(0,Q.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,Q.jsx)(`label`,{className:`text-xs text-muted-foreground`,children:`Window width (distance from the best match)`}),(0,Q.jsx)(W,{value:u,onChange:e=>d(e.target.value),placeholder:`0.035`,className:`h-8 w-28 font-mono text-xs`,inputMode:`decimal`}),(0,Q.jsx)(`p`,{className:`text-[11px] text-muted-foreground`,children:`Wider keeps photos that match your query only partly, which is what a photo matching TWO things (a snowy window) looks like, since it sits further from each one than a pure example does. Too wide and everything gets in.`})]}),(0,Q.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,Q.jsx)(`label`,{className:`text-xs text-muted-foreground`,children:c===`relative`?`Hard ceiling (cosine distance, 0–2; blank = off)`:`Image relevance floor (cosine distance, 0–2; blank = off)`}),(0,Q.jsx)(W,{value:o,onChange:e=>s(e.target.value),placeholder:`0.95`,className:`h-8 w-28 font-mono text-xs`,inputMode:`decimal`}),(0,Q.jsx)(`p`,{className:`text-[11px] text-muted-foreground`,children:c===`relative`?`Nothing beyond this counts as a match however close it is to the best one. This is what lets a search of something you have no photos of come back empty. Without it, a relative window always returns its nearest photo.`:`Lower = stricter (fewer, more-relevant photos); raise if relevant photos are missing.`})]}),(0,Q.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,Q.jsx)(`label`,{className:`text-xs text-muted-foreground`,children:`Hybrid fusion weights (0 disables a signal)`}),(0,Q.jsx)(`div`,{className:`flex flex-wrap items-end gap-x-3 gap-y-2`,children:[[`lexical`,f,p],[`text semantic`,m,h],[`image`,g,_]].map(([e,t,n])=>(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsx)(`span`,{className:`block text-[11px] text-muted-foreground`,children:e}),(0,Q.jsx)(W,{value:t,onChange:e=>n(e.target.value),className:`h-8 w-16 font-mono text-xs`,inputMode:`decimal`})]},e))}),(0,Q.jsx)(`p`,{className:`text-[11px] text-muted-foreground`,children:`Ranking mix: lexical (filenames/content) · text semantics · photo matches. Equal lexical/image ranks a matching photo like a matching document.`})]}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(U,{size:`sm`,variant:`outline`,onClick:async()=>{y(!0),x(null);try{let t=o.trim(),n=t===``?null:Number(t);if(n!==null&&!Number.isFinite(n)){x(`Enter a number (e.g. 0.95) or leave empty`);return}let r={fts:Number(f),dense:Number(m),image:Number(g)};if(Object.values(r).some(e=>!Number.isFinite(e)||e<0)){x(`Weights must be numbers ≥ 0`);return}let i=Number(u.trim());if(c===`relative`&&(!Number.isFinite(i)||i<=0)){x(`Window width must be a positive number (e.g. 0.035)`);return}await ot(e,{imageMaxDistance:n,imageFloorMode:c,...c===`relative`?{imageRelativeMargin:i}:{},searchWeights:r}),x(`Saved`),a()}catch(e){x(e instanceof Error?e.message:`Failed to save`)}finally{y(!1)}},disabled:v,children:v?`Saving…`:`Save`}),b&&(0,Q.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:b})]}),(0,Q.jsxs)(`div`,{className:`space-y-1.5 border-t border-border pt-3`,children:[(0,Q.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:S,onChange:e=>{Pn(e.target.checked),C(e.target.checked)}}),(0,Q.jsx)(`span`,{children:`Debug query`})]}),(0,Q.jsx)(`p`,{className:`text-[11px] text-muted-foreground`,children:`Attaches the raw (unfloored) image distances to each search and shows them above the document list: the numbers this floor should be picked from. Local to this browser; costs a little extra work per query, so leave it off day to day.`})]})]})}function Di({stats:e,isLoading:t,onRefresh:n,workspaceName:r}){return t?(0,Q.jsxs)(`div`,{className:`flex items-center gap-2 text-sm text-muted-foreground`,children:[(0,Q.jsx)(w,{className:`h-4 w-4 animate-spin`}),`Loading stats…`]}):(0,Q.jsxs)(`div`,{className:`space-y-4`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between`,children:[(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Live snapshot from synapsd. Click refresh for updated counts.`}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:n,disabled:t,children:[(0,Q.jsx)(w,{className:`mr-2 h-3.5 w-3.5`}),`Refresh`]})]}),(0,Q.jsx)(ji,{workspaceName:r,vectorSpaces:Object.keys(e?.semantic?.vectorSpaces||{}),onDone:n}),e?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`mb-3 flex items-center gap-2`,children:[(0,Q.jsx)(ve,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Core Index`})]}),(0,Q.jsx)($,{label:`Backend`,value:e.dbBackend,mono:!0}),(0,Q.jsx)($,{label:`Status`,value:e.status}),(0,Q.jsx)($,{label:`Path`,value:e.dbPath,mono:!0}),(0,Q.jsx)($,{label:`Documents`,value:e.documentCount?.toLocaleString()}),(0,Q.jsx)($,{label:`Metadata entries`,value:e.metadataCount?.toLocaleString()}),(0,Q.jsx)($,{label:`Checksum index`,value:e.checksumIndexSize?.toLocaleString()}),(0,Q.jsx)($,{label:`Bitmap store`,value:e.bitmapStoreSize?.toLocaleString()}),(0,Q.jsx)($,{label:`Bitmap cache`,value:e.bitmapCacheSize?.toLocaleString()}),(0,Q.jsx)($,{label:`Deleted (pending GC)`,value:e.deletedDocumentsCount?.toLocaleString()})]}),e.fts&&(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`mb-3 flex items-center gap-2`,children:[(0,Q.jsx)(ge,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Full-Text Search (LanceDB)`})]}),`ready`in e.fts&&(0,Q.jsx)($,{label:`Ready`,value:String(e.fts.ready)}),e.fts.rowCount!==void 0&&(0,Q.jsx)($,{label:`Row count`,value:e.fts.rowCount?.toLocaleString()}),e.fts.error&&(0,Q.jsx)($,{label:`Error`,value:(0,Q.jsx)(`span`,{className:`text-destructive`,children:e.fts.error})})]}),e.semantic&&(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`mb-3 flex items-center gap-2`,children:[(0,Q.jsx)(ge,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Semantic / Vector Search`})]}),(0,Q.jsx)($,{label:`Enabled`,value:String(e.semantic.enabled)}),e.semantic.enabled&&(0,Q.jsxs)(Q.Fragment,{children:[e.inferd?.routing?Object.entries(e.inferd.routing).map(([e,t])=>(0,Q.jsx)($,{label:`Embeds → ${e}`,value:(0,Q.jsx)(`span`,{className:`font-mono text-[11px]`,children:t.join(`, `)})},`route-${e}`)):(0,Q.jsx)($,{label:`Text-embeddable (gap default)`,value:e.semantic.embeddableSchemas?.join(`, `)}),e.semantic.vectorSpaces&&Object.entries(e.semantic.vectorSpaces).map(([e,t])=>(0,Q.jsx)($,{label:`Space: ${e} (${t.dim??`?`}-d)`,value:t.error?(0,Q.jsx)(`span`,{className:`text-destructive`,children:t.error}):`${(t.embeddedDocs??0).toLocaleString()} docs · ${(t.chunkRows??0).toLocaleString()} vectors`},e)),(0,Q.jsx)($,{label:`Photo relevance`,value:e.semantic.imageFloorMode===`absolute`?`absolute cutoff`:`relative ±${e.semantic.imageRelativeMargin??.035}`,mono:!0}),(0,Q.jsx)($,{label:e.semantic.imageFloorMode===`absolute`?`Image relevance floor`:`Hard ceiling`,value:e.semantic.imageMaxDistance==null?`off`:e.semantic.imageMaxDistance,mono:!0}),(0,Q.jsx)($,{label:`Fusion weights (lexical · text · image)`,value:`${e.semantic.searchWeights?.fts??2} · ${e.semantic.searchWeights?.dense??1} · ${e.semantic.searchWeights?.image??2}`,mono:!0}),(0,Q.jsx)(Ei,{workspaceName:r,current:e.semantic.imageMaxDistance,weights:e.semantic.searchWeights,floorMode:e.semantic.imageFloorMode,relativeMargin:e.semantic.imageRelativeMargin,onDone:n})]})]})]}):(0,Q.jsx)(`div`,{className:`rounded-md border p-4 text-sm text-muted-foreground`,children:`No stats available.`})]})}function Oi(){let{workspaceName:e}=l();return(0,Q.jsx)(ki,{},e)}function ki(){let{workspaceName:e,tab:t}=l(),[n,r]=d(),i=n.get(`section`),a=[`pending`,`rules`,`hooks`,`scripts`,`runs`].find(e=>e===i),o=n.get(`handler`)||void 0,s=n.get(`run`)||void 0,f=n.get(`addRuleKind`),p=n.get(`addRulePath`),m=n.get(`addRuleTarget`),g=n.get(`addRuleStoreTo`),_=n.get(`addRuleStoreFolder`),v=(0,Z.useMemo)(()=>f===`store`||f===`download`?!p&&!g&&!_?null:{kind:f,path:p||``,storeTo:g||``,storeFolder:_||``}:p?{kind:`mirror`,path:p,target:m||`dir:/`}:null,[f,p,m,g,_]),y=(0,Z.useCallback)(()=>{r(e=>{for(let t of[`addRuleKind`,`addRulePath`,`addRuleTarget`,`addRuleStoreTo`,`addRuleStoreFolder`])e.delete(t);return e},{replace:!0})},[r]),b=u(),{showToast:x,showProgress:S}=q(),C=_t(),{goBack:T}=nn(),E=an(t);(0,Z.useEffect)(()=>{t!==E&&b(`/workspaces/${e}/settings/${E}`,{replace:!0})},[t,E,e,b]);let[D,ee]=(0,Z.useState)(null),O=tn(D),[k,j]=(0,Z.useState)(0),[M,te]=(0,Z.useState)(!0),[N,ne]=(0,Z.useState)(!1),[P,F]=(0,Z.useState)(!1),[re,ie]=(0,Z.useState)(``),[L,ae]=(0,Z.useState)(``),[R,z]=(0,Z.useState)(`#FFFFFF`),[oe,se]=(0,Z.useState)(null),[ce,le]=(0,Z.useState)(null),[ue,B]=(0,Z.useState)([]),[fe,me]=(0,Z.useState)(null),[he,ve]=(0,Z.useState)([]),[V,H]=(0,Z.useState)(null),[ye,G]=(0,Z.useState)(null),[be,xe]=(0,Z.useState)(!1),[K,Se]=(0,Z.useState)([]),[Ce,Te]=(0,Z.useState)([]),[De,Oe]=(0,Z.useState)(!1),[ke,Ae]=(0,Z.useState)(null),J=e||D?.id||``,je=(0,Z.useRef)(J);(0,Z.useEffect)(()=>{je.current=J});let Me=(0,Z.useCallback)(async e=>{let t=e??je.current;if(t)try{let e=await Xe(t);B(e.publicCanvasShares)}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to load shares`,variant:`destructive`})}},[x]),Fe=(0,Z.useCallback)(async e=>{let t=e??je.current;if(t){xe(!0);try{let e=await Ue(t);G(e)}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to load DB stats`,variant:`destructive`})}finally{xe(!1)}}},[x]),Y=(0,Z.useCallback)(async e=>{let t=e??je.current;if(t){Oe(!0);try{let[e,n]=await Promise.all([On(),An(t)]);Se(e),Te(n)}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to load devices`,variant:`destructive`})}finally{Oe(!1)}}},[x]),Le=async e=>{Ae(e);try{let t=K.find(t=>t.deviceId===e);if(!t)throw Error(`Device not found in your account`);await jn(J,t),await Y(),x({title:`Linked`,description:`Device linked to workspace`})}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to link device`,variant:`destructive`})}finally{Ae(null)}},Re=async e=>{Ae(e);try{await Dn(J,e),Te(t=>t.filter(t=>t.deviceId!==e)),x({title:`Unlinked`,description:`Device removed from workspace`})}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to unlink device`,variant:`destructive`})}finally{Ae(null)}},Ve=async(e,t)=>{Ae(`edit:${e}`);try{let n=await kn(e,t);Se(t=>t.map(t=>t.deviceId===e?{...t,...n}:t)),x({title:`Saved`,description:`Device updated`})}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to update device`,variant:`destructive`})}finally{Ae(null)}},X=(0,Z.useCallback)(async e=>{let t=e??je.current;if(!t)return;let[n,r]=await Promise.all([tt(t).catch(()=>null),He(t).catch(()=>[])]);me(n),ve(r.filter(e=>e.kind===`storage`))},[]),Ge=he.some(e=>e.resyncing);(0,Z.useEffect)(()=>{if(!O||!Ge||!J)return;let e=setInterval(()=>{X()},4e3);return()=>clearInterval(e)},[O,Ge,J,X]),(0,Z.useEffect)(()=>{let t=!1;async function n(){try{let n=await Pe();if(t)return;let r=n.find(t=>t.id===e)??n.find(t=>t.name===e);if(ee(r??null),r){if(!tn(r))return;ie(r.label||``),ae(r.description||``),z(r.color||`#FFFFFF`),se(r.icon??null),r.status===`active`&&await Promise.all([Me(e||r.id),X(e||r.id)])}}catch{t||x({title:`Error`,description:`Failed to load workspace`,variant:`destructive`})}finally{t||te(!1)}}return n(),()=>{t=!0}},[e,Me,X,x,k]),(0,Z.useEffect)(()=>{let e=()=>j(e=>e+1);return window.addEventListener(`workspaces:refresh`,e),()=>window.removeEventListener(`workspaces:refresh`,e)},[]);let qe=(0,Z.useRef)(!1),Je=(0,Z.useRef)(!1);(0,Z.useEffect)(()=>{qe.current=!!ye||be,Je.current=De||K.length>0}),(0,Z.useEffect)(()=>{O&&(D?.status===`active`&&E===`db`&&J&&!qe.current&&Fe(),D?.status===`active`&&E===`devices`&&J&&!Je.current&&Y())},[O,E,J,D?.status,Fe,Y]);let Ye=async e=>{if(e.preventDefault(),D){ne(!0);try{await Ee(J,{label:re.trim(),description:L.trim(),color:R,icon:oe}),window.dispatchEvent(new CustomEvent(`workspaces:refresh`)),x({title:`Saved`,description:`Workspace settings updated`})}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Save failed`,variant:`destructive`})}finally{ne(!1)}}},Ze=async()=>{if(D&&window.prompt(`Destroy workspace "${D.label||D.name}" and all its documents? This cannot be undone. Type the workspace name "${D.name}" to confirm:`)===D.name){F(!0);try{await ze(J),window.dispatchEvent(new CustomEvent(`workspaces:refresh`)),x({title:`Destroyed`,description:`${D.label||D.name} has been removed`}),b(`/workspaces`)}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Destroy failed`,variant:`destructive`})}finally{F(!1)}}},Qe=()=>{for(let t of[e,D?.name,D?.id])t&&Ke(t,`backends`);window.dispatchEvent(new CustomEvent(`workspace:tree:refresh`,{detail:{workspaceName:J}}))},$e=async e=>{if(e.config?.supported!==!1){H(`backend:${e.address}`);try{await Ie(J,e.driver,e.address,{enabled:!e.enabled}),await X(),Qe(),x({title:`Saved`,description:`${e.address} ${e.enabled?`disabled`:`enabled`}`})}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to update backend`,variant:`destructive`})}finally{H(null)}}},nt=async(e,t,n)=>{H(`${n}:${e.address}`);try{await Ie(J,e.driver,e.address,t),await X()}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to update backend`,variant:`destructive`})}finally{H(null)}},it=async e=>{let t=typeof e.config?.label==`string`&&e.config.label||e.address;if(window.confirm(`Remove backend "${t}"? No files are deleted; indexed entries remain until purged from the backends tree.`)){H(`remove:${e.address}`);try{await rt(J,e.driver,e.address),await X(),Qe(),x({title:`Removed`,description:`${t} unmounted`})}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to remove backend`,variant:`destructive`})}finally{H(null)}}},at=async e=>{H(`resync:${e.address}`);try{await Qt(S,J,e.driver,e.address,e=>{ve(t=>t.map(t=>t.driver===e.driver&&t.address===e.address?e:t))}),await X()}catch{}finally{H(null)}},ot=async e=>{H(`test:${e.address}`);try{await Be(J,e.driver,e.address),x({title:`Connected`,description:`${e.address}: credentials and root folder verified`})}catch(e){x({title:`Connection failed`,description:e instanceof Error?e.message:`Test failed`,variant:`destructive`})}finally{H(null)}},st=async e=>{H(`stopsync:${e.address}`);try{await We(J,e.driver,e.address),await X(),x({title:`Sync stopped`,description:`${e.address}: indexed files are kept; Re-sync resumes where it left off`})}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to stop sync`,variant:`destructive`})}finally{H(null)}},ct=async(e,t)=>{H(`service:${e}`);try{t?await we(J,e):await Ne(J,e),await X(),x({title:t?`Disabled`:`Enabled`,description:`${e} ${t?`disabled`:`enabled`}`})}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to toggle service`,variant:`destructive`})}finally{H(null)}},lt=async e=>{let t=`${window.location.origin}${e.url}`;await navigator.clipboard?.writeText(t),x({title:`Copied`,description:t})},ut=async e=>{if(D&&window.confirm(`Revoke public share for "${e.path}"?`)){H(`share:${e.code}`);try{await et(J,e.code),await Me(J),window.dispatchEvent(new CustomEvent(`workspace:tree:refresh`,{detail:{workspaceName:J}})),x({title:`Revoked`,description:`Public canvas link no longer works`})}catch(e){x({title:`Error`,description:e instanceof Error?e.message:`Failed to revoke share`,variant:`destructive`})}finally{H(null)}}},dt=cn.find(e=>e.id===E);return M?(0,Q.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Loading...`}):D?O?(0,Q.jsx)(`div`,{className:`h-full min-h-0 overflow-y-auto`,children:(0,Q.jsxs)(`div`,{className:`mx-auto max-w-5xl p-6 pb-12 max-md:px-4 max-md:pb-rail-stack`,children:[(0,Q.jsx)(hn,{compact:!0,className:`mb-6 border-b pb-4`,title:`${dt.label} - ${D.label||D.name}`,description:`This workspace · ${dt.description}`,backTo:`/workspaces/${e}`,onBack:C?T:void 0}),D.status!==`active`&&E!==`general`&&E!==`backups`&&(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Start this workspace to access these settings. General settings and protection remain available while stopped.`}),E===`backups`&&(0,Q.jsx)(Yn,{workspaceId:J,onRestored:()=>j(e=>e+1)}),E===`general`&&(0,Q.jsxs)(`div`,{className:`space-y-6`,children:[(0,Q.jsxs)(`form`,{onSubmit:Ye,className:`space-y-4 rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`label`,{htmlFor:`ws-id`,className:`text-sm font-medium`,children:`Workspace ID`}),(0,Q.jsx)(W,{id:`ws-id`,value:D.id,readOnly:!0,className:`font-mono text-sm`,onFocus:e=>e.currentTarget.select()})]}),(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`label`,{htmlFor:`ws-label`,className:`text-sm font-medium`,children:`Label`}),(0,Q.jsx)(W,{id:`ws-label`,value:re,onChange:e=>ie(e.target.value),placeholder:`Workspace Label`})]}),(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`label`,{htmlFor:`ws-description`,className:`text-sm font-medium`,children:`Description`}),(0,Q.jsx)(W,{id:`ws-description`,value:L,onChange:e=>ae(e.target.value),placeholder:`Description (optional)`})]}),(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`label`,{className:`text-sm font-medium`,children:`Icon & color`}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(`button`,{type:`button`,title:`Pick icon & color`,onClick:e=>le({x:Math.min(e.clientX,window.innerWidth-290),y:Math.min(e.clientY,window.innerHeight-360)}),className:`flex h-10 w-10 items-center justify-center rounded-md border hover:bg-accent`,children:(0,Q.jsx)(c,{icon:oe||`ph:stack-fill`,width:22,height:22,color:bt(R)})}),(0,Q.jsx)(W,{id:`ws-color`,type:`color`,value:R,onChange:e=>z(e.target.value),className:`h-10 w-16 p-1`}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>z(xt()),children:`Randomize`})]})]}),(0,Q.jsx)(U,{type:`submit`,disabled:N||!re.trim(),children:N?`Saving...`:`Save Changes`})]}),ce&&(0,Q.jsx)(mn,{x:ce.x,y:ce.y,current:{icon:oe??void 0,color:R},onChange:e=>{`icon`in e&&se(e.icon??null),`color`in e&&e.color&&z(e.color)},onClose:()=>le(null)}),(0,Q.jsx)(sr,{currentMode:D.protection?.mode,workspaceId:J,isActive:D.status===`active`,onChanged:()=>j(e=>e+1)}),D.status===`active`&&(0,Q.jsx)(Ai,{workspaceName:e}),D.status===`active`&&(0,Q.jsx)(Si,{workspaceId:J}),(0,Q.jsx)(mi,{workspaceId:J,workspaceName:D.label||D.name,isActive:D.status===`active`,onChanged:()=>j(e=>e+1)}),(0,Q.jsxs)(`section`,{className:`rounded-lg border border-destructive/30 p-4`,children:[(0,Q.jsx)(`h2`,{className:`mb-3 text-sm font-semibold text-destructive`,children:`Danger Zone`}),(0,Q.jsxs)(U,{variant:`destructive`,disabled:P,onClick:Ze,children:[(0,Q.jsx)(A,{className:`mr-2 h-4 w-4`}),P?`Destroying...`:`Destroy Workspace`]})]})]}),D.status===`active`&&E===`shares`&&(0,Q.jsxs)(`div`,{className:`space-y-4`,children:[(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`mb-3`,children:[(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Members`}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Share this workspace with teammates by e-mail or with a whole directory group. It shows up in their workspace list on this server.`})]}),(0,Q.jsx)(pi,{workspaceId:J})]}),(0,Q.jsxs)(`section`,{className:`space-y-3 rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Public Canvas Shares`}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Active public canvas links for this workspace.`})]}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>Me(),children:[(0,Q.jsx)(w,{className:`mr-2 h-3.5 w-3.5`}),`Refresh`]})]}),ue.length===0?(0,Q.jsx)(`div`,{className:`rounded-md border p-4 text-sm text-muted-foreground`,children:`No public canvas shares. Good.`}):ue.map(e=>{let t=`${window.location.origin}${e.url}`;return(0,Q.jsxs)(`div`,{className:`rounded-md border p-3`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between sm:gap-3`,children:[(0,Q.jsxs)(`div`,{className:`min-w-0`,children:[(0,Q.jsx)(`div`,{className:`truncate font-mono text-sm`,children:e.path}),(0,Q.jsxs)(`div`,{className:`mt-1 text-xs text-muted-foreground`,children:[e.treeName,` / `,e.treeType,` - created `,new Date(e.createdAt).toLocaleString()]}),e.lockedBy?.length>0&&(0,Q.jsxs)(`div`,{className:`mt-1 text-[11px] text-muted-foreground`,children:[`locks: `,e.lockedBy.join(`, `)]})]}),(0,Q.jsxs)(`div`,{className:`flex shrink-0 gap-2`,children:[(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>lt(e),children:(0,Q.jsx)(_e,{className:`h-3.5 w-3.5`})}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>window.open(t,`_blank`,`noreferrer`),children:(0,Q.jsx)(de,{className:`h-3.5 w-3.5`})}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:V===`share:${e.code}`,onClick:()=>ut(e),className:`text-destructive hover:text-destructive`,children:(0,Q.jsx)(h,{className:`h-3.5 w-3.5`})})]})]}),(0,Q.jsx)(`div`,{className:`mt-2 truncate rounded bg-muted/40 px-2 py-1 font-mono text-xs text-muted-foreground`,children:t})]},e.code)})]}),(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`mb-3`,children:[(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Workspace ACL Tokens`}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Generate token-based workspace access for devices, scripts, and other users.`})]}),(0,Q.jsx)(li,{workspaceId:J})]})]}),D.status===`active`&&E===`data`&&(0,Q.jsxs)(`div`,{className:`space-y-3`,children:[he.map(e=>{let t=e.config||{},n=e.address,r=t.device||null,i=e.driver===`gdrive`,a=e.driver===`s3`,o=(e.driver===`file`||i||a)&&t.managed!==!0&&!hi[n],s=hi[n]||{title:typeof t.label==`string`&&t.label||n,description:a?`S3 bucket ${t.bucket||``}${t.prefix?`/${t.prefix}`:``}${t.endpoint?` at ${t.endpoint}`:``}. Filing copies only.`:i?`Google Drive${typeof t.account==`string`&&t.account&&t.account!==t.label?` (${t.account})`:``}, folder ${typeof t.folderId==`string`&&t.folderId?t.folderId:`root`}${e.treePath?`, mirrored at ${e.treePath}`:``}.`:o?`Local folder${r?.name?` on ${r.name}`:``}, indexed in place${e.treePath?` and mirrored at ${e.treePath}`:``}.`:`Workspace data backend.`},l=t.supported!==!1,u=n===`workspace:data`,d=l&&!u,f=d&&t.managed!==!0,p=yt(e);return(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,Q.jsxs)(`div`,{className:`min-w-0`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(c,{icon:p.icon,width:16,height:16,color:p.color,className:`shrink-0`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:s.title}),!l&&(0,Q.jsx)(`span`,{className:`rounded bg-muted px-2 py-0.5 text-[11px] text-muted-foreground`,children:`unsupported`})]}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:s.description}),typeof t.root==`string`&&t.root&&(0,Q.jsx)(`p`,{className:`mt-2 truncate font-mono text-xs text-muted-foreground`,children:t.root}),(0,Q.jsxs)(`div`,{className:`mt-2 flex flex-wrap gap-2 text-[11px] text-muted-foreground`,children:[(0,Q.jsxs)(`span`,{children:[`status: `,e.status]}),e.resyncing&&(0,Q.jsxs)(`span`,{className:`flex items-center gap-1 rounded bg-primary/10 px-1.5 py-0.5 font-medium text-primary`,children:[(0,Q.jsx)(w,{className:`h-3 w-3 animate-spin`}),`indexing`,e.progress?` ${e.progress.scanned}${e.progress.total==null?``:` / ${e.progress.total}`}`:``]}),r?.name&&(0,Q.jsxs)(`span`,{title:r.id,children:[`device: `,r.name]}),t.readOnly===!0&&(0,Q.jsx)(`span`,{className:`rounded bg-warning/15 px-1.5 py-0.5 font-medium text-warning`,children:`read-only`}),e.lastSyncAt&&(0,Q.jsxs)(`span`,{children:[`last scan: `,new Date(e.lastSyncAt).toLocaleString()]})]}),e.lastError&&(0,Q.jsx)(`p`,{className:`mt-2 text-xs text-destructive`,children:e.lastError}),t.readOnly===!0&&(0,Q.jsx)(`p`,{className:`mt-1 text-[11px] text-warning`,children:`Read-only: the web UI / REST API never deletes bytes on this backend. Destroy degrades to a reference drop. Recommended when the folder is exported elsewhere (e.g. via Samba).`})]}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3 border-t pt-3`,children:[e.capabilities?.sync&&l&&(0,Q.jsxs)(`label`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground`,title:`Scan this backend in the background whenever the workspace starts. Manual scans remain available when off.`,children:[`Scan on start`,(0,Q.jsx)(_i,{checked:t.scanOnStart===!0,disabled:V===`startup:${n}`,onClick:()=>nt(e,{scanOnStart:t.scanOnStart!==!0},`startup`)})]}),(e.driver===`file`||i||a)&&d&&(0,Q.jsxs)(`label`,{className:`flex items-center gap-1.5 text-[11px] text-muted-foreground`,title:i?`Poll the Drive changes feed so edits made in Drive show up without a manual re-sync.`:void 0,children:[`Watch for changes`,(0,Q.jsx)(_i,{checked:!!t.watch,disabled:!e.enabled||V===`watch:${n}`,onClick:()=>nt(e,{watch:!t.watch},`watch`)})]}),f&&(0,Q.jsxs)(`label`,{className:`flex items-center gap-1.5 text-[11px] text-muted-foreground`,title:`Read-only: Destroy never deletes bytes on this backend (references are dropped instead). Important when the folder is exported via Samba or shared otherwise.`,children:[`read-only`,(0,Q.jsx)(_i,{checked:t.readOnly===!0,disabled:V===`readonly:${n}`,onClick:()=>nt(e,{readOnly:t.readOnly!==!0},`readonly`)})]}),(e.driver===`file`||e.driver===`cacache`)&&l&&(0,Q.jsx)(bi,{workspaceId:J,backend:e}),e.capabilities?.test&&(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:V===`test:${n}`,onClick:()=>ot(e),title:`Verify credentials and root folder`,children:[(0,Q.jsx)(ge,{className:`mr-2 h-3.5 w-3.5`}),V===`test:${n}`?`Testing…`:`Test`]}),e.capabilities?.sync&&(e.resyncing?(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:V===`stopsync:${n}`,onClick:()=>st(e),title:`Stop the running scan. Files indexed so far are kept; Re-sync later resumes where it left off (unchanged files are skipped).`,children:[(0,Q.jsx)(pe,{className:`mr-2 h-3.5 w-3.5 fill-current`}),V===`stopsync:${n}`?`Stopping…`:`Stop sync`]}):(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:V===`resync:${n}`,onClick:()=>at(e),children:[(0,Q.jsx)(w,{className:`mr-2 h-3.5 w-3.5 ${V===`resync:${n}`?`animate-spin`:``}`}),`Re-sync`]})),u?(0,Q.jsx)(`span`,{className:`rounded bg-muted px-2 py-0.5 text-[11px] text-muted-foreground`,title:`Structural workspace store; cannot be disabled`,children:`always on`}):(0,Q.jsx)(_i,{checked:!!e.enabled,disabled:!d||V===`backend:${n}`,onClick:()=>$e(e)}),o&&(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,className:`text-destructive hover:text-destructive`,disabled:V===`remove:${n}`,title:`Unmount this folder (no files are deleted)`,onClick:()=>it(e),children:(0,Q.jsx)(A,{className:`h-3.5 w-3.5`})})]})]}),l&&(0,Q.jsx)(Mn,{value:t.removalPolicy,backends:he,source:n,destination:t.propagatedRemovalSupported===!0,onSave:async t=>{await Ie(J,e.driver,e.address,{removalPolicy:t}),await X()}}),a&&(0,Q.jsx)(or,{workspaceId:J,backend:e,onSaved:async()=>{await X(),Qe()}}),e.driver===`file`&&l&&!u&&(0,Q.jsx)(Ci,{backend:e,busy:V===`exclude:${n}`,onSave:t=>nt(e,{exclude:t},`exclude`)})]},n)}),(0,Q.jsx)(ar,{workspaceId:J,children:(0,Q.jsx)(xi,{workspaceId:J})},J),(0,Q.jsxs)(`div`,{className:`flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-start`,children:[(0,Q.jsx)(vi,{workspaceId:J,onAdded:async()=>{await X(),Qe()}}),(0,Q.jsx)(yi,{workspaceId:J,onAdded:async()=>{await X(),Qe()}}),(0,Q.jsx)(or,{workspaceId:J,onSaved:async()=>{await X(),Qe()}})]}),e&&(0,Q.jsx)(`section`,{className:`border-t pt-4`,children:(0,Q.jsx)(Nn,{workspaceName:e})})]}),D.status===`active`&&E===`db`&&(0,Q.jsxs)(`div`,{className:`space-y-8`,children:[J&&(0,Q.jsxs)(`section`,{className:`space-y-3`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Embeddings`}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Which model turns this workspace's content into vectors, and where those vectors live.`})]}),(0,Q.jsx)(vr,{workspaceId:J,workspaceName:e})]}),(0,Q.jsx)(Di,{stats:ye,isLoading:be,onRefresh:()=>Fe(),workspaceName:e})]}),D.status===`active`&&E===`devices`&&(0,Q.jsx)(Ti,{allDevices:K,linkedDevices:Ce,isLoading:De,deviceBusy:ke,onRefresh:()=>Y(),onLink:Le,onUnlink:Re,onUpdate:Ve}),D.status===`active`&&E===`sync`&&(0,Q.jsx)(`section`,{className:`rounded-lg border p-4`,children:(0,Q.jsx)(Zr,{workspaceId:J,workspaceUuid:D?.id})}),D.status===`active`&&E===`services`&&(0,Q.jsxs)(`div`,{className:`space-y-4`,children:[J&&(0,Q.jsx)(ii,{workspaceId:J}),(0,Q.jsx)(`div`,{className:`space-y-3`,children:gi.map(e=>{let t=fe?.[e.id]||fe?.[e.id===`git`?`dotfiles`:e.id===`webdav`?`home`:`imap`],n=!!t?.enabled;return(0,Q.jsx)(`section`,{className:`rounded-lg border p-4`,children:(0,Q.jsxs)(`div`,{className:`flex items-center justify-between gap-4`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(I,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:e.title})]}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:e.description}),(0,Q.jsxs)(`p`,{className:`mt-2 text-[11px] text-muted-foreground`,children:[`initialized: `,t?.initialized?`true`:`false`]})]}),(0,Q.jsx)(_i,{checked:n,disabled:V===`service:${e.id}`,onClick:()=>ct(e.id,n)})]})},e.id)})}),(0,Q.jsx)(`section`,{className:`rounded-lg border p-4`,children:(0,Q.jsx)(ci,{workspaceId:J,enabled:!!(fe?.imap?.enabled||fe?.imapSync?.enabled)})})]}),D.status===`active`&&E===`policies`&&(0,Q.jsx)(Lr,{workspaceId:J}),D.status===`active`&&E===`hooks`&&(0,Q.jsx)(`section`,{className:`rounded-lg border p-4`,children:(0,Q.jsx)(Mr,{workspaceId:J,prefillRule:v,onPrefillConsumed:y,initialSection:a,initialRunsHandler:o,initialRunId:s})})]})}):(0,Q.jsxs)(`div`,{className:`space-y-3 p-6`,children:[(0,Q.jsx)(`h1`,{className:`font-semibold`,children:D.label||D.name}),(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Workspace settings are available to owners and admins.`}),(0,Q.jsx)(U,{variant:`outline`,onClick:()=>b(`/workspaces/${e}`),children:`Back to workspace`})]}):(0,Q.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Workspace not found`})}function Ai({workspaceName:e}){let{selected:t,setSelected:n,tree:r,setTree:i,toggle:a}=Ln(),[o,s]=(0,Z.useState)(!1),[c,l]=(0,Z.useState)(null);return(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Default folders`}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`Create a set of common folders with matching icons and colors.`}),(0,Q.jsx)(`div`,{className:`mt-3`,children:(0,Q.jsx)(zn,{selected:t,onToggle:a,tree:r,onTreeChange:i,disabled:o,idPrefix:`settings-default-folders`})}),(0,Q.jsxs)(`div`,{className:`mt-3 flex items-center gap-3`,children:[(0,Q.jsx)(U,{type:`button`,size:`sm`,disabled:o||t.size===0,onClick:async()=>{s(!0),l(null);let{ok:i,failed:a}=await Rn(e,Array.from(t),r);s(!1),l(a?`${i} created, ${a} failed`:`${i} folder(s) created`),n(new Set)},children:o?`Creating…`:`Create ${t.size||``} folder(s)`}),c&&(0,Q.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:c})]})]})}function ji({workspaceName:e,vectorSpaces:t,onDone:n}){let[r,i]=(0,Z.useState)(null),[a,o]=(0,Z.useState)(null),s=async(e,t,r)=>{if(!(r&&!window.confirm(r))){i(e),o(null);try{let e=await t();o(e.message),n()}catch(e){o(e instanceof Error?e.message:`Reindex failed`)}finally{i(null)}}},c=[{title:`Bitmaps`,ops:[{key:`timelines`,label:`Reindex timelines`,description:`Rebuild the created/updated timelines from document data.`,fn:()=>Me(e)},{key:`mime`,label:`Reindex MIME types`,description:`Rebuild the per-MIME-type presence bitmaps (data/mime/*) from stored docs. Backfills blobs indexed before mime bitmaps existed.`,fn:()=>Re(e)}]},{title:`Full-text (BM25)`,ops:[{key:`fts`,label:`Backfill`,description:`Index documents missing from the full-text table. Fast, skips already-indexed.`,fn:()=>J(e)},{key:`fts-rebuild`,label:`Rebuild`,description:`Wipe the full-text table and rebuild from scratch. Use when counts drift.`,confirm:`Wipe the FTS index and rebuild it from scratch?`,fn:()=>J(e,!0)},{key:`fts-optimize`,label:`Optimize`,description:`Compact fragments + prune old versions of the full-text table.`,fn:()=>Te(e,`fts`)}]},...t.length>0?[{title:`Vector tables (LanceDB)`,ops:t.map(t=>({key:`${t}-optimize`,label:`Optimize ${t}`,description:`Compact fragments + prune old versions of the '${t}' vector table and rebuild its ANN index.`,fn:()=>Te(e,t)}))}]:[]];return(0,Q.jsxs)(`section`,{className:`rounded-lg border p-4`,children:[(0,Q.jsxs)(`div`,{className:`mb-1 flex items-center gap-2`,children:[(0,Q.jsx)(w,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Index maintenance`})]}),(0,Q.jsx)(`p`,{className:`mb-3 text-xs text-muted-foreground`,children:`Runs against this workspace. Reindex operations are idempotent.`}),(0,Q.jsx)(`div`,{className:`space-y-4`,children:c.map(e=>(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`h3`,{className:`mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground`,children:e.title}),(0,Q.jsx)(`div`,{className:`space-y-2`,children:e.ops.map(e=>(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-3 gap-y-1`,children:[(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,className:`w-44 justify-start`,disabled:r!==null,onClick:()=>s(e.key,e.fn,e.confirm),children:[r===e.key&&(0,Q.jsx)(w,{className:`mr-2 h-3 w-3 animate-spin`}),e.label]}),(0,Q.jsx)(`span`,{className:`min-w-0 flex-1 basis-52 text-xs text-muted-foreground`,children:e.description})]},e.key))})]},e.title))}),a&&(0,Q.jsx)(`p`,{className:`mt-3 text-xs text-muted-foreground`,children:a})]})}export{Oi as default};
@@ -1 +1 @@
1
- import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{l as t,u as n}from"./code-editor-BOIngd5F.js";import{_ as ee,g as r,v as i}from"./chunk-62JRHF6Z-vspAGgtK.js";import{G as te,Ot as a,X as o}from"./ui-BSqPn1LJ.js";import{t as s}from"./utils-2pl1SbIp.js";import{t as c}from"./button-Bv4zA1qr.js";import{t as l}from"./input-BOtCd4HP.js";import{n as ne}from"./use-toast-BlLDqSUO.js";import{t as re}from"./use-mobile-Db-vWu9Y.js";import{E as ie,a as ae,c as oe,f as u,r as d,v as se,w as ce,x as le}from"./context-D_K4g9SQ.js";import{t as f}from"./PathPickerField-cVs5sZuG.js";import{i as ue}from"./use-menu-BVkqNEIx.js";import{gt as p,pt as de}from"./index-DtZS4JJ4.js";import{n as fe}from"./page-header-GtTr9Ff_.js";var m=e(n(),1),h=t(),g=[{value:`documentRead`,label:`Read`},{value:`documentWrite`,label:`Write`},{value:`documentReadWrite`,label:`Read & write`}];function _(){let{contextId:e,tab:t}=ee(),[n]=i(),_=n.get(`ownerId`)||void 0,v=r(),{showToast:y}=ne(),{selectEntity:b,goBack:x,closeM2:S}=ue(),C=re(),w=p(t),T=_?`?ownerId=${encodeURIComponent(_)}`:``;(0,m.useEffect)(()=>{t!==w&&e&&v(`/contexts/${e}/settings/${w}${T}`,{replace:!0})},[t,w,e,T,v]);let[E,D]=(0,m.useState)(null),[pe,me]=(0,m.useState)(!0),[O,k]=(0,m.useState)(!1),[A,j]=(0,m.useState)(!1),[M,N]=(0,m.useState)(``),[P,F]=(0,m.useState)(``),[I,L]=(0,m.useState)(null),[R,z]=(0,m.useState)(``),[B,V]=(0,m.useState)(``),[H,he]=(0,m.useState)(),[U,W]=(0,m.useState)([]),[G,K]=(0,m.useState)(!1),[q,J]=(0,m.useState)(``),[Y,ge]=(0,m.useState)(g[0].value),[X,Z]=(0,m.useState)(null);(0,m.useEffect)(()=>{if(!e)return;let t=!1;return ae(e,_).then(e=>{t||(D(e),b(e.id),N(e.name||``),F(e.description||``),L(typeof e.metadata?.ui?.color==`string`?e.metadata.ui.color:null),z(e.url||``),V(e.baseUrl||``))}).catch(()=>{t||y({title:`Error`,description:`Failed to load context`,variant:`destructive`})}).finally(()=>{t||me(!1)}),()=>{t=!0}},[e,_,b,y]);let Q=(t=e)=>{t&&(K(!0),u(t).then(W).catch(()=>W([])).finally(()=>K(!1)))};(0,m.useEffect)(()=>{if(w!==`shares`||!e)return;let t=!1;return u(e).then(e=>{t||W(e)}).catch(()=>{t||W([])}),()=>{t=!0}},[w,e]);let _e=async t=>{if(t.preventDefault(),e){k(!0);try{let t=await se(e,{name:M.trim(),description:P.trim(),metadata:{...E?.metadata,ui:{...E?.metadata?.ui,color:I}}},_);D(t),window.dispatchEvent(new CustomEvent(`contexts:refresh`)),y({title:`Saved`,description:`Context updated`})}catch(e){y({title:`Error`,description:e instanceof Error?e.message:`Save failed`,variant:`destructive`})}finally{k(!1)}}},ve=async t=>{if(t.preventDefault(),e){k(!0);try{await Promise.all([ie(e,R.trim(),_,H),ce(e,{baseUrl:B.trim()||null},_)]),window.dispatchEvent(new CustomEvent(`contexts:refresh`)),y({title:`Saved`,description:`Context URL updated`})}catch(e){y({title:`Error`,description:e instanceof Error?e.message:`Save failed`,variant:`destructive`})}finally{k(!1)}}},ye=async()=>{if(e&&window.confirm(`Delete context "${e}"? This cannot be undone.`)){j(!0);try{await d(e,_),S(),b(null),y({title:`Deleted`,description:`Context "${e}" removed`}),v(`/contexts`,{replace:!0})}catch(e){y({title:`Error`,description:e instanceof Error?e.message:`Delete failed`,variant:`destructive`})}finally{j(!1)}}},be=async t=>{if(t.preventDefault(),!(!e||!q.trim())){Z(`grant`);try{await oe(e,q.trim(),Y),J(``),Q(),y({title:`Shared`,description:`Access granted to ${q.trim()}`})}catch(e){y({title:`Error`,description:e instanceof Error?e.message:`Failed to grant access`,variant:`destructive`})}finally{Z(null)}}},xe=async t=>{if(e&&window.confirm(`Revoke ${t.userEmail}'s access to "${e}"?`)){Z(t.userEmail);try{await le(e,t.userEmail),Q(),y({title:`Revoked`,description:`${t.userEmail} no longer has access`})}catch(e){y({title:`Error`,description:e instanceof Error?e.message:`Failed to revoke access`,variant:`destructive`})}finally{Z(null)}}};if(pe)return(0,h.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Loading...`});if(!E)return(0,h.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Context not found`});let $=de.find(e=>e.id===w),Se=`/contexts/${e}${T}`;return(0,h.jsx)(`div`,{className:`h-full min-h-0 overflow-y-auto`,children:(0,h.jsxs)(`div`,{className:`mx-auto max-w-3xl p-6 pb-12 max-md:px-4 max-md:pb-rail-stack`,children:[(0,h.jsx)(fe,{compact:!0,className:`mb-6 border-b pb-4`,title:`${$.label} - ${E.name||E.id}`,description:$.description,backTo:Se,onBack:C?x:void 0}),w===`general`&&(0,h.jsxs)(`div`,{className:`space-y-6`,children:[(0,h.jsxs)(`form`,{onSubmit:_e,className:`space-y-4 rounded-lg border p-4`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{className:`text-sm font-medium`,children:`Context ID`}),(0,h.jsx)(`div`,{className:`mt-1 flex h-10 items-center rounded-md bg-muted px-3 font-mono text-sm text-muted-foreground`,children:E.id})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`ctx-name`,className:`text-sm font-medium`,children:`Name`}),(0,h.jsx)(l,{id:`ctx-name`,value:M,onChange:e=>N(e.target.value),placeholder:`My Context`})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`ctx-description`,className:`text-sm font-medium`,children:`Description`}),(0,h.jsx)(l,{id:`ctx-description`,value:P,onChange:e=>F(e.target.value),placeholder:`Optional description`})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`ctx-color`,className:`text-sm font-medium`,children:`Color`}),(0,h.jsxs)(`div`,{className:`mt-1 flex items-center gap-2`,children:[(0,h.jsx)(l,{id:`ctx-color`,type:`color`,value:/^#[0-9a-f]{6}$/i.test(I||E.color||``)?I||E.color:`#808080`,onChange:e=>L(e.target.value),className:`h-10 w-16 p-1`,disabled:O}),(0,h.jsx)(c,{type:`button`,variant:`outline`,size:`sm`,disabled:O||I===null,onClick:()=>L(null),children:`Use inherited color`})]}),(0,h.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:I===null?`Using the tree folder or workspace color.`:`Applies only to this context.`})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{className:`text-sm font-medium`,children:`Workspace`}),(0,h.jsx)(`div`,{className:`mt-1 flex h-10 items-center rounded-md bg-muted px-3 text-sm text-muted-foreground`,children:E.workspaceName||E.workspaceId||E.workspace})]}),(0,h.jsx)(c,{type:`submit`,disabled:O,children:O?`Saving...`:`Save Changes`})]}),(0,h.jsxs)(`section`,{className:`rounded-lg border border-destructive/30 p-4`,children:[(0,h.jsx)(`h2`,{className:`mb-3 text-sm font-semibold text-destructive`,children:`Danger Zone`}),(0,h.jsxs)(c,{variant:`destructive`,disabled:A,onClick:ye,children:[(0,h.jsx)(o,{className:`mr-2 h-4 w-4`}),A?`Deleting...`:`Delete Context`]})]})]}),w===`location`&&(0,h.jsxs)(`form`,{onSubmit:ve,className:`space-y-4 rounded-lg border p-4`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`ctx-url`,className:`text-sm font-medium`,children:`URL`}),(0,h.jsx)(f,{id:`ctx-url`,value:R,onChange:z,fixedWorkspaceName:E.workspaceName,pickerTitle:`Switch context to…`,placeholder:`workspace://path`,onPickTarget:(e,t)=>{z(`${t.workspaceName}://${e.replace(/^\/+/,``)}`),he(t.treeName)}}),H&&(0,h.jsxs)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:[`Destination tree: `,H]})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsxs)(`label`,{htmlFor:`ctx-base-url`,className:`text-sm font-medium`,children:[`Base URL `,(0,h.jsx)(`span`,{className:`font-normal text-muted-foreground`,children:`(optional)`})]}),(0,h.jsx)(f,{id:`ctx-base-url`,value:B,onChange:V,fixedWorkspaceName:E.workspaceName,placeholder:`workspace://base/path`,pickerTitle:`Pick a base path…`})]}),(0,h.jsx)(c,{type:`submit`,disabled:O,children:O?`Saving...`:`Save Changes`})]}),w===`shares`&&(0,h.jsxs)(`div`,{className:`space-y-4`,children:[(0,h.jsxs)(`section`,{className:`space-y-3 rounded-lg border p-4`,children:[(0,h.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`h2`,{className:`text-sm font-semibold`,children:`People with access`}),(0,h.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Only the context owner can see and change this list.`})]}),(0,h.jsxs)(c,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>Q(),disabled:G,children:[(0,h.jsx)(a,{className:s(`mr-2 h-3.5 w-3.5`,G&&`animate-spin`)}),`Refresh`]})]}),U.length===0?(0,h.jsx)(`div`,{className:`rounded-md border border-dashed p-4 text-sm text-muted-foreground`,children:`Not shared with anyone.`}):U.map(e=>(0,h.jsxs)(`div`,{className:`flex items-start justify-between gap-3 rounded-md border p-3`,children:[(0,h.jsxs)(`div`,{className:`min-w-0`,children:[(0,h.jsx)(`div`,{className:`truncate text-sm font-medium`,children:e.userEmail}),(0,h.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[g.find(t=>t.value===e.accessLevel)?.label||e.accessLevel||`unknown access`,e.sharedAt&&` - since ${new Date(e.sharedAt).toLocaleDateString()}`]})]}),(0,h.jsx)(c,{type:`button`,variant:`outline`,size:`sm`,className:`text-destructive hover:text-destructive`,disabled:X===e.userEmail,onClick:()=>xe(e),children:(0,h.jsx)(te,{className:`h-3.5 w-3.5`})})]},e.userEmail))]}),(0,h.jsxs)(`form`,{onSubmit:be,className:`space-y-4 rounded-lg border p-4`,children:[(0,h.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Grant access`}),(0,h.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`share-email`,className:`text-sm font-medium`,children:`User email`}),(0,h.jsx)(l,{id:`share-email`,type:`email`,value:q,onChange:e=>J(e.target.value),placeholder:`person@example.com`}),(0,h.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`The user must already have an account on this server.`})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`share-level`,className:`text-sm font-medium`,children:`Access level`}),(0,h.jsx)(`select`,{id:`share-level`,value:Y,onChange:e=>ge(e.target.value),className:`mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring`,children:g.map(e=>(0,h.jsx)(`option`,{value:e.value,children:e.label},e.value))})]})]}),(0,h.jsx)(c,{type:`submit`,disabled:X===`grant`||!q.trim(),children:X===`grant`?`Sharing...`:`Share context`})]})]})]})})}export{_ as default};
1
+ import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{l as t,u as n}from"./code-editor-BOIngd5F.js";import{_ as ee,g as r,v as i}from"./chunk-62JRHF6Z-vspAGgtK.js";import{G as te,Ot as a,X as o}from"./ui-Byg1cZDr.js";import{t as s}from"./utils-2pl1SbIp.js";import{t as c}from"./button-SgR3GVvV.js";import{t as l}from"./input-BOtCd4HP.js";import{n as ne}from"./use-toast-BlLDqSUO.js";import{t as re}from"./use-mobile-Db-vWu9Y.js";import{E as ie,a as ae,c as oe,f as u,r as d,v as se,w as ce,x as le}from"./context-mfIOB8dN.js";import{t as f}from"./PathPickerField-DBe7jykL.js";import{i as ue}from"./use-menu-BiJ8N7sS.js";import{gt as p,pt as de}from"./index-CiBgHujl.js";import{n as fe}from"./page-header-DkOHw0g-.js";var m=e(n(),1),h=t(),g=[{value:`documentRead`,label:`Read`},{value:`documentWrite`,label:`Write`},{value:`documentReadWrite`,label:`Read & write`}];function _(){let{contextId:e,tab:t}=ee(),[n]=i(),_=n.get(`ownerId`)||void 0,v=r(),{showToast:y}=ne(),{selectEntity:b,goBack:x,closeM2:S}=ue(),C=re(),w=p(t),T=_?`?ownerId=${encodeURIComponent(_)}`:``;(0,m.useEffect)(()=>{t!==w&&e&&v(`/contexts/${e}/settings/${w}${T}`,{replace:!0})},[t,w,e,T,v]);let[E,D]=(0,m.useState)(null),[pe,me]=(0,m.useState)(!0),[O,k]=(0,m.useState)(!1),[A,j]=(0,m.useState)(!1),[M,N]=(0,m.useState)(``),[P,F]=(0,m.useState)(``),[I,L]=(0,m.useState)(null),[R,z]=(0,m.useState)(``),[B,V]=(0,m.useState)(``),[H,he]=(0,m.useState)(),[U,W]=(0,m.useState)([]),[G,K]=(0,m.useState)(!1),[q,J]=(0,m.useState)(``),[Y,ge]=(0,m.useState)(g[0].value),[X,Z]=(0,m.useState)(null);(0,m.useEffect)(()=>{if(!e)return;let t=!1;return ae(e,_).then(e=>{t||(D(e),b(e.id),N(e.name||``),F(e.description||``),L(typeof e.metadata?.ui?.color==`string`?e.metadata.ui.color:null),z(e.url||``),V(e.baseUrl||``))}).catch(()=>{t||y({title:`Error`,description:`Failed to load context`,variant:`destructive`})}).finally(()=>{t||me(!1)}),()=>{t=!0}},[e,_,b,y]);let Q=(t=e)=>{t&&(K(!0),u(t).then(W).catch(()=>W([])).finally(()=>K(!1)))};(0,m.useEffect)(()=>{if(w!==`shares`||!e)return;let t=!1;return u(e).then(e=>{t||W(e)}).catch(()=>{t||W([])}),()=>{t=!0}},[w,e]);let _e=async t=>{if(t.preventDefault(),e){k(!0);try{let t=await se(e,{name:M.trim(),description:P.trim(),metadata:{...E?.metadata,ui:{...E?.metadata?.ui,color:I}}},_);D(t),window.dispatchEvent(new CustomEvent(`contexts:refresh`)),y({title:`Saved`,description:`Context updated`})}catch(e){y({title:`Error`,description:e instanceof Error?e.message:`Save failed`,variant:`destructive`})}finally{k(!1)}}},ve=async t=>{if(t.preventDefault(),e){k(!0);try{await Promise.all([ie(e,R.trim(),_,H),ce(e,{baseUrl:B.trim()||null},_)]),window.dispatchEvent(new CustomEvent(`contexts:refresh`)),y({title:`Saved`,description:`Context URL updated`})}catch(e){y({title:`Error`,description:e instanceof Error?e.message:`Save failed`,variant:`destructive`})}finally{k(!1)}}},ye=async()=>{if(e&&window.confirm(`Delete context "${e}"? This cannot be undone.`)){j(!0);try{await d(e,_),S(),b(null),y({title:`Deleted`,description:`Context "${e}" removed`}),v(`/contexts`,{replace:!0})}catch(e){y({title:`Error`,description:e instanceof Error?e.message:`Delete failed`,variant:`destructive`})}finally{j(!1)}}},be=async t=>{if(t.preventDefault(),!(!e||!q.trim())){Z(`grant`);try{await oe(e,q.trim(),Y),J(``),Q(),y({title:`Shared`,description:`Access granted to ${q.trim()}`})}catch(e){y({title:`Error`,description:e instanceof Error?e.message:`Failed to grant access`,variant:`destructive`})}finally{Z(null)}}},xe=async t=>{if(e&&window.confirm(`Revoke ${t.userEmail}'s access to "${e}"?`)){Z(t.userEmail);try{await le(e,t.userEmail),Q(),y({title:`Revoked`,description:`${t.userEmail} no longer has access`})}catch(e){y({title:`Error`,description:e instanceof Error?e.message:`Failed to revoke access`,variant:`destructive`})}finally{Z(null)}}};if(pe)return(0,h.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Loading...`});if(!E)return(0,h.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Context not found`});let $=de.find(e=>e.id===w),Se=`/contexts/${e}${T}`;return(0,h.jsx)(`div`,{className:`h-full min-h-0 overflow-y-auto`,children:(0,h.jsxs)(`div`,{className:`mx-auto max-w-3xl p-6 pb-12 max-md:px-4 max-md:pb-rail-stack`,children:[(0,h.jsx)(fe,{compact:!0,className:`mb-6 border-b pb-4`,title:`${$.label} - ${E.name||E.id}`,description:$.description,backTo:Se,onBack:C?x:void 0}),w===`general`&&(0,h.jsxs)(`div`,{className:`space-y-6`,children:[(0,h.jsxs)(`form`,{onSubmit:_e,className:`space-y-4 rounded-lg border p-4`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{className:`text-sm font-medium`,children:`Context ID`}),(0,h.jsx)(`div`,{className:`mt-1 flex h-10 items-center rounded-md bg-muted px-3 font-mono text-sm text-muted-foreground`,children:E.id})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`ctx-name`,className:`text-sm font-medium`,children:`Name`}),(0,h.jsx)(l,{id:`ctx-name`,value:M,onChange:e=>N(e.target.value),placeholder:`My Context`})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`ctx-description`,className:`text-sm font-medium`,children:`Description`}),(0,h.jsx)(l,{id:`ctx-description`,value:P,onChange:e=>F(e.target.value),placeholder:`Optional description`})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`ctx-color`,className:`text-sm font-medium`,children:`Color`}),(0,h.jsxs)(`div`,{className:`mt-1 flex items-center gap-2`,children:[(0,h.jsx)(l,{id:`ctx-color`,type:`color`,value:/^#[0-9a-f]{6}$/i.test(I||E.color||``)?I||E.color:`#808080`,onChange:e=>L(e.target.value),className:`h-10 w-16 p-1`,disabled:O}),(0,h.jsx)(c,{type:`button`,variant:`outline`,size:`sm`,disabled:O||I===null,onClick:()=>L(null),children:`Use inherited color`})]}),(0,h.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:I===null?`Using the tree folder or workspace color.`:`Applies only to this context.`})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{className:`text-sm font-medium`,children:`Workspace`}),(0,h.jsx)(`div`,{className:`mt-1 flex h-10 items-center rounded-md bg-muted px-3 text-sm text-muted-foreground`,children:E.workspaceName||E.workspaceId||E.workspace})]}),(0,h.jsx)(c,{type:`submit`,disabled:O,children:O?`Saving...`:`Save Changes`})]}),(0,h.jsxs)(`section`,{className:`rounded-lg border border-destructive/30 p-4`,children:[(0,h.jsx)(`h2`,{className:`mb-3 text-sm font-semibold text-destructive`,children:`Danger Zone`}),(0,h.jsxs)(c,{variant:`destructive`,disabled:A,onClick:ye,children:[(0,h.jsx)(o,{className:`mr-2 h-4 w-4`}),A?`Deleting...`:`Delete Context`]})]})]}),w===`location`&&(0,h.jsxs)(`form`,{onSubmit:ve,className:`space-y-4 rounded-lg border p-4`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`ctx-url`,className:`text-sm font-medium`,children:`URL`}),(0,h.jsx)(f,{id:`ctx-url`,value:R,onChange:z,fixedWorkspaceName:E.workspaceName,pickerTitle:`Switch context to…`,placeholder:`workspace://path`,onPickTarget:(e,t)=>{z(`${t.workspaceName}://${e.replace(/^\/+/,``)}`),he(t.treeName)}}),H&&(0,h.jsxs)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:[`Destination tree: `,H]})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsxs)(`label`,{htmlFor:`ctx-base-url`,className:`text-sm font-medium`,children:[`Base URL `,(0,h.jsx)(`span`,{className:`font-normal text-muted-foreground`,children:`(optional)`})]}),(0,h.jsx)(f,{id:`ctx-base-url`,value:B,onChange:V,fixedWorkspaceName:E.workspaceName,placeholder:`workspace://base/path`,pickerTitle:`Pick a base path…`})]}),(0,h.jsx)(c,{type:`submit`,disabled:O,children:O?`Saving...`:`Save Changes`})]}),w===`shares`&&(0,h.jsxs)(`div`,{className:`space-y-4`,children:[(0,h.jsxs)(`section`,{className:`space-y-3 rounded-lg border p-4`,children:[(0,h.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`h2`,{className:`text-sm font-semibold`,children:`People with access`}),(0,h.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Only the context owner can see and change this list.`})]}),(0,h.jsxs)(c,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>Q(),disabled:G,children:[(0,h.jsx)(a,{className:s(`mr-2 h-3.5 w-3.5`,G&&`animate-spin`)}),`Refresh`]})]}),U.length===0?(0,h.jsx)(`div`,{className:`rounded-md border border-dashed p-4 text-sm text-muted-foreground`,children:`Not shared with anyone.`}):U.map(e=>(0,h.jsxs)(`div`,{className:`flex items-start justify-between gap-3 rounded-md border p-3`,children:[(0,h.jsxs)(`div`,{className:`min-w-0`,children:[(0,h.jsx)(`div`,{className:`truncate text-sm font-medium`,children:e.userEmail}),(0,h.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[g.find(t=>t.value===e.accessLevel)?.label||e.accessLevel||`unknown access`,e.sharedAt&&` - since ${new Date(e.sharedAt).toLocaleDateString()}`]})]}),(0,h.jsx)(c,{type:`button`,variant:`outline`,size:`sm`,className:`text-destructive hover:text-destructive`,disabled:X===e.userEmail,onClick:()=>xe(e),children:(0,h.jsx)(te,{className:`h-3.5 w-3.5`})})]},e.userEmail))]}),(0,h.jsxs)(`form`,{onSubmit:be,className:`space-y-4 rounded-lg border p-4`,children:[(0,h.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Grant access`}),(0,h.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`share-email`,className:`text-sm font-medium`,children:`User email`}),(0,h.jsx)(l,{id:`share-email`,type:`email`,value:q,onChange:e=>J(e.target.value),placeholder:`person@example.com`}),(0,h.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:`The user must already have an account on this server.`})]}),(0,h.jsxs)(`div`,{children:[(0,h.jsx)(`label`,{htmlFor:`share-level`,className:`text-sm font-medium`,children:`Access level`}),(0,h.jsx)(`select`,{id:`share-level`,value:Y,onChange:e=>ge(e.target.value),className:`mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring`,children:g.map(e=>(0,h.jsx)(`option`,{value:e.value,children:e.label},e.value))})]})]}),(0,h.jsx)(c,{type:`submit`,disabled:X===`grant`||!q.trim(),children:X===`grant`?`Sharing...`:`Share context`})]})]})]})})}export{_ as default};
@@ -1 +1 @@
1
- import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{l as t,u as n}from"./code-editor-BOIngd5F.js";import{r}from"./chunk-62JRHF6Z-vspAGgtK.js";import{ci as i}from"./ui-BSqPn1LJ.js";import{t as a}from"./input-BOtCd4HP.js";import{Pt as o}from"./workspace-CJ1PWQ29.js";import{p as s}from"./context-D_K4g9SQ.js";import{i as c,m as l,p as u}from"./use-menu-BVkqNEIx.js";import{ft as d,mt as f,pt as p,q as m}from"./index-DtZS4JJ4.js";import{n as h}from"./page-header-GtTr9Ff_.js";var g=e(n(),1),_=t(),v=[{id:`workspace`,label:`Workspace`,path:`workspaces`,description:`Files, search, devices and sharing for one workspace. These settings override account defaults.`,sections:f,load:async()=>(await o()).map(e=>({id:e.name,label:e.label||e.name}))},{id:`context`,label:`Context`,path:`contexts`,description:`Choose the tree and path a context opens, and who can access that view. File storage belongs to its workspace.`,sections:p,load:async()=>(await s()).map(e=>({id:e.id,label:e.name||e.id}))},{id:`agent`,label:`Agent`,path:`agents`,description:`Configure one assistant: its instructions, chat model, tools and access to Canvas.`,sections:d,load:async()=>(await m()).map(e=>({id:e.id,label:e.label||e.name}))}];function y({to:e,label:t,description:n,icon:a}){return(0,_.jsxs)(r,{to:e,className:`group flex items-start gap-3 rounded-lg border bg-card p-4 transition-colors hover:border-primary/50 hover:bg-accent/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary`,children:[(0,_.jsx)(a,{className:`mt-0.5 h-5 w-5 shrink-0 text-muted-foreground`}),(0,_.jsxs)(`span`,{className:`min-w-0 flex-1`,children:[(0,_.jsx)(`span`,{className:`block text-sm font-semibold`,children:t}),(0,_.jsx)(`span`,{className:`mt-1 block text-sm leading-relaxed text-muted-foreground`,children:n})]}),(0,_.jsx)(i,{className:`mt-0.5 h-4 w-4 shrink-0 text-muted-foreground group-hover:text-primary`})]})}function b({group:e,query:t}){let[n,r]=(0,g.useState)([]),[i,a]=(0,g.useState)(``),[o,s]=(0,g.useState)(`loading`),[c,u]=(0,g.useState)(0);(0,g.useEffect)(()=>{let t=!1;return e.load().then(e=>{t||(r(e),s(`ready`))}).catch(()=>{t||s(`error`)}),()=>{t=!0}},[e,c]);let d=e.sections.filter(n=>l(t,e.label,n.label,n.description));return d.length?(0,_.jsxs)(`section`,{id:e.id,className:`scroll-mt-6 space-y-3`,children:[(0,_.jsx)(`h2`,{className:`text-lg font-semibold`,children:e.label}),(0,_.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e.description}),(0,_.jsxs)(`label`,{className:`block max-w-md text-sm font-medium`,children:[`Choose a `,e.label.toLowerCase(),(0,_.jsxs)(`select`,{className:`mt-2 h-10 w-full rounded-md border bg-background px-3 text-sm`,value:i,disabled:o!==`ready`||!n.length,onChange:e=>a(e.target.value),children:[(0,_.jsx)(`option`,{value:``,children:o===`loading`?`Loading…`:o===`error`?`Unable to load list`:n.length?`Select ${e.label.toLowerCase()}…`:`No ${e.path} available`}),n.map(e=>(0,_.jsx)(`option`,{value:e.id,children:e.label},e.id))]})]}),o===`error`&&(0,_.jsxs)(`p`,{role:`status`,className:`text-sm text-muted-foreground`,children:[`The list could not be loaded. `,(0,_.jsx)(`button`,{type:`button`,className:`underline`,onClick:()=>{s(`loading`),u(e=>e+1)},children:`Retry`})]}),(0,_.jsxs)(`details`,{open:!!(i||t.trim()),children:[(0,_.jsx)(`summary`,{className:`cursor-pointer py-2 text-sm text-muted-foreground`,children:i?`Settings for ${n.find(e=>e.id===i)?.label||i}`:`What you can configure (${d.length})`}),(0,_.jsx)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))] gap-3`,children:d.map(t=>i?(0,_.jsx)(y,{to:`/${e.path}/${encodeURIComponent(i)}/settings/${t.id}`,...t},t.id):(0,_.jsxs)(`div`,{className:`rounded-lg border border-dashed p-4`,children:[(0,_.jsx)(`p`,{className:`text-sm font-medium`,children:t.label}),(0,_.jsx)(`p`,{className:`mt-1 text-sm text-muted-foreground`,children:t.description})]},t.id))})]})]}):null}function x(){let{state:e}=c(),[t,n]=(0,g.useState)(``),r=u.filter(t=>t.id!==`server`||e.user?.userType===`admin`).map(e=>({...e,items:e.items.filter(n=>l(t,e.label,n.label,n.description))})).filter(e=>e.items.length),i=v.some(e=>e.sections.some(n=>l(t,e.label,n.label,n.description)));return(0,_.jsxs)(`div`,{className:`mx-auto max-w-6xl space-y-8 pb-8 max-md:pb-rail-stack`,children:[(0,_.jsx)(h,{title:`Settings`,description:`Start with what you want to change. Each section explains where your changes apply.`}),(0,_.jsx)(a,{type:`search`,className:`max-w-xl`,"aria-label":`Search all settings`,placeholder:`Find a setting: offline, folders, models, sharing…`,value:t,onChange:e=>n(e.target.value)}),r.filter(e=>e.id===`device`||e.id===`account`).map(e=>(0,_.jsxs)(`section`,{className:`space-y-3`,children:[(0,_.jsx)(`h2`,{className:`text-lg font-semibold`,children:e.label}),(0,_.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e.description}),(0,_.jsx)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))] gap-3`,children:e.items.map(e=>(0,_.jsx)(y,{to:e.path,...e},e.path))})]},e.id)),v.map(e=>(0,_.jsx)(b,{group:e,query:t},e.id)),r.filter(e=>e.id===`server`||e.id===`information`).map(e=>(0,_.jsxs)(`section`,{className:`space-y-3`,children:[(0,_.jsx)(`h2`,{className:`text-lg font-semibold`,children:e.label}),(0,_.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e.description}),(0,_.jsx)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))] gap-3`,children:e.items.map(e=>(0,_.jsx)(y,{to:e.path,...e},e.path))})]},e.id)),!r.length&&!i&&(0,_.jsxs)(`p`,{role:`status`,className:`text-sm text-muted-foreground`,children:[`No settings match “`,t,`”. Try a shorter phrase or clear the search.`]})]})}export{x as default};
1
+ import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{l as t,u as n}from"./code-editor-BOIngd5F.js";import{r}from"./chunk-62JRHF6Z-vspAGgtK.js";import{li as i}from"./ui-Byg1cZDr.js";import{t as a}from"./input-BOtCd4HP.js";import{Pt as o}from"./workspace-DvxC7pw1.js";import{p as s}from"./context-mfIOB8dN.js";import{i as c,m as l,p as u}from"./use-menu-BiJ8N7sS.js";import{ft as d,mt as f,pt as p,q as m}from"./index-CiBgHujl.js";import{n as h}from"./page-header-DkOHw0g-.js";var g=e(n(),1),_=t(),v=[{id:`workspace`,label:`Workspace`,path:`workspaces`,description:`Files, search, devices and sharing for one workspace. These settings override account defaults.`,sections:f,load:async()=>(await o()).map(e=>({id:e.name,label:e.label||e.name}))},{id:`context`,label:`Context`,path:`contexts`,description:`Choose the tree and path a context opens, and who can access that view. File storage belongs to its workspace.`,sections:p,load:async()=>(await s()).map(e=>({id:e.id,label:e.name||e.id}))},{id:`agent`,label:`Agent`,path:`agents`,description:`Configure one assistant: its instructions, chat model, tools and access to Canvas.`,sections:d,load:async()=>(await m()).map(e=>({id:e.id,label:e.label||e.name}))}];function y({to:e,label:t,description:n,icon:a}){return(0,_.jsxs)(r,{to:e,className:`group flex items-start gap-3 rounded-lg border bg-card p-4 transition-colors hover:border-primary/50 hover:bg-accent/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary`,children:[(0,_.jsx)(a,{className:`mt-0.5 h-5 w-5 shrink-0 text-muted-foreground`}),(0,_.jsxs)(`span`,{className:`min-w-0 flex-1`,children:[(0,_.jsx)(`span`,{className:`block text-sm font-semibold`,children:t}),(0,_.jsx)(`span`,{className:`mt-1 block text-sm leading-relaxed text-muted-foreground`,children:n})]}),(0,_.jsx)(i,{className:`mt-0.5 h-4 w-4 shrink-0 text-muted-foreground group-hover:text-primary`})]})}function b({group:e,query:t}){let[n,r]=(0,g.useState)([]),[i,a]=(0,g.useState)(``),[o,s]=(0,g.useState)(`loading`),[c,u]=(0,g.useState)(0);(0,g.useEffect)(()=>{let t=!1;return e.load().then(e=>{t||(r(e),s(`ready`))}).catch(()=>{t||s(`error`)}),()=>{t=!0}},[e,c]);let d=e.sections.filter(n=>l(t,e.label,n.label,n.description));return d.length?(0,_.jsxs)(`section`,{id:e.id,className:`scroll-mt-6 space-y-3`,children:[(0,_.jsx)(`h2`,{className:`text-lg font-semibold`,children:e.label}),(0,_.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e.description}),(0,_.jsxs)(`label`,{className:`block max-w-md text-sm font-medium`,children:[`Choose a `,e.label.toLowerCase(),(0,_.jsxs)(`select`,{className:`mt-2 h-10 w-full rounded-md border bg-background px-3 text-sm`,value:i,disabled:o!==`ready`||!n.length,onChange:e=>a(e.target.value),children:[(0,_.jsx)(`option`,{value:``,children:o===`loading`?`Loading…`:o===`error`?`Unable to load list`:n.length?`Select ${e.label.toLowerCase()}…`:`No ${e.path} available`}),n.map(e=>(0,_.jsx)(`option`,{value:e.id,children:e.label},e.id))]})]}),o===`error`&&(0,_.jsxs)(`p`,{role:`status`,className:`text-sm text-muted-foreground`,children:[`The list could not be loaded. `,(0,_.jsx)(`button`,{type:`button`,className:`underline`,onClick:()=>{s(`loading`),u(e=>e+1)},children:`Retry`})]}),(0,_.jsxs)(`details`,{open:!!(i||t.trim()),children:[(0,_.jsx)(`summary`,{className:`cursor-pointer py-2 text-sm text-muted-foreground`,children:i?`Settings for ${n.find(e=>e.id===i)?.label||i}`:`What you can configure (${d.length})`}),(0,_.jsx)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))] gap-3`,children:d.map(t=>i?(0,_.jsx)(y,{to:`/${e.path}/${encodeURIComponent(i)}/settings/${t.id}`,...t},t.id):(0,_.jsxs)(`div`,{className:`rounded-lg border border-dashed p-4`,children:[(0,_.jsx)(`p`,{className:`text-sm font-medium`,children:t.label}),(0,_.jsx)(`p`,{className:`mt-1 text-sm text-muted-foreground`,children:t.description})]},t.id))})]})]}):null}function x(){let{state:e}=c(),[t,n]=(0,g.useState)(``),r=u.filter(t=>t.id!==`server`||e.user?.userType===`admin`).map(e=>({...e,items:e.items.filter(n=>l(t,e.label,n.label,n.description))})).filter(e=>e.items.length),i=v.some(e=>e.sections.some(n=>l(t,e.label,n.label,n.description)));return(0,_.jsxs)(`div`,{className:`mx-auto max-w-6xl space-y-8 pb-8 max-md:pb-rail-stack`,children:[(0,_.jsx)(h,{title:`Settings`,description:`Start with what you want to change. Each section explains where your changes apply.`}),(0,_.jsx)(a,{type:`search`,className:`max-w-xl`,"aria-label":`Search all settings`,placeholder:`Find a setting: offline, folders, models, sharing…`,value:t,onChange:e=>n(e.target.value)}),r.filter(e=>e.id===`device`||e.id===`account`).map(e=>(0,_.jsxs)(`section`,{className:`space-y-3`,children:[(0,_.jsx)(`h2`,{className:`text-lg font-semibold`,children:e.label}),(0,_.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e.description}),(0,_.jsx)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))] gap-3`,children:e.items.map(e=>(0,_.jsx)(y,{to:e.path,...e},e.path))})]},e.id)),v.map(e=>(0,_.jsx)(b,{group:e,query:t},e.id)),r.filter(e=>e.id===`server`||e.id===`information`).map(e=>(0,_.jsxs)(`section`,{className:`space-y-3`,children:[(0,_.jsx)(`h2`,{className:`text-lg font-semibold`,children:e.label}),(0,_.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e.description}),(0,_.jsx)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))] gap-3`,children:e.items.map(e=>(0,_.jsx)(y,{to:e.path,...e},e.path))})]},e.id)),!r.length&&!i&&(0,_.jsxs)(`p`,{role:`status`,className:`text-sm text-muted-foreground`,children:[`No settings match “`,t,`”. Try a shorter phrase or clear the search.`]})]})}export{x as default};